From 771c5de189b3e9659b1d438627992eb6e73cd2aa Mon Sep 17 00:00:00 2001 From: Josiah Lee Date: Mon, 28 Oct 2024 16:04:51 -0700 Subject: [PATCH 1/5] add some icons (#2800) --- weave-js/src/assets/icons/icon-enter-return.svg | 3 +++ .../src/assets/icons/icon-sandbox-playground.svg | 3 +++ weave-js/src/assets/icons/icon-swap.svg | 4 ++++ weave-js/src/components/Icon/Icon.tsx | 15 +++++++++++++++ weave-js/src/components/Icon/index.ts | 3 +++ weave-js/src/components/Icon/types.ts | 3 +++ 6 files changed, 31 insertions(+) create mode 100644 weave-js/src/assets/icons/icon-enter-return.svg create mode 100644 weave-js/src/assets/icons/icon-sandbox-playground.svg create mode 100644 weave-js/src/assets/icons/icon-swap.svg diff --git a/weave-js/src/assets/icons/icon-enter-return.svg b/weave-js/src/assets/icons/icon-enter-return.svg new file mode 100644 index 00000000000..ffcf6e4f0e5 --- /dev/null +++ b/weave-js/src/assets/icons/icon-enter-return.svg @@ -0,0 +1,3 @@ + + + diff --git a/weave-js/src/assets/icons/icon-sandbox-playground.svg b/weave-js/src/assets/icons/icon-sandbox-playground.svg new file mode 100644 index 00000000000..0fe4a7234f9 --- /dev/null +++ b/weave-js/src/assets/icons/icon-sandbox-playground.svg @@ -0,0 +1,3 @@ + + + diff --git a/weave-js/src/assets/icons/icon-swap.svg b/weave-js/src/assets/icons/icon-swap.svg new file mode 100644 index 00000000000..86c8353d4fd --- /dev/null +++ b/weave-js/src/assets/icons/icon-swap.svg @@ -0,0 +1,4 @@ + + + + diff --git a/weave-js/src/components/Icon/Icon.tsx b/weave-js/src/components/Icon/Icon.tsx index 55779a33788..f1d04196b1e 100644 --- a/weave-js/src/components/Icon/Icon.tsx +++ b/weave-js/src/components/Icon/Icon.tsx @@ -64,6 +64,7 @@ import {ReactComponent as ImportDragGripHorizontal} from '../../assets/icons/ico import {ReactComponent as ImportEducationAcademic} from '../../assets/icons/icon-education-academic.svg'; import {ReactComponent as ImportEmailAt} from '../../assets/icons/icon-email-at.svg'; import {ReactComponent as ImportEmailEnvelope} from '../../assets/icons/icon-email-envelope.svg'; +import {ReactComponent as ImportEnterReturn} from '../../assets/icons/icon-enter-return.svg'; import {ReactComponent as ImportExpandRight} from '../../assets/icons/icon-expand-right.svg'; import {ReactComponent as ImportExpandUncollapse} from '../../assets/icons/icon-expand-uncollapse.svg'; import {ReactComponent as ImportExportShareUpload} from '../../assets/icons/icon-export-share-upload.svg'; @@ -182,6 +183,7 @@ import {ReactComponent as ImportRowHeightSmall} from '../../assets/icons/icon-ro import {ReactComponent as ImportRowHeightXlarge} from '../../assets/icons/icon-row-height-xlarge.svg'; import {ReactComponent as ImportRun} from '../../assets/icons/icon-run.svg'; import {ReactComponent as ImportRunningRepeat} from '../../assets/icons/icon-running-repeat.svg'; +import {ReactComponent as ImportSandboxPlayground} from '../../assets/icons/icon-sandbox-playground.svg'; import {ReactComponent as ImportSave} from '../../assets/icons/icon-save.svg'; import {ReactComponent as ImportScikitLogo} from '../../assets/icons/icon-scikit-logo.svg'; import {ReactComponent as ImportSearch} from '../../assets/icons/icon-search.svg'; @@ -202,6 +204,7 @@ import {ReactComponent as ImportStar} from '../../assets/icons/icon-star.svg'; import {ReactComponent as ImportStarFilled} from '../../assets/icons/icon-star-filled.svg'; import {ReactComponent as ImportStop} from '../../assets/icons/icon-stop.svg'; import {ReactComponent as ImportStopped} from '../../assets/icons/icon-stopped.svg'; +import {ReactComponent as ImportSwap} from '../../assets/icons/icon-swap.svg'; import {ReactComponent as ImportSweepBayes} from '../../assets/icons/icon-sweep-bayes.svg'; import {ReactComponent as ImportSweepGrid} from '../../assets/icons/icon-sweep-grid.svg'; import {ReactComponent as ImportSweepRandomSearch} from '../../assets/icons/icon-sweep-random-search.svg'; @@ -455,6 +458,9 @@ export const IconEmailAt = (props: SVGIconProps) => ( export const IconEmailEnvelope = (props: SVGIconProps) => ( ); +export const IconEnterReturn = (props: SVGIconProps) => ( + +); export const IconExpandRight = (props: SVGIconProps) => ( ); @@ -809,6 +815,9 @@ export const IconRun = (props: SVGIconProps) => ( export const IconRunningRepeat = (props: SVGIconProps) => ( ); +export const IconSandboxPlayground = (props: SVGIconProps) => ( + +); export const IconSave = (props: SVGIconProps) => ( ); @@ -869,6 +878,9 @@ export const IconStop = (props: SVGIconProps) => ( export const IconStopped = (props: SVGIconProps) => ( ); +export const IconSwap = (props: SVGIconProps) => ( + +); export const IconSweepBayes = (props: SVGIconProps) => ( ); @@ -1076,6 +1088,7 @@ const ICON_NAME_TO_ICON: Record = { 'education-academic': IconEducationAcademic, 'email-at': IconEmailAt, 'email-envelope': IconEmailEnvelope, + 'enter-return': IconEnterReturn, 'expand-right': IconExpandRight, 'expand-uncollapse': IconExpandUncollapse, 'export-share-upload': IconExportShareUpload, @@ -1194,6 +1207,7 @@ const ICON_NAME_TO_ICON: Record = { 'row-height-xlarge': IconRowHeightXlarge, run: IconRun, 'running-repeat': IconRunningRepeat, + 'sandbox-playground': IconSandboxPlayground, save: IconSave, 'scikit-logo': IconScikitLogo, search: IconSearch, @@ -1214,6 +1228,7 @@ const ICON_NAME_TO_ICON: Record = { 'star-filled': IconStarFilled, stop: IconStop, stopped: IconStopped, + swap: IconSwap, 'sweep-bayes': IconSweepBayes, 'sweep-grid': IconSweepGrid, 'sweep-random-search': IconSweepRandomSearch, diff --git a/weave-js/src/components/Icon/index.ts b/weave-js/src/components/Icon/index.ts index 46908984a07..5c717f5de7f 100644 --- a/weave-js/src/components/Icon/index.ts +++ b/weave-js/src/components/Icon/index.ts @@ -64,6 +64,7 @@ export { IconEducationAcademic, IconEmailAt, IconEmailEnvelope, + IconEnterReturn, IconExpandRight, IconExpandUncollapse, IconExportShareUpload, @@ -182,6 +183,7 @@ export { IconRowHeightXlarge, IconRun, IconRunningRepeat, + IconSandboxPlayground, IconSave, IconScikitLogo, IconSearch, @@ -202,6 +204,7 @@ export { IconStarFilled, IconStop, IconStopped, + IconSwap, IconSweepBayes, IconSweepGrid, IconSweepRandomSearch, diff --git a/weave-js/src/components/Icon/types.ts b/weave-js/src/components/Icon/types.ts index c4d343bba17..9ab1adc21d1 100644 --- a/weave-js/src/components/Icon/types.ts +++ b/weave-js/src/components/Icon/types.ts @@ -63,6 +63,7 @@ export const IconNames = { EducationAcademic: 'education-academic', EmailAt: 'email-at', EmailEnvelope: 'email-envelope', + EnterReturn: 'enter-return', ExpandRight: 'expand-right', ExpandUncollapse: 'expand-uncollapse', ExportShareUpload: 'export-share-upload', @@ -181,6 +182,7 @@ export const IconNames = { RowHeightXlarge: 'row-height-xlarge', Run: 'run', RunningRepeat: 'running-repeat', + SandboxPlayground: 'sandbox-playground', Save: 'save', ScikitLogo: 'scikit-logo', Search: 'search', @@ -201,6 +203,7 @@ export const IconNames = { StarFilled: 'star-filled', Stop: 'stop', Stopped: 'stopped', + Swap: 'swap', SweepBayes: 'sweep-bayes', SweepGrid: 'sweep-grid', SweepRandomSearch: 'sweep-random-search', From 9a5d0395947e059f54213d55d7aa134e64532b38 Mon Sep 17 00:00:00 2001 From: Justin Tulk Date: Mon, 28 Oct 2024 16:39:27 -0700 Subject: [PATCH 2/5] Flipping loader color (#2805) --- weave-js/src/components/Loaders/WaveLoader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weave-js/src/components/Loaders/WaveLoader.tsx b/weave-js/src/components/Loaders/WaveLoader.tsx index c07bd291a54..7cddb0a5e75 100644 --- a/weave-js/src/components/Loaders/WaveLoader.tsx +++ b/weave-js/src/components/Loaders/WaveLoader.tsx @@ -12,7 +12,7 @@ const Dot = React.memo( ); const classes = classNames( - 'rounded-full bg-moon-650 dark:bg-moon-350 animate-wave', + 'rounded-full bg-moon-350 dark:bg-moon-650 animate-wave', { 'h-8 w-8': size === 'huge', 'h-6 w-6': size === 'small', From 035ca0fa8f5066f4de252a9da9f9765b9edfc30d Mon Sep 17 00:00:00 2001 From: Griffin Tarpenning Date: Mon, 28 Oct 2024 17:09:43 -0700 Subject: [PATCH 3/5] chore(ui): add marker and refresh icons (#2803) --- weave-js/src/assets/icons/icon-marker.svg | 5 +++++ weave-js/src/assets/icons/icon-reload-refresh.svg | 3 +++ weave-js/src/components/Icon/Icon.tsx | 10 ++++++++++ weave-js/src/components/Icon/index.ts | 2 ++ weave-js/src/components/Icon/types.ts | 2 ++ 5 files changed, 22 insertions(+) create mode 100644 weave-js/src/assets/icons/icon-marker.svg create mode 100644 weave-js/src/assets/icons/icon-reload-refresh.svg diff --git a/weave-js/src/assets/icons/icon-marker.svg b/weave-js/src/assets/icons/icon-marker.svg new file mode 100644 index 00000000000..d142990e86a --- /dev/null +++ b/weave-js/src/assets/icons/icon-marker.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/weave-js/src/assets/icons/icon-reload-refresh.svg b/weave-js/src/assets/icons/icon-reload-refresh.svg new file mode 100644 index 00000000000..f6c97a6a71c --- /dev/null +++ b/weave-js/src/assets/icons/icon-reload-refresh.svg @@ -0,0 +1,3 @@ + + + diff --git a/weave-js/src/components/Icon/Icon.tsx b/weave-js/src/components/Icon/Icon.tsx index f1d04196b1e..f8cec63146b 100644 --- a/weave-js/src/components/Icon/Icon.tsx +++ b/weave-js/src/components/Icon/Icon.tsx @@ -126,6 +126,7 @@ import {ReactComponent as ImportLogoColab} from '../../assets/icons/icon-logo-co import {ReactComponent as ImportMagicWandStar} from '../../assets/icons/icon-magic-wand-star.svg'; import {ReactComponent as ImportMagicWandStick} from '../../assets/icons/icon-magic-wand-stick.svg'; import {ReactComponent as ImportMarkdown} from '../../assets/icons/icon-markdown.svg'; +import {ReactComponent as ImportMarker} from '../../assets/icons/icon-marker.svg'; import {ReactComponent as ImportMenu} from '../../assets/icons/icon-menu.svg'; import {ReactComponent as ImportMicrophoneAudio} from '../../assets/icons/icon-microphone-audio.svg'; import {ReactComponent as ImportMillerColumns} from '../../assets/icons/icon-miller-columns.svg'; @@ -171,6 +172,7 @@ import {ReactComponent as ImportRedditSocial} from '../../assets/icons/icon-redd import {ReactComponent as ImportRedo} from '../../assets/icons/icon-redo.svg'; import {ReactComponent as ImportRegex} from '../../assets/icons/icon-regex.svg'; import {ReactComponent as ImportRegistries} from '../../assets/icons/icon-registries.svg'; +import {ReactComponent as ImportReloadRefresh} from '../../assets/icons/icon-reload-refresh.svg'; import {ReactComponent as ImportRemove} from '../../assets/icons/icon-remove.svg'; import {ReactComponent as ImportRemoveAlt} from '../../assets/icons/icon-remove-alt.svg'; import {ReactComponent as ImportReport} from '../../assets/icons/icon-report.svg'; @@ -644,6 +646,9 @@ export const IconMagicWandStick = (props: SVGIconProps) => ( export const IconMarkdown = (props: SVGIconProps) => ( ); +export const IconMarker = (props: SVGIconProps) => ( + +); export const IconMenu = (props: SVGIconProps) => ( ); @@ -779,6 +784,9 @@ export const IconRegex = (props: SVGIconProps) => ( export const IconRegistries = (props: SVGIconProps) => ( ); +export const IconReloadRefresh = (props: SVGIconProps) => ( + +); export const IconRemove = (props: SVGIconProps) => ( ); @@ -1150,6 +1158,7 @@ const ICON_NAME_TO_ICON: Record = { 'magic-wand-star': IconMagicWandStar, 'magic-wand-stick': IconMagicWandStick, markdown: IconMarkdown, + marker: IconMarker, menu: IconMenu, 'microphone-audio': IconMicrophoneAudio, 'miller-columns': IconMillerColumns, @@ -1195,6 +1204,7 @@ const ICON_NAME_TO_ICON: Record = { redo: IconRedo, regex: IconRegex, registries: IconRegistries, + 'reload-refresh': IconReloadRefresh, remove: IconRemove, 'remove-alt': IconRemoveAlt, report: IconReport, diff --git a/weave-js/src/components/Icon/index.ts b/weave-js/src/components/Icon/index.ts index 5c717f5de7f..fa9e1c10454 100644 --- a/weave-js/src/components/Icon/index.ts +++ b/weave-js/src/components/Icon/index.ts @@ -126,6 +126,7 @@ export { IconMagicWandStar, IconMagicWandStick, IconMarkdown, + IconMarker, IconMenu, IconMicrophoneAudio, IconMillerColumns, @@ -171,6 +172,7 @@ export { IconRedo, IconRegex, IconRegistries, + IconReloadRefresh, IconRemove, IconRemoveAlt, IconReport, diff --git a/weave-js/src/components/Icon/types.ts b/weave-js/src/components/Icon/types.ts index 9ab1adc21d1..e536e365157 100644 --- a/weave-js/src/components/Icon/types.ts +++ b/weave-js/src/components/Icon/types.ts @@ -125,6 +125,7 @@ export const IconNames = { MagicWandStar: 'magic-wand-star', MagicWandStick: 'magic-wand-stick', Markdown: 'markdown', + Marker: 'marker', Menu: 'menu', MicrophoneAudio: 'microphone-audio', MillerColumns: 'miller-columns', @@ -170,6 +171,7 @@ export const IconNames = { Redo: 'redo', Regex: 'regex', Registries: 'registries', + ReloadRefresh: 'reload-refresh', Remove: 'remove', RemoveAlt: 'remove-alt', Report: 'report', From 984197453df024bdcc0273f084287b24cfae2228 Mon Sep 17 00:00:00 2001 From: Andrew Truong Date: Mon, 28 Oct 2024 22:58:34 -0400 Subject: [PATCH 4/5] feat(weave): Add initial JS SDK (#2704) --- .github/workflows/weave-node-tests.yaml | 32 + sdks/node/.gitignore | 2 + sdks/node/.prettierrc | 16 + sdks/node/CantinaBand3.wav | Bin 0 -> 132344 bytes sdks/node/Makefile | 7 + sdks/node/README-DEV.md | 23 + sdks/node/README.md | 202 + sdks/node/examples/classesWithOps.ts | 28 + sdks/node/examples/createDataset.ts | 53 + sdks/node/examples/evaluate.ts | 33 + .../examples/evaluateWithColumnMapping.ts | 39 + sdks/node/examples/evaluateWithImages.ts | 71 + sdks/node/examples/imageGeneration.ts | 19 + sdks/node/examples/loggingVariousDataTypes.ts | 53 + sdks/node/examples/quickstart.ts | 28 + sdks/node/examples/quickstartEvaluate.ts | 67 + sdks/node/examples/streamFunctionCalls.ts | 63 + sdks/node/examples/tsconfig.examples.json | 16 + sdks/node/logs.png | Bin 0 -> 24701 bytes sdks/node/package-lock.json | 4312 +++++++++++++++++ sdks/node/package.json | 88 + sdks/node/pnpm-lock.yaml | 3756 ++++++++++++++ sdks/node/scripts/loadTest.ts | 39 + sdks/node/scripts/testApi.ts | 72 + sdks/node/src/__tests__/clientApi.test.ts | 54 + sdks/node/src/__tests__/clientMock.ts | 19 + sdks/node/src/__tests__/digest.test.ts | 108 + sdks/node/src/__tests__/evaluation.test.ts | 171 + .../integrations/integrationOpenAI.test.ts | 303 ++ .../__tests__/integrations/openai2.test.ts | 241 + .../__tests__/integrations/openaiMock.test.ts | 157 + sdks/node/src/__tests__/live/dataset.test.ts | 39 + sdks/node/src/__tests__/live/fn.test.ts | 42 + sdks/node/src/__tests__/live/publish.test.ts | 83 + sdks/node/src/__tests__/live/table.test.ts | 43 + .../src/__tests__/live/weaveObject.test.ts | 48 + sdks/node/src/__tests__/login.test.ts | 70 + sdks/node/src/__tests__/media.test.ts | 21 + sdks/node/src/__tests__/opFlow.test.ts | 354 ++ sdks/node/src/__tests__/openaiMock.ts | 217 + .../__tests__/util/concurrentLimit.test.ts | 30 + sdks/node/src/__tests__/util/netrc.test.ts | 99 + sdks/node/src/__tests__/util/retry.test.ts | 65 + .../node/src/__tests__/wandb/settings.test.ts | 81 + .../__tests__/wandb/wandbServerApi.test.ts | 47 + sdks/node/src/__tests__/weaveClient.test.ts | 230 + sdks/node/src/clientApi.ts | 159 + sdks/node/src/constants.ts | 2 + sdks/node/src/dataset.ts | 92 + sdks/node/src/digest.ts | 49 + sdks/node/src/evaluation.ts | 345 ++ sdks/node/src/fn.ts | 36 + sdks/node/src/generated/traceServerApi.ts | 1530 ++++++ sdks/node/src/inMemoryTraceServer.ts | 176 + sdks/node/src/index.ts | 14 + sdks/node/src/integrations/checkOpenai.ts | 94 + sdks/node/src/integrations/index.ts | 1 + sdks/node/src/integrations/openai.ts | 238 + sdks/node/src/media.ts | 79 + sdks/node/src/op.ts | 202 + sdks/node/src/opType.ts | 68 + sdks/node/src/refs.ts | 2 + sdks/node/src/settings.ts | 14 + sdks/node/src/summary.ts | 69 + sdks/node/src/table.ts | 46 + sdks/node/src/tsconfig.src.json | 8 + sdks/node/src/types.ts | 4 + sdks/node/src/urls.ts | 23 + sdks/node/src/utils/concurrencyLimit.ts | 49 + sdks/node/src/utils/netrc.ts | 86 + sdks/node/src/utils/retry.ts | 59 + sdks/node/src/utils/userAgent.ts | 24 + sdks/node/src/utils/warnOnce.ts | 8 + sdks/node/src/wandb/settings.ts | 46 + sdks/node/src/wandb/wandbServerApi.ts | 73 + sdks/node/src/weaveClient.ts | 781 +++ sdks/node/src/weaveObject.ts | 101 + sdks/node/tsconfig.json | 24 + sdks/node/weave.openapi.json | 3369 +++++++++++++ 79 files changed, 19412 insertions(+) create mode 100644 .github/workflows/weave-node-tests.yaml create mode 100644 sdks/node/.gitignore create mode 100644 sdks/node/.prettierrc create mode 100644 sdks/node/CantinaBand3.wav create mode 100644 sdks/node/Makefile create mode 100644 sdks/node/README-DEV.md create mode 100644 sdks/node/README.md create mode 100644 sdks/node/examples/classesWithOps.ts create mode 100644 sdks/node/examples/createDataset.ts create mode 100644 sdks/node/examples/evaluate.ts create mode 100644 sdks/node/examples/evaluateWithColumnMapping.ts create mode 100644 sdks/node/examples/evaluateWithImages.ts create mode 100644 sdks/node/examples/imageGeneration.ts create mode 100644 sdks/node/examples/loggingVariousDataTypes.ts create mode 100644 sdks/node/examples/quickstart.ts create mode 100644 sdks/node/examples/quickstartEvaluate.ts create mode 100644 sdks/node/examples/streamFunctionCalls.ts create mode 100644 sdks/node/examples/tsconfig.examples.json create mode 100644 sdks/node/logs.png create mode 100644 sdks/node/package-lock.json create mode 100644 sdks/node/package.json create mode 100644 sdks/node/pnpm-lock.yaml create mode 100644 sdks/node/scripts/loadTest.ts create mode 100644 sdks/node/scripts/testApi.ts create mode 100644 sdks/node/src/__tests__/clientApi.test.ts create mode 100644 sdks/node/src/__tests__/clientMock.ts create mode 100644 sdks/node/src/__tests__/digest.test.ts create mode 100644 sdks/node/src/__tests__/evaluation.test.ts create mode 100644 sdks/node/src/__tests__/integrations/integrationOpenAI.test.ts create mode 100644 sdks/node/src/__tests__/integrations/openai2.test.ts create mode 100644 sdks/node/src/__tests__/integrations/openaiMock.test.ts create mode 100644 sdks/node/src/__tests__/live/dataset.test.ts create mode 100644 sdks/node/src/__tests__/live/fn.test.ts create mode 100644 sdks/node/src/__tests__/live/publish.test.ts create mode 100644 sdks/node/src/__tests__/live/table.test.ts create mode 100644 sdks/node/src/__tests__/live/weaveObject.test.ts create mode 100644 sdks/node/src/__tests__/login.test.ts create mode 100644 sdks/node/src/__tests__/media.test.ts create mode 100644 sdks/node/src/__tests__/opFlow.test.ts create mode 100644 sdks/node/src/__tests__/openaiMock.ts create mode 100644 sdks/node/src/__tests__/util/concurrentLimit.test.ts create mode 100644 sdks/node/src/__tests__/util/netrc.test.ts create mode 100644 sdks/node/src/__tests__/util/retry.test.ts create mode 100644 sdks/node/src/__tests__/wandb/settings.test.ts create mode 100644 sdks/node/src/__tests__/wandb/wandbServerApi.test.ts create mode 100644 sdks/node/src/__tests__/weaveClient.test.ts create mode 100644 sdks/node/src/clientApi.ts create mode 100644 sdks/node/src/constants.ts create mode 100644 sdks/node/src/dataset.ts create mode 100644 sdks/node/src/digest.ts create mode 100644 sdks/node/src/evaluation.ts create mode 100644 sdks/node/src/fn.ts create mode 100644 sdks/node/src/generated/traceServerApi.ts create mode 100644 sdks/node/src/inMemoryTraceServer.ts create mode 100644 sdks/node/src/index.ts create mode 100644 sdks/node/src/integrations/checkOpenai.ts create mode 100644 sdks/node/src/integrations/index.ts create mode 100644 sdks/node/src/integrations/openai.ts create mode 100644 sdks/node/src/media.ts create mode 100644 sdks/node/src/op.ts create mode 100644 sdks/node/src/opType.ts create mode 100644 sdks/node/src/refs.ts create mode 100644 sdks/node/src/settings.ts create mode 100644 sdks/node/src/summary.ts create mode 100644 sdks/node/src/table.ts create mode 100644 sdks/node/src/tsconfig.src.json create mode 100644 sdks/node/src/types.ts create mode 100644 sdks/node/src/urls.ts create mode 100644 sdks/node/src/utils/concurrencyLimit.ts create mode 100644 sdks/node/src/utils/netrc.ts create mode 100644 sdks/node/src/utils/retry.ts create mode 100644 sdks/node/src/utils/userAgent.ts create mode 100644 sdks/node/src/utils/warnOnce.ts create mode 100644 sdks/node/src/wandb/settings.ts create mode 100644 sdks/node/src/wandb/wandbServerApi.ts create mode 100644 sdks/node/src/weaveClient.ts create mode 100644 sdks/node/src/weaveObject.ts create mode 100644 sdks/node/tsconfig.json create mode 100644 sdks/node/weave.openapi.json diff --git a/.github/workflows/weave-node-tests.yaml b/.github/workflows/weave-node-tests.yaml new file mode 100644 index 00000000000..ad401503879 --- /dev/null +++ b/.github/workflows/weave-node-tests.yaml @@ -0,0 +1,32 @@ +name: Node.js Tests + +on: + push: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20] + steps: + - uses: actions/checkout@v4 + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + run_install: false + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + cache-dependency-path: sdks/node/pnpm-lock.yaml + - name: Install dependencies + run: pnpm install + working-directory: sdks/node + - name: Run tests + run: pnpm test + working-directory: sdks/node + env: + WANDB_API_KEY: ${{ secrets.WANDB_API_KEY }} diff --git a/sdks/node/.gitignore b/sdks/node/.gitignore new file mode 100644 index 00000000000..3091757a3b2 --- /dev/null +++ b/sdks/node/.gitignore @@ -0,0 +1,2 @@ +node_modules +coverage \ No newline at end of file diff --git a/sdks/node/.prettierrc b/sdks/node/.prettierrc new file mode 100644 index 00000000000..38e0c860df1 --- /dev/null +++ b/sdks/node/.prettierrc @@ -0,0 +1,16 @@ +{ + "trailingComma": "es5", + "singleQuote": true, + "bracketSpacing": false, + "bracketSameLine": true, + "tabWidth": 2, + "arrowParens": "avoid", + "overrides": [ + { + "files": ["*.ts", "*.tsx"], + "options": { + "parser": "typescript" + } + } + ] +} diff --git a/sdks/node/CantinaBand3.wav b/sdks/node/CantinaBand3.wav new file mode 100644 index 0000000000000000000000000000000000000000..41f020438468229763ec4a2321325e5916e09106 GIT binary patch literal 132344 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 literal 0 HcmV?d00001 diff --git a/sdks/node/Makefile b/sdks/node/Makefile new file mode 100644 index 00000000000..a2ab6172b5c --- /dev/null +++ b/sdks/node/Makefile @@ -0,0 +1,7 @@ +install-pnpm: + curl -fsSL https://get.pnpm.io/install.sh | sh - + +bootstrap: install-pnpm + pnpm env use --global 20 + pnpm install + pnpm run generate-api diff --git a/sdks/node/README-DEV.md b/sdks/node/README-DEV.md new file mode 100644 index 00000000000..0a044dc483e --- /dev/null +++ b/sdks/node/README-DEV.md @@ -0,0 +1,23 @@ +# Working notes + +1. Setup your env + + ```sh + make boostrap + + pnpm install + pnpm link --global + pnpm link --global weave + ``` + +2. Run tests + + ```sh + pnpm test + ``` + +3. Format + + ```sh + pnpm format + ``` diff --git a/sdks/node/README.md b/sdks/node/README.md new file mode 100644 index 00000000000..c23dc752cd5 --- /dev/null +++ b/sdks/node/README.md @@ -0,0 +1,202 @@ +# Weave (Alpha) + +Weave is a library for tracing and monitoring AI applications. + +This is an Alpha release, APIs are extremely subject to change. + +## Installation + +You can install Weave via npm: + +```bash +npm install weave +``` + +Ensure you have a wandb API key in ~/.netrc. + +Like + +``` +machine api.wandb.ai + login user + password +``` + +Get your wandb API key from [here](https://wandb.ai/authorize). + +## Quickstart + +Put this in a file called `predict.mjs`: + +```javascript +import { OpenAI } from "openai"; +import { init, op, wrapOpenAI } from "weave"; + +const openai = wrapOpenAI(new OpenAI()); + +async function extractDinos(input) { + const response = await openai.chat.completions.create({ + model: "gpt-4o", + messages: [ + { + role: "user", + content: `In JSON format extract a list of 'dinosaurs', with their 'name', their 'common_name', and whether its 'diet' is a herbivore or carnivore: ${input}`, + }, + ], + }); + return response.choices[0].message.content; +} +const extractDinosOp = op(extractDinos); + +async function main() { + await init("weave-quickstart"); + const result = await extractDinosOp( + "I watched as a Tyrannosaurus rex (T. rex) chased after a Triceratops (Trike), both carnivore and herbivore locked in an ancient dance. Meanwhile, a gentle giant Brachiosaurus (Brachi) calmly munched on treetops, blissfully unaware of the chaos below." + ); + console.log(result); +} + +main(); +``` + +and then run + +``` +node predict.mjs +``` + +## Usage + +### Initializing a Project + +Before you can start tracing operations, you need to initialize a project. This sets up the necessary environment for trace collection. + +```javascript +import { init } from "weave"; + +// Initialize your project with a unique project name +init("my-awesome-ai-project"); +``` + +### Tracing Operations + +You can trace specific operations using the `op` function. This function wraps your existing functions and tracks their execution. + +```javascript +import { op } from "weave"; + +// Define a function you want to trace +async function myFunction(arg1, arg2) { + // Your function logic + return arg1 + arg2; +} + +// Wrap the function with op to enable tracing +const tracedFunction = op(myFunction, "myFunction"); + +// Call the traced function +tracedFunction(5, 10); +``` + +### OpenAI Integration + +Weave provides an integration with OpenAI, allowing you to trace API calls made to OpenAI's services seamlessly. + +```javascript +import { wrapOpenAI } from "weave/integrations/openai"; + +// Create a patched instance of OpenAI +const openai = wrapOpenAI(); + +// Use the OpenAI instance as usual +openai.chat.completions.create({ + model: "text-davinci-003", + prompt: 'Translate the following English text to French: "Hello, world!"', + max_tokens: 60, +}); + +// Weave tracks images too! +openai.images.generate({ + prompt: "A cute baby sea otter", + n: 3, + size: "256x256", + response_format: "b64_json", +}); +``` + +### Evaluations + +```typescript +import { init, op, Dataset, Evaluation } from "weave"; + +async function main() { + await init("weavejsdev-eval6"); + const ds = new Dataset({ + id: "My Dataset", + description: "This is a dataset", + rows: [ + { name: "Alice", age: 25 }, + { name: "Bob", age: 30 }, + { name: "Charlie", age: 34 }, + ], + }); + const evaluation = new Evaluation({ + dataset: ds, + scorers: [ + op( + (modelOutput: any, datasetItem: any) => modelOutput == datasetItem.age, + { name: "isEqual" } + ), + ], + }); + + const model = op(async function myModel(input) { + return input.age; + }); + + const results = await evaluation.evaluate({ model }); + console.log(JSON.stringify(results, null, 2)); +} + +main(); +``` + +## Configuration + +Weave reads API keys from the `.netrc` file located in your home directory. Ensure you have the required API keys configured for seamless integration and tracking. + +``` +machine api.wandb.ai + login user + password +``` + +Get your wandb API key from [here](https://wandb.ai/authorize). + +## License + +This project is licensed under the Apaache2 License - see the [LICENSE](../LICENSE) file for details. + +### Roadmap / TODO + +- [x] Return token counts +- [x] Summary merging +- [x] Image support +- [x] Decide how to handle args in js, since they're not named +- [x] Make sure LLM streaming is handled +- [x] Op versioning / code capture +- [ ] Retry logic +- [ ] Handle other wandb backends (managed / self-hosted) +- [ ] Include system information in call attributes including package version. +- [x] Objects / Datasets / Models / Evaluations +- [ ] Ref tracking +- [ ] More integrations + +## Known Issues + +- [ ] openai choices > 1 and "tools" not handled (function works though) +- [ ] we always inject params.stream_options.include_usage in openai request, need to not send back usage chunk if user didn't ask for it. +- [ ] handle all openai.images.generate formats, and images in inputs. +- [ ] openai.images.generate token counts. +- [ ] if a streaming op with streamReducer returns its iterator instead of iterating within the op, summary rollups won't work. +- [ ] Decide what's in public interface (WeaveClient) diff --git a/sdks/node/examples/classesWithOps.ts b/sdks/node/examples/classesWithOps.ts new file mode 100644 index 00000000000..c0a1ee0e6b5 --- /dev/null +++ b/sdks/node/examples/classesWithOps.ts @@ -0,0 +1,28 @@ +import OpenAI from 'openai'; +import * as weave from 'weave'; + +class ExampleModel { + private oaiClient: OpenAI; + + constructor() { + this.oaiClient = weave.wrapOpenAI(new OpenAI()); + this.invoke = weave.op(this, this.invoke); + } + + async invoke(input: string) { + const response = await this.oaiClient.chat.completions.create({ + model: 'gpt-4o', + messages: [{role: 'user', content: input}], + }); + return response.choices[0].message.content; + } +} + +async function main() { + await weave.init('examples'); + + const model = new ExampleModel(); + await model.invoke('Tell me a joke'); +} + +main(); diff --git a/sdks/node/examples/createDataset.ts b/sdks/node/examples/createDataset.ts new file mode 100644 index 00000000000..e7033c7d800 --- /dev/null +++ b/sdks/node/examples/createDataset.ts @@ -0,0 +1,53 @@ +import fs from 'fs'; +import 'source-map-support/register'; +import * as weave from 'weave'; + +const sentences = [ + 'There are many fruits that were found on the recently discovered planet Goocrux. There are neoskizzles that grow there, which are purple and taste like candy.', + 'Pounits are a bright green color and are more savory than sweet.', + 'Finally, there are fruits called glowls, which have a very sour and bitter taste which is acidic and caustic, and a pale orange tinge to them.', +]; +const labels = [ + {fruit: 'neoskizzles', color: 'purple', flavor: 'candy'}, + {fruit: 'pounits', color: 'bright green', flavor: 'savory'}, + {fruit: 'glowls', color: 'pale orange', flavor: 'sour and bitter'}, +]; +const logsPng = fs.readFileSync('logs.png'); +const audioClip = fs.readFileSync('CantinaBand3.wav'); +const examples = [ + { + id: '0', + sentence: sentences[0], + target: labels[0], + image: weave.weaveImage({data: logsPng, imageType: 'png'}), + audio: weave.weaveAudio({data: audioClip, audioType: 'wav'}), + }, + { + id: '1', + sentence: sentences[1], + target: labels[1], + image: weave.weaveImage({data: logsPng, imageType: 'png'}), + audio: weave.weaveAudio({data: audioClip, audioType: 'wav'}), + }, + { + id: '2', + sentence: sentences[2], + target: labels[2], + image: weave.weaveImage({data: logsPng, imageType: 'png'}), + audio: weave.weaveAudio({data: audioClip, audioType: 'wav'}), + }, +]; + +async function main() { + await weave.init('examples'); + const ds = new weave.Dataset({ + id: 'Fruit Dataset', + rows: examples, + }); + + ds.save(); + const ref = await ds.__savedRef; + console.log(ref); +} + +main(); diff --git a/sdks/node/examples/evaluate.ts b/sdks/node/examples/evaluate.ts new file mode 100644 index 00000000000..da88e506e57 --- /dev/null +++ b/sdks/node/examples/evaluate.ts @@ -0,0 +1,33 @@ +import 'source-map-support/register'; +import * as weave from 'weave'; + +async function main() { + await weave.init('examples'); + + const ds = new weave.Dataset({ + id: 'My Dataset', + description: 'This is a dataset', + rows: [ + {name: 'Alice', age: 25}, + {name: 'Bob', age: 30}, + {name: 'Charlie', age: 34}, + ], + }); + const evaluation = new weave.Evaluation({ + dataset: ds, + scorers: [ + weave.op(({modelOutput, datasetItem}) => modelOutput == datasetItem.age, { + name: 'isEqual', + }), + ], + }); + + const model = weave.op(async function myModel({datasetRow}) { + return datasetRow.age >= 30; + }); + + const results = await evaluation.evaluate({model}); + console.log('Evaluation results:', JSON.stringify(results, null, 2)); +} + +main(); diff --git a/sdks/node/examples/evaluateWithColumnMapping.ts b/sdks/node/examples/evaluateWithColumnMapping.ts new file mode 100644 index 00000000000..44b7f1bf14e --- /dev/null +++ b/sdks/node/examples/evaluateWithColumnMapping.ts @@ -0,0 +1,39 @@ +import 'source-map-support/register'; +import * as weave from 'weave'; + +async function main() { + await weave.init('examples'); + + const ds = new weave.Dataset({ + id: 'My Dataset', + description: 'This is a dataset', + rows: [ + {firstName: 'Alice', yearsOld: 25}, + {firstName: 'Bob', yearsOld: 30}, + {firstName: 'Charlie', yearsOld: 34}, + ], + }); + const evaluation = new weave.Evaluation({ + dataset: ds, + scorers: [ + weave.op(({modelOutput, datasetItem}) => modelOutput == datasetItem.age, { + name: 'isEqual', + }), + ], + // Specify a column mapping to map the model inputs to dataset columns. + // The order is always "model input": "dataset column". + columnMapping: { + name: 'firstName', + age: 'yearsOld', + }, + }); + + const model = weave.op(async function myModel({datasetRow}) { + return datasetRow.age >= 30; + }); + + const results = await evaluation.evaluate({model}); + console.log('Evaluation results:', JSON.stringify(results, null, 2)); +} + +main(); diff --git a/sdks/node/examples/evaluateWithImages.ts b/sdks/node/examples/evaluateWithImages.ts new file mode 100644 index 00000000000..dd12f0afffd --- /dev/null +++ b/sdks/node/examples/evaluateWithImages.ts @@ -0,0 +1,71 @@ +import {OpenAI} from 'openai'; +import 'source-map-support/register'; +import * as weave from 'weave'; + +const sentences = [ + 'There are many fruits that were found on the recently discovered planet Goocrux. There are neoskizzles that grow there, which are purple and taste like candy.', + 'Pounits are a bright green color and are more savory than sweet.', + 'Finally, there are fruits called glowls, which have a very sour and bitter taste which is acidic and caustic, and a pale orange tinge to them.', + 'There are many fruits that were found on the recently discovered planet Goocrux. There are neoskizzles that grow there, which are purple and taste like candy.', +]; +const labels = [ + {fruit: 'neoskizzles', color: 'purple', flavor: 'candy'}, + {fruit: 'pounits', color: 'bright green', flavor: 'savory'}, + {fruit: 'glowls', color: 'pale orange', flavor: 'sour and bitter'}, +]; +const examples = [ + {id: '0', sentence: sentences[0], target: labels[0]}, + {id: '1', sentence: sentences[1], target: labels[1]}, + {id: '2', sentence: sentences[2], target: labels[2]}, +]; + +const openaiClient = weave.wrapOpenAI(new OpenAI()); + +const model = weave.op(async function myModel({datasetRow}) { + const prompt = `Extract fields ("fruit": , "color": , "flavor") from the following text, as json: ${datasetRow.sentence}`; + const response = await openaiClient.chat.completions.create({ + model: 'gpt-3.5-turbo', + messages: [{role: 'user', content: prompt}], + response_format: {type: 'json_object'}, + }); + const result = response.choices[0].message.content; + if (result == null) { + throw new Error('No response from model'); + } + if (datasetRow.id == '3') { + throw new Error('This is an error'); + } + return JSON.parse(result); +}); + +async function main() { + await weave.init('examples'); + const ds = new weave.Dataset({ + id: 'Fruit Dataset', + rows: examples, + }); + const evaluation = new weave.Evaluation({ + dataset: ds, + scorers: [ + weave.op(function fruitEqual({modelOutput, datasetRow}) { + return { + correct: modelOutput.fruit == datasetRow.target.fruit, + }; + }), + weave.op(async function genImage({modelOutput, datasetRow}) { + const result = await openaiClient.images.generate({ + prompt: `A fruit that's ${modelOutput.color} and ${modelOutput.flavor}`, + n: 1, + size: '256x256', + response_format: 'b64_json', + }); + return result.data[0]; + }), + ], + }); + + const results = await evaluation.evaluate({model}); + console.log(JSON.stringify(results, null, 2)); +} + +main(); diff --git a/sdks/node/examples/imageGeneration.ts b/sdks/node/examples/imageGeneration.ts new file mode 100644 index 00000000000..b858dfaf2a5 --- /dev/null +++ b/sdks/node/examples/imageGeneration.ts @@ -0,0 +1,19 @@ +import OpenAI from 'openai'; +import * as weave from 'weave'; + +async function main() { + const client = await weave.init('examples'); + const openai = weave.wrapOpenAI(new OpenAI()); + + // Generate an image + const result = await openai.images.generate({ + prompt: 'A cute baby sea otter', + n: 3, + size: '256x256', + response_format: 'b64_json', + }); + + console.log('Generated image result:', result); +} + +main(); diff --git a/sdks/node/examples/loggingVariousDataTypes.ts b/sdks/node/examples/loggingVariousDataTypes.ts new file mode 100644 index 00000000000..391c209c5cf --- /dev/null +++ b/sdks/node/examples/loggingVariousDataTypes.ts @@ -0,0 +1,53 @@ +import fs from 'fs'; +import * as weave from 'weave'; + +const primitiveOp = weave.op(async function primitive(input: string) { + return `Hi ${input}!`; +}); + +const jsonOp = weave.op(async function json(name: string, age: number) { + return {name, age}; +}); + +const imageOp = weave.op(async function image() { + return weave.weaveImage({ + data: fs.readFileSync('logs.png'), + imageType: 'png', + }); +}); + +const audioOp = weave.op(async function audio() { + return weave.weaveAudio({ + data: fs.readFileSync('CantinaBand3.wav'), + audioType: 'wav', + }); +}); + +const datasetOp = weave.op(async function dataset() { + return new weave.Dataset({ + id: 'my-dataset', + rows: [ + {name: 'Alice', age: 10}, + {name: 'Bob', age: 20}, + {name: 'Charlie', age: 30}, + ], + }); +}); + +async function main() { + await weave.init('examples'); + + const primitivePromise = primitiveOp('world'); + const jsonPromise = jsonOp('Alice', 10); + const imagePromise = imageOp(); + const audioPromise = audioOp(); + const datasetPromise = datasetOp(); + + console.log('Primitive Result:', await primitivePromise); + console.log('JSON Result:', await jsonPromise); + console.log('Image Result:', await imagePromise); + console.log('Audio Result:', await audioPromise); + console.log('Dataset Result:', await datasetPromise); +} + +main(); diff --git a/sdks/node/examples/quickstart.ts b/sdks/node/examples/quickstart.ts new file mode 100644 index 00000000000..500efba3238 --- /dev/null +++ b/sdks/node/examples/quickstart.ts @@ -0,0 +1,28 @@ +import OpenAI from 'openai'; +import * as weave from 'weave'; + +const openai = weave.wrapOpenAI(new OpenAI()); + +async function extractDinos(input: string) { + const response = await openai.chat.completions.create({ + model: 'gpt-4o', + messages: [ + { + role: 'user', + content: `In JSON format extract a list of 'dinosaurs', with their 'name', their 'common_name', and whether its 'diet' is a herbivore or carnivore: ${input}`, + }, + ], + }); + return response.choices[0].message.content; +} +const extractDinosOp = weave.op(extractDinos); + +async function main() { + await weave.init('examples'); + const result = await extractDinosOp( + 'I watched as a Tyrannosaurus rex (T. rex) chased after a Triceratops (Trike), both carnivore and herbivore locked in an ancient dance. Meanwhile, a gentle giant Brachiosaurus (Brachi) calmly munched on treetops, blissfully unaware of the chaos below.' + ); + console.log(result); +} + +main(); diff --git a/sdks/node/examples/quickstartEvaluate.ts b/sdks/node/examples/quickstartEvaluate.ts new file mode 100644 index 00000000000..241cd29a28a --- /dev/null +++ b/sdks/node/examples/quickstartEvaluate.ts @@ -0,0 +1,67 @@ +import {OpenAI} from 'openai'; +import 'source-map-support/register'; +import * as weave from 'weave'; + +const sentences = [ + 'There are many fruits that were found on the recently discovered planet Goocrux. There are neoskizzles that grow there, which are purple and taste like candy.', + 'Pounits are a bright green color and are more savory than sweet.', + 'Finally, there are fruits called glowls, which have a very sour and bitter taste which is acidic and caustic, and a pale orange tinge to them.', +]; +const labels = [ + {fruit: 'neoskizzles', color: 'purple', flavor: 'candy'}, + {fruit: 'pounits', color: 'bright green', flavor: 'savory'}, + {fruit: 'glowls', color: 'pale orange', flavor: 'sour and bitter'}, +]; +const examples = [ + {id: '0', sentence: sentences[0], target: labels[0]}, + {id: '1', sentence: sentences[1], target: labels[1]}, + {id: '2', sentence: sentences[2], target: labels[2]}, + {id: '3', sentence: sentences[0], target: labels[0]}, + {id: '4', sentence: sentences[1], target: labels[1]}, + {id: '5', sentence: sentences[2], target: labels[2]}, + {id: '6', sentence: sentences[0], target: labels[0]}, + {id: '7', sentence: sentences[1], target: labels[1]}, + {id: '8', sentence: sentences[2], target: labels[2]}, + {id: '9', sentence: sentences[0], target: labels[0]}, + {id: '10', sentence: sentences[1], target: labels[1]}, + {id: '11', sentence: sentences[2], target: labels[2]}, +]; + +const openaiClient = weave.wrapOpenAI(new OpenAI()); + +const model = weave.op(async function myModel(input) { + const prompt = `Extract fields ("fruit": , "color": , "flavor") from the following text, as json: ${input.sentence}`; + const response = await openaiClient.chat.completions.create({ + model: 'gpt-3.5-turbo', + messages: [{role: 'user', content: prompt}], + response_format: {type: 'json_object'}, + }); + const result = response.choices[0].message.content; + if (result == null) { + throw new Error('No response from model'); + } + return JSON.parse(result); +}); + +async function main() { + await weave.init('examples'); + const ds = new weave.Dataset({ + id: 'Fruit Dataset', + rows: examples, + }); + const evaluation = new weave.Evaluation({ + dataset: ds, + scorers: [ + weave.op(function fruitEqual({modelOutput, datasetItem}) { + return { + correct: modelOutput.fruit == datasetItem.target.fruit, + }; + }), + ], + }); + + const results = await evaluation.evaluate({model}); + console.log(JSON.stringify(results, null, 2)); +} + +main(); diff --git a/sdks/node/examples/streamFunctionCalls.ts b/sdks/node/examples/streamFunctionCalls.ts new file mode 100644 index 00000000000..21efcd6d4c6 --- /dev/null +++ b/sdks/node/examples/streamFunctionCalls.ts @@ -0,0 +1,63 @@ +import OpenAI from 'openai'; +import * as weave from 'weave'; + +const openai = weave.wrapOpenAI(new OpenAI()); + +async function extractDinos(input: string) { + const functions = [ + { + name: 'get_current_weather', + description: 'Get the current weather for a given location.', + parameters: { + type: 'object', + properties: { + location: { + type: 'string', + description: 'The name of the city or location to get weather for.', + }, + }, + required: ['location'], + }, + }, + { + name: 'get_time_in_location', + description: 'Get the current time for a given location.', + parameters: { + type: 'object', + properties: { + location: { + type: 'string', + description: + 'The name of the city or location to get the current time for.', + }, + }, + required: ['location'], + }, + }, + ]; + const response = await openai.chat.completions.create({ + stream: true, + // stream_options: { "include_usage": true }, + model: 'gpt-4o', + functions: functions, + messages: [ + { + role: 'user', + content: `what is the weather and time in ${input}? Tell me what your'e going to do as you do it.`, + }, + ], + }); + console.log(JSON.stringify(response)); + for await (const chunk of response) { + console.log(JSON.stringify(chunk)); + } +} +const extractDinosOp = weave.op(extractDinos); + +async function main() { + await weave.init('examples'); + const result = await extractDinosOp('London'); + console.log(result); +} + +main(); diff --git a/sdks/node/examples/tsconfig.examples.json b/sdks/node/examples/tsconfig.examples.json new file mode 100644 index 00000000000..0dd447ac548 --- /dev/null +++ b/sdks/node/examples/tsconfig.examples.json @@ -0,0 +1,16 @@ +{ + "extends": "../tsconfig.json", + "exclude": [], + "compilerOptions": { + "rootDir": ".", + "outDir": "../dist/examples", + "paths": { + "weave": ["../dist/src"] + } + }, + "references": [ + { + "path": "../src/tsconfig.src.json" + } + ] +} diff --git a/sdks/node/logs.png b/sdks/node/logs.png new file mode 100644 index 0000000000000000000000000000000000000000..5fc54397386d9e6e6a2c1617f22a5a66d12ca111 GIT binary patch literal 24701 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 Y6)A1bcx(p#tpJ9cl(OW9r$#~l2X%68y8r+H literal 0 HcmV?d00001 diff --git a/sdks/node/package-lock.json b/sdks/node/package-lock.json new file mode 100644 index 00000000000..2fbfa2dd718 --- /dev/null +++ b/sdks/node/package-lock.json @@ -0,0 +1,4312 @@ +{ + "name": "weave", + "version": "0.6.9", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "weave", + "version": "0.6.9", + "license": "Apache-2.0", + "dependencies": { + "cli-progress": "^3.12.0", + "openai": "^4.57.0", + "uuidv7": "^1.0.1" + }, + "devDependencies": { + "@types/cli-progress": "^3.11.6", + "@types/jest": "^29.5.12", + "@types/node": "^22.5.1", + "jest": "^29.7.0", + "ts-jest": "^29.2.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz", + "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", + "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cli-progress": { + "version": "3.11.6", + "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", + "integrity": "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "22.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", + "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001655", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz", + "integrity": "sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", + "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-progress": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz", + "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-changed-files/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-circus/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runner/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openai": { + "version": "4.63.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.63.0.tgz", + "integrity": "sha512-Y9V4KODbmrOpqiOmCDVnPfMxMqKLOx8Hwcdn/r8mePq4yv7FSXGnxCs8/jZKO7zCB/IVPWihpJXwJNAIOEiZ2g==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.47.tgz", + "integrity": "sha512-1f7dB3BL/bpd9tnDJrrHb66Y+cVrhxSOTGorRNdHwYTUlTay3HuTDPKo9a/4vX9pMQkhYBcAbL4jQdNlhCFP9A==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-jest": { + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uuidv7": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uuidv7/-/uuidv7-1.0.1.tgz", + "integrity": "sha512-2noB909GbI352dKfASOY6VHHl59KvevZ1FF8gCAXCwDyrt2kkZhuFbczF9udqTfeejiRYEmO4wzUZ0WhVP+IUA==", + "license": "Apache-2.0", + "bin": { + "uuidv7": "cli.js" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "optional": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/sdks/node/package.json b/sdks/node/package.json new file mode 100644 index 00000000000..48c496d1260 --- /dev/null +++ b/sdks/node/package.json @@ -0,0 +1,88 @@ +{ + "name": "weave", + "version": "0.6.9", + "description": "AI development toolkit", + "types": "dist/src/index.d.ts", + "main": "dist/src/index.js", + "type": "commonjs", + "scripts": { + "test": "jest", + "test:coverage": "jest --coverage", + "test:watch": "jest --watch", + "format": "prettier --write \"src/**/*.ts\" \"examples/**/*.ts\"", + "run": "tsx", + "generate-api": "swagger-typescript-api -p ./weave.openapi.json -o ./src/generated -n traceServerApi.ts", + "dev": "nodemon" + }, + "repository": { + "type": "git", + "url": "https://github.com/wandb/weave/js" + }, + "author": "", + "license": "Apache-2.0", + "jest": { + "testEnvironment": "node", + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testMatch": [ + "**/__tests__/**/*.test.ts?(x)", + "**/?(*.)+(spec|test).ts?(x)" + ], + "moduleFileExtensions": [ + "js", + "jsx", + "ts", + "tsx", + "json", + "node" + ], + "moduleNameMapper": { + "^weave$": "/src/index.ts" + }, + "collectCoverage": true, + "coveragePathIgnorePatterns": [ + "/src/generated", + "/src/utils/userAgent.ts", + "/src/inMemoryTraceServer.ts" + ], + "coverageDirectory": "coverage", + "coverageReporters": [ + "text", + "lcov" + ], + "coverageThreshold": { + "global": { + "branches": 80, + "functions": 80, + "lines": 80, + "statements": 80 + } + } + }, + "nodemonConfig": { + "watch": [ + "." + ], + "ext": "ts,json", + "exec": "tsx examples/evaluate.ts" + }, + "dependencies": { + "cli-progress": "^3.12.0", + "openai": "^4.68.4", + "uuidv7": "^1.0.1" + }, + "devDependencies": { + "@types/cli-progress": "^3.11.6", + "@types/jest": "^29.5.13", + "@types/node": "^22.5.1", + "jest": "^29.7.0", + "nyc": "^17.1.0", + "prettier": "^3.3.3", + "source-map-support": "^0.5.21", + "swagger-typescript-api": "^13.0.22", + "ts-jest": "^29.2.5", + "tsconfig-paths": "^4.2.0", + "tsx": "^4.19.1" + } +} diff --git a/sdks/node/pnpm-lock.yaml b/sdks/node/pnpm-lock.yaml new file mode 100644 index 00000000000..e55b4d16df3 --- /dev/null +++ b/sdks/node/pnpm-lock.yaml @@ -0,0 +1,3756 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + cli-progress: + specifier: ^3.12.0 + version: 3.12.0 + openai: + specifier: ^4.68.4 + version: 4.68.4 + uuidv7: + specifier: ^1.0.1 + version: 1.0.2 + devDependencies: + '@types/cli-progress': + specifier: ^3.11.6 + version: 3.11.6 + '@types/jest': + specifier: ^29.5.13 + version: 29.5.14 + '@types/node': + specifier: ^22.5.1 + version: 22.8.0 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@22.8.0) + nyc: + specifier: ^17.1.0 + version: 17.1.0 + prettier: + specifier: ^3.3.3 + version: 3.3.3 + source-map-support: + specifier: ^0.5.21 + version: 0.5.21 + swagger-typescript-api: + specifier: ^13.0.22 + version: 13.0.22 + ts-jest: + specifier: ^29.2.5 + version: 29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.0))(typescript@5.5.4) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 + tsx: + specifier: ^4.19.1 + version: 4.19.1 + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.26.0': + resolution: {integrity: sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.26.0': + resolution: {integrity: sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.26.0': + resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.26.0': + resolution: {integrity: sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.9': + resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.25.9': + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.26.0': + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.25.9': + resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.25.9': + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.25.9': + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.25.9': + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.26.0': + resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.26.0': + resolution: {integrity: sha512-aP8x5pIw3xvYr/sXT+SEUwyhrXT8rUJRZltK/qN3Db80dcKpTett8cJxHyjk+xYSVXvNnl2SfcJVjbwxpOSscA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.25.9': + resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.25.9': + resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.25.9': + resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.9': + resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.26.0': + resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@exodus/schemasafe@1.3.0': + resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect-utils@29.7.0': + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + + '@types/cli-progress@3.11.6': + resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} + + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + + '@types/node-fetch@2.6.11': + resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} + + '@types/node@18.19.59': + resolution: {integrity: sha512-vizm2EqwV/7Zay+A6J3tGl9Lhr7CjZe2HmWS988sefiEmsyP9CeXEleho6i4hJk/8UtZAo0bWN4QPZZr83RxvQ==} + + '@types/node@22.8.0': + resolution: {integrity: sha512-84rafSBHC/z1i1E3p0cJwKA+CfYDNSXX9WSZBRopjIzLET8oNt6ht2tei4C7izwDeEiLLfdeSVBv1egOH916hg==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/swagger-schema-official@2.0.25': + resolution: {integrity: sha512-T92Xav+Gf/Ik1uPW581nA+JftmjWPgskw/WBf4TJzxRG/SJ+DfNnNE+WuZ4mrXuzflQMqMkm1LSYjzYW7MB1Cg==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + append-transform@2.0.0: + resolution: {integrity: sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==} + engines: {node: '>=8'} + + archy@1.0.0: + resolution: {integrity: sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + babel-preset-current-node-syntax@1.1.0: + resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} + peerDependencies: + '@babel/core': ^7.0.0 + + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.24.2: + resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + caching-transform@4.0.0: + resolution: {integrity: sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==} + engines: {node: '>=8'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001669: + resolution: {integrity: sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.4.1: + resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-progress@3.12.0: + resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==} + engines: {node: '>=4'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + dedent@1.5.3: + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-require-extensions@3.0.1: + resolution: {integrity: sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==} + engines: {node: '>=8'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.45: + resolution: {integrity: sha512-vOzZS6uZwhhbkZbcRyiy99Wg+pYFV5hk+5YaECvx0+Z31NR3Tt5zS6dze2OepT6PCTzVzT0dIJItti+uAW5zmw==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es6-error@4.1.1: + resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} + + es6-promise@3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + eta@2.2.0: + resolution: {integrity: sha512-UVQ72Rqjy/ZKQalzV5dCCJP80GrmPrMxh6NlNf+erV6ObL0ZFkhCstWRawS85z3smdr3d2wXPsZEY7rDPfGd2g==} + engines: {node: '>=6.0.0'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + foreground-child@2.0.0: + resolution: {integrity: sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==} + engines: {node: '>=8.0.0'} + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.1: + resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + + fromentries@1.3.2: + resolution: {integrity: sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-tsconfig@4.8.1: + resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasha@5.2.2: + resolution: {integrity: sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + http2-client@1.3.5: + resolution: {integrity: sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-hook@3.0.0: + resolution: {integrity: sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==} + engines: {node: '>=8'} + + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-processinfo@2.0.3: + resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + + jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.0.2: + resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash.flattendeep@4.4.0: + resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch-h2@2.3.0: + resolution: {integrity: sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==} + engines: {node: 4.x || >=6.0.0} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-preload@0.2.1: + resolution: {integrity: sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==} + engines: {node: '>=8'} + + node-readfiles@0.2.0: + resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nyc@17.1.0: + resolution: {integrity: sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==} + engines: {node: '>=18'} + hasBin: true + + oas-kit-common@1.0.8: + resolution: {integrity: sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==} + + oas-linter@3.2.2: + resolution: {integrity: sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==} + + oas-resolver@2.5.6: + resolution: {integrity: sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==} + hasBin: true + + oas-schema-walker@1.1.5: + resolution: {integrity: sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==} + + oas-validator@5.0.8: + resolution: {integrity: sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + openai@4.68.4: + resolution: {integrity: sha512-LRinV8iU9VQplkr25oZlyrsYGPGasIwYN8KFMAAFTHHLHjHhejtJ5BALuLFrkGzY4wfbKhOhuT+7lcHZ+F3iEA==} + hasBin: true + peerDependencies: + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-map@3.0.0: + resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} + engines: {node: '>=8'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-hash@4.0.0: + resolution: {integrity: sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==} + engines: {node: '>=8'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + process-on-spawn@1.0.0: + resolution: {integrity: sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==} + engines: {node: '>=8'} + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + reftools@1.1.9: + resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} + + release-zalgo@1.0.0: + resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==} + engines: {node: '>=4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + should-equal@2.0.0: + resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} + + should-format@3.0.3: + resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==} + + should-type-adaptors@1.1.0: + resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==} + + should-type@1.4.0: + resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==} + + should-util@1.0.1: + resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==} + + should@13.2.3: + resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spawn-wrap@2.0.0: + resolution: {integrity: sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==} + engines: {node: '>=8'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + swagger-schema-official@2.0.0-bab6bed: + resolution: {integrity: sha512-rCC0NWGKr/IJhtRuPq/t37qvZHI/mH4I4sxflVM+qgVe5Z2uOCivzWaVbuioJaB61kvm5UvB7b49E+oBY0M8jA==} + + swagger-typescript-api@13.0.22: + resolution: {integrity: sha512-LVLOWvozOE3izesDrfmhOpwr6XsCRGsrfJuAXsaHkzQxYPAcpSRIAzodmz1hcGJ8BOPiBCKocH1LQ96F0lmmAw==} + engines: {node: '>=18.0.0'} + hasBin: true + + swagger2openapi@7.0.8: + resolution: {integrity: sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-jest@29.2.5: + resolution: {integrity: sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tsx@4.19.1: + resolution: {integrity: sha512-0flMz1lh74BR4wOvBjuh9olbnwqCPc35OOlfyzHba0Dc+QNUeWX/Gq2YTbnwcWPO3BMd8fkzRVrHcsR+a7z7rA==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + update-browserslist-db@1.1.1: + resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + uuidv7@1.0.2: + resolution: {integrity: sha512-8JQkH4ooXnm1JCIhqTMbtmdnYEn6oKukBxHn1Ic9878jMkL7daTI7anTExfY18VRCX7tcdn5quzvCb6EWrR8PA==} + hasBin: true + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.26.0': + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.26.0': {} + + '@babel/core@7.26.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.26.0 + '@babel/generator': 7.26.0 + '@babel/helper-compilation-targets': 7.25.9 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) + '@babel/helpers': 7.26.0 + '@babel/parser': 7.26.0 + '@babel/template': 7.25.9 + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.26.0': + dependencies: + '@babel/parser': 7.26.0 + '@babel/types': 7.26.0 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.0.2 + + '@babel/helper-compilation-targets@7.25.9': + dependencies: + '@babel/compat-data': 7.26.0 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.25.9': + dependencies: + '@babel/traverse': 7.25.9 + '@babel/types': 7.26.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.25.9 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.25.9': {} + + '@babel/helper-string-parser@7.25.9': {} + + '@babel/helper-validator-identifier@7.25.9': {} + + '@babel/helper-validator-option@7.25.9': {} + + '@babel/helpers@7.26.0': + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + + '@babel/parser@7.26.0': + dependencies: + '@babel/types': 7.26.0 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': + dependencies: + '@babel/core': 7.26.0 + '@babel/helper-plugin-utils': 7.25.9 + + '@babel/template@7.25.9': + dependencies: + '@babel/code-frame': 7.26.0 + '@babel/parser': 7.26.0 + '@babel/types': 7.26.0 + + '@babel/traverse@7.25.9': + dependencies: + '@babel/code-frame': 7.26.0 + '@babel/generator': 7.26.0 + '@babel/parser': 7.26.0 + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.26.0': + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + + '@bcoe/v8-coverage@0.2.3': {} + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@exodus/schemasafe@1.3.0': {} + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.8.0) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + jest-mock: 29.7.0 + + '@jest/expect-utils@29.7.0': + dependencies: + jest-get-type: 29.6.3 + + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 22.8.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 22.8.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jest/source-map@29.6.3': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.26.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.6 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/types@29.6.3': + dependencies: + '@jest/schemas': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 22.8.0 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@sinclair/typebox@0.27.8': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.26.0 + '@babel/types': 7.26.0 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.26.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.26.0 + '@babel/types': 7.26.0 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.26.0 + + '@types/cli-progress@3.11.6': + dependencies: + '@types/node': 22.8.0 + + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 22.8.0 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@29.5.14': + dependencies: + expect: 29.7.0 + pretty-format: 29.7.0 + + '@types/node-fetch@2.6.11': + dependencies: + '@types/node': 22.8.0 + form-data: 4.0.1 + + '@types/node@18.19.59': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.8.0': + dependencies: + undici-types: 6.19.8 + + '@types/stack-utils@2.0.3': {} + + '@types/swagger-schema-official@2.0.25': {} + + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + agentkeepalive@4.5.0: + dependencies: + humanize-ms: 1.2.1 + + aggregate-error@3.1.0: + dependencies: + clean-stack: 2.2.0 + indent-string: 4.0.0 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + append-transform@2.0.0: + dependencies: + default-require-extensions: 3.0.1 + + archy@1.0.0: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + babel-jest@29.7.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.26.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.25.9 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.25.9 + '@babel/types': 7.26.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.20.6 + + babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.0) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0) + + babel-preset-jest@29.6.3(@babel/core@7.26.0): + dependencies: + '@babel/core': 7.26.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + + balanced-match@1.0.2: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.24.2: + dependencies: + caniuse-lite: 1.0.30001669 + electron-to-chromium: 1.5.45 + node-releases: 2.0.18 + update-browserslist-db: 1.1.1(browserslist@4.24.2) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + + buffer-from@1.1.2: {} + + caching-transform@4.0.0: + dependencies: + hasha: 5.2.2 + make-dir: 3.1.0 + package-hash: 4.0.0 + write-file-atomic: 3.0.3 + + call-me-maybe@1.0.2: {} + + callsites@3.1.0: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001669: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + char-regex@1.0.2: {} + + ci-info@3.9.0: {} + + cjs-module-lexer@1.4.1: {} + + clean-stack@2.2.0: {} + + cli-progress@3.12.0: + dependencies: + string-width: 4.2.3 + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + co@4.6.0: {} + + collect-v8-coverage@1.0.2: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commondir@1.0.1: {} + + concat-map@0.0.1: {} + + consola@3.2.3: {} + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cosmiconfig@9.0.0(typescript@5.5.4): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.5.4 + + create-jest@29.7.0(@types/node@22.8.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.8.0) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + dedent@1.5.3: {} + + deepmerge@4.3.1: {} + + default-require-extensions@3.0.1: + dependencies: + strip-bom: 4.0.0 + + delayed-stream@1.0.0: {} + + detect-newline@3.1.0: {} + + didyoumean@1.2.2: {} + + diff-sequences@29.6.3: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.2 + + electron-to-chromium@1.5.45: {} + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + env-paths@2.2.1: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es6-error@4.1.1: {} + + es6-promise@3.3.1: {} + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + escalade@3.2.0: {} + + escape-string-regexp@2.0.0: {} + + esprima@4.0.1: {} + + eta@2.2.0: {} + + event-target-shim@5.0.1: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit@0.1.2: {} + + expect@29.7.0: + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + + fast-json-stable-stringify@2.1.0: {} + + fast-safe-stringify@2.1.1: {} + + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + foreground-child@2.0.0: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 3.0.7 + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + form-data-encoder@1.7.2: {} + + form-data@4.0.1: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + + fromentries@1.3.2: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-package-type@0.1.0: {} + + get-stream@6.0.1: {} + + get-tsconfig@4.8.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@11.12.0: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + hasha@5.2.2: + dependencies: + is-stream: 2.0.1 + type-fest: 0.8.1 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + html-escaper@2.0.2: {} + + http2-client@1.3.5: {} + + human-signals@2.1.0: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-arrayish@0.2.1: {} + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-number@7.0.0: {} + + is-stream@2.0.1: {} + + is-typedarray@1.0.0: {} + + is-windows@1.0.2: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-hook@3.0.0: + dependencies: + append-transform: 2.0.0 + + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.26.0 + '@babel/parser': 7.26.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + istanbul-lib-processinfo@2.0.3: + dependencies: + archy: 1.0.0 + cross-spawn: 7.0.3 + istanbul-lib-coverage: 3.2.2 + p-map: 3.0.0 + rimraf: 3.0.2 + uuid: 8.3.2 + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.3.7 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.5.3 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@29.7.0(@types/node@22.8.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@22.8.0) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@22.8.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@22.8.0): + dependencies: + '@babel/core': 7.26.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 22.8.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-diff@29.7.0: + dependencies: + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + + jest-get-type@29.6.3: {} + + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 22.8.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-matcher-utils@29.7.0: + dependencies: + chalk: 4.1.2 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + + jest-message-util@29.7.0: + dependencies: + '@babel/code-frame': 7.26.0 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.6 + + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + jest-util: 29.7.0 + + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + + jest-regex-util@29.6.3: {} + + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.8 + resolve.exports: 2.0.2 + slash: 3.0.0 + + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + chalk: 4.1.2 + cjs-module-lexer: 1.4.1 + collect-v8-coverage: 1.0.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.26.0 + '@babel/generator': 7.26.0 + '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) + '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) + '@babel/types': 7.26.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + + jest-util@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + chalk: 4.1.2 + ci-info: 3.9.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.8.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + + jest-worker@29.7.0: + dependencies: + '@types/node': 22.8.0 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@29.7.0(@types/node@22.8.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@22.8.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + + json5@2.2.3: {} + + kleur@3.0.3: {} + + leven@3.1.0: {} + + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash.flattendeep@4.4.0: {} + + lodash.memoize@4.1.2: {} + + lodash@4.17.21: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + + make-dir@4.0.0: + dependencies: + semver: 7.6.3 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + + merge-stream@2.0.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.7: {} + + natural-compare@1.4.0: {} + + node-domexception@1.0.0: {} + + node-fetch-h2@2.3.0: + dependencies: + http2-client: 1.3.5 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-int64@0.4.0: {} + + node-preload@0.2.1: + dependencies: + process-on-spawn: 1.0.0 + + node-readfiles@0.2.0: + dependencies: + es6-promise: 3.3.1 + + node-releases@2.0.18: {} + + normalize-path@3.0.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nyc@17.1.0: + dependencies: + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + caching-transform: 4.0.0 + convert-source-map: 1.9.0 + decamelize: 1.2.0 + find-cache-dir: 3.3.2 + find-up: 4.1.0 + foreground-child: 3.3.0 + get-package-type: 0.1.0 + glob: 7.2.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-hook: 3.0.0 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-processinfo: 2.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.7 + make-dir: 3.1.0 + node-preload: 0.2.1 + p-map: 3.0.0 + process-on-spawn: 1.0.0 + resolve-from: 5.0.0 + rimraf: 3.0.2 + signal-exit: 3.0.7 + spawn-wrap: 2.0.0 + test-exclude: 6.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - supports-color + + oas-kit-common@1.0.8: + dependencies: + fast-safe-stringify: 2.1.1 + + oas-linter@3.2.2: + dependencies: + '@exodus/schemasafe': 1.3.0 + should: 13.2.3 + yaml: 1.10.2 + + oas-resolver@2.5.6: + dependencies: + node-fetch-h2: 2.3.0 + oas-kit-common: 1.0.8 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 17.7.2 + + oas-schema-walker@1.1.5: {} + + oas-validator@5.0.8: + dependencies: + call-me-maybe: 1.0.2 + oas-kit-common: 1.0.8 + oas-linter: 3.2.2 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + reftools: 1.1.9 + should: 13.2.3 + yaml: 1.10.2 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + openai@4.68.4: + dependencies: + '@types/node': 18.19.59 + '@types/node-fetch': 2.6.11 + abort-controller: 3.0.0 + agentkeepalive: 4.5.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-map@3.0.0: + dependencies: + aggregate-error: 3.1.0 + + p-try@2.2.0: {} + + package-hash@4.0.0: + dependencies: + graceful-fs: 4.2.11 + hasha: 5.2.2 + lodash.flattendeep: 4.4.0 + release-zalgo: 1.0.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.26.0 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + pirates@4.0.6: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + prettier@3.3.3: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + process-on-spawn@1.0.0: + dependencies: + fromentries: 1.3.2 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + pure-rand@6.1.0: {} + + react-is@18.3.1: {} + + reftools@1.1.9: {} + + release-zalgo@1.0.0: + dependencies: + es6-error: 4.1.1 + + require-directory@2.1.1: {} + + require-main-filename@2.0.0: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve.exports@2.0.2: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + semver@6.3.1: {} + + semver@7.6.3: {} + + set-blocking@2.0.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + should-equal@2.0.0: + dependencies: + should-type: 1.4.0 + + should-format@3.0.3: + dependencies: + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + + should-type-adaptors@1.1.0: + dependencies: + should-type: 1.4.0 + should-util: 1.0.1 + + should-type@1.4.0: {} + + should-util@1.0.1: {} + + should@13.2.3: + dependencies: + should-equal: 2.0.0 + should-format: 3.0.3 + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + should-util: 1.0.1 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + spawn-wrap@2.0.0: + dependencies: + foreground-child: 2.0.0 + is-windows: 1.0.2 + make-dir: 3.1.0 + rimraf: 3.0.2 + signal-exit: 3.0.7 + which: 2.0.2 + + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swagger-schema-official@2.0.0-bab6bed: {} + + swagger-typescript-api@13.0.22: + dependencies: + '@types/swagger-schema-official': 2.0.25 + consola: 3.2.3 + cosmiconfig: 9.0.0(typescript@5.5.4) + didyoumean: 1.2.2 + eta: 2.2.0 + js-yaml: 4.1.0 + lodash: 4.17.21 + nanoid: 3.3.7 + prettier: 3.3.3 + swagger-schema-official: 2.0.0-bab6bed + swagger2openapi: 7.0.8 + typescript: 5.5.4 + transitivePeerDependencies: + - encoding + + swagger2openapi@7.0.8: + dependencies: + call-me-maybe: 1.0.2 + node-fetch: 2.7.0 + node-fetch-h2: 2.3.0 + node-readfiles: 0.2.0 + oas-kit-common: 1.0.8 + oas-resolver: 2.5.6 + oas-schema-walker: 1.1.5 + oas-validator: 5.0.8 + reftools: 1.1.9 + yaml: 1.10.2 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + tmpl@1.0.5: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@0.0.3: {} + + ts-jest@29.2.5(@babel/core@7.26.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.0))(jest@29.7.0(@types/node@22.8.0))(typescript@5.5.4): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@22.8.0) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.5.4 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.26.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.26.0) + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tsx@4.19.1: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.1 + optionalDependencies: + fsevents: 2.3.3 + + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@0.8.1: {} + + typedarray-to-buffer@3.1.5: + dependencies: + is-typedarray: 1.0.0 + + typescript@5.5.4: {} + + undici-types@5.26.5: {} + + undici-types@6.19.8: {} + + update-browserslist-db@1.1.1(browserslist@4.24.2): + dependencies: + browserslist: 4.24.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uuid@8.3.2: {} + + uuidv7@1.0.2: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-module@2.0.1: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + write-file-atomic@3.0.3: + dependencies: + imurmurhash: 0.1.4 + is-typedarray: 1.0.0 + signal-exit: 3.0.7 + typedarray-to-buffer: 3.1.5 + + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yaml@1.10.2: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} diff --git a/sdks/node/scripts/loadTest.ts b/sdks/node/scripts/loadTest.ts new file mode 100644 index 00000000000..2850b83b232 --- /dev/null +++ b/sdks/node/scripts/loadTest.ts @@ -0,0 +1,39 @@ +import * as weave from 'weave'; + +const func = weave.op(async () => 1); +const myFunction = async (a: number = 1, b: string = 'hello', c: boolean = true) => { + return { first: a, second: b, third: c }; +}; +const func2 = weave.op(myFunction); +const func3 = weave.op(async () => { + throw new Error('hmm'); +}); +const myFunction2 = async ({ a, b = 'wuh' }: { a?: number; b?: string }) => { + return { first: a, second: b }; +}; +const func4 = weave.op(myFunction2, { parameterNames: 'useParam0Object' }); + +async function bench(func: weave.Op, calls: number, client: weave.WeaveClient) { + console.log(`Benchmarking with ${calls} calls...`); + const startTime = Date.now(); + const promises = Array(calls) + .fill(null) + .map(() => func({ a: 3 })); + await Promise.all(promises); + await client.waitForBatchProcessing(); + + const endTime = Date.now(); + const duration = (endTime - startTime) / 1000; // Convert to seconds + console.log(`Completed ${calls} calls in ${duration.toFixed(2)} seconds`); +} + +async function main() { + const client = await weave.init('examples'); + // for (let x = 1; x <= 5; x++) { + // const calls = Math.pow(10, x); + // await bench(calls, client); + // } + await bench(func4, 1, client); +} + +main(); diff --git a/sdks/node/scripts/testApi.ts b/sdks/node/scripts/testApi.ts new file mode 100644 index 00000000000..673c540f2b7 --- /dev/null +++ b/sdks/node/scripts/testApi.ts @@ -0,0 +1,72 @@ +import OpenAI from 'openai'; +import * as weave from 'weave'; + +// Initialize the API +weave.init('examples'); + +// Create OpenAI client +const openai = weave.wrapOpenAI(new OpenAI()); + +// Define a simple function to be wrapped +function add(a: number, b: number): number { + return a + b; +} + +// Wrap the function using op +const wrappedAdd = weave.op(add); + +// Function to demonstrate async behavior +async function delayedMultiply(a: number, b: number): Promise { + await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second delay + return a * b; +} + +// Wrap the async function +const wrappedDelayedMultiply = weave.op(delayedMultiply); + +// Function to call OpenAI +async function callOpenAI(prompt: string): Promise { + const completion = await openai.chat.completions.create({ + model: 'gpt-3.5-turbo', + messages: [{ role: 'user', content: prompt }], + }); + return completion.choices[0].message.content || ''; +} + +// Wrap the OpenAI function +const wrappedCallOpenAI = weave.op(callOpenAI); + +// Function to demonstrate nested calls including OpenAI +async function complexOperationWithAI(a: number, b: number, c: number): Promise { + const sum = await wrappedAdd(a, b); + const product = await wrappedDelayedMultiply(sum, c); + const prompt = `What is an interesting fact about the number ${product}?`; + const aiResponse = await wrappedCallOpenAI(prompt); + return `The result of the calculation is ${product}. ${aiResponse}`; +} + +// Wrap the complex function +const wrappedComplexOperationWithAI = weave.op(complexOperationWithAI); + +// Main async function to run our tests +async function runTests() { + console.log('Starting tests...'); + + // Test the wrapped add function + console.log('\nTesting wrapped add function:'); + console.log('2 + 3 =', await wrappedAdd(2, 3)); + + // Test the wrapped async multiply function + console.log('\nTesting wrapped delayed multiply function:'); + console.log('3 * 4 =', await wrappedDelayedMultiply(3, 4)); + + // Test the complex operation with nested calls including OpenAI + console.log('\nTesting complex operation with nested calls including OpenAI:'); + const result = await wrappedComplexOperationWithAI(2, 3, 4); + console.log(result); + + console.log('\nTests completed.'); +} + +// Run the tests +runTests().catch(error => console.error('An error occurred:', error)); diff --git a/sdks/node/src/__tests__/clientApi.test.ts b/sdks/node/src/__tests__/clientApi.test.ts new file mode 100644 index 00000000000..0a83641f62d --- /dev/null +++ b/sdks/node/src/__tests__/clientApi.test.ts @@ -0,0 +1,54 @@ +import {init, requireGlobalClient} from '../clientApi'; +import {getWandbConfigs} from '../wandb/settings'; +import {WandbServerApi} from '../wandb/wandbServerApi'; + +jest.mock('../wandb/wandbServerApi'); +jest.mock('../wandb/settings'); + +describe('Client API', () => { + beforeEach(() => { + jest.clearAllMocks(); + + // Mock getWandbConfigs + (getWandbConfigs as jest.Mock).mockReturnValue({ + apiKey: 'mock-api-key', + baseUrl: 'https://api.wandb.ai', + traceBaseUrl: 'https://trace.wandb.ai', + domain: 'api.wandb.ai', + host: 'api.wandb.ai', + }); + + // Mock WandbServerApi + (WandbServerApi as jest.Mock).mockImplementation(() => ({ + defaultEntityName: jest.fn().mockResolvedValue('test-entity'), + })); + }); + + describe('initialization', () => { + test('initializes with project name', async () => { + const client = await init('test-project'); + const gottenClient = requireGlobalClient(); + + expect(gottenClient).toBeDefined(); + expect(gottenClient).toBe(client); + expect(WandbServerApi).toHaveBeenCalledWith( + 'https://api.wandb.ai', + 'mock-api-key' + ); + expect(gottenClient.projectId).toBe('test-entity/test-project'); + }); + + test('initializes with entity/project', async () => { + const client = await init('custom-entity/test-project'); + const gottenClient = requireGlobalClient(); + + expect(gottenClient).toBeDefined(); + expect(gottenClient).toBe(client); + expect(WandbServerApi).toHaveBeenCalledWith( + 'https://api.wandb.ai', + 'mock-api-key' + ); + expect(gottenClient.projectId).toBe('custom-entity/test-project'); + }); + }); +}); diff --git a/sdks/node/src/__tests__/clientMock.ts b/sdks/node/src/__tests__/clientMock.ts new file mode 100644 index 00000000000..660eac9e59c --- /dev/null +++ b/sdks/node/src/__tests__/clientMock.ts @@ -0,0 +1,19 @@ +import {setGlobalClient} from '../clientApi'; +import {Api as TraceServerApi} from '../generated/traceServerApi'; +import {InMemoryTraceServer} from '../inMemoryTraceServer'; +import {Settings} from '../settings'; +import {WandbServerApi} from '../wandb/wandbServerApi'; +import {WeaveClient} from '../weaveClient'; + +export function initWithCustomTraceServer( + projectName: string, + customTraceServer: InMemoryTraceServer +) { + const client = new WeaveClient( + customTraceServer as unknown as TraceServerApi, + {} as WandbServerApi, // Placeholder, as we don't use WandbServerApi in this case + projectName, + new Settings(true) + ); + setGlobalClient(client); +} diff --git a/sdks/node/src/__tests__/digest.test.ts b/sdks/node/src/__tests__/digest.test.ts new file mode 100644 index 00000000000..08c2dc20d4a --- /dev/null +++ b/sdks/node/src/__tests__/digest.test.ts @@ -0,0 +1,108 @@ +import {stringifyPythonDumps} from '../digest'; + +describe('stringifyPythonDumps', () => { + test('Basic types', async () => { + const testData1 = { + a: 1, + b: ['a', 'b', 'd,e', ["f',j,y"]], + c: 3, + d: 4, + e: 5, + }; + const expected1 = + '{"a": 1, "b": ["a", "b", "d,e", ["f\',j,y"]], "c": 3, "d": 4, "e": 5}'; + expect(stringifyPythonDumps(testData1)).toBe(expected1); + }); + + test('Special numbers', async () => { + const testData2 = { + int_max: 9007199254740991, // Max safe integer in JS + int_min: -9007199254740991, // Min safe integer in JS + float: 3.14159, + exp_pos: 1e100, + exp_neg: 1e-100, + zero: 0, + neg_zero: -0, + }; + const expected2 = + '{"exp_neg": 1e-100, "exp_pos": 1e+100, "float": 3.14159, "int_max": 9007199254740991, "int_min": -9007199254740991, "neg_zero": 0, "zero": 0}'; + expect(stringifyPythonDumps(testData2)).toBe(expected2); + }); + + test('Special values', async () => { + const testData3 = { + null: null, + bool_true: true, + bool_false: false, + empty_list: [], + empty_dict: {}, + }; + const expected3 = + '{"bool_false": false, "bool_true": true, "empty_dict": {}, "empty_list": [], "null": null}'; + expect(stringifyPythonDumps(testData3)).toBe(expected3); + }); + + test('Unicode and escaping', async () => { + const testData4 = { + unicode: 'こんにちは', + escape_chars: '\b\f\n\r\t', + quotes: '"Hello," she said.', + backslash: 'C:\\path\\to\\file', + }; + const expected4 = + '{"backslash": "C:\\\\path\\\\to\\\\file", "escape_chars": "\\b\\f\\n\\r\\t", "quotes": "\\"Hello,\\" she said.", "unicode": "こんにちは"}'; + expect(stringifyPythonDumps(testData4)).toBe(expected4); + }); + + test('Nested structures', async () => { + const testData5 = { + nested: { + list: [1, [2, [3, [4]]]], + dict: {a: {b: {c: {d: 4}}}}, + }, + }; + const expected5 = + '{"nested": {"dict": {"a": {"b": {"c": {"d": 4}}}}, "list": [1, [2, [3, [4]]]]}}'; + expect(stringifyPythonDumps(testData5)).toBe(expected5); + }); + + test('Array of mixed types', async () => { + const testData6 = [1, 'two', 3, [4, 5], {six: 6}, null, true, false]; + const expected6 = '[1, "two", 3, [4, 5], {"six": 6}, null, true, false]'; + expect(stringifyPythonDumps(testData6)).toBe(expected6); + }); + + test('Empty string keys and values', async () => { + const testData7 = {'': 'empty_key', empty_value: ''}; + const expected7 = '{"": "empty_key", "empty_value": ""}'; + expect(stringifyPythonDumps(testData7)).toBe(expected7); + }); + + // TODO: This is a generated test that fails. I didn't look into what the behavior should actually + // be, because we're not using stringifyPythonDumps anywhere yet. + test.skip('Non-string keys', async () => { + const testData8 = {1: 'one', 2.0: 'two', true: 'true'}; + const expected8 = '{"1": "true", "2.0": "two"}'; + expect(stringifyPythonDumps(testData8)).toBe(expected8); + }); + + test('Special characters in strings', async () => { + const testData9 = { + control_chars: '\u0000\u0001\u0002\u0003', + emoji: '😀🌍🚀', + surrogate_pair: '\uD83D\uDE00', + }; + const expected9 = + '{"control_chars": "\\u0000\\u0001\\u0002\\u0003", "emoji": "😀🌍🚀", "surrogate_pair": "😀"}'; + expect(stringifyPythonDumps(testData9)).toBe(expected9); + }); +}); + +// describe('encodeNumber', () => { +// test('Basic numbers', () => { +// expect(encodeNumber(1)).toBe('1'); +// expect(encodeNumber(1.0)).toBe('1.0'); +// expect(encodeNumber(1.1)).toBe('1.1'); +// expect(encodeNumber(1e9)).toBe('1e9'); +// }); +// }); diff --git a/sdks/node/src/__tests__/evaluation.test.ts b/sdks/node/src/__tests__/evaluation.test.ts new file mode 100644 index 00000000000..86d2bd3170a --- /dev/null +++ b/sdks/node/src/__tests__/evaluation.test.ts @@ -0,0 +1,171 @@ +import {Dataset} from '../dataset'; +import {Evaluation} from '../evaluation'; +import {ColumnMapping} from '../fn'; +import {op} from '../op'; + +const createMockDataset = () => + new Dataset({ + rows: [ + {id: 0, text: 'Example 0'}, + {id: 1, text: 'Example 1'}, + {id: 2, text: 'Example 2'}, + {id: 3, text: 'Example 3'}, + {id: 4, text: 'Example 4'}, + ], + }); + +const createMockDatasetWithDifferentColumnNames = () => + new Dataset({ + rows: [ + {identifier: 0, description: 'Example 0'}, + {identifier: 1, description: 'Example 1'}, + {identifier: 2, description: 'Example 2'}, + {identifier: 3, description: 'Example 3'}, + {identifier: 4, description: 'Example 4'}, + ], + }); + +const createMockModel = (failable: boolean) => { + return op(async function mockPrediction({ + datasetRow, + }: { + datasetRow: {id: number; text: string}; + }) { + if (failable && datasetRow.id === 0) throw new Error('Model failed'); + if (failable && datasetRow.text === undefined) + throw new Error('Model failed'); + return `Prediction for ${datasetRow.text}`; + }); +}; + +const createMockScorers = (failable: boolean) => { + return [ + op(async function lengthScorer({ + datasetRow, + modelOutput, + }: { + datasetRow: {id: number; text: string}; + modelOutput: string; + }) { + if (failable && datasetRow.id === 3) throw new Error('Scorer 1 failed'); + return { + explanation: 'length is ' + modelOutput.length, + length: modelOutput.length, + }; + }), + op(async function inclusionScorer({ + modelOutput, + datasetRow, + }: { + modelOutput: string; + datasetRow: {id: number; text: string}; + }) { + return modelOutput.includes(datasetRow.text); + }), + ]; +}; + +const createMockEvaluation = ( + failable: boolean, + dataset: Dataset = createMockDataset(), + columnMapping?: ColumnMapping +) => { + return new Evaluation({ + dataset, + scorers: createMockScorers(failable), + columnMapping, + }); +}; + +describe('Evaluation', () => { + test('summarizeResults', async () => { + const mockEval = createMockEvaluation(false); + const mockModel = createMockModel(false); + + const results = await mockEval.evaluate({model: mockModel}); + const expectedResults = { + model_success: {true_count: 5, true_fraction: 1}, + inclusionScorer: { + true_count: 5, + true_fraction: 1, + }, + lengthScorer: { + length: { + mean: 24, + }, + }, + model_latency: {mean: expect.any(Number)}, + }; + + expect(results).toEqual(expectedResults); + }); + test('summarizeResults with failed predictions and scorers', async () => { + const mockEval = createMockEvaluation(true); + const mockModel = createMockModel(true); + + const results = await mockEval.evaluate({model: mockModel}); + const expectedResults = { + model_success: {true_count: 4, true_fraction: 0.8}, + inclusionScorer: { + true_count: 4, + true_fraction: 0.8, + }, + lengthScorer: { + length: { + mean: 14.4, + }, + }, + model_latency: {mean: expect.any(Number)}, + }; + + expect(results).toEqual(expectedResults); + }); + + test('evaluate with a valid column mapping', async () => { + const mockEval = createMockEvaluation( + true, + createMockDatasetWithDifferentColumnNames(), + { + id: 'identifier', + text: 'description', + } + ); + const mockModel = createMockModel(true); + const res = await mockEval.evaluate({model: mockModel}); + expect(res).toEqual({ + model_success: { + true_count: 4, + true_fraction: 0.8, + }, + inclusionScorer: { + true_count: 4, + true_fraction: 0.8, + }, + lengthScorer: { + length: { + mean: 14.4, + }, + }, + model_latency: {mean: expect.any(Number)}, + }); + }); + + test('evaluate with an invalid column mapping', async () => { + // These cols dont map as expected, so the model should fail + const mockEval = createMockEvaluation( + true, + createMockDatasetWithDifferentColumnNames(), + { + id: 'totallyNot', + text: 'validMapping', + } + ); + const mockModel = createMockModel(true); + + const res = await mockEval.evaluate({model: mockModel}); + expect(res).toEqual({ + model_success: {true_count: 0, true_fraction: 0}, + model_latency: {mean: expect.any(Number)}, + }); + }); +}); diff --git a/sdks/node/src/__tests__/integrations/integrationOpenAI.test.ts b/sdks/node/src/__tests__/integrations/integrationOpenAI.test.ts new file mode 100644 index 00000000000..e7aec87b2f3 --- /dev/null +++ b/sdks/node/src/__tests__/integrations/integrationOpenAI.test.ts @@ -0,0 +1,303 @@ +import {InMemoryTraceServer} from '../../inMemoryTraceServer'; +import {wrapOpenAI} from '../../integrations/openai'; +import {initWithCustomTraceServer} from '../clientMock'; +import {makeMockOpenAIChat} from '../openaiMock'; + +// Helper function to get calls +async function getCalls(traceServer: InMemoryTraceServer, projectId: string) { + const calls = await traceServer.calls + .callsStreamQueryPost({ + project_id: projectId, + limit: 100, + }) + .then(result => result.calls); + return calls; +} + +const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +describe('OpenAI Integration', () => { + let inMemoryTraceServer: InMemoryTraceServer; + const testProjectName = 'test-project'; + let mockOpenAI: any; + let patchedOpenAI: any; + + beforeEach(() => { + inMemoryTraceServer = new InMemoryTraceServer(); + initWithCustomTraceServer(testProjectName, inMemoryTraceServer); + + const mockOpenAIChat = makeMockOpenAIChat(messages => ({ + content: messages[messages.length - 1].content.toUpperCase(), + functionCalls: [], + })); + + mockOpenAI = { + chat: { + completions: {create: mockOpenAIChat}, + }, + beta: { + chat: { + completions: { + parse: () => { + throw new Error('not implemented'); + }, + }, + }, + }, + images: { + generate: () => { + throw new Error('not implemented'); + }, + }, + }; + patchedOpenAI = wrapOpenAI(mockOpenAI); + }); + + test('non-streaming chat completion', async () => { + const messages = [{role: 'user', content: 'Hello, AI!'}]; + + // Direct API call + const directResult = await mockOpenAI.chat.completions.create({messages}); + + // Op-wrapped API call + const opResult = await patchedOpenAI.chat.completions.create({messages}); + + // Wait for any pending batch processing + await wait(300); + + // Check results + expect(opResult).toMatchObject({ + object: directResult.object, + model: directResult.model, + choices: directResult.choices, + usage: directResult.usage, + }); + expect(opResult.id).toMatch(/^chatcmpl-/); + expect(opResult.system_fingerprint).toMatch(/^fp_/); + expect(opResult.created).toBeCloseTo(directResult.created, -2); // Allow 1 second difference + expect(opResult.choices[0].message.content).toBe('HELLO, AI!'); + + // Check logged Call values + const calls = await getCalls(inMemoryTraceServer, testProjectName); + expect(calls).toHaveLength(1); + expect(calls[0].op_name).toContain('openai.chat.completions.create'); + expect(calls[0].inputs).toEqual({messages}); + expect(calls[0].output).toMatchObject({ + object: opResult.object, + model: opResult.model, + choices: opResult.choices, + usage: opResult.usage, + }); + expect(calls[0].output.id).toMatch(/^chatcmpl-/); + expect(calls[0].output.system_fingerprint).toMatch(/^fp_/); + expect(calls[0].output.created).toBeCloseTo(opResult.created, -2); + expect(calls[0].summary).toEqual({ + usage: { + 'gpt-4o-2024-05-13': { + requests: 1, + prompt_tokens: 2, + completion_tokens: 2, + total_tokens: 4, + }, + }, + }); + // Ensure stream_options is not present in the logged call for non-streaming requests + expect(calls[0].inputs).not.toHaveProperty('stream_options'); + }); + + test('streaming chat completion basic', async () => { + const messages = [{role: 'user', content: 'Hello, streaming AI!'}]; + + // Direct API call + const directStream = await mockOpenAI.chat.completions.create({ + messages, + stream: true, + }); + let directContent = ''; + for await (const chunk of directStream) { + if (chunk.choices && chunk.choices[0]?.delta?.content) { + directContent += chunk.choices[0].delta.content; + } + } + + // Op-wrapped API call + const opStream = await patchedOpenAI.chat.completions.create({ + messages, + stream: true, + }); + let opContent = ''; + let usageChunkSeen = false; + for await (const chunk of opStream) { + if (chunk.choices && chunk.choices[0]?.delta?.content) { + opContent += chunk.choices[0].delta.content; + } + if ('usage' in chunk) { + usageChunkSeen = true; + } + } + + // Wait for any pending batch processing + await wait(300); + + // Check results + expect(opContent).toBe(directContent); + expect(opContent).toBe('HELLO, STREAMING AI!'); + + // TOOD: this is broken still! + // expect(usageChunkSeen).toBe(false); // Ensure no usage chunk is seen in the user-facing stream + + // Check logged Call values + const calls = await getCalls(inMemoryTraceServer, testProjectName); + expect(calls).toHaveLength(1); + expect(calls[0].op_name).toContain('openai.chat.completions.create'); + expect(calls[0].inputs).toEqual({messages, stream: true}); + expect(calls[0].output).toMatchObject({ + choices: [ + { + message: { + content: 'HELLO, STREAMING AI!', + }, + }, + ], + }); + expect(calls[0].summary).toEqual({ + usage: { + 'gpt-4o-2024-05-13': { + requests: 1, + prompt_tokens: 3, + completion_tokens: 3, + total_tokens: 6, + }, + }, + }); + }); + + // Add a new test for streaming with explicit usage request + test('streaming chat completion with explicit usage request', async () => { + const messages = [ + {role: 'user', content: 'Hello, streaming AI with usage!'}, + ]; + + // Op-wrapped API call with explicit usage request + const opStream = await patchedOpenAI.chat.completions.create({ + messages, + stream: true, + stream_options: {include_usage: true}, + }); + let opContent = ''; + let usageChunkSeen = false; + for await (const chunk of opStream) { + if (chunk.choices[0]?.delta?.content) { + opContent += chunk.choices[0].delta.content; + } + if ('usage' in chunk) { + usageChunkSeen = true; + } + } + + // Wait for any pending batch processing + await wait(300); + + // Check results + expect(opContent).toBe('HELLO, STREAMING AI WITH USAGE!'); + expect(usageChunkSeen).toBe(true); // Ensure usage chunk is seen when explicitly requested + + // Check logged Call values + const calls = await getCalls(inMemoryTraceServer, testProjectName); + expect(calls).toHaveLength(1); + expect(calls[0].summary).toEqual({ + usage: { + 'gpt-4o-2024-05-13': { + requests: 1, + prompt_tokens: 5, + completion_tokens: 5, + total_tokens: 10, + }, + }, + }); + }); + + test('chat completion with function call', async () => { + const messages = [{role: 'user', content: "What's the weather in London?"}]; + const functions = [ + { + name: 'get_weather', + description: 'Get the weather in a location', + parameters: { + type: 'object', + properties: { + location: {type: 'string'}, + }, + required: ['location'], + }, + }, + ]; + + // Update mock to include function call + const mockOpenAIChat = makeMockOpenAIChat(() => ({ + content: '', + functionCalls: [ + { + name: 'get_weather', + arguments: {location: 'London'}, + }, + ], + })); + mockOpenAI.chat.completions.create = mockOpenAIChat; + + // Direct API call + const directResult = await mockOpenAI.chat.completions.create({ + messages, + functions, + }); + + // Op-wrapped API call + const opResult = await patchedOpenAI.chat.completions.create({ + messages, + functions, + }); + + // Wait for any pending batch processing + await wait(300); + + // Check results + expect(opResult).toMatchObject({ + object: directResult.object, + model: directResult.model, + choices: directResult.choices, + usage: directResult.usage, + }); + expect(opResult.id).toMatch(/^chatcmpl-/); + expect(opResult.system_fingerprint).toMatch(/^fp_/); + expect(opResult.created).toBeCloseTo(directResult.created, -2); // Allow 1 second difference + expect(opResult.choices[0].message.function_call).toEqual({ + name: 'get_weather', + arguments: '{"location":"London"}', + }); + + // Check logged Call values + const calls = await getCalls(inMemoryTraceServer, testProjectName); + expect(calls).toHaveLength(1); + expect(calls[0].op_name).toContain('openai.chat.completions.create'); + expect(calls[0].inputs).toEqual({messages, functions}); + expect(calls[0].output).toMatchObject({ + object: opResult.object, + model: opResult.model, + choices: opResult.choices, + usage: opResult.usage, + }); + expect(calls[0].output.id).toMatch(/^chatcmpl-/); + expect(calls[0].output.system_fingerprint).toMatch(/^fp_/); + expect(calls[0].output.created).toBeCloseTo(opResult.created, -2); + expect(calls[0].summary).toEqual({ + usage: { + 'gpt-4o-2024-05-13': { + requests: 1, + prompt_tokens: 5, + completion_tokens: 3, + total_tokens: 8, + }, + }, + }); + }); +}); diff --git a/sdks/node/src/__tests__/integrations/openai2.test.ts b/sdks/node/src/__tests__/integrations/openai2.test.ts new file mode 100644 index 00000000000..1e2f679c9db --- /dev/null +++ b/sdks/node/src/__tests__/integrations/openai2.test.ts @@ -0,0 +1,241 @@ +import {Api as TraceServerApi} from '../../generated/traceServerApi'; +import { + makeOpenAIImagesGenerateOp, + openAIStreamReducer, + wrapOpenAI, +} from '../../integrations/openai'; +import {isWeaveImage} from '../../media'; +import {WandbServerApi} from '../../wandb/wandbServerApi'; +import {WeaveClient} from '../../weaveClient'; + +// Mock WeaveClient dependencies +jest.mock('../../generated/traceServerApi'); +jest.mock('../../wandb/wandbServerApi'); + +describe('OpenAI Integration', () => { + let mockOpenAI: any; + let wrappedOpenAI: any; + let mockTraceServerApi: jest.Mocked>; + let mockWandbServerApi: jest.Mocked; + let weaveClient: WeaveClient; + + beforeEach(() => { + // Setup mock OpenAI client + mockOpenAI = { + chat: { + completions: { + create: jest.fn(), + }, + }, + images: { + generate: jest.fn(), + }, + beta: { + chat: { + completions: { + parse: jest.fn(), + }, + }, + }, + }; + + // Setup WeaveClient + mockTraceServerApi = { + obj: { + objCreateObjCreatePost: jest.fn().mockResolvedValue({ + data: {digest: 'test-digest'}, + }), + }, + call: { + callStartBatchCallUpsertBatchPost: jest.fn(), + }, + } as any; + mockWandbServerApi = {} as any; + weaveClient = new WeaveClient( + mockTraceServerApi, + mockWandbServerApi, + 'test-project' + ); + + wrappedOpenAI = wrapOpenAI(mockOpenAI); + }); + + describe('openAIStreamReducer', () => { + it('should correctly reduce stream chunks for basic chat completion', () => { + const state = {...openAIStreamReducer.initialState}; + + const chunks = [ + { + id: 'test-id', + object: 'chat.completion.chunk', + created: 1234567890, + model: 'gpt-4', + choices: [ + { + index: 0, + delta: { + role: 'assistant', + content: 'Hello', + }, + }, + ], + }, + { + choices: [ + { + index: 0, + delta: { + content: ' world!', + }, + finish_reason: 'stop', + }, + ], + usage: { + prompt_tokens: 10, + completion_tokens: 20, + total_tokens: 30, + }, + }, + ]; + + let finalState = state; + chunks.forEach(chunk => { + finalState = openAIStreamReducer.reduceFn(finalState, chunk); + }); + + expect(finalState).toEqual({ + id: 'test-id', + object: 'chat.completion.chunk', + created: 1234567890, + model: 'gpt-4', + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: 'Hello world!', + function_call: null, + }, + finish_reason: 'stop', + }, + ], + usage: { + prompt_tokens: 10, + completion_tokens: 20, + total_tokens: 30, + }, + }); + }); + + it('should handle function calls in stream chunks', () => { + const state = {...openAIStreamReducer.initialState}; + + const chunks = [ + { + id: 'func-call-id', + choices: [ + { + delta: { + role: 'assistant', + function_call: { + name: 'test_function', + }, + }, + }, + ], + }, + { + choices: [ + { + delta: { + function_call: { + arguments: '{"arg1":', + }, + }, + }, + ], + }, + { + choices: [ + { + delta: { + function_call: { + arguments: '"value1"}', + }, + }, + finish_reason: 'function_call', + }, + ], + }, + ]; + + let finalState = state; + chunks.forEach(chunk => { + finalState = openAIStreamReducer.reduceFn(finalState, chunk); + }); + + expect(finalState.choices[0].message.function_call).toEqual({ + name: 'test_function', + arguments: '{"arg1":"value1"}', + }); + expect(finalState.choices[0].finish_reason).toBe('function_call'); + }); + }); + + describe('wrapOpenAI', () => { + it('should wrap transparently', async () => { + const mockCreate = jest.fn(async params => ({ + id: 'test-id', + choices: [{message: {content: 'Hello'}}], + })); + + // Test both wrapped and unwrapped versions + mockOpenAI.chat.completions.create = mockCreate; + const unwrappedResult = await mockOpenAI.chat.completions.create({ + model: 'gpt-4', + messages: [{role: 'user', content: 'Hi'}], + }); + + const wrappedResult = await wrappedOpenAI.chat.completions.create({ + model: 'gpt-4', + messages: [{role: 'user', content: 'Hi'}], + }); + + // Verify wrapped matches unwrapped + expect(wrappedResult).toEqual(unwrappedResult); + }); + }); +}); + +describe('makeOpenAIImagesGenerateOp', () => { + it('converts b64_json images to WeaveImage objects and preserves other items', async () => { + const mockGenerate = jest.fn().mockResolvedValue({ + data: [ + { + url: 'https://example.com/image.png', + }, + { + b64_json: + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==', + }, + ], + }); + + const wrappedGenerate = makeOpenAIImagesGenerateOp(mockGenerate); + const result = await wrappedGenerate({prompt: 'draw a picture'}); + + // Verify the result structure + expect(result.data).toHaveLength(2); + + // First item should remain unchanged + expect(result.data[0]).toEqual({url: 'https://example.com/image.png'}); + + // Second item should be converted to WeaveImage + expect(isWeaveImage(result.data[1])).toBe(true); + expect(result.data[1].imageType).toBe('png'); + expect(Buffer.isBuffer(result.data[1].data)).toBe(true); + + // Verify the original function was called with correct args + expect(mockGenerate).toHaveBeenCalledWith({prompt: 'draw a picture'}); + }); +}); diff --git a/sdks/node/src/__tests__/integrations/openaiMock.test.ts b/sdks/node/src/__tests__/integrations/openaiMock.test.ts new file mode 100644 index 00000000000..1b931ea191f --- /dev/null +++ b/sdks/node/src/__tests__/integrations/openaiMock.test.ts @@ -0,0 +1,157 @@ +import {makeMockOpenAIChat} from '../openaiMock'; + +describe('OpenAI Mock', () => { + const mockResponse = (messages: any[]) => ({ + content: messages[0].content.toUpperCase(), + functionCalls: [ + { + name: 'test_function', + arguments: {arg: 'value'}, + }, + ], + }); + + test('non-streaming response', async () => { + const testOpenAIChat = makeMockOpenAIChat(mockResponse); + const response = await testOpenAIChat({ + messages: [{role: 'user', content: 'Hello, AI!'}], + }); + + expect(response).toEqual({ + id: expect.any(String), + object: 'chat.completion', + created: expect.any(Number), + model: 'gpt-4o-2024-05-13', + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: 'HELLO, AI!', + function_call: { + name: 'test_function', + arguments: '{"arg":"value"}', + }, + refusal: null, + }, + logprobs: null, + finish_reason: 'function_call', + }, + ], + usage: { + prompt_tokens: 2, + completion_tokens: 4, // 2 for content + 2 for function call + total_tokens: 6, + }, + system_fingerprint: expect.any(String), + }); + }); + + test('streaming response without include_usage', async () => { + const testOpenAIChat = makeMockOpenAIChat(mockResponse); + const stream = (await testOpenAIChat({ + messages: [{role: 'user', content: 'Hello, AI!'}], + stream: true, + })) as AsyncIterable; + + let chunks = []; + for await (const chunk of stream) { + chunks.push(chunk); + } + + expect(chunks.length).toBeGreaterThan(1); + expect(chunks[0]).toEqual({ + id: expect.any(String), + object: 'chat.completion.chunk', + created: expect.any(Number), + model: 'gpt-4o-2024-05-13', + system_fingerprint: expect.any(String), + choices: [ + { + index: 0, + delta: {role: 'assistant', content: '', refusal: null}, + logprobs: null, + finish_reason: null, + }, + ], + }); + expect(chunks[chunks.length - 1]).toEqual({ + id: expect.any(String), + object: 'chat.completion.chunk', + created: expect.any(Number), + model: 'gpt-4o-2024-05-13', + system_fingerprint: expect.any(String), + choices: [ + { + index: 0, + delta: {}, + logprobs: null, + finish_reason: 'function_call', + }, + ], + }); + expect(chunks.every(chunk => !('usage' in chunk))).toBe(true); + }); + + test('streaming response with include_usage', async () => { + const testOpenAIChat = makeMockOpenAIChat(mockResponse); + const stream = (await testOpenAIChat({ + messages: [{role: 'user', content: 'Hello, AI!'}], + stream: true, + stream_options: {include_usage: true}, + })) as AsyncIterable; + + let chunks = []; + for await (const chunk of stream) { + chunks.push(chunk); + } + + expect(chunks.length).toBeGreaterThan(1); + expect(chunks[0]).toEqual({ + id: expect.any(String), + object: 'chat.completion.chunk', + created: expect.any(Number), + model: 'gpt-4o-2024-05-13', + system_fingerprint: expect.any(String), + choices: [ + { + index: 0, + delta: {role: 'assistant', content: '', refusal: null}, + logprobs: null, + finish_reason: null, + }, + ], + usage: null, + }); + expect(chunks[chunks.length - 2]).toEqual({ + id: expect.any(String), + object: 'chat.completion.chunk', + created: expect.any(Number), + model: 'gpt-4o-2024-05-13', + system_fingerprint: expect.any(String), + choices: [ + { + index: 0, + delta: {}, + logprobs: null, + finish_reason: 'function_call', + }, + ], + usage: null, + }); + expect(chunks[chunks.length - 1]).toEqual({ + id: expect.any(String), + object: 'chat.completion.chunk', + created: expect.any(Number), + model: 'gpt-4o-2024-05-13', + system_fingerprint: expect.any(String), + choices: [], + usage: { + prompt_tokens: 2, + completion_tokens: 4, + total_tokens: 6, + }, + }); + expect(chunks.slice(0, -1).every(chunk => chunk.usage === null)).toBe(true); + }); +}); diff --git a/sdks/node/src/__tests__/live/dataset.test.ts b/sdks/node/src/__tests__/live/dataset.test.ts new file mode 100644 index 00000000000..e2b87f215e8 --- /dev/null +++ b/sdks/node/src/__tests__/live/dataset.test.ts @@ -0,0 +1,39 @@ +import {init, login} from '../../clientApi'; +import {Dataset} from '../../dataset'; + +describe('Dataset', () => { + beforeEach(async () => { + await login({apiKey: process.env.WANDB_API_KEY ?? ''}); + }); + + test('should save a dataset', async () => { + const client = await init('test-project'); + const data = [ + {id: 1, value: 2}, + {id: 2, value: 3}, + {id: 3, value: 4}, + ]; + + const dataset = new Dataset({rows: data}); + const ref = await dataset.save(); + + const [entity, project] = ref.projectId.split('/') ?? []; + expect(project).toBe('test-project'); + + // Dataset has same rows as the original data + expect(dataset.length).toBe(3); + + // TODO: idk why this fails in CI + // let i = 0; + // for await (const row of dataset) { + // expect(row).toEqual(data[i]); + // const rowRef = await row?.__savedRef; + // const [rowEntity, rowProject] = rowRef?.projectId.split('/') ?? []; + + // // Rows have refs back to the table + // expect(rowProject).toBe('test-project'); + // expect(rowRef?.digest).toBe(ref.digest); + // i++; + // } + }); +}); diff --git a/sdks/node/src/__tests__/live/fn.test.ts b/sdks/node/src/__tests__/live/fn.test.ts new file mode 100644 index 00000000000..20488144d89 --- /dev/null +++ b/sdks/node/src/__tests__/live/fn.test.ts @@ -0,0 +1,42 @@ +import {init, login} from '../../clientApi'; +import {CallableObject} from '../../fn'; +import {op} from '../../op'; +import {WeaveObjectParameters} from '../../weaveObject'; + +interface ParametrizedFunctionOptions extends WeaveObjectParameters { + magicNumber?: number; +} + +class ParametrizedFunction extends CallableObject< + {input: number}, + {output: number} +> { + private magicNumber: number; + + constructor(options: ParametrizedFunctionOptions = {}) { + super(options); + this.magicNumber = options.magicNumber ?? 42; + + this.run = op(this, this.run, { + parameterNames: ['input'], + }); + } + + async run(input: {input: number}): Promise<{output: number}> { + return {output: input.input + this.magicNumber}; + } +} + +describe('Fn', () => { + beforeEach(async () => { + await login({apiKey: process.env.WANDB_API_KEY ?? ''}); + }); + + test('use fn', async () => { + const client = await init('test-project'); + + const fn = new ParametrizedFunction({magicNumber: 7}); + const res = await fn.run({input: 1}); + expect(res).toEqual({output: 8}); + }); +}); diff --git a/sdks/node/src/__tests__/live/publish.test.ts b/sdks/node/src/__tests__/live/publish.test.ts new file mode 100644 index 00000000000..67edc1b5066 --- /dev/null +++ b/sdks/node/src/__tests__/live/publish.test.ts @@ -0,0 +1,83 @@ +import {init, login} from '../../clientApi'; +import {Dataset, op, weaveAudio, weaveImage} from '../../index'; + +describe('Publishing Various Data Types', () => { + beforeEach(async () => { + await login({apiKey: process.env.WANDB_API_KEY ?? ''}); + }); + + const primitiveOp = op(async function primitive(input: string) { + return `Hi ${input}!`; + }); + + const jsonOp = op(async function json(name: string, age: number) { + return {name, age}; + }); + + const imageOp = op(async function image() { + const width = 16; + const height = 16; + const buffer = Buffer.alloc(width * height * 4); // 4 bytes per pixel (RGBA) + + for (let i = 0; i < buffer.length; i++) { + buffer[i] = Math.floor(Math.random() * 256); + } + + return weaveImage({ + data: buffer, + imageType: 'png', + }); + }); + + const audioOp = op(async function audio() { + // Create a small audio buffer with random samples + const sampleRate = 44100; // Standard CD quality + const duration = 0.1; // 100ms + const numSamples = Math.floor(sampleRate * duration); + const buffer = Buffer.alloc(numSamples * 2); // 2 bytes per sample for 16-bit audio + + for (let i = 0; i < buffer.length; i += 2) { + // Generate random 16-bit sample between -32768 and 32767 + const sample = Math.floor(Math.random() * 65536 - 32768); + buffer.writeInt16LE(sample, i); + } + + return weaveAudio({ + data: buffer, + audioType: 'wav', + }); + }); + + const datasetOp = op(async function dataset() { + return new Dataset({ + id: 'my-dataset', + rows: [ + {name: 'Alice', age: 10}, + {name: 'Bob', age: 20}, + {name: 'Charlie', age: 30}, + ], + }); + }); + + test('publish various data types', async () => { + const client = await init('test-project'); + + const primitiveResult = await primitiveOp('world'); + expect(primitiveResult).toBe('Hi world!'); + + const jsonResult = await jsonOp('Alice', 10); + expect(jsonResult).toEqual({name: 'Alice', age: 10}); + + const imageResult = await imageOp(); + expect(imageResult).toHaveProperty('data'); + expect(imageResult).toHaveProperty('imageType', 'png'); + + const audioResult = await audioOp(); + expect(audioResult).toHaveProperty('data'); + expect(audioResult).toHaveProperty('audioType', 'wav'); + + const datasetResult = await datasetOp(); + expect(datasetResult).toBeInstanceOf(Dataset); + expect(datasetResult.rows).toHaveLength(3); + }); +}); diff --git a/sdks/node/src/__tests__/live/table.test.ts b/sdks/node/src/__tests__/live/table.test.ts new file mode 100644 index 00000000000..b5a02360b53 --- /dev/null +++ b/sdks/node/src/__tests__/live/table.test.ts @@ -0,0 +1,43 @@ +import {init, login} from '../../clientApi'; +import {Table} from '../../table'; + +describe('table', () => { + beforeEach(async () => { + await login({apiKey: process.env.WANDB_API_KEY ?? ''}); + }); + + test('example', async () => { + // Table behaves like a container of rows + const rows = [ + {a: 1, b: 2}, + {a: 3, b: 4}, + {a: 5, b: 6}, + ]; + + const table = new Table(rows); + expect(table.length).toEqual(rows.length); + let i = 0; + for await (const row of table) { + expect(row).toEqual(rows[i]); + i++; + } + + // Saving the table generates refs for the table and its rows + const client = await init('test-project'); + + (client as any).saveTable(table); // TODO: Saving a Table is not public... but maybe it should be? + const ref = await table.__savedRef; + + // not sure how to test entity here + // test that the ref is for the right entity, project + const [entity, project] = ref?.projectId.split('/') ?? []; + expect(project).toEqual('test-project'); + expect(ref?.uri()).toContain('test-project'); + + const row = table.row(0); + const ref2 = await (row as any).__savedRef; // TODO: This seems wrong... you have to cast to get the ref? I guess users would rarely do this... + const [entity2, project2, digest2] = ref2?.projectId.split('/') ?? []; + expect(project2).toEqual('test-project'); + expect(ref2?.uri()).toContain('test-project'); + }); +}); diff --git a/sdks/node/src/__tests__/live/weaveObject.test.ts b/sdks/node/src/__tests__/live/weaveObject.test.ts new file mode 100644 index 00000000000..fdf48ab95eb --- /dev/null +++ b/sdks/node/src/__tests__/live/weaveObject.test.ts @@ -0,0 +1,48 @@ +import {init, login} from '../../clientApi'; +import {op} from '../../op'; +import {WeaveObject} from '../../weaveObject'; + +class ExampleObject extends WeaveObject { + constructor( + public name: string, + public value: number + ) { + super({}); + + this.method = op(this.method); + } + + async method() { + return this.name + '!'; + } +} + +describe('weaveObject', () => { + beforeEach(async () => { + await login({apiKey: process.env.WANDB_API_KEY ?? ''}); + }); + + test('basic-example', async () => { + // TODO: Do we support saving basic objects? + // const client = await init('test-project'); + // const obj = { name: 'test', value: 1 }; + // client.saveObject(obj as any); + // const ref = await (obj as any).__savedRef; + // console.log(ref); + }); + + test('class-example', async () => { + const client = await init('test-project'); + const obj = new ExampleObject('test', 1); + + // save an object + client.publish(obj); + + const ref = await obj.__savedRef; + const [entity, project] = ref?.projectId.split('/') ?? []; + expect(project).toBe('test-project'); + console.log(ref); + + // also save its ops + }); +}); diff --git a/sdks/node/src/__tests__/login.test.ts b/sdks/node/src/__tests__/login.test.ts new file mode 100644 index 00000000000..8bdfcb19cfa --- /dev/null +++ b/sdks/node/src/__tests__/login.test.ts @@ -0,0 +1,70 @@ +import {login} from '../clientApi'; +import {Api as TraceServerApi} from '../generated/traceServerApi'; +import {getUrls} from '../urls'; +import {Netrc} from '../utils/netrc'; + +// Mock dependencies +jest.mock('../utils/netrc'); +jest.mock('../urls'); +jest.mock('../generated/traceServerApi'); + +describe('login', () => { + beforeEach(() => { + jest.clearAllMocks(); + console.log = jest.fn(); // Mock console.log + }); + + it('should successfully log in and save credentials', async () => { + (getUrls as jest.Mock).mockReturnValue({ + traceBaseUrl: 'https://api.wandb.ai', + domain: 'wandb.ai', + }); + + const mockSetEntry = jest.fn(); + const mockSave = jest.fn(); + (Netrc as jest.Mock).mockImplementation(() => ({ + setEntry: mockSetEntry, + save: mockSave, + })); + + (TraceServerApi as jest.Mock).mockImplementation(() => ({ + health: { + readRootHealthGet: jest.fn().mockResolvedValue({}), + }, + })); + + await login({apiKey: 'test-api-key'}); + + expect(mockSetEntry).toHaveBeenCalledWith('wandb.ai', { + login: 'user', + password: 'test-api-key', + }); + expect(mockSave).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalledWith( + 'Successfully logged in. Credentials saved for wandb.ai' + ); + }); + + it('should throw an error if API key is not provided', async () => { + await expect(login()).rejects.toThrow('API Key must be specified'); + }); + + it('should throw an error if connection verification fails', async () => { + (getUrls as jest.Mock).mockReturnValue({ + traceBaseUrl: 'https://api.wandb.ai', + domain: 'wandb.ai', + }); + + (TraceServerApi as jest.Mock).mockImplementation(() => ({ + health: { + readRootHealthGet: jest + .fn() + .mockRejectedValue(new Error('Connection failed')), + }, + })); + + await expect(login({apiKey: 'test-api-key'})).rejects.toThrow( + 'Unable to verify connection to the weave trace server with given API Key' + ); + }); +}); diff --git a/sdks/node/src/__tests__/media.test.ts b/sdks/node/src/__tests__/media.test.ts new file mode 100644 index 00000000000..84c96833314 --- /dev/null +++ b/sdks/node/src/__tests__/media.test.ts @@ -0,0 +1,21 @@ +import {weaveAudio, weaveImage} from '../media'; + +describe('media', () => { + test('logging weaveImage', () => { + const imageBuffer = Buffer.from('mock image data'); + const image = weaveImage({data: imageBuffer}); + + expect(image).toHaveProperty('_weaveType', 'Image'); + expect(image).toHaveProperty('data', imageBuffer); + expect(image).toHaveProperty('imageType', 'png'); + }); + + test('logging weaveAudio', () => { + const audioBuffer = Buffer.from('mock audio data'); + const audio = weaveAudio({data: audioBuffer}); + + expect(audio).toHaveProperty('_weaveType', 'Audio'); + expect(audio).toHaveProperty('data', audioBuffer); + expect(audio).toHaveProperty('audioType', 'wav'); + }); +}); diff --git a/sdks/node/src/__tests__/opFlow.test.ts b/sdks/node/src/__tests__/opFlow.test.ts new file mode 100644 index 00000000000..39f5d872cd0 --- /dev/null +++ b/sdks/node/src/__tests__/opFlow.test.ts @@ -0,0 +1,354 @@ +import {InMemoryTraceServer} from '../inMemoryTraceServer'; +import {makeOpenAIChatCompletionsOp} from '../integrations/openai'; +import {op} from '../op'; +import {initWithCustomTraceServer} from './clientMock'; +import {makeMockOpenAIChat} from './openaiMock'; + +// Helper function to get calls +async function getCalls( + traceServer: InMemoryTraceServer, + projectId: string, + limit?: number, + filters?: any +) { + return traceServer.calls + .callsStreamQueryPost({ + project_id: projectId, + limit, + filters, + }) + .then(result => result.calls); +} + +describe('Op Flow', () => { + let inMemoryTraceServer: InMemoryTraceServer; + const testProjectName = 'test-project'; + + beforeEach(() => { + inMemoryTraceServer = new InMemoryTraceServer(); + initWithCustomTraceServer(testProjectName, inMemoryTraceServer); + }); + + test('end-to-end op flow', async () => { + // Create an inner op + const innerOp = op((x: number) => x * 2, {name: 'innerOp'}); + + // Create an outer op that calls the inner op + const outerOp = op( + async (x: number) => { + const result1 = await innerOp(x); + const result2 = await innerOp(result1); + return result2; + }, + {name: 'outerOp'} + ); + + // Call the outer op a couple of times + await outerOp(5); + await outerOp(10); + + // Wait for any pending batch processing + await new Promise(resolve => setTimeout(resolve, 300)); + + // Fetch the logged calls using the helper function + const calls = await getCalls(inMemoryTraceServer, testProjectName); + + // Assertions + expect(calls).toHaveLength(6); // 2 outer calls + 4 inner calls + + const outerCalls = calls.filter(call => call.op_name.includes('outerOp')); + const innerCalls = calls.filter(call => call.op_name.includes('innerOp')); + + expect(outerCalls).toHaveLength(2); + expect(innerCalls).toHaveLength(4); + + // Check the first outer call + expect(outerCalls[0].inputs).toEqual({arg0: 5}); + expect(outerCalls[0].output).toBe(20); + + // Check the second outer call + expect(outerCalls[1].inputs).toEqual({arg0: 10}); + expect(outerCalls[1].output).toBe(40); + + // Check that inner calls have correct parent_id + innerCalls.forEach(innerCall => { + expect( + outerCalls.some(outerCall => outerCall.id === innerCall.parent_id) + ).toBeTruthy(); + }); + + // Check that all calls have a trace_id + calls.forEach(call => { + expect(call.trace_id).toBeTruthy(); + }); + }); + + test('end-to-end async op flow with concurrency', async () => { + // Create an inner async op with a random delay + const innerAsyncOp = op( + async (x: number) => { + const delay = Math.random() * 50 + 10; // Random delay between 10-60ms + await new Promise(resolve => setTimeout(resolve, delay)); + return x * 2; + }, + {name: 'innerAsyncOp'} + ); + + // Create an outer async op that calls the inner async op + const outerAsyncOp = op( + async (x: number) => { + const result1 = await innerAsyncOp(x); + const result2 = await innerAsyncOp(result1); + return result2; + }, + {name: 'outerAsyncOp'} + ); + + // Call the outer async op concurrently with a small delay between calls + const [result1, result2] = await Promise.all([ + outerAsyncOp(5), + (async () => { + await new Promise(resolve => setTimeout(resolve, 5)); // 5ms delay + return outerAsyncOp(10); + })(), + ]); + + // Wait for any pending batch processing + await new Promise(resolve => setTimeout(resolve, 300)); + + // Fetch the logged calls using the helper function + const calls = await getCalls(inMemoryTraceServer, testProjectName); + + // Assertions + expect(calls).toHaveLength(6); // 2 outer calls + 4 inner calls + expect(result1).toBe(20); + expect(result2).toBe(40); + + const outerCalls = calls.filter(call => + call.op_name.includes('outerAsyncOp') + ); + const innerCalls = calls.filter(call => + call.op_name.includes('innerAsyncOp') + ); + + expect(outerCalls).toHaveLength(2); + expect(innerCalls).toHaveLength(4); + + // Check that outer calls have different start times + const outerStartTimes = outerCalls.map(call => + new Date(call.started_at).getTime() + ); + expect(outerStartTimes[0]).not.toBe(outerStartTimes[1]); + + // Check that inner calls have correct parent_id + innerCalls.forEach(innerCall => { + expect( + outerCalls.some(outerCall => outerCall.id === innerCall.parent_id) + ).toBeTruthy(); + }); + + // Check that all calls have a trace_id + calls.forEach(call => { + expect(call.trace_id).toBeTruthy(); + }); + + // Check that the duration of async calls is greater than 0 + calls.forEach(call => { + const duration = + new Date(call.ended_at!).getTime() - + new Date(call.started_at).getTime(); + expect(duration).toBeGreaterThan(0); + }); + + // Check that the calls are properly nested + outerCalls.forEach(outerCall => { + const outerStartTime = new Date(outerCall.started_at).getTime(); + const outerEndTime = new Date(outerCall.ended_at!).getTime(); + const relatedInnerCalls = innerCalls.filter( + innerCall => innerCall.parent_id === outerCall.id + ); + expect(relatedInnerCalls).toHaveLength(2); + relatedInnerCalls.forEach(innerCall => { + const innerStartTime = new Date(innerCall.started_at).getTime(); + const innerEndTime = new Date(innerCall.ended_at!).getTime(); + expect(innerStartTime).toBeGreaterThanOrEqual(outerStartTime); + expect(innerEndTime).toBeLessThanOrEqual(outerEndTime); + }); + }); + }); + + test('op with custom summary', async () => { + const customSummaryOp = op((x: number) => x * 2, { + name: 'customSummaryOp', + summarize: result => ({doubledValue: result}), + }); + + await customSummaryOp(5); + + // Wait for any pending batch processing + await new Promise(resolve => setTimeout(resolve, 300)); + + const calls = await getCalls(inMemoryTraceServer, testProjectName); + + expect(calls).toHaveLength(1); + expect(calls[0].op_name).toContain('customSummaryOp'); + expect(calls[0].inputs).toEqual({arg0: 5}); + expect(calls[0].output).toBe(10); + expect(calls[0].summary).toEqual({doubledValue: 10}); + }); + + test('openai-like op with token usage summary', async () => { + const testOpenAIChat = makeMockOpenAIChat(messages => ({ + content: messages[0].content.toUpperCase(), + })); + + const openaiLikeOp = makeOpenAIChatCompletionsOp( + testOpenAIChat, + 'testOpenAIChat' + ); + + await openaiLikeOp({messages: [{role: 'user', content: 'Hello, AI!'}]}); + + // Wait for any pending batch processing + await new Promise(resolve => setTimeout(resolve, 300)); + + const calls = await getCalls(inMemoryTraceServer, testProjectName); + + expect(calls).toHaveLength(1); + expect(calls[0].op_name).toContain('testOpenAIChat'); + expect(calls[0].inputs).toEqual({ + messages: [{role: 'user', content: 'Hello, AI!'}], + }); + expect(calls[0].output).toEqual({ + id: expect.any(String), + object: 'chat.completion', + created: expect.any(Number), + model: 'gpt-4o-2024-05-13', + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: 'HELLO, AI!', + function_call: null, + refusal: null, + }, + logprobs: null, + finish_reason: 'stop', + }, + ], + usage: { + prompt_tokens: 2, + completion_tokens: 2, + total_tokens: 4, + }, + system_fingerprint: expect.any(String), + }); + expect(calls[0].summary).toEqual({ + usage: { + 'gpt-4o-2024-05-13': { + requests: 1, + completion_tokens: 2, + prompt_tokens: 2, + total_tokens: 4, + }, + }, + }); + }); + + test('nested op calls with summaries', async () => { + const leafOp = op((x: number) => x, { + name: 'leafOp', + summarize: result => ({leaf: {count: 1, sum: result}}), + }); + + const midOp = op( + async (x: number, y: number) => { + const [res1, res2] = await Promise.all([leafOp(x), leafOp(y)]); + return res1 + res2; + }, + { + name: 'midOp', + summarize: result => ({mid: {count: 1, sum: result}}), + } + ); + + const rootOp = op( + async (a: number, b: number, c: number) => { + const [res1, res2] = await Promise.all([midOp(a, b), leafOp(c)]); + return res1 + res2; + }, + { + name: 'rootOp', + summarize: result => ({root: {count: 1, sum: result}}), + } + ); + + await rootOp(1, 2, 3); + + // Wait for any pending batch processing + await new Promise(resolve => setTimeout(resolve, 300)); + + const calls = await getCalls(inMemoryTraceServer, testProjectName); + + expect(calls).toHaveLength(5); // 1 root + 1 mid + 3 leaf calls + + const rootCall = calls.find(call => call.op_name.includes('rootOp')); + expect(rootCall).toBeDefined(); + expect(rootCall?.summary).toEqual({ + root: {count: 1, sum: 6}, + mid: {count: 1, sum: 3}, + leaf: {count: 3, sum: 6}, // This is correct: 3 leaf calls, sum of 1+2+3 + }); + + const midCall = calls.find(call => call.op_name.includes('midOp')); + expect(midCall).toBeDefined(); + expect(midCall?.summary).toEqual({ + mid: {count: 1, sum: 3}, + leaf: {count: 2, sum: 3}, // This is correct: 2 leaf calls within midOp, sum of 1+2 + }); + + const leafCalls = calls.filter(call => call.op_name.includes('leafOp')); + expect(leafCalls).toHaveLength(3); + + const leafCallsUnderMid = leafCalls.filter( + call => call.parent_id === midCall?.id + ); + const leafCallUnderRoot = leafCalls.find( + call => call.parent_id === rootCall?.id + ); + + expect(leafCallsUnderMid).toEqual( + expect.arrayContaining([ + expect.objectContaining({summary: {leaf: {count: 1, sum: 1}}}), + expect.objectContaining({summary: {leaf: {count: 1, sum: 2}}}), + ]) + ); + expect(leafCallsUnderMid).toHaveLength(2); + + expect(leafCallUnderRoot).toEqual( + expect.objectContaining({summary: {leaf: {count: 1, sum: 3}}}) + ); + + // Ensure we have exactly these three summaries + expect(leafCalls).toHaveLength(3); + + // Check parent-child relationships + expect(midCall?.parent_id).toBe(rootCall?.id); + expect(leafCallsUnderMid).toHaveLength(2); + expect(leafCallUnderRoot).toBeDefined(); + + // Ensure all leaf calls have either midCall or rootCall as parent + leafCalls.forEach(call => { + expect( + call.parent_id === midCall?.id || call.parent_id === rootCall?.id + ).toBeTruthy(); + }); + + // Check that all calls have the same trace_id + const traceId = rootCall?.trace_id; + calls.forEach(call => { + expect(call.trace_id).toBe(traceId); + }); + }); +}); diff --git a/sdks/node/src/__tests__/openaiMock.ts b/sdks/node/src/__tests__/openaiMock.ts new file mode 100644 index 00000000000..cec6b39dbbb --- /dev/null +++ b/sdks/node/src/__tests__/openaiMock.ts @@ -0,0 +1,217 @@ +function generateId() { + return 'chatcmpl-' + Math.random().toString(36).substr(2, 9); +} + +function generateSystemFingerprint() { + return 'fp_' + Math.random().toString(36).substr(2, 9); +} + +type FunctionCall = { + name: string; + arguments: Record; +}; + +type ResponseFn = (messages: any[]) => { + content: string; + functionCalls?: FunctionCall[]; +}; + +// Simple function to estimate token count +function estimateTokenCount(text: string): number { + return Math.ceil(text.split(/\s+/).length); // 1 token per word for testing +} + +export function makeMockOpenAIChat(responseFn: ResponseFn) { + return function openaiChatCompletionsCreate({ + messages, + stream = false, + model = 'gpt-4o-2024-05-13', + stream_options, + ...otherOptions + }: { + messages: any[]; + stream?: boolean; + model?: string; + stream_options?: {include_usage?: boolean}; + [key: string]: any; + }) { + const response = responseFn(messages); + const {content, functionCalls = []} = response; + + const promptTokens = messages.reduce( + (acc, msg) => acc + estimateTokenCount(msg.content), + 0 + ); + const completionTokens = + estimateTokenCount(content) + + functionCalls.reduce( + (acc, fc) => + acc + + estimateTokenCount(fc.name) + + estimateTokenCount(JSON.stringify(fc.arguments)), + 0 + ); + const totalTokens = promptTokens + completionTokens; + + if (stream) { + return { + [Symbol.asyncIterator]: async function* () { + yield* generateChunks( + content, + functionCalls, + model, + promptTokens, + completionTokens, + totalTokens, + stream_options + ); + }, + }; + } else { + return { + id: generateId(), + object: 'chat.completion', + created: Math.floor(Date.now() / 1000), + model: model, + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: content, + function_call: functionCalls[0] + ? { + name: functionCalls[0].name, + arguments: JSON.stringify(functionCalls[0].arguments), + } + : null, + refusal: null, + }, + logprobs: null, + finish_reason: functionCalls.length > 0 ? 'function_call' : 'stop', + }, + ], + usage: { + prompt_tokens: promptTokens, + completion_tokens: completionTokens, + total_tokens: totalTokens, + }, + system_fingerprint: generateSystemFingerprint(), + }; + } + }; +} + +function* generateChunks( + content: string, + functionCalls: FunctionCall[], + model: string, + promptTokens: number, + completionTokens: number, + totalTokens: number, + stream_options?: {include_usage?: boolean} +) { + const id = generateId(); + const systemFingerprint = generateSystemFingerprint(); + const created = Math.floor(Date.now() / 1000); + + const baseChunk = { + id, + object: 'chat.completion.chunk', + created, + model, + system_fingerprint: systemFingerprint, + }; + + const includeUsage = stream_options?.include_usage; + + // Initial chunk + yield { + ...baseChunk, + choices: [ + { + index: 0, + delta: {role: 'assistant', content: '', refusal: null}, + logprobs: null, + finish_reason: null, + }, + ], + ...(includeUsage && {usage: null}), + }; + + // Content chunks + const words = content.split(' '); + for (let i = 0; i < words.length; i++) { + yield { + ...baseChunk, + choices: [ + { + index: 0, + delta: {content: words[i] + (i < words.length - 1 ? ' ' : '')}, + logprobs: null, + finish_reason: null, + }, + ], + ...(includeUsage && {usage: null}), + }; + } + + // Function call chunks + for (const functionCall of functionCalls) { + yield { + ...baseChunk, + choices: [ + { + index: 0, + delta: {function_call: {name: functionCall.name, arguments: ''}}, + logprobs: null, + finish_reason: null, + }, + ], + ...(includeUsage && {usage: null}), + }; + + const args = JSON.stringify(functionCall.arguments); + for (let i = 0; i < args.length; i += 10) { + yield { + ...baseChunk, + choices: [ + { + index: 0, + delta: {function_call: {arguments: args.slice(i, i + 10)}}, + logprobs: null, + finish_reason: null, + }, + ], + ...(includeUsage && {usage: null}), + }; + } + } + + // Second to last chunk (finish_reason) + yield { + ...baseChunk, + choices: [ + { + index: 0, + delta: {}, + logprobs: null, + finish_reason: functionCalls.length > 0 ? 'function_call' : 'stop', + }, + ], + ...(includeUsage && {usage: null}), + }; + + // Final chunk with usage information (only if include_usage is true) + if (includeUsage) { + yield { + ...baseChunk, + choices: [], + usage: { + prompt_tokens: promptTokens, + completion_tokens: completionTokens, + total_tokens: totalTokens, + }, + }; + } +} diff --git a/sdks/node/src/__tests__/util/concurrentLimit.test.ts b/sdks/node/src/__tests__/util/concurrentLimit.test.ts new file mode 100644 index 00000000000..8b0b518f044 --- /dev/null +++ b/sdks/node/src/__tests__/util/concurrentLimit.test.ts @@ -0,0 +1,30 @@ +import {ConcurrencyLimiter} from '../../utils/concurrencyLimit'; + +describe('concurrency limiting', () => { + test('it works', async () => { + const limit = 2; + const numJobs = 10; + + const limiter = new ConcurrencyLimiter(limit); + let currentlyRunning = 0; + let maxConcurrent = 0; + + const mockJob = jest.fn(async () => { + currentlyRunning++; + expect(currentlyRunning).toBe(limiter.active); + maxConcurrent = Math.max(maxConcurrent, currentlyRunning); + await new Promise(resolve => setTimeout(resolve, 100)); + currentlyRunning--; + }); + + const limitedJob = limiter.limitFunction(mockJob); + + const promises = []; + for (let i = 0; i < numJobs; i++) { + promises.push(limitedJob()); + } + await Promise.all(promises); + + expect(maxConcurrent).toBeLessThanOrEqual(limit); + }); +}); diff --git a/sdks/node/src/__tests__/util/netrc.test.ts b/sdks/node/src/__tests__/util/netrc.test.ts new file mode 100644 index 00000000000..59d01538187 --- /dev/null +++ b/sdks/node/src/__tests__/util/netrc.test.ts @@ -0,0 +1,99 @@ +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import {Netrc} from '../../utils/netrc'; + +jest.mock('fs'); +jest.mock('os'); + +describe('Netrc', () => { + const mockHomedir = '/mock/home'; + const mockNetrcPath = path.join(mockHomedir, '.netrc'); + + beforeEach(() => { + jest.resetAllMocks(); + (os.homedir as jest.Mock).mockReturnValue(mockHomedir); + }); + + test('load parses netrc file correctly', () => { + const mockContent = ` + machine example.com + login user1 + password pass1 + machine api.example.com + login user2 + password pass2 + account acc2 + `; + (fs.readFileSync as jest.Mock).mockReturnValue(mockContent); + + const netrc = new Netrc(); + + expect(netrc.entries.size).toBe(2); + expect(netrc.getEntry('example.com')).toEqual({ + machine: 'example.com', + login: 'user1', + password: 'pass1', + }); + expect(netrc.getEntry('api.example.com')).toEqual({ + machine: 'api.example.com', + login: 'user2', + password: 'pass2', + account: 'acc2', + }); + }); + + test('load handles non-existent file', () => { + (fs.readFileSync as jest.Mock).mockImplementation(() => { + throw new Error('File not found'); + }); + + const netrc = new Netrc(); + expect(netrc.entries.size).toBe(0); + }); + + test('save writes entries correctly', () => { + const netrc = new Netrc(); + netrc.setEntry('example.com', {login: 'user1', password: 'pass1'}); + netrc.setEntry('api.example.com', { + login: 'user2', + password: 'pass2', + account: 'acc2', + }); + + netrc.save(); + + const expectedContent = `machine example.com + login user1 + password pass1 + +machine api.example.com + login user2 + password pass2 + account acc2 +`; + + expect(fs.writeFileSync).toHaveBeenCalledWith( + mockNetrcPath, + expectedContent, + {mode: 0o600} + ); + }); + + test('getLastEntry returns the last entry', () => { + const netrc = new Netrc(); + netrc.setEntry('example1.com', {login: 'user1', password: 'pass1'}); + netrc.setEntry('example2.com', {login: 'user2', password: 'pass2'}); + + expect(netrc.getLastEntry()).toEqual({ + machine: 'example2.com', + login: 'user2', + password: 'pass2', + }); + }); + + test('getLastEntry returns undefined for empty entries', () => { + const netrc = new Netrc(); + expect(netrc.getLastEntry()).toBeUndefined(); + }); +}); diff --git a/sdks/node/src/__tests__/util/retry.test.ts b/sdks/node/src/__tests__/util/retry.test.ts new file mode 100644 index 00000000000..1216c7c87cd --- /dev/null +++ b/sdks/node/src/__tests__/util/retry.test.ts @@ -0,0 +1,65 @@ +import {createFetchWithRetry} from '../../utils/retry'; + +describe('retry', () => { + let originalFetch: typeof global.fetch; + const mockSuccess = {ok: true, status: 200} as Response; + const mockFailure = {ok: false, status: 404} as Response; + const baseDelay = 2; + + beforeEach(() => { + originalFetch = global.fetch; + }); + + afterEach(() => { + jest.resetAllMocks(); + global.fetch = originalFetch; + }); + + test('fetch happy path', async () => { + const mockFetch = jest.fn(() => Promise.resolve(mockSuccess)); + global.fetch = mockFetch; + const fetchWithRetry = createFetchWithRetry({baseDelay}); + + const response = await fetchWithRetry('https://api.test.com'); + expect(response).toEqual(mockSuccess); + }); + + test('fetch intermittent failure then success', async () => { + const mockFetch = jest + .fn() + .mockResolvedValueOnce(mockFailure) + .mockResolvedValueOnce(mockFailure) + .mockResolvedValue(mockSuccess); + global.fetch = mockFetch; + const fetchWithRetry = createFetchWithRetry({baseDelay}); + + const response = await fetchWithRetry('https://api.test.com'); + expect(mockFetch).toHaveBeenCalledTimes(3); + expect(response).toEqual(mockSuccess); + }); + + test('fetch retry failure', async () => { + const maxRetries = 3; + + const mockFetch = jest.fn().mockResolvedValue(mockFailure); + global.fetch = mockFetch; + const fetchWithRetry = createFetchWithRetry({maxRetries, baseDelay}); + + const response = await fetchWithRetry('https://api.test.com'); + expect(mockFetch).toHaveBeenCalledTimes(maxRetries + 1); + expect(response).toEqual(mockFailure); + }); + + test('fetch exception then success', async () => { + const mockFetch = jest + .fn() + .mockRejectedValueOnce(new Error('test')) + .mockResolvedValue(mockSuccess); + global.fetch = mockFetch; + const fetchWithRetry = createFetchWithRetry({baseDelay}); + + const response = await fetchWithRetry('https://api.test.com'); + expect(mockFetch).toHaveBeenCalledTimes(2); + expect(response).toEqual(mockSuccess); + }); +}); diff --git a/sdks/node/src/__tests__/wandb/settings.test.ts b/sdks/node/src/__tests__/wandb/settings.test.ts new file mode 100644 index 00000000000..22b2f6c1b85 --- /dev/null +++ b/sdks/node/src/__tests__/wandb/settings.test.ts @@ -0,0 +1,81 @@ +import {Netrc} from '../../utils/netrc'; +import {getApiKey, getWandbConfigs} from '../../wandb/settings'; + +jest.mock('../../utils/netrc'); +const MockedNetrc = Netrc as jest.MockedClass; + +describe('settings', () => { + beforeEach(() => { + jest.clearAllMocks(); + delete process.env.WANDB_API_KEY; + }); + + describe('getApiKey', () => { + it('returns API key from environment variable', () => { + process.env.WANDB_API_KEY = 'test-api-key'; + expect(getApiKey('api.wandb.ai')).toBe('test-api-key'); + }); + + it('returns API key from netrc file', () => { + MockedNetrc.prototype.entries = new Map([ + [ + 'api.wandb.ai', + {machine: 'api.wandb.ai', login: 'user', password: 'netrc-api-key'}, + ], + ]); + expect(getApiKey('api.wandb.ai')).toBe('netrc-api-key'); + }); + + it('throws error when no API key is found', () => { + MockedNetrc.prototype.entries = new Map(); + expect(() => getApiKey('api.wandb.ai')).toThrow( + 'wandb API key not found' + ); + }); + }); + + describe('getWandbConfigs', () => { + it('returns correct config when netrc has entry', () => { + // Mock successful netrc entry + MockedNetrc.prototype.getLastEntry = jest.fn().mockReturnValue({ + machine: 'api.wandb.ai', + login: 'user', + password: 'test-api-key', + }); + MockedNetrc.prototype.entries = new Map([ + [ + 'api.wandb.ai', + {machine: 'api.wandb.ai', login: 'user', password: 'test-api-key'}, + ], + ]); + + const configs = getWandbConfigs(); + expect(configs).toEqual({ + apiKey: 'test-api-key', + baseUrl: expect.stringContaining('api.wandb.ai'), + traceBaseUrl: expect.stringContaining('https://trace.wandb.ai'), + domain: expect.any(String), + }); + }); + + it('throws error when no netrc entry is found', () => { + // Mock netrc with no entries + MockedNetrc.prototype.getLastEntry = jest.fn().mockReturnValue(null); + + expect(() => getWandbConfigs()).toThrow( + 'Could not find entry in netrc file' + ); + }); + + it('throws error when netrc throws error', () => { + // Mock netrc throwing error + MockedNetrc.prototype.getLastEntry = jest.fn().mockImplementation(() => { + throw new Error('Failed to read netrc'); + }); + + expect(() => getWandbConfigs()).toThrow( + 'Could not find entry in netrc file' + ); + }); + }); +}); diff --git a/sdks/node/src/__tests__/wandb/wandbServerApi.test.ts b/sdks/node/src/__tests__/wandb/wandbServerApi.test.ts new file mode 100644 index 00000000000..9ddf1109209 --- /dev/null +++ b/sdks/node/src/__tests__/wandb/wandbServerApi.test.ts @@ -0,0 +1,47 @@ +import {WandbServerApi} from '../../wandb/wandbServerApi'; + +const originalFetch = global.fetch; +const api = new WandbServerApi('https://api.wandb.ai', 'abcdef123456'); + +const mockGoodResponse = { + ok: true, + json: jest + .fn() + .mockResolvedValue({data: {viewer: {defaultEntity: {name: 'test'}}}}), +}; +const mockInvalidEntityResponse = { + ok: true, + json: jest.fn().mockResolvedValue({data: {viewer: {defaultEntity: {}}}}), +}; +const mockBadGQLResponse = { + ok: true, + json: jest.fn().mockResolvedValue({errors: [{message: 'problem'}]}), +}; + +describe('wandbServerApi', () => { + afterEach(() => { + global.fetch = originalFetch; + }); + + test('default entity happy path', async () => { + const mockFetch = jest.fn().mockResolvedValue(mockGoodResponse); + global.fetch = mockFetch; + + const result = await api.defaultEntityName(); + expect(result).toEqual('test'); + }); + + test('default entity error path', async () => { + const mockFetch = jest.fn().mockResolvedValue(mockInvalidEntityResponse); + global.fetch = mockFetch; + + await expect(api.defaultEntityName()).rejects.toThrow(/name not found/); + }); + + test('gql error path', async () => { + const mockFetch = jest.fn().mockResolvedValue(mockBadGQLResponse); + global.fetch = mockFetch; + + await expect(api.defaultEntityName()).rejects.toThrow(/GraphQL Error/); + }); +}); diff --git a/sdks/node/src/__tests__/weaveClient.test.ts b/sdks/node/src/__tests__/weaveClient.test.ts new file mode 100644 index 00000000000..3a3523daa20 --- /dev/null +++ b/sdks/node/src/__tests__/weaveClient.test.ts @@ -0,0 +1,230 @@ +import {ReadableStream} from 'stream/web'; +import {Api as TraceServerApi} from '../generated/traceServerApi'; +import {WandbServerApi} from '../wandb/wandbServerApi'; +import {WeaveClient} from '../weaveClient'; + +// Mock the TraceServerApi and WandbServerApi +jest.mock('../generated/traceServerApi'); +jest.mock('../wandb/wandbServerApi'); + +describe('WeaveClient', () => { + let client: WeaveClient; + let mockTraceServerApi: jest.Mocked>; + let mockWandbServerApi: jest.Mocked; + + beforeEach(() => { + mockTraceServerApi = { + calls: { + callsQueryStreamCallsStreamQueryPost: jest.fn(), + }, + } as any; + mockWandbServerApi = {} as any; + client = new WeaveClient( + mockTraceServerApi, + mockWandbServerApi, + 'test-project' + ); + }); + + describe('getCalls', () => { + it('should fetch and return calls', async () => { + const mockCalls = [ + {id: '1', name: 'call1'}, + {id: '2', name: 'call2'}, + ]; + const encoder = new TextEncoder(); + const stream = new ReadableStream({ + start(controller) { + mockCalls.forEach(call => { + controller.enqueue(encoder.encode(JSON.stringify(call) + '\n')); + }); + controller.close(); + }, + }); + ( + mockTraceServerApi.calls + .callsQueryStreamCallsStreamQueryPost as jest.Mock + ).mockResolvedValue({ + body: stream, + } as any); + + // Call the method + const filter = {}; + const includeCosts = true; + const limit = 500; + const result = await client.getCalls(filter, includeCosts, limit); + + // Verify the results + expect(result).toEqual(mockCalls); + expect( + mockTraceServerApi.calls.callsQueryStreamCallsStreamQueryPost + ).toHaveBeenCalledWith({ + project_id: 'test-project', + filter, + include_costs: includeCosts, + limit, + }); + }); + + it('should handle remaining buffer data after stream ends', async () => { + const encoder = new TextEncoder(); + const stream = new ReadableStream({ + start(controller) { + // Send data without newline at the end + controller.enqueue(encoder.encode('{"id": "1"}\n{"id": "2"}')); + controller.close(); + }, + }); + + ( + mockTraceServerApi.calls + .callsQueryStreamCallsStreamQueryPost as jest.Mock + ).mockResolvedValue({ + body: stream, + } as any); + + const result = await client.getCalls(); + + // Should process both objects, including the one without newline + expect(result).toEqual([{id: '1'}, {id: '2'}]); + }); + }); + + describe('Batch Processing', () => { + let client: WeaveClient; + let mockTraceServerApi: jest.Mocked>; + let mockWandbServerApi: jest.Mocked; + + beforeEach(() => { + mockTraceServerApi = { + call: { + callStartBatchCallUpsertBatchPost: jest.fn(), + }, + } as any; + mockWandbServerApi = {} as any; + client = new WeaveClient( + mockTraceServerApi, + mockWandbServerApi, + 'test-project' + ); + // Speed up tests by reducing batch interval + (client as any).BATCH_INTERVAL = 10; + }); + + it('should batch multiple calls together', async () => { + // Add test calls to queue + (client as any).callQueue.push( + {mode: 'start', data: {id: '1'}}, + {mode: 'start', data: {id: '2'}} + ); + + await (client as any).processBatch(); + + expect( + mockTraceServerApi.call.callStartBatchCallUpsertBatchPost + ).toHaveBeenCalledWith({ + batch: [ + {mode: 'start', req: {id: '1'}}, + {mode: 'start', req: {id: '2'}}, + ], + }); + expect((client as any).callQueue.length).toBe(0); + + (client as any).callQueue.push( + {mode: 'start', data: {id: '3'}}, + {mode: 'start', data: {id: '4'}}, + {mode: 'start', data: {id: '5'}} + ); + + await (client as any).processBatch(); + + expect( + mockTraceServerApi.call.callStartBatchCallUpsertBatchPost + ).toHaveBeenCalledWith({ + batch: [ + {mode: 'start', req: {id: '3'}}, + {mode: 'start', req: {id: '4'}}, + {mode: 'start', req: {id: '5'}}, + ], + }); + expect((client as any).callQueue.length).toBe(0); + + expect( + mockTraceServerApi.call.callStartBatchCallUpsertBatchPost + ).toHaveBeenCalledTimes(2); + }); + + it('should handle API errors gracefully', async () => { + const mockConsoleError = jest + .spyOn(console, 'error') + .mockImplementation(); + + // Add multiple items to queue + const items = [ + {mode: 'start', data: {id: '1'}}, + {mode: 'start', data: {id: '2'}}, + ]; + (client as any).callQueue.push(...items); + + // First API call fails + ( + mockTraceServerApi.call.callStartBatchCallUpsertBatchPost as jest.Mock + ).mockRejectedValueOnce(new Error('API Error')); + + await (client as any).processBatch(); + + // Should log error but continue processing, with failed items back in queue + expect(mockConsoleError).toHaveBeenCalledWith( + 'Error processing batch:', + expect.any(Error) + ); + expect((client as any).callQueue).toEqual(items); + + // Second API call succeeds + ( + mockTraceServerApi.call.callStartBatchCallUpsertBatchPost as jest.Mock + ).mockResolvedValueOnce({}); + + await (client as any).processBatch(); + + // Verify items were processed in original order + expect( + mockTraceServerApi.call.callStartBatchCallUpsertBatchPost + ).toHaveBeenCalledWith({ + batch: [ + {mode: 'start', req: {id: '1'}}, + {mode: 'start', req: {id: '2'}}, + ], + }); + expect((client as any).callQueue.length).toBe(0); + + mockConsoleError.mockRestore(); + }); + + it('should prevent concurrent batch processing', async () => { + (client as any).isBatchProcessing = true; + (client as any).scheduleBatchProcessing(); + expect((client as any).batchProcessTimeout).toBeNull(); + }); + + it('should wait for all pending batches', async () => { + // Simulate slow API + ( + mockTraceServerApi.call.callStartBatchCallUpsertBatchPost as jest.Mock + ).mockImplementation( + () => new Promise(resolve => setTimeout(resolve, 50)) + ); + + (client as any).callQueue.push( + {mode: 'start', data: {id: '1'}}, + {mode: 'start', data: {id: '2'}} + ); + + (client as any).scheduleBatchProcessing(); + await client.waitForBatchProcessing(); + + expect((client as any).batchProcessingPromises.size).toBe(0); + expect((client as any).callQueue.length).toBe(0); + }); + }); +}); diff --git a/sdks/node/src/clientApi.ts b/sdks/node/src/clientApi.ts new file mode 100644 index 00000000000..499322e8d5b --- /dev/null +++ b/sdks/node/src/clientApi.ts @@ -0,0 +1,159 @@ +import {Api as TraceServerApi} from './generated/traceServerApi'; +import {Settings} from './settings'; +import {getUrls, setGlobalDomain} from './urls'; +import {ConcurrencyLimiter} from './utils/concurrencyLimit'; +import {Netrc} from './utils/netrc'; +import {createFetchWithRetry} from './utils/retry'; +import {getWandbConfigs} from './wandb/settings'; +import {WandbServerApi} from './wandb/wandbServerApi'; +import {CallStackEntry, WeaveClient} from './weaveClient'; + +export interface LoginOptions { + apiKey: string; + host?: string; +} + +// Global client instance +export let globalClient: WeaveClient | null = null; + +/** + * Log in to Weights & Biases (W&B) using the provided API key. + * This function saves the credentials to your netrc file for future use. + * + * @param options - The login options. + * @param options.apiKey - Your W&B API key. + * @param options.host - (Optional) The host name (usually only needed if you're using a custom W&B server). + * @throws {Error} If the API key is not specified or if the connection to the weave trace server cannot be verified. + */ +export async function login(options?: LoginOptions) { + if (!options?.apiKey) { + throw Error('API Key must be specified'); + } + const {traceBaseUrl, domain} = getUrls(options?.host); + + // Test the connection to the traceServerApi + const testTraceServerApi = new TraceServerApi({ + baseUrl: traceBaseUrl, + baseApiParams: { + headers: { + 'User-Agent': `W&B Weave JS Client ${process.env.VERSION || 'unknown'}`, + Authorization: `Basic ${Buffer.from(`api:${options.apiKey}`).toString('base64')}`, + }, + }, + }); + try { + await testTraceServerApi.health.readRootHealthGet({}); + } catch (error) { + throw new Error( + 'Unable to verify connection to the weave trace server with given API Key' + ); + } + + const netrc = new Netrc(); + netrc.setEntry(domain, {login: 'user', password: options.apiKey}); + netrc.save(); + console.log(`Successfully logged in. Credentials saved for ${domain}`); +} + +/** + * Initialize the Weave client, which is required for weave tracing to work. + * + * @param project - The W&B project name (can be project or entity/project). + * @param settings - (Optional) Weave tracing settings + * @returns A promise that resolves to the initialized Weave client. + * @throws {Error} If the initialization fails + */ +export async function init( + project: string, + settings?: Settings +): Promise { + const {apiKey, baseUrl, traceBaseUrl, domain} = getWandbConfigs(); + try { + const wandbServerApi = new WandbServerApi(baseUrl, apiKey); + + let entityName: string | undefined; + let projectName: string; + if (project.includes('/')) { + [entityName, projectName] = project.split('/'); + } else { + entityName = await wandbServerApi.defaultEntityName(); + projectName = project; + } + const projectId = `${entityName}/${projectName}`; + + const retryFetch = createFetchWithRetry({ + baseDelay: 1000, + maxDelay: 5 * 60 * 1000, // 5 minutes + maxRetryTime: 12 * 60 * 60 * 1000, // 12 hours + retryOnStatus: (status: number) => + status === 429 || (status >= 500 && status < 600), + }); + const concurrencyLimiter = new ConcurrencyLimiter(20); + const concurrencyLimitedFetch = concurrencyLimiter.limitFunction( + async (...fetchParams: Parameters) => { + const result = await retryFetch(...fetchParams); + // Useful for debugging + // console.log(`Active: ${concurrencyLimiter.active} Pending: ${concurrencyLimiter.pending}`); + return result; + } + ); + + const traceServerApi = new TraceServerApi({ + baseUrl: traceBaseUrl, + baseApiParams: { + headers: { + 'User-Agent': `W&B Weave JS Client ${process.env.VERSION || 'unknown'}`, + Authorization: `Basic ${Buffer.from(`api:${apiKey}`).toString('base64')}`, + }, + }, + customFetch: concurrencyLimitedFetch, + }); + + const client = new WeaveClient( + traceServerApi, + wandbServerApi, + projectId, + settings + ); + setGlobalClient(client); + setGlobalDomain(domain); + console.log(`Initializing project: ${projectId}`); + return client; + } catch (error) { + console.error('Error during initialization:', error); + throw error; + } +} + +export function requireCurrentCallStackEntry(): CallStackEntry { + const client = getGlobalClient(); + if (!client) { + throw new Error('Weave client not initialized'); + } + const callStackEntry = client.getCallStack().peek(); + if (!callStackEntry) { + throw new Error('No current call stack entry'); + } + return callStackEntry; +} + +export function requireCurrentChildSummary(): {[key: string]: any} { + const callStackEntry = requireCurrentCallStackEntry(); + return callStackEntry.childSummary; +} + +export function getGlobalClient(): WeaveClient | null { + return globalClient; +} + +export function requireGlobalClient(): WeaveClient { + const client = getGlobalClient(); + if (!client) { + throw new Error('Weave client not initialized'); + } + return client; +} + +export function setGlobalClient(client: WeaveClient) { + globalClient = client; +} diff --git a/sdks/node/src/constants.ts b/sdks/node/src/constants.ts new file mode 100644 index 00000000000..726f5343a86 --- /dev/null +++ b/sdks/node/src/constants.ts @@ -0,0 +1,2 @@ +export const TRACE_CALL_EMOJI = '🍩'; +export const TRACE_OBJECT_EMOJI = '📦'; diff --git a/sdks/node/src/dataset.ts b/sdks/node/src/dataset.ts new file mode 100644 index 00000000000..ebcc2addc6d --- /dev/null +++ b/sdks/node/src/dataset.ts @@ -0,0 +1,92 @@ +import {requireGlobalClient} from './clientApi'; +import {Table} from './table'; +import {ObjectRef, WeaveObject, WeaveObjectParameters} from './weaveObject'; + +interface DatasetParameters + extends WeaveObjectParameters { + rows: R[]; +} + +export class DatasetRowRef { + constructor( + public projectId: string, + public objId: string, + public digest: string, + public rowDigest: string + ) {} + + public uri() { + return `weave:///${this.projectId}/object/${this.objId}:${this.digest}/attr/rows/id/${this.rowDigest}`; + } +} + +export type DatasetRow = Record & { + __savedRef?: DatasetRowRef | Promise; +}; + +/** + * Dataset object with easy saving and automatic versioning + * + * @example + * // Create a dataset + * const dataset = new Dataset({ + * id: 'grammar-dataset', + * rows: [ + * { id: '0', sentence: "He no likes ice cream.", correction: "He doesn't like ice cream." }, + * { id: '1', sentence: "She goed to the store.", correction: "She went to the store." }, + * { id: '2', sentence: "They plays video games all day.", correction: "They play video games all day." } + * ] + * }) + * + * // Access a specific example + * const exampleLabel = dataset.getRow(2).sentence; + * + * // Save the dataset + * const ref = await dataset.save() + * + */ +export class Dataset extends WeaveObject { + public rows: Table; + + constructor(parameters: DatasetParameters) { + const baseParameters = { + id: parameters.id, + description: parameters.description, + }; + super(baseParameters); + this.rows = new Table(parameters.rows); + } + + async save(): Promise { + return requireGlobalClient().publish(this); + } + + get length(): number { + return this.rows.length; + } + + async *[Symbol.asyncIterator](): AsyncIterator { + for (let i = 0; i < this.length; i++) { + yield this.getRow(i); + } + } + + getRow(index: number): R { + const tableRow = this.rows.row(index); + const datasetRow: R = {...tableRow, __savedRef: undefined}; + if (this.__savedRef && tableRow.__savedRef) { + datasetRow.__savedRef = Promise.all([ + this.__savedRef, + tableRow.__savedRef, + ]).then(([ref, tableRowRef]) => { + return new DatasetRowRef( + ref.projectId, + ref.objectId, + ref.digest, + tableRowRef.rowDigest + ); + }); + } + return datasetRow; + } +} diff --git a/sdks/node/src/digest.ts b/sdks/node/src/digest.ts new file mode 100644 index 00000000000..0d281a601df --- /dev/null +++ b/sdks/node/src/digest.ts @@ -0,0 +1,49 @@ +import {Buffer} from 'buffer'; +import crypto from 'crypto'; + +export function computeDigest(data: Buffer): string { + // Must match python server algorithm in clickhouse_trace_server_batched.py + const hasher = crypto.createHash('sha256'); + hasher.update(data); + const hashBytes = hasher.digest(); + const base64EncodedHash = hashBytes.toString('base64url'); + return base64EncodedHash + .replace(/-/g, 'X') + .replace(/_/g, 'Y') + .replace(/=/g, ''); +} + +export function stringDigest(data: string): string { + return computeDigest(Buffer.from(data)); +} + +export function encodeNumber(num: number): string { + return String(num); +} + +export function stringifyPythonDumps(obj: any): string { + if (obj === null) { + return 'null'; + } + if (typeof obj === 'string') { + return JSON.stringify(obj); + } + if (typeof obj === 'number' || typeof obj === 'boolean') { + return String(obj); + } + if (Array.isArray(obj)) { + const items = obj.map(stringifyPythonDumps); + return '[' + items.join(', ') + ']'; + } + if (typeof obj === 'object') { + const pairs = Object.keys(obj) + .sort() + .map(key => JSON.stringify(key) + ': ' + stringifyPythonDumps(obj[key])); + return '{' + pairs.join(', ') + '}'; + } + throw new Error('Unsupported type'); +} + +export function valDigest(data: any): string { + return stringDigest(stringifyPythonDumps(data)); +} diff --git a/sdks/node/src/evaluation.ts b/sdks/node/src/evaluation.ts new file mode 100644 index 00000000000..bcbfbc4b1c6 --- /dev/null +++ b/sdks/node/src/evaluation.ts @@ -0,0 +1,345 @@ +import cliProgress from 'cli-progress'; +import {Dataset, DatasetRow} from './dataset'; +import {ColumnMapping, mapArgs} from './fn'; +import {isMedia} from './media'; +import {op} from './op'; +import {Op, getOpName} from './opType'; +import {WeaveObject, WeaveObjectParameters} from './weaveObject'; + +const PROGRESS_BAR = false; + +// Column mapping takes a dataset row of type R and maps it to a scorer's dataset row of type E +interface EvaluationParameters + extends WeaveObjectParameters { + dataset: Dataset; + scorers: WeaveCallable<(...args: [{datasetRow: E; modelOutput: M}]) => any>[]; + maxConcurrency?: number; + columnMapping?: ColumnMapping; +} + +interface Runnable any> { + id: string; + invoke: (...args: Parameters) => ReturnType; +} + +type WeaveCallable any> = Op | Runnable; + +function callWeaveCallable any>( + callable: WeaveCallable, + ...args: Parameters +) { + if (typeof callable === 'function') { + return callable(...args); + } + return callable.invoke(...args); +} + +function weaveCallableName any>( + callable: WeaveCallable +) { + if (typeof callable === 'function') { + return getOpName(callable); + } + return callable.id; +} + +async function* repeatAsyncIterator( + asyncIterator: AsyncIterable, + repeatCount: number +) { + for (let i = 0; i < repeatCount; i++) { + yield* asyncIterator; + } +} + +async function* asyncParallelMap( + asyncIterator: AsyncIterable, + fn: (item: T, ...args: any[]) => Promise, + fnParams: (item: T) => any[], + maxConcurrency: number +) { + const itemPromiseMap: Map< + T, + Promise<{item: T; result: Awaited}> + > = new Map(); + async function runOne(item: T) { + return { + item, + // @ts-ignore + result: await fn(...fnParams(item)), + }; + } + let nDone = 0; + for await (const item of asyncIterator) { + if (itemPromiseMap.size >= maxConcurrency) { + const done = await Promise.race(itemPromiseMap.values()); + itemPromiseMap.delete(done.item); + yield { + ...done, + nRunning: itemPromiseMap.size, + nDone: ++nDone, + }; + } + const prom = runOne(item); + itemPromiseMap.set(item, prom); + } + + // Flush remaining items + while (itemPromiseMap.size > 0) { + const done = await Promise.race(itemPromiseMap.values()); + itemPromiseMap.delete(done.item); + yield { + ...done, + nRunning: itemPromiseMap.size, + nDone: ++nDone, + }; + } +} + +/** + * Sets up an evaluation which includes a set of scorers and a dataset. + * + * Calling evaluation.evaluate(model) will pass in rows form a dataset into a model matching + * the names of the columns of the dataset to the argument names in model.predict. + * + * Then it will call all of the scorers and save the results in weave. + * + * @example + * // Collect your examples into a dataset + * const dataset = new weave.Dataset({ + * id: 'my-dataset', + * rows: [ + * { question: 'What is the capital of France?', expected: 'Paris' }, + * { question: 'Who wrote "To Kill a Mockingbird"?', expected: 'Harper Lee' }, + * { question: 'What is the square root of 64?', expected: '8' }, + * ], + * }); + * + * // Define any custom scoring function + * const scoringFunction = weave.op(function isEqual({ modelOutput, datasetRow }) { + * return modelOutput == datasetRow.expected; + * }); + * + * // Define the function to evaluate + * const model = weave.op(async function alwaysParisModel({ question }) { + * return 'Paris'; + * }); + * + * // Start evaluating + * const evaluation = new weave.Evaluation({ + * id: 'my-evaluation', + * dataset: dataset, + * scorers: [scoringFunction], + * }); + * + * const results = await evaluation.evaluate({ model }); + */ +export class Evaluation< + R extends DatasetRow, + E extends DatasetRow, + M, +> extends WeaveObject { + private dataset: Dataset; + private scorers: WeaveCallable< + (...args: [{datasetRow: E; modelOutput: M}]) => any + >[]; + private columnMapping?: ColumnMapping; + + constructor(parameters: EvaluationParameters) { + super(parameters); + this.dataset = parameters.dataset; + this.scorers = parameters.scorers; + this.evaluate = op(this, this.evaluate, { + parameterNames: 'useParam0Object', + callDisplayName: inputs => + `${this.id}_${weaveCallableName(inputs.model)}`, + }); + this.predictAndScore = op(this, this.predictAndScore, { + parameterNames: 'useParam0Object', + }); + this.columnMapping = parameters.columnMapping; + } + + async evaluate({ + model, + nTrials = 1, + maxConcurrency = 5, + }: { + model: WeaveCallable<(...args: [{datasetRow: R}]) => Promise>; + nTrials?: number; + maxConcurrency?: number; + }) { + const results: Array<{ + model_output: M; + model_success: boolean; + model_latency: number; + [key: string]: any; + }> = []; + + const progressBar = new cliProgress.SingleBar({ + format: + 'Evaluating |{bar}| {percentage}% | ETA: {eta}s | {modelErrors} errors | {value}/{total} examples | {running} running', + barCompleteChar: '\u2588', + barIncompleteChar: '\u2591', + hideCursor: true, + }); + + if (PROGRESS_BAR) { + progressBar.start(this.dataset.length * nTrials, 0, { + running: 0, + modelErrors: 0, + }); + } + + let modelErrors = 0; + let datasetExamples = this.dataset; + if (nTrials > 1) { + // @ts-ignore + datasetExamples = repeatAsyncIterator(this.dataset, nTrials); + } + + for await (const {result, nRunning, nDone} of asyncParallelMap( + datasetExamples, + this.predictAndScore, + item => [{model, example: item, columnMapping: this.columnMapping}], + maxConcurrency + )) { + const {scores} = result; + console.log('>>>result', result); + results.push({ + model_success: result.model_success, + model_output: result.model_output, + ...scores, + model_latency: result.model_latency, + }); + modelErrors += result.model_success ? 0 : 1; + if (PROGRESS_BAR) { + progressBar.update(nDone, {running: nRunning, modelErrors}); + } else { + console.log( + `Evaluating ${nDone}/${this.dataset.length * nTrials} examples (${nRunning} running, ${modelErrors} errors)` + ); + } + } + + if (PROGRESS_BAR) { + progressBar.stop(); + } + + return this.summarizeResults(results); + } + + async predictAndScore({ + model, + example, + columnMapping, + }: { + model: WeaveCallable<(...args: [{datasetRow: E}]) => Promise>; + example: R; + columnMapping?: ColumnMapping; + }) { + const startTime = new Date(); + let modelOutput; + let modelError = false; + let datasetRow: E = example as unknown as E; + if (columnMapping) { + datasetRow = mapArgs(example, columnMapping) as E; + } + try { + modelOutput = await callWeaveCallable(model, {datasetRow}); + } catch (e) { + console.error(e); + modelError = true; + } + const endTime = new Date(); + const modelLatency = (endTime.getTime() - startTime.getTime()) / 1000; // Convert to seconds + + const scores: {[key: string]: any} = {}; + if (!modelError) { + for (const scorer of this.scorers) { + let score = undefined; + try { + score = await callWeaveCallable(scorer, {datasetRow, modelOutput}); + } catch (e) { + console.error(e); + } + scores[weaveCallableName(scorer)] = score; + } + } + + return { + model_success: !modelError, + model_output: modelOutput, + scores, + model_latency: modelLatency, + }; + } + + private summarizeResults( + results: Array<{ + model_output: any; + model_success: boolean; + model_latency: number; + [key: string]: any; + }> + ) { + const summarizeNestedObject = ( + results: Array + ): Record => { + const nestedSummary: Record = {}; + + // Get all unique keys from all results + const allKeys = new Set(results.flatMap(obj => Object.keys(obj ?? {}))); + + for (const key of allKeys) { + const values = results.map(result => + result == null ? null : result[key] + ); + if ( + values.some( + v => + typeof v === 'object' && + v !== null && + !Array.isArray(v) && + !isMedia(v) + ) + ) { + const result = summarizeNestedObject(values); + if (Object.keys(result).length > 0) { + nestedSummary[key] = result; + } + } else { + const columnSummary = this.summarizeColumn(values); + if (Object.keys(columnSummary).length > 0) { + nestedSummary[key] = columnSummary; + } + } + } + + return nestedSummary; + }; + + return summarizeNestedObject(results); + } + + private summarizeColumn(values: any[]): Record { + const nonNilValues = values.filter(v => v != null); + if (nonNilValues.length === 0) { + return {}; // Return an empty object if there are no valid values + } + + if (nonNilValues.every(v => typeof v === 'boolean')) { + const trueCount = nonNilValues.filter(v => v).length; + return { + true_count: trueCount, + true_fraction: values.length > 0 ? trueCount / values.length : 0, + }; + } else if (nonNilValues.every(v => typeof v === 'number')) { + const sum = nonNilValues.reduce((acc, v) => acc + v, 0); + return { + mean: values.length > 0 ? sum / values.length : 0, + }; + } + return {}; + } +} diff --git a/sdks/node/src/fn.ts b/sdks/node/src/fn.ts new file mode 100644 index 00000000000..be8d124e70a --- /dev/null +++ b/sdks/node/src/fn.ts @@ -0,0 +1,36 @@ +import {WeaveObject} from './weaveObject'; + +export type ColumnMapping = { + [K in keyof O]: keyof I; +}; +export type ArgsObject = {[key: string]: any}; +export type Row = {[key: string]: any}; + +export interface Callable { + run: (input: I) => Promise; +} +export type FnInputs> = + T extends Callable ? I : never; +export type FnOutput> = + T extends Callable ? O : never; + +export abstract class CallableObject + extends WeaveObject + implements Callable +{ + abstract run(input: I): Promise; +} + +export function mapArgs< + T extends Record, + M extends Record, +>(input: T, mapping: M): {[K in keyof M]: T[M[K]]} { + const result: Partial<{[K in keyof M]: T[M[K]]}> = {}; + + for (const [newKey, oldKey] of Object.entries(mapping)) { + if (oldKey in input) { + result[newKey as keyof M] = input[oldKey]; + } + } + return result as {[K in keyof M]: T[M[K]]}; +} diff --git a/sdks/node/src/generated/traceServerApi.ts b/sdks/node/src/generated/traceServerApi.ts new file mode 100644 index 00000000000..246a9b39a84 --- /dev/null +++ b/sdks/node/src/generated/traceServerApi.ts @@ -0,0 +1,1530 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +/** AndOperation */ +export interface AndOperation { + /** $And */ + $and: ( + | LiteralOperation + | GetFieldOperator + | ConvertOperation + | AndOperation + | OrOperation + | NotOperation + | EqOperation + | GtOperation + | GteOperation + | InOperation + | ContainsOperation + )[]; +} + +/** Body_file_create_file_create_post */ +export interface BodyFileCreateFileCreatePost { + /** Project Id */ + project_id: string; + /** + * File + * @format binary + */ + file: File; +} + +/** CallBatchEndMode */ +export interface CallBatchEndMode { + /** + * Mode + * @default "end" + */ + mode?: string; + req: CallEndReq; +} + +/** CallBatchStartMode */ +export interface CallBatchStartMode { + /** + * Mode + * @default "start" + */ + mode?: string; + req: CallStartReq; +} + +/** CallCreateBatchReq */ +export interface CallCreateBatchReq { + /** Batch */ + batch: (CallBatchStartMode | CallBatchEndMode)[]; +} + +/** CallCreateBatchRes */ +export interface CallCreateBatchRes { + /** Res */ + res: (CallStartRes | CallEndRes)[]; +} + +/** CallEndReq */ +export interface CallEndReq { + end: EndedCallSchemaForInsert; +} + +/** CallEndRes */ +export type CallEndRes = object; + +/** CallReadReq */ +export interface CallReadReq { + /** Project Id */ + project_id: string; + /** Id */ + id: string; + /** + * Include Costs + * @default false + */ + include_costs?: boolean | null; +} + +/** CallReadRes */ +export interface CallReadRes { + call: CallSchema | null; +} + +/** CallSchema */ +export interface CallSchema { + /** Id */ + id: string; + /** Project Id */ + project_id: string; + /** Op Name */ + op_name: string; + /** Display Name */ + display_name?: string | null; + /** Trace Id */ + trace_id: string; + /** Parent Id */ + parent_id?: string | null; + /** + * Started At + * @format date-time + */ + started_at: string; + /** Attributes */ + attributes: object; + /** Inputs */ + inputs: object; + /** Ended At */ + ended_at?: string | null; + /** Exception */ + exception?: string | null; + /** Output */ + output?: null; + summary?: object; + /** Wb User Id */ + wb_user_id?: string | null; + /** Wb Run Id */ + wb_run_id?: string | null; + /** Deleted At */ + deleted_at?: string | null; +} + +/** CallStartReq */ +export interface CallStartReq { + start: StartedCallSchemaForInsert; +} + +/** CallStartRes */ +export interface CallStartRes { + /** Id */ + id: string; + /** Trace Id */ + trace_id: string; +} + +/** CallUpdateReq */ +export interface CallUpdateReq { + /** Project Id */ + project_id: string; + /** Call Id */ + call_id: string; + /** Display Name */ + display_name?: string | null; + /** + * Wb User Id + * Do not set directly. Server will automatically populate this field. + */ + wb_user_id?: string | null; +} + +/** CallUpdateRes */ +export type CallUpdateRes = object; + +/** CallsDeleteReq */ +export interface CallsDeleteReq { + /** Project Id */ + project_id: string; + /** Call Ids */ + call_ids: string[]; + /** + * Wb User Id + * Do not set directly. Server will automatically populate this field. + */ + wb_user_id?: string | null; +} + +/** CallsDeleteRes */ +export type CallsDeleteRes = object; + +/** CallsFilter */ +export interface CallsFilter { + /** Op Names */ + op_names?: string[] | null; + /** Input Refs */ + input_refs?: string[] | null; + /** Output Refs */ + output_refs?: string[] | null; + /** Parent Ids */ + parent_ids?: string[] | null; + /** Trace Ids */ + trace_ids?: string[] | null; + /** Call Ids */ + call_ids?: string[] | null; + /** Trace Roots Only */ + trace_roots_only?: boolean | null; + /** Wb User Ids */ + wb_user_ids?: string[] | null; + /** Wb Run Ids */ + wb_run_ids?: string[] | null; +} + +/** CallsQueryReq */ +export interface CallsQueryReq { + /** Project Id */ + project_id: string; + filter?: CallsFilter | null; + /** Limit */ + limit?: number | null; + /** Offset */ + offset?: number | null; + /** Sort By */ + sort_by?: SortBy[] | null; + query?: Query | null; + /** + * Include Costs + * @default false + */ + include_costs?: boolean | null; + /** Columns */ + columns?: string[] | null; + /** + * Expand Columns + * Columns to expand, i.e. refs to other objects + */ + expand_columns?: string[] | null; +} + +/** CallsQueryStatsReq */ +export interface CallsQueryStatsReq { + /** Project Id */ + project_id: string; + filter?: CallsFilter | null; + query?: Query | null; +} + +/** CallsQueryStatsRes */ +export interface CallsQueryStatsRes { + /** Count */ + count: number; +} + +/** ContainsOperation */ +export interface ContainsOperation { + $contains: ContainsSpec; +} + +/** ContainsSpec */ +export interface ContainsSpec { + /** Input */ + input: + | LiteralOperation + | GetFieldOperator + | ConvertOperation + | AndOperation + | OrOperation + | NotOperation + | EqOperation + | GtOperation + | GteOperation + | InOperation + | ContainsOperation; + /** Substr */ + substr: + | LiteralOperation + | GetFieldOperator + | ConvertOperation + | AndOperation + | OrOperation + | NotOperation + | EqOperation + | GtOperation + | GteOperation + | InOperation + | ContainsOperation; + /** + * Case Insensitive + * @default false + */ + case_insensitive?: boolean | null; +} + +/** ConvertOperation */ +export interface ConvertOperation { + $convert: ConvertSpec; +} + +/** ConvertSpec */ +export interface ConvertSpec { + /** Input */ + input: + | LiteralOperation + | GetFieldOperator + | ConvertOperation + | AndOperation + | OrOperation + | NotOperation + | EqOperation + | GtOperation + | GteOperation + | InOperation + | ContainsOperation; + /** To */ + to: 'double' | 'string' | 'int' | 'bool' | 'exists'; +} + +/** EndedCallSchemaForInsert */ +export interface EndedCallSchemaForInsert { + /** Project Id */ + project_id: string; + /** Id */ + id: string; + /** + * Ended At + * @format date-time + */ + ended_at: string; + /** Exception */ + exception?: string | null; + /** Output */ + output?: null; + summary: SummaryInsertMap; +} + +/** EqOperation */ +export interface EqOperation { + /** + * $Eq + * @maxItems 2 + * @minItems 2 + */ + $eq: any[]; +} + +/** FeedbackCreateReq */ +export interface FeedbackCreateReq { + /** Project Id */ + project_id: string; + /** Weave Ref */ + weave_ref: string; + /** Creator */ + creator?: string | null; + /** Feedback Type */ + feedback_type: string; + /** Payload */ + payload: object; + /** + * Wb User Id + * Do not set directly. Server will automatically populate this field. + */ + wb_user_id?: string | null; +} + +/** FeedbackCreateRes */ +export interface FeedbackCreateRes { + /** Id */ + id: string; + /** + * Created At + * @format date-time + */ + created_at: string; + /** Wb User Id */ + wb_user_id: string; + /** Payload */ + payload: object; +} + +/** FeedbackPurgeReq */ +export interface FeedbackPurgeReq { + /** Project Id */ + project_id: string; + query: Query; +} + +/** FeedbackPurgeRes */ +export type FeedbackPurgeRes = object; + +/** FeedbackQueryReq */ +export interface FeedbackQueryReq { + /** Project Id */ + project_id: string; + /** Fields */ + fields?: string[] | null; + query?: Query | null; + /** Sort By */ + sort_by?: SortBy[] | null; + /** Limit */ + limit?: number | null; + /** Offset */ + offset?: number | null; +} + +/** FeedbackQueryRes */ +export interface FeedbackQueryRes { + /** Result */ + result: object[]; +} + +/** FileContentReadReq */ +export interface FileContentReadReq { + /** Project Id */ + project_id: string; + /** Digest */ + digest: string; +} + +/** FileCreateRes */ +export interface FileCreateRes { + /** Digest */ + digest: string; +} + +/** GetFieldOperator */ +export interface GetFieldOperator { + /** $Getfield */ + $getField: string; +} + +/** GtOperation */ +export interface GtOperation { + /** + * $Gt + * @maxItems 2 + * @minItems 2 + */ + $gt: any[]; +} + +/** GteOperation */ +export interface GteOperation { + /** + * $Gte + * @maxItems 2 + * @minItems 2 + */ + $gte: any[]; +} + +/** HTTPValidationError */ +export interface HTTPValidationError { + /** Detail */ + detail?: ValidationError[]; +} + +/** InOperation */ +export interface InOperation { + /** + * $In + * @maxItems 2 + * @minItems 2 + */ + $in: any[]; +} + +/** LLMUsageSchema */ +export interface LLMUsageSchema { + /** Prompt Tokens */ + prompt_tokens?: number | null; + /** Input Tokens */ + input_tokens?: number | null; + /** Completion Tokens */ + completion_tokens?: number | null; + /** Output Tokens */ + output_tokens?: number | null; + /** Requests */ + requests?: number | null; + /** Total Tokens */ + total_tokens?: number | null; +} + +/** LiteralOperation */ +export interface LiteralOperation { + /** $Literal */ + $literal: + | string + | number + | boolean + | Record + | LiteralOperation[] + | null; +} + +/** NotOperation */ +export interface NotOperation { + /** + * $Not + * @maxItems 1 + * @minItems 1 + */ + $not: any[]; +} + +/** ObjCreateReq */ +export interface ObjCreateReq { + obj: ObjSchemaForInsert; +} + +/** ObjCreateRes */ +export interface ObjCreateRes { + /** Digest */ + digest: string; +} + +/** ObjQueryReq */ +export interface ObjQueryReq { + /** Project Id */ + project_id: string; + filter?: ObjectVersionFilter | null; +} + +/** ObjQueryRes */ +export interface ObjQueryRes { + /** Objs */ + objs: ObjSchema[]; +} + +/** ObjReadReq */ +export interface ObjReadReq { + /** Project Id */ + project_id: string; + /** Object Id */ + object_id: string; + /** Digest */ + digest: string; +} + +/** ObjReadRes */ +export interface ObjReadRes { + obj: ObjSchema; +} + +/** ObjSchema */ +export interface ObjSchema { + /** Project Id */ + project_id: string; + /** Object Id */ + object_id: string; + /** + * Created At + * @format date-time + */ + created_at: string; + /** Deleted At */ + deleted_at?: string | null; + /** Digest */ + digest: string; + /** Version Index */ + version_index: number; + /** Is Latest */ + is_latest: number; + /** Kind */ + kind: string; + /** Base Object Class */ + base_object_class: string | null; + /** Val */ + val: any; +} + +/** ObjSchemaForInsert */ +export interface ObjSchemaForInsert { + /** Project Id */ + project_id: string; + /** Object Id */ + object_id: string; + /** Val */ + val: any; +} + +/** ObjectVersionFilter */ +export interface ObjectVersionFilter { + /** Base Object Classes */ + base_object_classes?: string[] | null; + /** Object Ids */ + object_ids?: string[] | null; + /** Is Op */ + is_op?: boolean | null; + /** Latest Only */ + latest_only?: boolean | null; +} + +/** OrOperation */ +export interface OrOperation { + /** $Or */ + $or: ( + | LiteralOperation + | GetFieldOperator + | ConvertOperation + | AndOperation + | OrOperation + | NotOperation + | EqOperation + | GtOperation + | GteOperation + | InOperation + | ContainsOperation + )[]; +} + +/** Query */ +export interface Query { + /** $Expr */ + $expr: + | AndOperation + | OrOperation + | NotOperation + | EqOperation + | GtOperation + | GteOperation + | InOperation + | ContainsOperation; +} + +/** RefsReadBatchReq */ +export interface RefsReadBatchReq { + /** Refs */ + refs: string[]; +} + +/** RefsReadBatchRes */ +export interface RefsReadBatchRes { + /** Vals */ + vals: any[]; +} + +/** ServerInfoRes */ +export interface ServerInfoRes { + /** Min Required Weave Python Version */ + min_required_weave_python_version: string; +} + +/** SortBy */ +export interface SortBy { + /** Field */ + field: string; + /** Direction */ + direction: 'asc' | 'desc'; +} + +/** StartedCallSchemaForInsert */ +export interface StartedCallSchemaForInsert { + /** Project Id */ + project_id: string; + /** Id */ + id?: string | null; + /** Op Name */ + op_name: string; + /** Display Name */ + display_name?: string | null; + /** Trace Id */ + trace_id?: string | null; + /** Parent Id */ + parent_id?: string | null; + /** + * Started At + * @format date-time + */ + started_at: string; + /** Attributes */ + attributes: object; + /** Inputs */ + inputs: object; + /** + * Wb User Id + * Do not set directly. Server will automatically populate this field. + */ + wb_user_id?: string | null; + /** Wb Run Id */ + wb_run_id?: string | null; +} + +/** SummaryInsertMap */ +export interface SummaryInsertMap { + /** Usage */ + usage?: Record; + [key: string]: any; +} + +/** TableAppendSpec */ +export interface TableAppendSpec { + append: TableAppendSpecPayload; +} + +/** TableAppendSpecPayload */ +export interface TableAppendSpecPayload { + /** Row */ + row: object; +} + +/** TableCreateReq */ +export interface TableCreateReq { + table: TableSchemaForInsert; +} + +/** TableCreateRes */ +export interface TableCreateRes { + /** Digest */ + digest: string; +} + +/** TableInsertSpec */ +export interface TableInsertSpec { + insert: TableInsertSpecPayload; +} + +/** TableInsertSpecPayload */ +export interface TableInsertSpecPayload { + /** Index */ + index: number; + /** Row */ + row: object; +} + +/** TablePopSpec */ +export interface TablePopSpec { + pop: TablePopSpecPayload; +} + +/** TablePopSpecPayload */ +export interface TablePopSpecPayload { + /** Index */ + index: number; +} + +/** TableQueryReq */ +export interface TableQueryReq { + /** Project Id */ + project_id: string; + /** Digest */ + digest: string; + filter?: TableRowFilter | null; + /** Limit */ + limit?: number | null; + /** Offset */ + offset?: number | null; +} + +/** TableQueryRes */ +export interface TableQueryRes { + /** Rows */ + rows: TableRowSchema[]; +} + +/** TableRowFilter */ +export interface TableRowFilter { + /** Row Digests */ + row_digests?: string[] | null; +} + +/** TableRowSchema */ +export interface TableRowSchema { + /** Digest */ + digest: string; + /** Val */ + val: any; +} + +/** TableSchemaForInsert */ +export interface TableSchemaForInsert { + /** Project Id */ + project_id: string; + /** Rows */ + rows: object[]; +} + +/** TableUpdateReq */ +export interface TableUpdateReq { + /** Project Id */ + project_id: string; + /** Base Digest */ + base_digest: string; + /** Updates */ + updates: (TableAppendSpec | TablePopSpec | TableInsertSpec)[]; +} + +/** TableUpdateRes */ +export interface TableUpdateRes { + /** Digest */ + digest: string; +} + +/** ValidationError */ +export interface ValidationError { + /** Location */ + loc: (string | number)[]; + /** Message */ + msg: string; + /** Error Type */ + type: string; +} + +export type QueryParamsType = Record; +export type ResponseFormat = keyof Omit; + +export interface FullRequestParams extends Omit { + /** set parameter to `true` for call `securityWorker` for this request */ + secure?: boolean; + /** request path */ + path: string; + /** content type of request body */ + type?: ContentType; + /** query params */ + query?: QueryParamsType; + /** format of response (i.e. response.json() -> format: "json") */ + format?: ResponseFormat; + /** request body */ + body?: unknown; + /** base url */ + baseUrl?: string; + /** request cancellation token */ + cancelToken?: CancelToken; +} + +export type RequestParams = Omit< + FullRequestParams, + 'body' | 'method' | 'query' | 'path' +>; + +export interface ApiConfig { + baseUrl?: string; + baseApiParams?: Omit; + securityWorker?: ( + securityData: SecurityDataType | null + ) => Promise | RequestParams | void; + customFetch?: typeof fetch; +} + +export interface HttpResponse + extends Response { + data: D; + error: E; +} + +type CancelToken = Symbol | string | number; + +export enum ContentType { + Json = 'application/json', + FormData = 'multipart/form-data', + UrlEncoded = 'application/x-www-form-urlencoded', + Text = 'text/plain', +} + +export class HttpClient { + public baseUrl: string = ''; + private securityData: SecurityDataType | null = null; + private securityWorker?: ApiConfig['securityWorker']; + private abortControllers = new Map(); + private customFetch = (...fetchParams: Parameters) => + fetch(...fetchParams); + + private baseApiParams: RequestParams = { + credentials: 'same-origin', + headers: {}, + redirect: 'follow', + referrerPolicy: 'no-referrer', + }; + + constructor(apiConfig: ApiConfig = {}) { + Object.assign(this, apiConfig); + } + + public setSecurityData = (data: SecurityDataType | null) => { + this.securityData = data; + }; + + protected encodeQueryParam(key: string, value: any) { + const encodedKey = encodeURIComponent(key); + return `${encodedKey}=${encodeURIComponent(typeof value === 'number' ? value : `${value}`)}`; + } + + protected addQueryParam(query: QueryParamsType, key: string) { + return this.encodeQueryParam(key, query[key]); + } + + protected addArrayQueryParam(query: QueryParamsType, key: string) { + const value = query[key]; + return value.map((v: any) => this.encodeQueryParam(key, v)).join('&'); + } + + protected toQueryString(rawQuery?: QueryParamsType): string { + const query = rawQuery || {}; + const keys = Object.keys(query).filter( + key => 'undefined' !== typeof query[key] + ); + return keys + .map(key => + Array.isArray(query[key]) + ? this.addArrayQueryParam(query, key) + : this.addQueryParam(query, key) + ) + .join('&'); + } + + protected addQueryParams(rawQuery?: QueryParamsType): string { + const queryString = this.toQueryString(rawQuery); + return queryString ? `?${queryString}` : ''; + } + + private contentFormatters: Record any> = { + [ContentType.Json]: (input: any) => + input !== null && (typeof input === 'object' || typeof input === 'string') + ? JSON.stringify(input) + : input, + [ContentType.Text]: (input: any) => + input !== null && typeof input !== 'string' + ? JSON.stringify(input) + : input, + [ContentType.FormData]: (input: any) => + Object.keys(input || {}).reduce((formData, key) => { + const property = input[key]; + formData.append( + key, + property instanceof Blob + ? property + : typeof property === 'object' && property !== null + ? JSON.stringify(property) + : `${property}` + ); + return formData; + }, new FormData()), + [ContentType.UrlEncoded]: (input: any) => this.toQueryString(input), + }; + + protected mergeRequestParams( + params1: RequestParams, + params2?: RequestParams + ): RequestParams { + return { + ...this.baseApiParams, + ...params1, + ...(params2 || {}), + headers: { + ...(this.baseApiParams.headers || {}), + ...(params1.headers || {}), + ...((params2 && params2.headers) || {}), + }, + }; + } + + protected createAbortSignal = ( + cancelToken: CancelToken + ): AbortSignal | undefined => { + if (this.abortControllers.has(cancelToken)) { + const abortController = this.abortControllers.get(cancelToken); + if (abortController) { + return abortController.signal; + } + return void 0; + } + + const abortController = new AbortController(); + this.abortControllers.set(cancelToken, abortController); + return abortController.signal; + }; + + public abortRequest = (cancelToken: CancelToken) => { + const abortController = this.abortControllers.get(cancelToken); + + if (abortController) { + abortController.abort(); + this.abortControllers.delete(cancelToken); + } + }; + + public request = async ({ + body, + secure, + path, + type, + query, + format, + baseUrl, + cancelToken, + ...params + }: FullRequestParams): Promise> => { + const secureParams = + ((typeof secure === 'boolean' ? secure : this.baseApiParams.secure) && + this.securityWorker && + (await this.securityWorker(this.securityData))) || + {}; + const requestParams = this.mergeRequestParams(params, secureParams); + const queryString = query && this.toQueryString(query); + const payloadFormatter = this.contentFormatters[type || ContentType.Json]; + const responseFormat = format || requestParams.format; + + return this.customFetch( + `${baseUrl || this.baseUrl || ''}${path}${queryString ? `?${queryString}` : ''}`, + { + ...requestParams, + headers: { + ...(requestParams.headers || {}), + ...(type && type !== ContentType.FormData + ? {'Content-Type': type} + : {}), + }, + signal: + (cancelToken + ? this.createAbortSignal(cancelToken) + : requestParams.signal) || null, + body: + typeof body === 'undefined' || body === null + ? null + : payloadFormatter(body), + } + ).then(async response => { + const r = response.clone() as HttpResponse; + r.data = null as unknown as T; + r.error = null as unknown as E; + + const data = !responseFormat + ? r + : await response[responseFormat]() + .then(data => { + if (r.ok) { + r.data = data; + } else { + r.error = data; + } + return r; + }) + .catch(e => { + r.error = e; + return r; + }); + + if (cancelToken) { + this.abortControllers.delete(cancelToken); + } + + if (!response.ok) throw data; + return data; + }); + }; +} + +/** + * @title FastAPI + * @version 0.1.0 + */ +export class Api< + SecurityDataType extends unknown, +> extends HttpClient { + health = { + /** + * No description + * + * @tags Service + * @name ReadRootHealthGet + * @summary Read Root + * @request GET:/health + */ + readRootHealthGet: (params: RequestParams = {}) => + this.request({ + path: `/health`, + method: 'GET', + format: 'json', + ...params, + }), + }; + serverInfo = { + /** + * No description + * + * @tags Service + * @name ServerInfoServerInfoGet + * @summary Server Info + * @request GET:/server_info + */ + serverInfoServerInfoGet: (params: RequestParams = {}) => + this.request({ + path: `/server_info`, + method: 'GET', + format: 'json', + ...params, + }), + }; + call = { + /** + * No description + * + * @tags Calls + * @name CallStartCallStartPost + * @summary Call Start + * @request POST:/call/start + * @secure + */ + callStartCallStartPost: (data: CallStartReq, params: RequestParams = {}) => + this.request({ + path: `/call/start`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Calls + * @name CallEndCallEndPost + * @summary Call End + * @request POST:/call/end + * @secure + */ + callEndCallEndPost: (data: CallEndReq, params: RequestParams = {}) => + this.request({ + path: `/call/end`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Calls + * @name CallStartBatchCallUpsertBatchPost + * @summary Call Start Batch + * @request POST:/call/upsert_batch + * @secure + */ + callStartBatchCallUpsertBatchPost: ( + data: CallCreateBatchReq, + params: RequestParams = {} + ) => + this.request({ + path: `/call/upsert_batch`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Calls + * @name CallUpdateCallUpdatePost + * @summary Call Update + * @request POST:/call/update + * @secure + */ + callUpdateCallUpdatePost: ( + data: CallUpdateReq, + params: RequestParams = {} + ) => + this.request({ + path: `/call/update`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Calls + * @name CallReadCallReadPost + * @summary Call Read + * @request POST:/call/read + * @secure + */ + callReadCallReadPost: (data: CallReadReq, params: RequestParams = {}) => + this.request({ + path: `/call/read`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; + calls = { + /** + * No description + * + * @tags Calls + * @name CallsDeleteCallsDeletePost + * @summary Calls Delete + * @request POST:/calls/delete + * @secure + */ + callsDeleteCallsDeletePost: ( + data: CallsDeleteReq, + params: RequestParams = {} + ) => + this.request({ + path: `/calls/delete`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Calls + * @name CallsQueryStatsCallsQueryStatsPost + * @summary Calls Query Stats + * @request POST:/calls/query_stats + * @secure + */ + callsQueryStatsCallsQueryStatsPost: ( + data: CallsQueryStatsReq, + params: RequestParams = {} + ) => + this.request({ + path: `/calls/query_stats`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Calls + * @name CallsQueryStreamCallsStreamQueryPost + * @summary Calls Query Stream + * @request POST:/calls/stream_query + * @secure + */ + callsQueryStreamCallsStreamQueryPost: ( + data: CallsQueryReq, + params: RequestParams = {} + ) => + this.request({ + path: `/calls/stream_query`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; + obj = { + /** + * No description + * + * @tags Objects + * @name ObjCreateObjCreatePost + * @summary Obj Create + * @request POST:/obj/create + * @secure + */ + objCreateObjCreatePost: (data: ObjCreateReq, params: RequestParams = {}) => + this.request({ + path: `/obj/create`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Objects + * @name ObjReadObjReadPost + * @summary Obj Read + * @request POST:/obj/read + * @secure + */ + objReadObjReadPost: (data: ObjReadReq, params: RequestParams = {}) => + this.request({ + path: `/obj/read`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; + objs = { + /** + * No description + * + * @tags Objects + * @name ObjsQueryObjsQueryPost + * @summary Objs Query + * @request POST:/objs/query + * @secure + */ + objsQueryObjsQueryPost: (data: ObjQueryReq, params: RequestParams = {}) => + this.request({ + path: `/objs/query`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; + table = { + /** + * No description + * + * @tags Tables + * @name TableCreateTableCreatePost + * @summary Table Create + * @request POST:/table/create + * @secure + */ + tableCreateTableCreatePost: ( + data: TableCreateReq, + params: RequestParams = {} + ) => + this.request({ + path: `/table/create`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Tables + * @name TableUpdateTableUpdatePost + * @summary Table Update + * @request POST:/table/update + * @secure + */ + tableUpdateTableUpdatePost: ( + data: TableUpdateReq, + params: RequestParams = {} + ) => + this.request({ + path: `/table/update`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Tables + * @name TableQueryTableQueryPost + * @summary Table Query + * @request POST:/table/query + * @secure + */ + tableQueryTableQueryPost: ( + data: TableQueryReq, + params: RequestParams = {} + ) => + this.request({ + path: `/table/query`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; + refs = { + /** + * No description + * + * @tags Refs + * @name RefsReadBatchRefsReadBatchPost + * @summary Refs Read Batch + * @request POST:/refs/read_batch + * @secure + */ + refsReadBatchRefsReadBatchPost: ( + data: RefsReadBatchReq, + params: RequestParams = {} + ) => + this.request({ + path: `/refs/read_batch`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; + file = { + /** + * No description + * + * @tags Files + * @name FileCreateFileCreatePost + * @summary File Create + * @request POST:/file/create + * @secure + */ + fileCreateFileCreatePost: ( + data: BodyFileCreateFileCreatePost, + params: RequestParams = {} + ) => + this.request({ + path: `/file/create`, + method: 'POST', + body: data, + secure: true, + type: ContentType.FormData, + format: 'json', + ...params, + }), + + /** + * No description + * + * @tags Files + * @name FileContentFileContentPost + * @summary File Content + * @request POST:/file/content + * @secure + */ + fileContentFileContentPost: ( + data: FileContentReadReq, + params: RequestParams = {} + ) => + this.request({ + path: `/file/content`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; + feedback = { + /** + * @description Add feedback to a call or object. + * + * @tags Feedback + * @name FeedbackCreateFeedbackCreatePost + * @summary Feedback Create + * @request POST:/feedback/create + * @secure + */ + feedbackCreateFeedbackCreatePost: ( + data: FeedbackCreateReq, + params: RequestParams = {} + ) => + this.request({ + path: `/feedback/create`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * @description Query for feedback. + * + * @tags Feedback + * @name FeedbackQueryFeedbackQueryPost + * @summary Feedback Query + * @request POST:/feedback/query + * @secure + */ + feedbackQueryFeedbackQueryPost: ( + data: FeedbackQueryReq, + params: RequestParams = {} + ) => + this.request({ + path: `/feedback/query`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + + /** + * @description Permanently delete feedback. + * + * @tags Feedback + * @name FeedbackPurgeFeedbackPurgePost + * @summary Feedback Purge + * @request POST:/feedback/purge + * @secure + */ + feedbackPurgeFeedbackPurgePost: ( + data: FeedbackPurgeReq, + params: RequestParams = {} + ) => + this.request({ + path: `/feedback/purge`, + method: 'POST', + body: data, + secure: true, + type: ContentType.Json, + format: 'json', + ...params, + }), + }; +} diff --git a/sdks/node/src/inMemoryTraceServer.ts b/sdks/node/src/inMemoryTraceServer.ts new file mode 100644 index 00000000000..365577cb19b --- /dev/null +++ b/sdks/node/src/inMemoryTraceServer.ts @@ -0,0 +1,176 @@ +import {uuidv7} from 'uuidv7'; + +// This is mostly used for testing +// TODO: Maybe move the interfaces to something like trace_server_interface.py + +interface Call { + project_id: string; + id: string; + op_name: string; + trace_id: string; + parent_id: string | null; + started_at: string; + ended_at?: string; + inputs: any; + output?: any; + exception?: string; + [key: string]: any; // Index signature to allow dynamic property access +} + +interface QueryParams { + project_id: string; + limit?: number; + order_by?: keyof Call; + order_dir?: 'asc' | 'desc'; + filters?: Partial; +} + +interface Obj { + project_id: string; + object_id: string; + created_at: string; + deleted_at: string | null; + digest: string; + version_index: number; + is_latest: number; + kind: string; + base_object_class: string | null; + val: any; +} + +interface File { + project_id: string; + digest: string; + content: Blob; +} + +export class InMemoryTraceServer { + private _calls: Call[] = []; + private _objs: Obj[] = []; + private _files: File[] = []; + + call = { + callStartBatchCallUpsertBatchPost: async (batchReq: { + batch: Array<{mode: 'start' | 'end'; req: any}>; + }) => { + for (const item of batchReq.batch) { + if (item.mode === 'start') { + this._calls.push(item.req.start); + } else if (item.mode === 'end') { + const call = this._calls.find(c => c.id === item.req.end.id); + if (call) { + Object.assign(call, item.req.end); + } + } + } + }, + }; + + calls = { + callsStreamQueryPost: async (queryParams: QueryParams) => { + let filteredCalls = this._calls.filter( + call => call.project_id === queryParams.project_id + ); + + // Apply filters if any + if (queryParams.filters) { + filteredCalls = filteredCalls.filter(call => { + return Object.entries(queryParams.filters || {}).every( + ([key, value]) => call[key] === value + ); + }); + } + + // Apply ordering + if (queryParams.order_by) { + filteredCalls.sort((a, b) => { + if (a[queryParams.order_by!] < b[queryParams.order_by!]) + return queryParams.order_dir === 'asc' ? -1 : 1; + if (a[queryParams.order_by!] > b[queryParams.order_by!]) + return queryParams.order_dir === 'asc' ? 1 : -1; + return 0; + }); + } + + // Apply limit + if (queryParams.limit) { + filteredCalls = filteredCalls.slice(0, queryParams.limit); + } + + return { + calls: filteredCalls, + next_page_token: null, // Simplified: no pagination in this in-memory version + }; + }, + }; + + obj = { + objCreateObjCreatePost: async (req: { + obj: {project_id: string; object_id: string; val: any}; + }) => { + const now = new Date().toISOString(); + const digest = this.generateDigest(req.obj.val); + + const newObj: Obj = { + project_id: req.obj.project_id, + object_id: req.obj.object_id, + created_at: now, + deleted_at: null, + digest: digest, + version_index: 0, + is_latest: 1, + kind: req.obj.val._type || 'unknown', + base_object_class: req.obj.val._bases ? req.obj.val._bases[0] : null, + val: req.obj.val, + }; + + // Update version_index and is_latest for existing objects + const existingObjs = this._objs.filter( + obj => + obj.project_id === req.obj.project_id && + obj.object_id === req.obj.object_id + ); + if (existingObjs.length > 0) { + newObj.version_index = existingObjs.length; + existingObjs.forEach(obj => (obj.is_latest = 0)); + } + + this._objs.push(newObj); + + return { + data: { + digest: digest, + }, + }; + }, + }; + + file = { + fileCreateFileCreatePost: async (data: { + project_id: string; + file: Blob; + }) => { + const digest = this.generateDigest(await data.file.arrayBuffer()); + + const newFile: File = { + project_id: data.project_id, + digest: digest, + content: data.file, + }; + + this._files.push(newFile); + + return { + digest: digest, + }; + }, + }; + + private generateDigest(data: ArrayBuffer): string { + // In a real implementation, you'd want to use a proper hashing algorithm. + // For simplicity, we're using uuidv7 here. + return uuidv7(); + } + + // ... other existing methods ... +} diff --git a/sdks/node/src/index.ts b/sdks/node/src/index.ts new file mode 100644 index 00000000000..664b52f7638 --- /dev/null +++ b/sdks/node/src/index.ts @@ -0,0 +1,14 @@ +export { + init, + login, + requireCurrentCallStackEntry, + requireCurrentChildSummary, +} from './clientApi'; +export {Dataset} from './dataset'; +export {Evaluation} from './evaluation'; +export {CallSchema, CallsFilter} from './generated/traceServerApi'; +export {wrapOpenAI} from './integrations'; +export {weaveAudio, weaveImage} from './media'; +export {op} from './op'; +export * from './types'; +export {WeaveObject} from './weaveObject'; diff --git a/sdks/node/src/integrations/checkOpenai.ts b/sdks/node/src/integrations/checkOpenai.ts new file mode 100644 index 00000000000..a09033e9a61 --- /dev/null +++ b/sdks/node/src/integrations/checkOpenai.ts @@ -0,0 +1,94 @@ +// Manual test for checking openai client + +import {OpenAI} from 'openai'; +import {wrapOpenAI, init} from '..'; +import {z} from 'zod'; +import {zodResponseFormat} from 'openai/helpers/zod'; + +async function betaParseCall(client: OpenAI) { + return await client.beta.chat.completions.parse({ + model: 'gpt-4o-2024-08-06', + temperature: 0.7, + messages: [ + { + role: 'user', + content: 'What is the capital of the US?', + }, + ], + response_format: zodResponseFormat(z.object({name: z.string()}), 'result'), + }); +} + +async function standardCall(client: OpenAI) { + return await client.chat.completions.create({ + model: 'gpt-4o-2024-08-06', + temperature: 0.7, + messages: [ + { + role: 'user', + content: 'What is the capital of the US?', + }, + ], + response_format: zodResponseFormat(z.object({name: z.string()}), 'result'), + }); +} + +async function streamCall(client: OpenAI) { + return await client.chat.completions.create({ + model: 'gpt-4o-2024-08-06', + temperature: 0.7, + messages: [ + { + role: 'user', + content: 'What is the capital of the US?', + }, + ], + stream: true, + response_format: zodResponseFormat(z.object({name: z.string()}), 'result'), + }); +} + +async function callTests(openai: OpenAI) { + try { + console.log(' BETA PARSE CALL'); + const response = await betaParseCall(openai); + console.log(' SUCCESS', JSON.stringify(response).length); + } catch (e) { + console.log(' ERROR', e); + } + try { + console.log(' STANDARD CALL'); + const response = await standardCall(openai); + console.log(' SUCCESS', JSON.stringify(response).length); + } catch (e) { + console.log(' ERROR', e); + } + try { + console.log(' STREAM CALL'); + const response = await streamCall(openai); + let fullRes = ''; + for await (const chunk of response) { + fullRes += JSON.stringify(chunk); + } + // console.log("FULL RESPONSE", fullRes); + console.log(' SUCCESS', fullRes.length); + } catch (e) { + console.log(' ERROR', e); + } +} + +export async function oaiParse() { + const openai = new OpenAI({timeout: 120 * 1000}); + console.log('OPENAI CLIENT TESTS'); + await callTests(openai); + + const client = wrapOpenAI(openai); + console.log('WRAPPED CLIENT TESTS'); + await callTests(client); + + await init('weavejs-dev-asynctest'); + console.log('WEAVE LOGGING TESTS'); + await callTests(client); +} + +oaiParse().then(result => console.log(result)); diff --git a/sdks/node/src/integrations/index.ts b/sdks/node/src/integrations/index.ts new file mode 100644 index 00000000000..5f8031da85f --- /dev/null +++ b/sdks/node/src/integrations/index.ts @@ -0,0 +1 @@ +export {wrapOpenAI} from './openai'; diff --git a/sdks/node/src/integrations/openai.ts b/sdks/node/src/integrations/openai.ts new file mode 100644 index 00000000000..c95587bbc2d --- /dev/null +++ b/sdks/node/src/integrations/openai.ts @@ -0,0 +1,238 @@ +import {weaveImage} from '../media'; +import {op} from '../op'; +import {OpOptions} from '../opType'; + +// exported just for testing +export const openAIStreamReducer = { + initialState: { + id: '', + object: 'chat.completion', + created: 0, + model: '', + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: '', + function_call: null, + }, + finish_reason: null, + }, + ], + usage: null, + }, + reduceFn: (state: any, chunk: any) => { + if (chunk.id) state.id = chunk.id; + if (chunk.object) state.object = chunk.object; + if (chunk.created) state.created = chunk.created; + if (chunk.model) state.model = chunk.model; + + if (chunk.choices && chunk.choices.length > 0) { + const choice = chunk.choices[0]; + if (choice.delta) { + if (choice.delta.role) { + state.choices[0].message.role = choice.delta.role; + } + if (choice.delta.content) { + state.choices[0].message.content += choice.delta.content; + } + if (choice.delta.function_call) { + if (!state.choices[0].message.function_call) { + state.choices[0].message.function_call = { + name: '', + arguments: '', + }; + } + if (choice.delta.function_call.name) { + state.choices[0].message.function_call.name = + choice.delta.function_call.name; + } + if (choice.delta.function_call.arguments) { + state.choices[0].message.function_call.arguments += + choice.delta.function_call.arguments; + } + } + } + if (choice.finish_reason) { + state.choices[0].finish_reason = choice.finish_reason; + } + } + + if (chunk.usage) { + state.usage = chunk.usage; + } + + return state; + }, +}; + +export function makeOpenAIChatCompletionsOp(originalCreate: any, name: string) { + function wrapped(...args: Parameters) { + const [originalParams]: any[] = args; + if (originalParams.stream) { + return originalCreate({ + ...originalParams, + stream_options: {...originalParams.stream_options, include_usage: true}, + }); + } + + return originalCreate(originalParams); + } + + const options: OpOptions = { + name: name, + parameterNames: 'useParam0Object', + summarize: result => ({ + usage: { + [result.model]: result.usage, + }, + }), + streamReducer: openAIStreamReducer, + }; + + return op(wrapped, options); +} + +export function makeOpenAIImagesGenerateOp(originalGenerate: any) { + async function wrapped(...args: Parameters) { + const result = await originalGenerate(...args); + + // Process the result to convert image data to WeaveImage + if (result.data) { + result.data = await Promise.all( + result.data.map(async (item: any) => { + if (item.b64_json) { + const buffer = Buffer.from(item.b64_json, 'base64'); + return weaveImage({data: buffer, imageType: 'png'}); + } + return item; + }) + ); + } + + return result; + } + + const options: OpOptions = { + name: 'openai.images.generate', + summarize: result => ({ + usage: { + 'dall-e': { + images_generated: result.data.length, + }, + }, + }), + }; + + return op(wrapped, options); +} + +interface OpenAIAPI { + chat: { + completions: { + create: any; + }; + }; + images: { + generate: any; + }; + beta: { + chat: { + completions: { + parse: any; + }; + }; + }; +} + +/** + * Wraps the OpenAI API to enable function tracing for OpenAI calls. + * + * @example + * const openai = wrapOpenAI(new OpenAI()); + * const result = await openai.chat.completions.create({ + * model: 'gpt-3.5-turbo', + * messages: [{ role: 'user', content: 'Hello, world!' }] + * }); + */ +export function wrapOpenAI(openai: T): T { + const chatCompletionsProxy = new Proxy(openai.chat.completions, { + get(target, p, receiver) { + const targetVal = Reflect.get(target, p, receiver); + if (p === 'create') { + return makeOpenAIChatCompletionsOp( + targetVal.bind(target), + 'openai.chat.completions.create' + ); + } + return targetVal; + }, + }); + const chatProxy = new Proxy(openai.chat, { + get(target, p, receiver) { + const targetVal = Reflect.get(target, p, receiver); + if (p === 'completions') { + return chatCompletionsProxy; + } + return targetVal; + }, + }); + + const imagesProxy = new Proxy(openai.images, { + get(target, p, receiver) { + const targetVal = Reflect.get(target, p, receiver); + if (p === 'generate') { + return makeOpenAIImagesGenerateOp(targetVal.bind(target)); + } + return targetVal; + }, + }); + + const betaChatCompletionsProxy = new Proxy(openai.beta.chat.completions, { + get(target, p, receiver) { + const targetVal = Reflect.get(target, p, receiver); + if (p === 'parse') { + return makeOpenAIChatCompletionsOp( + targetVal.bind(target), + 'openai.beta.chat.completions.parse' + ); + } + return targetVal; + }, + }); + const betaChatProxy = new Proxy(openai.beta.chat, { + get(target, p, receiver) { + const targetVal = Reflect.get(target, p, receiver); + if (p === 'completions') { + return betaChatCompletionsProxy; + } + return targetVal; + }, + }); + const betaProxy = new Proxy(openai.beta, { + get(target, p, receiver) { + const targetVal = Reflect.get(target, p, receiver); + if (p === 'chat') { + return betaChatProxy; + } + return targetVal; + }, + }); + + return new Proxy(openai, { + get(target, p, receiver) { + const targetVal = Reflect.get(target, p, receiver); + if (p === 'chat') { + return chatProxy; + } + if (p === 'images') { + return imagesProxy; + } + if (p === 'beta') { + return betaProxy; + } + return targetVal; + }, + }); +} diff --git a/sdks/node/src/media.ts b/sdks/node/src/media.ts new file mode 100644 index 00000000000..ca44ae118df --- /dev/null +++ b/sdks/node/src/media.ts @@ -0,0 +1,79 @@ +export const DEFAULT_IMAGE_TYPE = 'png'; +export const DEFAULT_AUDIO_TYPE = 'wav'; + +export type ImageType = 'png'; +export type AudioType = 'wav'; + +// Define WeaveImage type +type WeaveImageInput = { + data: Buffer; + imageType?: ImageType; +}; + +interface WeaveImage extends WeaveImageInput { + _weaveType: 'Image'; +} + +/** + * Create a new WeaveImage object + * + * @param options The options for this media type + * @param options.data The raw image data as a Buffer + * @param options.imageType (Optional) The type of image file, currently only 'png' is supported + * + * @example + * const imageBuffer = fs.readFileSync('path/to/image.png'); + * const weaveImage = weaveImage({ data: imageBuffer }); + */ +export function weaveImage({data, imageType}: WeaveImageInput): WeaveImage { + const resolvedImageType = imageType ?? DEFAULT_IMAGE_TYPE; + return { + _weaveType: 'Image', + data, + imageType: resolvedImageType, + }; +} + +// Function to check if a value is a WeaveImage +export function isWeaveImage(value: any): value is WeaveImage { + return value && value._weaveType === 'Image'; +} + +type WeaveAudioInput = { + data: Buffer; + audioType?: AudioType; +}; + +export interface WeaveAudio extends WeaveAudioInput { + _weaveType: 'Audio'; +} + +/** + * Create a new WeaveAudio object + * + * @param options The options for this media type + * @param options.data The raw audio data as a Buffer + * @param options.audioType (Optional) The type of audio file, currently only 'wav' is supported + * + * @example + * const audioBuffer = fs.readFileSync('path/to/audio.wav'); + * const weaveAudio = weaveAudio({ data: audioBuffer }); + */ +export function weaveAudio({data, audioType}: WeaveAudioInput): WeaveAudio { + const resolvedAudioType = audioType ?? DEFAULT_AUDIO_TYPE; + return { + _weaveType: 'Audio', + data, + audioType: resolvedAudioType, + }; +} + +export function isWeaveAudio(value: any): value is WeaveAudio { + return value && value._weaveType === 'Audio'; +} + +type WeaveMedia = WeaveImage | WeaveAudio; + +export function isMedia(value: any): value is WeaveMedia { + return isWeaveImage(value) || isWeaveAudio(value); +} diff --git a/sdks/node/src/op.ts b/sdks/node/src/op.ts new file mode 100644 index 00000000000..a21a90c30fc --- /dev/null +++ b/sdks/node/src/op.ts @@ -0,0 +1,202 @@ +import {getGlobalClient} from './clientApi'; +import {TRACE_CALL_EMOJI} from './constants'; +import {Op, OpOptions} from './opType'; +import {getGlobalDomain} from './urls'; +import {warnOnce} from './utils/warnOnce'; + +/** + * A wrapper to weave op-ify a function or method that works on sync and async functions. + * + * Wrapped functions: + * 1. Take the same inputs and return the same outputs as the original function. + * 2. Will automatically track calls in the Weave UI. + * + * If you don't call `weave.init` then the function will behave as if it were not wrapped. + * + * @param fn The function to wrap + * @param options Optional configs like call and param naming + * @returns The wrapped function + * + * @example + * // Basic usage + * import OpenAI from 'openai'; + * import * as weave from 'weave'; + * + * const client = await weave.init({ project: 'my-project' }); + * const oaiClient = weave.wrapOpenAI(new OpenAI()); + * + * const extract = weave.op(async function extract() { + * return await oaiClient.chat.completions.create({ + * model: 'gpt-4-turbo', + * messages: [{ role: 'user', content: 'Create a user as JSON' }], + * }); + * }); + * + * await extract(); + * + * // You can also wrap methods by passing the object as the first argument. + * // This will bind the method to the object and wrap it with op. + * class MyModel { + * private oaiClient: OpenAI; + * + * constructor() { + * this.oaiClient = weave.wrapOpenAI(new OpenAI()); + * this.invoke = weave.op(this, this.invoke); + * } + * + * async invoke() { + * return await this.oaiClient.chat.completions.create({ + * model: 'gpt-4-turbo', + * messages: [{ role: 'user', content: 'Create a user as JSON' }], + * }); + * } + * } + * + * const model = new MyModel(); + * const res = await model.invoke(); + */ +export function op any>( + fn: T, + options?: OpOptions +): Op<(...args: Parameters) => Promise>>>; +export function op any>( + thisArg: any, + fn: T, + options?: OpOptions +): Op<(...args: Parameters) => Promise>>>; +export function op any>( + fnOrThis: T | any, + fnOrOptions?: T | OpOptions, + maybeOptions?: OpOptions +): Op<(...args: Parameters) => Promise>>> { + let fn: T; + let options: OpOptions | undefined; + let bindThis: any; + + if (typeof fnOrThis === 'function') { + fn = fnOrThis; + options = fnOrOptions as OpOptions; + } else { + bindThis = fnOrThis; + fn = fnOrOptions as T; + options = maybeOptions; + + const boundFn = fn.bind(bindThis) as T; + return op(boundFn, {originalFunction: fn, bindThis, ...options}); + } + + const opWrapper = async function ( + ...params: Parameters + ): Promise> { + const client = getGlobalClient(); + + if (!client) { + warnOnce( + 'weave-not-initialized', + 'WARNING: Weave is not initialized, so calls wont be tracked. Call `weave.init` to initialize before calling ops. If this is intentional, you can safely ignore this warning.' + ); + return await fn(...params); + } + + const {currentCall, parentCall, newStack} = client.pushNewCall(); + const startTime = new Date(); + if (client.settings.shouldPrintCallLink && parentCall == null) { + const domain = getGlobalDomain(); + console.log( + `${TRACE_CALL_EMOJI} https://${domain}/${client.projectId}/r/call/${currentCall.callId}` + ); + } + const displayName = options?.callDisplayName + ? options.callDisplayName(...params) + : undefined; + const thisArg = options?.bindThis; + const startCallPromise = client.createCall( + opWrapper, + params, + options?.parameterNames, + thisArg, + currentCall, + parentCall, + startTime, + displayName + ); + + try { + let result = await client.runWithCallStack(newStack, async () => { + return await fn(...params); + }); + + if (options?.streamReducer && Symbol.asyncIterator in result) { + const {initialState, reduceFn} = options.streamReducer; + let state = initialState; + + const wrappedIterator = { + [Symbol.asyncIterator]: async function* () { + try { + for await (const chunk of result as AsyncIterable) { + state = reduceFn(state, chunk); + yield chunk; + } + } finally { + if (client) { + // Check if globalClient still exists + const endTime = new Date(); + await client.finishCall( + state, + currentCall, + parentCall, + options?.summarize, + endTime, + startCallPromise + ); + } + } + }, + }; + + return wrappedIterator as unknown as ReturnType; + } else { + const endTime = new Date(); + await client.finishCall( + result, + currentCall, + parentCall, + options?.summarize, + endTime, + startCallPromise + ); + return result; + } + } catch (error) { + // console.error(`Op ${actualOpName} failed:`, error); + const endTime = new Date(); + await client.finishCallWithException( + error, + currentCall, + parentCall, + endTime, + startCallPromise + ); + await client.waitForBatchProcessing(); + throw error; + } + }; + + const fnName = options?.originalFunction?.name || fn.name || 'anonymous'; + const className = + options?.bindThis && + Object.getPrototypeOf(options.bindThis).constructor.name; + const actualOpName = + options?.name || (className ? `${className}.${fnName}` : fnName); + + opWrapper.__name = actualOpName; + opWrapper.__isOp = true as true; + opWrapper.__wrappedFunction = options?.originalFunction ?? fn; + opWrapper.__boundThis = options?.bindThis; + + return opWrapper as Op; +} + +export function isOp(fn: any): fn is Op { + return fn?.__isOp === true; +} diff --git a/sdks/node/src/opType.ts b/sdks/node/src/opType.ts new file mode 100644 index 00000000000..7312aec42e0 --- /dev/null +++ b/sdks/node/src/opType.ts @@ -0,0 +1,68 @@ +import {getGlobalDomain} from './urls'; +import {WeaveObject} from './weaveObject'; + +export type ParameterNamesOption = 'useParam0Object' | string[] | undefined; + +export type Op any> = { + __isOp: true; + __wrappedFunction: T; + __boundThis?: WeaveObject; + __name: string; + __savedRef?: OpRef | Promise; +} & T; + +interface StreamReducer { + initialState: R; + reduceFn: (state: R, chunk: T) => R; +} + +export interface OpOptions any> { + name?: string; + streamReducer?: StreamReducer; + originalFunction?: T; + callDisplayName?: (...args: Parameters) => string; + summarize?: (result: Awaited>) => Record; + bindThis?: WeaveObject; + parameterNames?: ParameterNamesOption; +} + +export function isOp(value: any): value is Op { + return value && value.__isOp === true; +} + +export function getOpWrappedFunction any>( + opValue: Op +): T { + return opValue.__wrappedFunction; +} + +export function getOpName(opValue: Op): string { + return opValue.__name; +} + +export function getOpParameterNames(opValue: Op): ParameterNamesOption { + return opValue.__parameterNames; +} + +export class OpRef { + constructor( + public projectId: string, + public objectId: string, + public digest: string + ) {} + + // TODO: Add extra + + public uri() { + return `weave:///${this.projectId}/op/${this.objectId}:${this.digest}`; + } + + public ui_url() { + const domain = getGlobalDomain(); + return `https://${domain}/${this.projectId}/weave/ops/${this.objectId}/versions/${this.digest}`; + } + + public async get() { + throw new Error('Not implemented'); + } +} diff --git a/sdks/node/src/refs.ts b/sdks/node/src/refs.ts new file mode 100644 index 00000000000..80940feea8b --- /dev/null +++ b/sdks/node/src/refs.ts @@ -0,0 +1,2 @@ +// TODO: Implement parseUri +// TODO: Implement CallRef diff --git a/sdks/node/src/settings.ts b/sdks/node/src/settings.ts new file mode 100644 index 00000000000..f2774832fc7 --- /dev/null +++ b/sdks/node/src/settings.ts @@ -0,0 +1,14 @@ +export class Settings { + constructor(private printCallLink: boolean = true) {} + + get shouldPrintCallLink(): boolean { + if (process.env.WEAVE_PRINT_CALL_LINK === 'true') { + return true; + } + if (process.env.WEAVE_PRINT_CALL_LINK === 'false') { + return false; + } + + return this.printCallLink; + } +} diff --git a/sdks/node/src/summary.ts b/sdks/node/src/summary.ts new file mode 100644 index 00000000000..b57f499cf12 --- /dev/null +++ b/sdks/node/src/summary.ts @@ -0,0 +1,69 @@ +import {CallStackEntry} from './weaveClient'; + +/** + * Represents a summary object with string keys and any type of values. + */ +type Summary = Record; +/** + * Merges two summary objects, combining their values. + * + * @param left - The first summary object to merge. + * @param right - The second summary object to merge. + * @returns A new summary object containing the merged values. + * + * This function performs a deep merge of two summary objects: + * - For numeric values, it adds them together. + * - For nested objects, it recursively merges them. + * - For other types, the left value "wins". + */ +function mergeSummaries(left: Summary, right: Summary): Summary { + const result: Summary = {...right}; + for (const [key, leftValue] of Object.entries(left)) { + if (key in result) { + if (typeof leftValue === 'number' && typeof result[key] === 'number') { + result[key] = leftValue + result[key]; + } else if ( + typeof leftValue === 'object' && + typeof result[key] === 'object' + ) { + result[key] = mergeSummaries(leftValue, result[key]); + } else { + result[key] = leftValue; + } + } else { + result[key] = leftValue; + } + } + return result; +} + +export function processSummary( + result: any, + summarize: ((result: any) => Record) | undefined, + currentCall: CallStackEntry, + parentCall: CallStackEntry | undefined +) { + let ownSummary = summarize ? summarize(result) : {}; + + if (ownSummary.usage) { + for (const model in ownSummary.usage) { + if (typeof ownSummary.usage[model] === 'object') { + ownSummary.usage[model] = { + requests: 1, + ...ownSummary.usage[model], + }; + } + } + } + + const mergedSummary = mergeSummaries(ownSummary, currentCall.childSummary); + + if (parentCall) { + parentCall.childSummary = mergeSummaries( + mergedSummary, + parentCall.childSummary + ); + } + + return mergedSummary; +} diff --git a/sdks/node/src/table.ts b/sdks/node/src/table.ts new file mode 100644 index 00000000000..0bdaff9516f --- /dev/null +++ b/sdks/node/src/table.ts @@ -0,0 +1,46 @@ +export class TableRef { + constructor( + public projectId: string, + public digest: string + ) {} + + public uri() { + return `weave:///${this.projectId}/table/${this.digest}`; + } +} + +export class TableRowRef { + constructor( + public projectId: string, + public digest: string, + public rowDigest: string + ) {} + + public uri() { + return `weave:///${this.projectId}/table/${this.digest}/id/${this.rowDigest}`; + } +} + +type TableRow = Record & { + __savedRef?: TableRowRef | Promise; +}; + +export class Table { + __savedRef?: TableRef | Promise; + + constructor(public rows: R[]) {} + + get length(): number { + return this.rows.length; + } + + async *[Symbol.asyncIterator](): AsyncIterator { + for (let i = 0; i < this.length; i++) { + yield this.row(i); + } + } + + row(index: number): R { + return this.rows[index]; + } +} diff --git a/sdks/node/src/tsconfig.src.json b/sdks/node/src/tsconfig.src.json new file mode 100644 index 00000000000..82e46d097a6 --- /dev/null +++ b/sdks/node/src/tsconfig.src.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "exclude": [], + "compilerOptions": { + "rootDir": ".", + "outDir": "../dist/src" + } +} diff --git a/sdks/node/src/types.ts b/sdks/node/src/types.ts new file mode 100644 index 00000000000..6a98ef72467 --- /dev/null +++ b/sdks/node/src/types.ts @@ -0,0 +1,4 @@ +import {Op} from './opType'; +import {WeaveClient} from './weaveClient'; + +export {Op, WeaveClient}; diff --git a/sdks/node/src/urls.ts b/sdks/node/src/urls.ts new file mode 100644 index 00000000000..c96d0916016 --- /dev/null +++ b/sdks/node/src/urls.ts @@ -0,0 +1,23 @@ +export const defaultHost = 'api.wandb.ai'; +export const defaultDomain = 'wandb.ai'; + +export function getUrls(host?: string) { + const resolvedHost = host ?? defaultHost; + const isDefault = resolvedHost === defaultHost; + + return { + baseUrl: isDefault ? `https://api.wandb.ai` : `https://${resolvedHost}`, + traceBaseUrl: isDefault + ? `https://trace.wandb.ai` + : `https://${resolvedHost}`, + domain: isDefault ? defaultHost : resolvedHost, + }; +} + +let globalDomain: string | undefined = undefined; +export function getGlobalDomain() { + return globalDomain; +} +export function setGlobalDomain(domain: string) { + globalDomain = domain; +} diff --git a/sdks/node/src/utils/concurrencyLimit.ts b/sdks/node/src/utils/concurrencyLimit.ts new file mode 100644 index 00000000000..e675a30f6f9 --- /dev/null +++ b/sdks/node/src/utils/concurrencyLimit.ts @@ -0,0 +1,49 @@ +export class ConcurrencyLimiter { + private activeCount = 0; + private queue: (() => void)[] = []; + + constructor(private limit: number) {} + + get active(): number { + return this.activeCount; + } + + get pending(): number { + return this.queue.length; + } + + private tryExecuteNext() { + if (this.queue.length > 0 && this.activeCount < this.limit) { + const nextTask = this.queue.shift(); + this.activeCount++; + nextTask!(); + } + } + + limitFunction( + asyncFn: (...args: T) => Promise + ): (...args: T) => Promise { + return async (...args: T): Promise => { + return new Promise((resolve, reject) => { + const task = async () => { + try { + const result = await asyncFn(...args); + resolve(result); + } catch (e) { + reject(e); + } finally { + this.activeCount--; + this.tryExecuteNext(); + } + }; + + if (this.activeCount < this.limit) { + this.activeCount++; + task(); + } else { + this.queue.push(task); + } + }); + }; + } +} diff --git a/sdks/node/src/utils/netrc.ts b/sdks/node/src/utils/netrc.ts new file mode 100644 index 00000000000..f878f4b6997 --- /dev/null +++ b/sdks/node/src/utils/netrc.ts @@ -0,0 +1,86 @@ +import {readFileSync, writeFileSync} from 'fs'; +import {homedir} from 'os'; +import {join} from 'path'; + +interface NetrcEntry { + machine: string; + login: string; + password: string; + account?: string; +} + +export class Netrc { + private path: string; + public entries: Map; + + constructor(path: string = join(homedir(), '.netrc')) { + this.path = path; + this.entries = new Map(); + this.load(); + } + + private load(): void { + try { + const content = readFileSync(this.path, 'utf8'); + const lines = content.split('\n'); + let currentMachine: string | null = null; + let currentEntry: Partial = {}; + + for (const line of lines) { + const [key, value] = line.trim().split(/\s+/); + switch (key) { + case 'machine': + if (currentMachine && Object.keys(currentEntry).length) { + this.entries.set(currentMachine, currentEntry as NetrcEntry); + } + currentMachine = value; + currentEntry = {machine: value}; + break; + case 'login': + case 'password': + case 'account': + if (currentMachine) { + currentEntry[key] = value; + } + break; + } + } + + if (currentMachine && Object.keys(currentEntry).length > 1) { + this.entries.set(currentMachine, currentEntry as NetrcEntry); + } + } catch (error) { + // File doesn't exist or can't be read, starting with empty entries + } + } + + save(): void { + const content = Array.from(this.entries.entries()) + .map(([machine, entry]) => { + let str = `machine ${machine}\n`; + if (entry.login) str += ` login ${entry.login}\n`; + if (entry.password) str += ` password ${entry.password}\n`; + if (entry.account) str += ` account ${entry.account}\n`; + return str; + }) + .join('\n'); + + writeFileSync(this.path, content, {mode: 0o600}); + } + + getEntry(machine: string): NetrcEntry | undefined { + return this.entries.get(machine); + } + + setEntry(machine: string, entry: Partial): void { + const existingEntry = this.entries.get(machine) || {machine}; + const updatedEntry = {...existingEntry, ...entry} as NetrcEntry; + this.entries.delete(machine); + this.entries.set(machine, updatedEntry); + } + + getLastEntry(): NetrcEntry | undefined { + const entries = Array.from(this.entries.values()); + return entries[entries.length - 1]; + } +} diff --git a/sdks/node/src/utils/retry.ts b/sdks/node/src/utils/retry.ts new file mode 100644 index 00000000000..29ecf3c206e --- /dev/null +++ b/sdks/node/src/utils/retry.ts @@ -0,0 +1,59 @@ +type RetryOptions = { + maxRetries?: number; + baseDelay?: number; + maxDelay?: number; + maxRetryTime?: number; + retryOnStatus?: (status: number) => boolean; +}; + +export function createFetchWithRetry(options: RetryOptions = {}) { + const { + maxRetries = 5, + baseDelay = 100, + maxDelay = 10000, + maxRetryTime = 10000, + retryOnStatus = (status: number) => status !== 429 && status !== 500, + } = options; + + return async function fetchWithRetry( + ...fetchParams: Parameters + ): Promise { + let attempt = 0; + + while (attempt <= maxRetries) { + const startTime = Date.now(); + try { + const response = await fetch(...fetchParams); + + if ( + response.ok || + !retryOnStatus(response.status) || + attempt === maxRetries || + Date.now() - startTime > maxRetryTime + ) { + // Always return the response, even if it's not ok + return response; + } + + // Exponential backoff delay + const delay = Math.min(baseDelay * 2 ** attempt, maxDelay); + console.log( + `Return code: ${response.status}. Retrying fetch after ${delay}ms` + ); + await new Promise(resolve => setTimeout(resolve, delay)); + attempt++; + } catch (error) { + if (attempt === maxRetries || Date.now() - startTime > maxRetryTime) { + // Rethrow the original error + throw error; + } + // Exponential backoff delay + const delay = Math.min(baseDelay * 2 ** attempt, maxDelay); + console.log(`Exception ${error} Retrying fetch after ${delay}ms`); + await new Promise(resolve => setTimeout(resolve, delay)); + attempt++; + } + } + throw new Error("Failed to fetch. Shouldn't get here"); + }; +} diff --git a/sdks/node/src/utils/userAgent.ts b/sdks/node/src/utils/userAgent.ts new file mode 100644 index 00000000000..b293174c98a --- /dev/null +++ b/sdks/node/src/utils/userAgent.ts @@ -0,0 +1,24 @@ +import {existsSync, readFileSync} from 'fs'; +import {join} from 'path'; + +export let packageVersion: string; + +const twoLevelsUp = join(__dirname, '..', '..', 'package.json'); +const oneLevelUp = join(__dirname, '..', 'package.json'); + +if (existsSync(twoLevelsUp)) { + // This is the case in the built npm package + const packageJson = JSON.parse(readFileSync(twoLevelsUp, 'utf8')); + packageVersion = packageJson.version; +} else if (existsSync(oneLevelUp)) { + // This is the case in dev + const packageJson = JSON.parse(readFileSync(oneLevelUp, 'utf8')); + packageVersion = packageJson.version; +} else { + console.warn('Failed to find package.json'); + packageVersion = 'unknown'; +} + +export function userAgent() { + return `Weave JS Client ${packageVersion}`; +} diff --git a/sdks/node/src/utils/warnOnce.ts b/sdks/node/src/utils/warnOnce.ts new file mode 100644 index 00000000000..be872fb4ee9 --- /dev/null +++ b/sdks/node/src/utils/warnOnce.ts @@ -0,0 +1,8 @@ +const warnedKeys = new Set(); + +export function warnOnce(key: string, message: string): void { + if (!warnedKeys.has(key)) { + console.warn(message); + warnedKeys.add(key); + } +} diff --git a/sdks/node/src/wandb/settings.ts b/sdks/node/src/wandb/settings.ts new file mode 100644 index 00000000000..7b6b8ad7176 --- /dev/null +++ b/sdks/node/src/wandb/settings.ts @@ -0,0 +1,46 @@ +import {defaultDomain, defaultHost, getUrls} from '../urls'; +import {Netrc} from '../utils/netrc'; + +export function getApiKey(host: string): string { + let apiKey = process.env.WANDB_API_KEY; + if (!apiKey) { + const netrc = new Netrc(); + apiKey = netrc.entries.get(host)?.password; + } + if (!apiKey) { + // const domain = getGlobalDomain(); + const domain = defaultHost; + const apiKeyNotFoundMessage = ` + wandb API key not found. + + Go to https://${domain}/authorize to get your API key. + + You can either: + + 1. Set the WANDB_API_KEY environment variable. + 2. Log in using weave.login() + 3. Add your API key to your .netrc file, in a stanza like this: + machine ${domain} + login user + password + `; + throw new Error(apiKeyNotFoundMessage); + } + return apiKey; +} + +export function getWandbConfigs() { + let host; + try { + host = new Netrc().getLastEntry()!.machine; + } catch (error) { + throw new Error( + `Could not find entry in netrc file. + Visit https://${defaultDomain}/authorize to get an API key and run + \`weave.login({apiKey: $YOUR_API_KEY})\` or \`wandb login\` if you have that installed.` + ); + } + const apiKey = getApiKey(host); + const {baseUrl, traceBaseUrl, domain} = getUrls(host); + return {apiKey, baseUrl, traceBaseUrl, domain}; +} diff --git a/sdks/node/src/wandb/wandbServerApi.ts b/sdks/node/src/wandb/wandbServerApi.ts new file mode 100644 index 00000000000..376bc15c2a9 --- /dev/null +++ b/sdks/node/src/wandb/wandbServerApi.ts @@ -0,0 +1,73 @@ +import {userAgent} from '../utils/userAgent'; + +const VIEWER_DEFAULT_ENTITY_QUERY = ` +query DefaultEntity { + viewer { + username + defaultEntity { + name + } + } +} +`; + +export class WandbServerApi { + constructor( + public baseUrl: string, + private apiKey: string + ) {} + + private async graphqlRequest( + query: string, + variables: Record = {} + ) { + try { + const response = await fetch(`${this.baseUrl}/graphql`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'User-Agent': userAgent(), + Authorization: `Basic ${Buffer.from(`api:${this.apiKey}`).toString('base64')}`, + }, + body: JSON.stringify({ + query, + variables, + }), + }); + + if (!response.ok) { + throw new Error( + `HTTP error! status: ${response.status}, statusText: ${response.statusText}` + ); + } + + const result = await response.json(); + + if (result.errors) { + throw new Error(`GraphQL Error: ${JSON.stringify(result.errors)}`); + } + + return result.data; + } catch (error) { + console.error('Error in graphqlRequest:', error); + throw error; + } + } + + async defaultEntityName() { + try { + const result = await this.graphqlRequest(VIEWER_DEFAULT_ENTITY_QUERY); + if ( + !result.viewer || + !result.viewer.defaultEntity || + !result.viewer.defaultEntity.name + ) { + throw new Error('Default entity name not found in the response'); + } + return result.viewer.defaultEntity.name; + } catch (error) { + console.error('Error in defaultEntityName:', error); + throw error; + } + } +} diff --git a/sdks/node/src/weaveClient.ts b/sdks/node/src/weaveClient.ts new file mode 100644 index 00000000000..1a10cb2bec8 --- /dev/null +++ b/sdks/node/src/weaveClient.ts @@ -0,0 +1,781 @@ +import {AsyncLocalStorage} from 'async_hooks'; +import {uuidv7} from 'uuidv7'; + +import {Dataset} from './dataset'; +import {computeDigest} from './digest'; +import { + CallSchema, + CallsFilter, + EndedCallSchemaForInsert, + StartedCallSchemaForInsert, + Api as TraceServerApi, +} from './generated/traceServerApi'; +import { + AudioType, + DEFAULT_AUDIO_TYPE, + DEFAULT_IMAGE_TYPE, + ImageType, + isWeaveAudio, + isWeaveImage, +} from './media'; +import { + Op, + OpRef, + ParameterNamesOption, + getOpName, + getOpWrappedFunction, + isOp, +} from './opType'; +import {Settings} from './settings'; +import {Table, TableRef, TableRowRef} from './table'; +import {packageVersion} from './utils/userAgent'; +import {WandbServerApi} from './wandb/wandbServerApi'; +import {ObjectRef, WeaveObject, getClassChain} from './weaveObject'; + +export type CallStackEntry = { + callId: string; + traceId: string; + childSummary: Record; +}; + +function generateTraceId(): string { + return uuidv7(); +} + +function generateCallId(): string { + return uuidv7(); +} + +class CallStack { + constructor(private stack: CallStackEntry[] = []) {} + + peek(): CallStackEntry | null { + return this.stack[this.stack.length - 1] ?? null; + } + + pushNewCall(): { + currentCall: CallStackEntry; + parentCall?: CallStackEntry; + newStack: CallStack; + } { + const parentCall = this.stack[this.stack.length - 1]; + + const callId = generateCallId(); + const traceId = parentCall?.traceId ?? generateTraceId(); + const newCall: CallStackEntry = {callId, traceId, childSummary: {}}; + const newStack = new CallStack([...this.stack, newCall]); + return {currentCall: newCall, parentCall, newStack}; + } +} + +type CallStartParams = StartedCallSchemaForInsert; +type CallEndParams = EndedCallSchemaForInsert; + +export class WeaveClient { + private stackContext = new AsyncLocalStorage(); + private callQueue: Array<{mode: 'start' | 'end'; data: any}> = []; + private batchProcessTimeout: NodeJS.Timeout | null = null; + private isBatchProcessing: boolean = false; + private batchProcessingPromises: Set> = new Set(); + private readonly BATCH_INTERVAL: number = 200; + + constructor( + public traceServerApi: TraceServerApi, + private wandbServerApi: WandbServerApi, + public projectId: string, + public settings: Settings = new Settings() + ) {} + + private scheduleBatchProcessing() { + if (this.batchProcessTimeout || this.isBatchProcessing) return; + const promise = new Promise(resolve => { + this.batchProcessTimeout = setTimeout( + () => this.processBatch().then(resolve), + this.BATCH_INTERVAL + ); + }); + this.batchProcessingPromises.add(promise); + promise.finally(() => { + this.batchProcessingPromises.delete(promise); + }); + } + + public async waitForBatchProcessing() { + while (this.batchProcessingPromises.size > 0) { + await Promise.all(this.batchProcessingPromises); + } + } + + private async processBatch() { + if (this.isBatchProcessing || this.callQueue.length === 0) { + this.batchProcessTimeout = null; + return; + } + + this.isBatchProcessing = true; + + // We count characters item by item, and try to limit batches to about + // this size. + const maxBatchSizeChars = 5 * 1024 * 1024; + + let batchToProcess = []; + let currentBatchSize = 0; + + while (this.callQueue.length > 0 && currentBatchSize < maxBatchSizeChars) { + const item = this.callQueue[0]; + const itemSize = JSON.stringify(item).length; + + if (currentBatchSize + itemSize <= maxBatchSizeChars) { + batchToProcess.push(this.callQueue.shift()!); + currentBatchSize += itemSize; + } else { + break; + } + } + + const batchReq = { + batch: batchToProcess.map(item => ({ + mode: item.mode, + req: item.data, + })), + }; + + try { + await this.traceServerApi.call.callStartBatchCallUpsertBatchPost( + batchReq + ); + } catch (error) { + console.error('Error processing batch:', error); + // Put failed items back at the front of the queue + this.callQueue.unshift(...batchToProcess); + } finally { + this.isBatchProcessing = false; + this.batchProcessTimeout = null; + if (this.callQueue.length > 0) { + this.scheduleBatchProcessing(); + } + } + } + + public publish(obj: any, objId?: string): Promise { + if (obj.__savedRef) { + return obj.__savedRef; + } else if (obj instanceof WeaveObject) { + return this.saveObject(obj, objId); + } else if (isOp(obj)) { + return this.saveOp(obj); + } else { + return this.saveArbitrary(obj, objId); + } + } + + public async getCalls( + filter: CallsFilter = {}, + includeCosts: boolean = false, + limit: number = 1000 + ) { + const calls: CallSchema[] = []; + const iterator = this.getCallsIterator(filter, includeCosts, limit); + for await (const call of iterator) { + calls.push(call); + } + return calls; + } + public async *getCallsIterator( + filter: CallsFilter = {}, + includeCosts: boolean = false, + limit: number = 1000 + ): AsyncIterableIterator { + const resp = + await this.traceServerApi.calls.callsQueryStreamCallsStreamQueryPost({ + project_id: this.projectId, + filter, + include_costs: includeCosts, + limit, + }); + + const reader = resp.body!.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const {value, done} = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, {stream: true}); + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + if (line.trim()) { + try { + yield JSON.parse(line); + } catch (error) { + console.error('Error parsing JSON:', error, 'Line:', line); + } + } + } + } + + if (buffer.trim()) { + try { + yield JSON.parse(buffer); + } catch (error) { + console.error('Error parsing JSON:', error, 'Remaining data:', buffer); + } + } + } + + public async get(ref: ObjectRef): Promise { + let val: any; + try { + const res = await this.traceServerApi.obj.objReadObjReadPost({ + project_id: ref.projectId, + object_id: ref.objectId, + digest: ref.digest, + }); + val = res.data.obj.val; + } catch (error) { + if (error instanceof Error && error.message.includes('404')) { + throw new Error(`Unable to find object for ref uri: ${ref.uri()}`); + } + throw error; + } + + const t = val?._type; + if (t == 'Dataset') { + const {_baseParameters, rows} = val; + let obj = new Dataset({ + id: _baseParameters.id, + description: _baseParameters.description, + rows, + }); + obj.__savedRef = ref; + // TODO: The table row refs are not correct + return obj; + } else if (t == 'Table') { + const {rows} = val; + let obj = new Table(rows); + obj.__savedRef = ref; + return obj; + } else if (t == 'CustomWeaveType') { + const typeName = val.weave_type.type; + if (typeName == 'PIL.Image.Image') { + let loadedFiles: {[key: string]: Buffer} = {}; + for (const [name, digest] of Object.entries(val.files)) { + try { + const fileContent = + await this.traceServerApi.file.fileContentFileContentPost({ + project_id: this.projectId, + digest: digest as string, + }); + loadedFiles[name] = fileContent.data?.content; + } catch (error) { + console.error('Error loading file:', error); + } + } + // TODO: Implement getting img back as buffer + return 'Coming soon!'; + } else if (typeName == 'wave.Wave_read') { + let loadedFiles: {[key: string]: Buffer} = {}; + for (const [name, digest] of Object.entries(val.files)) { + try { + const fileContent = + await this.traceServerApi.file.fileContentFileContentPost({ + project_id: this.projectId, + digest: digest as string, + }); + loadedFiles[name] = fileContent.data?.content; + } catch (error) { + console.error('Error loading file:', error); + } + } + // TODO: Implement getting audio back as buffer + return 'Coming soon!'; + } + } + return val; + } + + // save* methods attached __savedRef promises to their values. These must + // be synchronous, so we can guarantee that calling savedWeaveValues + // immediately makes __savedRef promises available. + + private saveArbitrary(obj: any, objId?: string): Promise { + if (obj.__savedRef) { + return obj.__savedRef; + } + + const ref = (async () => { + if (!objId) { + objId = uuidv7(); + } + + const serializedObj = await this.serializedVal(obj); + const response = await this.traceServerApi.obj.objCreateObjCreatePost({ + obj: { + project_id: this.projectId, + object_id: objId, + val: serializedObj, + }, + }); + return new ObjectRef(this.projectId, objId, response.data.digest); + })(); + + obj.__savedRef = ref; + return ref; + } + + private saveObject(obj: WeaveObject, objId?: string): Promise { + if (obj.__savedRef) { + return Promise.resolve(obj.__savedRef); + } + for (const [key, value] of Object.entries(obj)) { + this.saveWeaveValues(value); + } + + obj.__savedRef = (async () => { + const classChain = getClassChain(obj); + const className = classChain[0]; + if (!objId) { + objId = obj.id; + } + + let saveAttrs = obj.saveAttrs(); + saveAttrs = await this.serializedVal(saveAttrs); + // Frontend does this overly specific check for datasets, so we need to add both _type and _class_name + // for now. + // data._type === 'Dataset' && + // data._class_name === 'Dataset' && + // _.isEqual(data._bases, ['Object', 'BaseModel']) + const saveValue = { + _type: className, + _class_name: className, + _bases: classChain.slice(1), + ...saveAttrs, + }; + const response = await this.traceServerApi.obj.objCreateObjCreatePost({ + obj: { + project_id: this.projectId, + object_id: objId, + val: saveValue, + }, + }); + const ref = new ObjectRef(this.projectId, objId, response.data.digest); + // console.log(`Saved object: ${ref.ui_url()}`); + return ref; + })(); + + return obj.__savedRef; + } + + private saveTable(table: Table): void { + if (table.__savedRef) { + return; + } + + table.__savedRef = (async () => { + const rowsWithoutRefs = table.rows.map(row => { + return {...row, __savedRef: undefined}; + }); + const rows = await this.serializedVal(rowsWithoutRefs); + const response = + await this.traceServerApi.table.tableCreateTableCreatePost({ + table: { + project_id: this.projectId, + rows, + }, + }); + const ref = new TableRef(this.projectId, response.data.digest); + return ref; + })(); + const tableQueryPromise = (async () => { + const tableRef = await table.__savedRef; + const tableQueryRes = + await this.traceServerApi.table.tableQueryTableQueryPost({ + project_id: this.projectId, + digest: tableRef?.digest!, + }); + return { + tableDigest: tableRef?.digest!, + tableQueryResult: tableQueryRes.data, + }; + })(); + for (let i = 0; i < table.rows.length; i++) { + const row = table.rows[i]; + row.__savedRef = (async () => { + const {tableDigest, tableQueryResult} = await tableQueryPromise; + return new TableRowRef( + this.projectId, + tableDigest, + tableQueryResult.rows[i].digest + ); + })(); + } + } + + /** + * Recursively save a Weave value, attaching __savedRef Promises to + * nested value that gets its own ref. + * + * This function must be synchronous, so that code that does ref-tracking + * (currently only Dataset/DatasetRow in the js client) has refs + * available immediately. + */ + private saveWeaveValues(val: any): void { + if (Array.isArray(val)) { + val.map(item => this.saveWeaveValues(item)); + } else if (val != null && val.__savedRef) { + return; + } else if (val instanceof WeaveObject) { + this.saveObject(val); + } else if (val instanceof Table) { + this.saveTable(val); + } else if (isWeaveImage(val)) { + } else if (isWeaveAudio(val)) { + } else if (isOp(val)) { + this.saveOp(val); + } else if (typeof val === 'object' && val !== null) { + for (const [key, value] of Object.entries(val)) { + this.saveWeaveValues(value); + } + } + } + + // serialize* methods are async, and return the serialized value + // of a Weave value. + + private async serializedFileBlob( + typeName: string, + fileName: string, + fileContent: Blob + ): Promise { + const buffer = await fileContent.arrayBuffer().then(Buffer.from); + const digest = computeDigest(buffer); + + const placeholder = { + _type: 'CustomWeaveType', + weave_type: {type: typeName}, + files: { + [fileName]: digest, + }, + load_op: 'NO_LOAD_OP', + }; + + try { + await this.traceServerApi.file.fileCreateFileCreatePost({ + project_id: this.projectId, + // @ts-ignore + file: fileContent, + }); + } catch (error) { + console.error('Error saving file:', error); + } + + return placeholder; + } + + private async serializedImage( + imageData: Buffer, + imageType: ImageType = DEFAULT_IMAGE_TYPE + ): Promise { + const blob = new Blob([imageData], {type: `image/${imageType}`}); + return this.serializedFileBlob('PIL.Image.Image', 'image.png', blob); + } + + private async serializedAudio( + audioData: Buffer, + audioType: AudioType = DEFAULT_AUDIO_TYPE + ): Promise { + const blob = new Blob([audioData], {type: `audio/${audioType}`}); + return this.serializedFileBlob('wave.Wave_read', 'audio.wav', blob); + } + + /** + * Get the serialized value of a Weave value, by recursively + * resolving any __savedRef promises to their uri(). + * + * This function is asynchronous, and must be called after saveWeaveValues + * has been called on the value. + */ + private async serializedVal(val: any): Promise { + if (Array.isArray(val)) { + return Promise.all(val.map(async item => this.serializedVal(item))); + } else if (val != null && val.__savedRef) { + return (await val.__savedRef).uri(); + } else if (isWeaveImage(val)) { + return await this.serializedImage(val.data, val.imageType); + } else if (isWeaveAudio(val)) { + return await this.serializedAudio(val.data, val.audioType); + } else if (val instanceof WeaveObject) { + throw new Error('Programming error: WeaveObject not saved'); + } else if (val instanceof Table) { + throw new Error('Programming error: Table not saved'); + } else if (isOp(val)) { + throw new Error('Programming error: Op not saved'); + } else if (typeof val === 'object' && val !== null) { + const result: {[key: string]: any} = {}; + for (const [key, value] of Object.entries(val)) { + result[key] = await this.serializedVal(value); + } + return result; + } else { + return val; + } + } + + private saveCallStart(callStart: CallStartParams) { + this.callQueue.push({mode: 'start', data: {start: callStart}}); + this.scheduleBatchProcessing(); + } + + private saveCallEnd(callEnd: CallEndParams) { + this.callQueue.push({mode: 'end', data: {end: callEnd}}); + this.scheduleBatchProcessing(); + } + + public getCallStack(): CallStack { + return this.stackContext.getStore() || new CallStack(); + } + + public pushNewCall() { + return this.getCallStack().pushNewCall(); + } + + public runWithCallStack(callStack: CallStack, fn: () => T): T { + return this.stackContext.run(callStack, fn); + } + + private async paramsToCallInputs( + params: any[], + thisArg: any, + parameterNames: ParameterNamesOption + ) { + let inputs: Record = {}; + + // Add 'self' first if thisArg is a WeaveObject + if (thisArg instanceof WeaveObject) { + inputs['self'] = thisArg; + } + if (parameterNames === 'useParam0Object') { + inputs = {...inputs, ...params[0]}; + } else if (parameterNames) { + params.forEach((arg, index) => { + inputs[parameterNames[index]] = arg; + }); + } else { + params.forEach((arg, index) => { + inputs[`arg${index}`] = arg; + }); + } + this.saveWeaveValues(inputs); + return await this.serializedVal(inputs); + } + + public async saveOp( + op: Op<(...args: any[]) => any>, + objId?: string + ): Promise { + if (op.__savedRef) { + return op.__savedRef; + } + op.__savedRef = (async () => { + const resolvedObjId = objId || getOpName(op); + const opFn = getOpWrappedFunction(op); + const formattedOpFn = await maybeFormatCode(opFn.toString()); + const saveValue = await this.serializedFileBlob( + 'Op', + 'obj.py', + new Blob([formattedOpFn]) + ); + const response = await this.traceServerApi.obj.objCreateObjCreatePost({ + obj: { + project_id: this.projectId, + object_id: resolvedObjId, + val: saveValue, + }, + }); + const ref = new OpRef( + this.projectId, + resolvedObjId, + response.data.digest + ); + + // console.log('Saved op: ', ref.ui_url()); + return ref; + })(); + return op.__savedRef; + } + + public async createCall( + opRef: OpRef | Op, + params: any[], + parameterNames: ParameterNamesOption, + thisArg: any, + currentCall: CallStackEntry, + parentCall: CallStackEntry | undefined, + startTime: Date, + displayName?: string + ) { + const inputs = await this.paramsToCallInputs( + params, + thisArg, + parameterNames + ); + if (isOp(opRef)) { + this.saveOp(opRef); + opRef = await opRef.__savedRef; + } + const startReq = { + project_id: this.projectId, + id: currentCall.callId, + op_name: opRef.uri(), + trace_id: currentCall.traceId, + parent_id: parentCall?.callId, + started_at: startTime.toISOString(), + display_name: displayName, + attributes: { + weave: { + client_version: packageVersion, + source: 'js-sdk', + }, + }, + inputs, + }; + return this.saveCallStart(startReq); + } + + public async finishCall( + result: any, + currentCall: CallStackEntry, + parentCall: CallStackEntry | undefined, + summarize: undefined | ((result: any) => Record), + endTime: Date, + startCallPromise: Promise + ) { + // Important to do this first before any awaiting, so we're guaranteed that children + // summaries are processed before parents! + const mergedSummary = processSummary( + result, + summarize, + currentCall, + parentCall + ); + // ensure end is logged after start is logged + await startCallPromise; + this.saveWeaveValues(result); + result = await this.serializedVal(result); + await this.saveCallEnd({ + project_id: this.projectId, + id: currentCall.callId, + ended_at: endTime.toISOString(), + output: result, + summary: mergedSummary, + }); + } + + public async finishCallWithException( + error: any, + currentCall: CallStackEntry, + parentCall: CallStackEntry | undefined, + endTime: Date, + startCallPromise: Promise + ) { + const mergedSummary = processSummary( + null, + undefined, + currentCall, + parentCall + ); + // ensure end is logged after start is logged + await startCallPromise; + await this.saveCallEnd({ + project_id: this.projectId, + id: currentCall.callId, + ended_at: endTime.toISOString(), + output: null, + summary: mergedSummary, + exception: error instanceof Error ? error.message : String(error), + }); + } +} + +/** + * Represents a summary object with string keys and any type of values. + */ +type Summary = Record; + +/** + * Merges two summary objects, combining their values. + * + * @param left - The first summary object to merge. + * @param right - The second summary object to merge. + * @returns A new summary object containing the merged values. + * + * This function performs a deep merge of two summary objects: + * - For numeric values, it adds them together. + * - For nested objects, it recursively merges them. + * - For other types, the left value "wins". + */ +function mergeSummaries(left: Summary, right: Summary): Summary { + const result: Summary = {...right}; + for (const [key, leftValue] of Object.entries(left)) { + if (key in result) { + if (typeof leftValue === 'number' && typeof result[key] === 'number') { + result[key] = leftValue + result[key]; + } else if ( + typeof leftValue === 'object' && + typeof result[key] === 'object' + ) { + result[key] = mergeSummaries(leftValue, result[key]); + } else { + result[key] = leftValue; + } + } else { + result[key] = leftValue; + } + } + return result; +} + +function processSummary( + result: any, + summarize: ((result: any) => Record) | undefined, + currentCall: CallStackEntry, + parentCall: CallStackEntry | undefined +) { + let ownSummary = summarize && result != null ? summarize(result) : {}; + + if (ownSummary.usage) { + for (const model in ownSummary.usage) { + if (typeof ownSummary.usage[model] === 'object') { + ownSummary.usage[model] = { + requests: 1, + ...ownSummary.usage[model], + }; + } + } + } + + const mergedSummary = mergeSummaries(ownSummary, currentCall.childSummary); + + if (parentCall) { + parentCall.childSummary = mergeSummaries( + mergedSummary, + parentCall.childSummary + ); + } + + return mergedSummary; +} + +async function maybeFormatCode(code: string) { + return code; + // try { + // const prettier = await import('prettier'); + // return prettier.format(code, { parser: 'babel' }); + // } catch (error) { + // // prettier not available or formatting failed, just use the original string + // return code; + // } +} diff --git a/sdks/node/src/weaveObject.ts b/sdks/node/src/weaveObject.ts new file mode 100644 index 00000000000..41a7eb046ef --- /dev/null +++ b/sdks/node/src/weaveObject.ts @@ -0,0 +1,101 @@ +import {requireGlobalClient} from './clientApi'; +import {isOp} from './op'; +import {getGlobalDomain} from './urls'; + +export interface Callable {} + +export interface WeaveObjectParameters { + id?: string; + description?: string; +} + +export class ObjectRef { + constructor( + public projectId: string, + public objectId: string, + public digest: string + ) {} + + // TODO: Add extra + + public uri() { + return `weave:///${this.projectId}/object/${this.objectId}:${this.digest}`; + } + + public ui_url() { + const domain = getGlobalDomain(); + return `https://${domain}/${this.projectId}/weave/objects/${this.objectId}/versions/${this.digest}`; + } + + public async get() { + const client = requireGlobalClient(); + return await client.get(this); + } +} + +export class WeaveObject { + __savedRef?: ObjectRef | Promise; + + constructor(protected _baseParameters: WeaveObjectParameters) {} + + className() { + return Object.getPrototypeOf(this).constructor.name; + } + + saveAttrs() { + const attrs: {[key: string]: any} = {}; + + const nonUnderscoreKeys = Object.keys(this).filter( + key => !key.startsWith('_') + ); + + // Include values first (non-functions) + for (const key of Object.keys(this)) { + // @ts-ignore + const value: any = this[key]; + if (typeof value !== 'function') { + attrs[key] = value; + } + } + + // Then ops + for (const key of nonUnderscoreKeys) { + // @ts-ignore + const value: any = this[key]; + if (isOp(value)) { + attrs[key] = value; + } + } + + return attrs; + } + + get id() { + return this._baseParameters.id ?? this.constructor.name; + } + + get description() { + return this._baseParameters.description; + } +} + +export function getClassChain(instance: WeaveObject): string[] { + const bases: string[] = []; + let currentProto = Object.getPrototypeOf(instance); + + while (currentProto && currentProto.constructor.name !== 'Object') { + const className = + currentProto.constructor.name === 'WeaveObject' + ? 'Object' + : currentProto.constructor.name; + bases.push(className); + currentProto = Object.getPrototypeOf(currentProto); + } + // Frontend does this overly specific check for datasets, so push BaseModel to ensure we pass for now. + // data._type === 'Dataset' && + // data._class_name === 'Dataset' && + // _.isEqual(data._bases, ['Object', 'BaseModel']) + bases.push('BaseModel'); + + return bases; +} diff --git a/sdks/node/tsconfig.json b/sdks/node/tsconfig.json new file mode 100644 index 00000000000..2b676b0408c --- /dev/null +++ b/sdks/node/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "composite": true, + "target": "es2018", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "outDir": "./dist", + "paths": { + "weave": ["./src/index.ts"] + } + }, + "exclude": ["src", "examples", "dist", "node_modules"], + "references": [ + { + "path": "./src/tsconfig.src.json" + }, + { + "path": "./examples/tsconfig.examples.json" + } + ] +} diff --git a/sdks/node/weave.openapi.json b/sdks/node/weave.openapi.json new file mode 100644 index 00000000000..b0d86f93ea0 --- /dev/null +++ b/sdks/node/weave.openapi.json @@ -0,0 +1,3369 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/health": { + "get": { + "tags": ["Service"], + "summary": "Read Root", + "operationId": "read_root_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/server_info": { + "get": { + "tags": ["Service"], + "summary": "Server Info", + "operationId": "server_info_server_info_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerInfoRes" + } + } + } + } + } + } + }, + "/call/start": { + "post": { + "tags": ["Calls"], + "summary": "Call Start", + "operationId": "call_start_call_start_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallStartReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallStartRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/call/end": { + "post": { + "tags": ["Calls"], + "summary": "Call End", + "operationId": "call_end_call_end_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallEndReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallEndRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/call/upsert_batch": { + "post": { + "tags": ["Calls"], + "summary": "Call Start Batch", + "operationId": "call_start_batch_call_upsert_batch_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallCreateBatchReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallCreateBatchRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/calls/delete": { + "post": { + "tags": ["Calls"], + "summary": "Calls Delete", + "operationId": "calls_delete_calls_delete_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallsDeleteReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallsDeleteRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/call/update": { + "post": { + "tags": ["Calls"], + "summary": "Call Update", + "operationId": "call_update_call_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallUpdateReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallUpdateRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/call/read": { + "post": { + "tags": ["Calls"], + "summary": "Call Read", + "operationId": "call_read_call_read_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallReadReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallReadRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/calls/query_stats": { + "post": { + "tags": ["Calls"], + "summary": "Calls Query Stats", + "operationId": "calls_query_stats_calls_query_stats_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallsQueryStatsReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallsQueryStatsRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/calls/stream_query": { + "post": { + "tags": ["Calls"], + "summary": "Calls Query Stream", + "operationId": "calls_query_stream_calls_stream_query_post", + "security": [ + { + "HTTPBasic": [] + } + ], + "parameters": [ + { + "name": "accept", + "in": "header", + "required": false, + "schema": { + "type": "string", + "default": "application/jsonl", + "title": "Accept" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CallsQueryReq" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/obj/create": { + "post": { + "tags": ["Objects"], + "summary": "Obj Create", + "operationId": "obj_create_obj_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjCreateReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjCreateRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/obj/read": { + "post": { + "tags": ["Objects"], + "summary": "Obj Read", + "operationId": "obj_read_obj_read_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjReadReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjReadRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/objs/query": { + "post": { + "tags": ["Objects"], + "summary": "Objs Query", + "operationId": "objs_query_objs_query_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjQueryReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ObjQueryRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/table/create": { + "post": { + "tags": ["Tables"], + "summary": "Table Create", + "operationId": "table_create_table_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TableCreateReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TableCreateRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/table/update": { + "post": { + "tags": ["Tables"], + "summary": "Table Update", + "operationId": "table_update_table_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TableUpdateReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TableUpdateRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/table/query": { + "post": { + "tags": ["Tables"], + "summary": "Table Query", + "operationId": "table_query_table_query_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TableQueryReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TableQueryRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/refs/read_batch": { + "post": { + "tags": ["Refs"], + "summary": "Refs Read Batch", + "operationId": "refs_read_batch_refs_read_batch_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefsReadBatchReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RefsReadBatchRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/file/create": { + "post": { + "tags": ["Files"], + "summary": "File Create", + "operationId": "file_create_file_create_post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_file_create_file_create_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileCreateRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/file/content": { + "post": { + "tags": ["Files"], + "summary": "File Content", + "operationId": "file_content_file_content_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileContentReadReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/feedback/create": { + "post": { + "tags": ["Feedback"], + "summary": "Feedback Create", + "description": "Add feedback to a call or object.", + "operationId": "feedback_create_feedback_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackCreateReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackCreateRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/feedback/query": { + "post": { + "tags": ["Feedback"], + "summary": "Feedback Query", + "description": "Query for feedback.", + "operationId": "feedback_query_feedback_query_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackQueryReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackQueryRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + }, + "/feedback/purge": { + "post": { + "tags": ["Feedback"], + "summary": "Feedback Purge", + "description": "Permanently delete feedback.", + "operationId": "feedback_purge_feedback_purge_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackPurgeReq" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackPurgeRes" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBasic": [] + } + ] + } + } + }, + "components": { + "schemas": { + "AndOperation": { + "properties": { + "$and": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + }, + "type": "array", + "title": "$And" + } + }, + "type": "object", + "required": ["$and"], + "title": "AndOperation" + }, + "Body_file_create_file_create_post": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": ["project_id", "file"], + "title": "Body_file_create_file_create_post" + }, + "CallBatchEndMode": { + "properties": { + "mode": { + "type": "string", + "title": "Mode", + "default": "end" + }, + "req": { + "$ref": "#/components/schemas/CallEndReq" + } + }, + "type": "object", + "required": ["req"], + "title": "CallBatchEndMode" + }, + "CallBatchStartMode": { + "properties": { + "mode": { + "type": "string", + "title": "Mode", + "default": "start" + }, + "req": { + "$ref": "#/components/schemas/CallStartReq" + } + }, + "type": "object", + "required": ["req"], + "title": "CallBatchStartMode" + }, + "CallCreateBatchReq": { + "properties": { + "batch": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/CallBatchStartMode" + }, + { + "$ref": "#/components/schemas/CallBatchEndMode" + } + ] + }, + "type": "array", + "title": "Batch" + } + }, + "type": "object", + "required": ["batch"], + "title": "CallCreateBatchReq" + }, + "CallCreateBatchRes": { + "properties": { + "res": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/CallStartRes" + }, + { + "$ref": "#/components/schemas/CallEndRes" + } + ] + }, + "type": "array", + "title": "Res" + } + }, + "type": "object", + "required": ["res"], + "title": "CallCreateBatchRes" + }, + "CallEndReq": { + "properties": { + "end": { + "$ref": "#/components/schemas/EndedCallSchemaForInsert" + } + }, + "type": "object", + "required": ["end"], + "title": "CallEndReq" + }, + "CallEndRes": { + "properties": {}, + "type": "object", + "title": "CallEndRes" + }, + "CallReadReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "id": { + "type": "string", + "title": "Id" + }, + "include_costs": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Include Costs", + "default": false + } + }, + "type": "object", + "required": ["project_id", "id"], + "title": "CallReadReq" + }, + "CallReadRes": { + "properties": { + "call": { + "anyOf": [ + { + "$ref": "#/components/schemas/CallSchema" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": ["call"], + "title": "CallReadRes" + }, + "CallSchema": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "project_id": { + "type": "string", + "title": "Project Id" + }, + "op_name": { + "type": "string", + "title": "Op Name" + }, + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Display Name" + }, + "trace_id": { + "type": "string", + "title": "Trace Id" + }, + "parent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Id" + }, + "started_at": { + "type": "string", + "format": "date-time", + "title": "Started At" + }, + "attributes": { + "type": "object", + "title": "Attributes" + }, + "inputs": { + "type": "object", + "title": "Inputs" + }, + "ended_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Ended At" + }, + "exception": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Exception" + }, + "output": { + "anyOf": [ + {}, + { + "type": "null" + } + ], + "title": "Output" + }, + "summary": { + "type": "object" + }, + "wb_user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Wb User Id" + }, + "wb_run_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Wb Run Id" + }, + "deleted_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Deleted At" + } + }, + "type": "object", + "required": ["id", "project_id", "op_name", "trace_id", "started_at", "attributes", "inputs"], + "title": "CallSchema" + }, + "CallStartReq": { + "properties": { + "start": { + "$ref": "#/components/schemas/StartedCallSchemaForInsert" + } + }, + "type": "object", + "required": ["start"], + "title": "CallStartReq" + }, + "CallStartRes": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "trace_id": { + "type": "string", + "title": "Trace Id" + } + }, + "type": "object", + "required": ["id", "trace_id"], + "title": "CallStartRes" + }, + "CallUpdateReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "call_id": { + "type": "string", + "title": "Call Id" + }, + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Display Name" + }, + "wb_user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Wb User Id", + "description": "Do not set directly. Server will automatically populate this field." + } + }, + "type": "object", + "required": ["project_id", "call_id"], + "title": "CallUpdateReq" + }, + "CallUpdateRes": { + "properties": {}, + "type": "object", + "title": "CallUpdateRes" + }, + "CallsDeleteReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "call_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Call Ids" + }, + "wb_user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Wb User Id", + "description": "Do not set directly. Server will automatically populate this field." + } + }, + "type": "object", + "required": ["project_id", "call_ids"], + "title": "CallsDeleteReq" + }, + "CallsDeleteRes": { + "properties": {}, + "type": "object", + "title": "CallsDeleteRes" + }, + "CallsFilter": { + "properties": { + "op_names": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Op Names" + }, + "input_refs": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Input Refs" + }, + "output_refs": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Output Refs" + }, + "parent_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Parent Ids" + }, + "trace_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Trace Ids" + }, + "call_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Call Ids" + }, + "trace_roots_only": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Trace Roots Only" + }, + "wb_user_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Wb User Ids" + }, + "wb_run_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Wb Run Ids" + } + }, + "type": "object", + "title": "CallsFilter" + }, + "CallsQueryReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "filter": { + "anyOf": [ + { + "$ref": "#/components/schemas/CallsFilter" + }, + { + "type": "null" + } + ] + }, + "limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Limit" + }, + "offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Offset" + }, + "sort_by": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/SortBy" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Sort By" + }, + "query": { + "anyOf": [ + { + "$ref": "#/components/schemas/Query" + }, + { + "type": "null" + } + ] + }, + "include_costs": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Include Costs", + "default": false + }, + "columns": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Columns" + }, + "expand_columns": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Expand Columns", + "description": "Columns to expand, i.e. refs to other objects", + "examples": [["inputs.self.message", "inputs.model.prompt"]] + } + }, + "type": "object", + "required": ["project_id"], + "title": "CallsQueryReq" + }, + "CallsQueryStatsReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "filter": { + "anyOf": [ + { + "$ref": "#/components/schemas/CallsFilter" + }, + { + "type": "null" + } + ] + }, + "query": { + "anyOf": [ + { + "$ref": "#/components/schemas/Query" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": ["project_id"], + "title": "CallsQueryStatsReq" + }, + "CallsQueryStatsRes": { + "properties": { + "count": { + "type": "integer", + "title": "Count" + } + }, + "type": "object", + "required": ["count"], + "title": "CallsQueryStatsRes" + }, + "ContainsOperation": { + "properties": { + "$contains": { + "$ref": "#/components/schemas/ContainsSpec" + } + }, + "type": "object", + "required": ["$contains"], + "title": "ContainsOperation" + }, + "ContainsSpec": { + "properties": { + "input": { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ], + "title": "Input" + }, + "substr": { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ], + "title": "Substr" + }, + "case_insensitive": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Case Insensitive", + "default": false + } + }, + "type": "object", + "required": ["input", "substr"], + "title": "ContainsSpec" + }, + "ConvertOperation": { + "properties": { + "$convert": { + "$ref": "#/components/schemas/ConvertSpec" + } + }, + "type": "object", + "required": ["$convert"], + "title": "ConvertOperation" + }, + "ConvertSpec": { + "properties": { + "input": { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ], + "title": "Input" + }, + "to": { + "type": "string", + "enum": ["double", "string", "int", "bool", "exists"], + "title": "To" + } + }, + "type": "object", + "required": ["input", "to"], + "title": "ConvertSpec" + }, + "EndedCallSchemaForInsert": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "id": { + "type": "string", + "title": "Id" + }, + "ended_at": { + "type": "string", + "format": "date-time", + "title": "Ended At" + }, + "exception": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Exception" + }, + "output": { + "anyOf": [ + {}, + { + "type": "null" + } + ], + "title": "Output" + }, + "summary": { + "$ref": "#/components/schemas/SummaryInsertMap" + } + }, + "type": "object", + "required": ["project_id", "id", "ended_at", "summary"], + "title": "EndedCallSchemaForInsert" + }, + "EqOperation": { + "properties": { + "$eq": { + "prefixItems": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + }, + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + } + ], + "type": "array", + "maxItems": 2, + "minItems": 2, + "title": "$Eq" + } + }, + "type": "object", + "required": ["$eq"], + "title": "EqOperation" + }, + "FeedbackCreateReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id", + "examples": ["entity/project"] + }, + "weave_ref": { + "type": "string", + "title": "Weave Ref", + "examples": ["weave:///entity/project/object/name:digest"] + }, + "creator": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Creator", + "examples": ["Jane Smith"] + }, + "feedback_type": { + "type": "string", + "title": "Feedback Type", + "examples": ["custom"] + }, + "payload": { + "type": "object", + "title": "Payload", + "examples": [ + { + "key": "value" + } + ] + }, + "wb_user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Wb User Id", + "description": "Do not set directly. Server will automatically populate this field." + } + }, + "type": "object", + "required": ["project_id", "weave_ref", "feedback_type", "payload"], + "title": "FeedbackCreateReq" + }, + "FeedbackCreateRes": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "wb_user_id": { + "type": "string", + "title": "Wb User Id" + }, + "payload": { + "type": "object", + "title": "Payload" + } + }, + "type": "object", + "required": ["id", "created_at", "wb_user_id", "payload"], + "title": "FeedbackCreateRes" + }, + "FeedbackPurgeReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id", + "examples": ["entity/project"] + }, + "query": { + "$ref": "#/components/schemas/Query" + } + }, + "type": "object", + "required": ["project_id", "query"], + "title": "FeedbackPurgeReq" + }, + "FeedbackPurgeRes": { + "properties": {}, + "type": "object", + "title": "FeedbackPurgeRes" + }, + "FeedbackQueryReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id", + "examples": ["entity/project"] + }, + "fields": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Fields", + "examples": [["id", "feedback_type", "payload.note"]] + }, + "query": { + "anyOf": [ + { + "$ref": "#/components/schemas/Query" + }, + { + "type": "null" + } + ] + }, + "sort_by": { + "anyOf": [ + { + "items": { + "$ref": "#/components/schemas/SortBy" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Sort By" + }, + "limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Limit", + "examples": [10] + }, + "offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Offset", + "examples": [0] + } + }, + "type": "object", + "required": ["project_id"], + "title": "FeedbackQueryReq" + }, + "FeedbackQueryRes": { + "properties": { + "result": { + "items": { + "type": "object" + }, + "type": "array", + "title": "Result" + } + }, + "type": "object", + "required": ["result"], + "title": "FeedbackQueryRes" + }, + "FileContentReadReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "digest": { + "type": "string", + "title": "Digest" + } + }, + "type": "object", + "required": ["project_id", "digest"], + "title": "FileContentReadReq" + }, + "FileCreateRes": { + "properties": { + "digest": { + "type": "string", + "title": "Digest" + } + }, + "type": "object", + "required": ["digest"], + "title": "FileCreateRes" + }, + "GetFieldOperator": { + "properties": { + "$getField": { + "type": "string", + "title": "$Getfield" + } + }, + "type": "object", + "required": ["$getField"], + "title": "GetFieldOperator" + }, + "GtOperation": { + "properties": { + "$gt": { + "prefixItems": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + }, + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + } + ], + "type": "array", + "maxItems": 2, + "minItems": 2, + "title": "$Gt" + } + }, + "type": "object", + "required": ["$gt"], + "title": "GtOperation" + }, + "GteOperation": { + "properties": { + "$gte": { + "prefixItems": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + }, + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + } + ], + "type": "array", + "maxItems": 2, + "minItems": 2, + "title": "$Gte" + } + }, + "type": "object", + "required": ["$gte"], + "title": "GteOperation" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "InOperation": { + "properties": { + "$in": { + "prefixItems": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + }, + { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + }, + "type": "array" + } + ], + "type": "array", + "maxItems": 2, + "minItems": 2, + "title": "$In" + } + }, + "type": "object", + "required": ["$in"], + "title": "InOperation" + }, + "LLMUsageSchema": { + "properties": { + "prompt_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Prompt Tokens" + }, + "input_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Input Tokens" + }, + "completion_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Completion Tokens" + }, + "output_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Output Tokens" + }, + "requests": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Requests" + }, + "total_tokens": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Total Tokens" + } + }, + "type": "object", + "title": "LLMUsageSchema" + }, + "LiteralOperation": { + "properties": { + "$literal": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "additionalProperties": { + "$ref": "#/components/schemas/LiteralOperation" + }, + "type": "object" + }, + { + "items": { + "$ref": "#/components/schemas/LiteralOperation" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "$Literal" + } + }, + "type": "object", + "required": ["$literal"], + "title": "LiteralOperation" + }, + "NotOperation": { + "properties": { + "$not": { + "prefixItems": [ + { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + } + ], + "type": "array", + "maxItems": 1, + "minItems": 1, + "title": "$Not" + } + }, + "type": "object", + "required": ["$not"], + "title": "NotOperation" + }, + "ObjCreateReq": { + "properties": { + "obj": { + "$ref": "#/components/schemas/ObjSchemaForInsert" + } + }, + "type": "object", + "required": ["obj"], + "title": "ObjCreateReq" + }, + "ObjCreateRes": { + "properties": { + "digest": { + "type": "string", + "title": "Digest" + } + }, + "type": "object", + "required": ["digest"], + "title": "ObjCreateRes" + }, + "ObjQueryReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "filter": { + "anyOf": [ + { + "$ref": "#/components/schemas/ObjectVersionFilter" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": ["project_id"], + "title": "ObjQueryReq" + }, + "ObjQueryRes": { + "properties": { + "objs": { + "items": { + "$ref": "#/components/schemas/ObjSchema" + }, + "type": "array", + "title": "Objs" + } + }, + "type": "object", + "required": ["objs"], + "title": "ObjQueryRes" + }, + "ObjReadReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "object_id": { + "type": "string", + "title": "Object Id" + }, + "digest": { + "type": "string", + "title": "Digest" + } + }, + "type": "object", + "required": ["project_id", "object_id", "digest"], + "title": "ObjReadReq" + }, + "ObjReadRes": { + "properties": { + "obj": { + "$ref": "#/components/schemas/ObjSchema" + } + }, + "type": "object", + "required": ["obj"], + "title": "ObjReadRes" + }, + "ObjSchema": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "object_id": { + "type": "string", + "title": "Object Id" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At" + }, + "deleted_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Deleted At" + }, + "digest": { + "type": "string", + "title": "Digest" + }, + "version_index": { + "type": "integer", + "title": "Version Index" + }, + "is_latest": { + "type": "integer", + "title": "Is Latest" + }, + "kind": { + "type": "string", + "title": "Kind" + }, + "base_object_class": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Object Class" + }, + "val": { + "title": "Val" + } + }, + "type": "object", + "required": [ + "project_id", + "object_id", + "created_at", + "digest", + "version_index", + "is_latest", + "kind", + "base_object_class", + "val" + ], + "title": "ObjSchema" + }, + "ObjSchemaForInsert": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "object_id": { + "type": "string", + "title": "Object Id" + }, + "val": { + "title": "Val" + } + }, + "type": "object", + "required": ["project_id", "object_id", "val"], + "title": "ObjSchemaForInsert" + }, + "ObjectVersionFilter": { + "properties": { + "base_object_classes": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Base Object Classes" + }, + "object_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Object Ids" + }, + "is_op": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Is Op" + }, + "latest_only": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Latest Only" + } + }, + "type": "object", + "title": "ObjectVersionFilter" + }, + "OrOperation": { + "properties": { + "$or": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/LiteralOperation" + }, + { + "$ref": "#/components/schemas/GetFieldOperator" + }, + { + "$ref": "#/components/schemas/ConvertOperation" + }, + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ] + }, + "type": "array", + "title": "$Or" + } + }, + "type": "object", + "required": ["$or"], + "title": "OrOperation" + }, + "Query": { + "properties": { + "$expr": { + "anyOf": [ + { + "$ref": "#/components/schemas/AndOperation" + }, + { + "$ref": "#/components/schemas/OrOperation" + }, + { + "$ref": "#/components/schemas/NotOperation" + }, + { + "$ref": "#/components/schemas/EqOperation" + }, + { + "$ref": "#/components/schemas/GtOperation" + }, + { + "$ref": "#/components/schemas/GteOperation" + }, + { + "$ref": "#/components/schemas/InOperation" + }, + { + "$ref": "#/components/schemas/ContainsOperation" + } + ], + "title": "$Expr" + } + }, + "type": "object", + "required": ["$expr"], + "title": "Query" + }, + "RefsReadBatchReq": { + "properties": { + "refs": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Refs" + } + }, + "type": "object", + "required": ["refs"], + "title": "RefsReadBatchReq" + }, + "RefsReadBatchRes": { + "properties": { + "vals": { + "items": {}, + "type": "array", + "title": "Vals" + } + }, + "type": "object", + "required": ["vals"], + "title": "RefsReadBatchRes" + }, + "ServerInfoRes": { + "properties": { + "min_required_weave_python_version": { + "type": "string", + "title": "Min Required Weave Python Version" + } + }, + "type": "object", + "required": ["min_required_weave_python_version"], + "title": "ServerInfoRes" + }, + "SortBy": { + "properties": { + "field": { + "type": "string", + "title": "Field" + }, + "direction": { + "type": "string", + "enum": ["asc", "desc"], + "title": "Direction" + } + }, + "type": "object", + "required": ["field", "direction"], + "title": "SortBy" + }, + "StartedCallSchemaForInsert": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Id" + }, + "op_name": { + "type": "string", + "title": "Op Name" + }, + "display_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Display Name" + }, + "trace_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Trace Id" + }, + "parent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Id" + }, + "started_at": { + "type": "string", + "format": "date-time", + "title": "Started At" + }, + "attributes": { + "type": "object", + "title": "Attributes" + }, + "inputs": { + "type": "object", + "title": "Inputs" + }, + "wb_user_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Wb User Id", + "description": "Do not set directly. Server will automatically populate this field." + }, + "wb_run_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Wb Run Id" + } + }, + "type": "object", + "required": ["project_id", "op_name", "started_at", "attributes", "inputs"], + "title": "StartedCallSchemaForInsert" + }, + "SummaryInsertMap": { + "properties": { + "usage": { + "additionalProperties": { + "$ref": "#/components/schemas/LLMUsageSchema" + }, + "type": "object", + "title": "Usage" + } + }, + "additionalProperties": true, + "type": "object", + "title": "SummaryInsertMap" + }, + "TableAppendSpec": { + "properties": { + "append": { + "$ref": "#/components/schemas/TableAppendSpecPayload" + } + }, + "type": "object", + "required": ["append"], + "title": "TableAppendSpec" + }, + "TableAppendSpecPayload": { + "properties": { + "row": { + "type": "object", + "title": "Row" + } + }, + "type": "object", + "required": ["row"], + "title": "TableAppendSpecPayload" + }, + "TableCreateReq": { + "properties": { + "table": { + "$ref": "#/components/schemas/TableSchemaForInsert" + } + }, + "type": "object", + "required": ["table"], + "title": "TableCreateReq" + }, + "TableCreateRes": { + "properties": { + "digest": { + "type": "string", + "title": "Digest" + } + }, + "type": "object", + "required": ["digest"], + "title": "TableCreateRes" + }, + "TableInsertSpec": { + "properties": { + "insert": { + "$ref": "#/components/schemas/TableInsertSpecPayload" + } + }, + "type": "object", + "required": ["insert"], + "title": "TableInsertSpec" + }, + "TableInsertSpecPayload": { + "properties": { + "index": { + "type": "integer", + "title": "Index" + }, + "row": { + "type": "object", + "title": "Row" + } + }, + "type": "object", + "required": ["index", "row"], + "title": "TableInsertSpecPayload" + }, + "TablePopSpec": { + "properties": { + "pop": { + "$ref": "#/components/schemas/TablePopSpecPayload" + } + }, + "type": "object", + "required": ["pop"], + "title": "TablePopSpec" + }, + "TablePopSpecPayload": { + "properties": { + "index": { + "type": "integer", + "title": "Index" + } + }, + "type": "object", + "required": ["index"], + "title": "TablePopSpecPayload" + }, + "TableQueryReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "digest": { + "type": "string", + "title": "Digest" + }, + "filter": { + "anyOf": [ + { + "$ref": "#/components/schemas/TableRowFilter" + }, + { + "type": "null" + } + ] + }, + "limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Limit" + }, + "offset": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Offset" + } + }, + "type": "object", + "required": ["project_id", "digest"], + "title": "TableQueryReq" + }, + "TableQueryRes": { + "properties": { + "rows": { + "items": { + "$ref": "#/components/schemas/TableRowSchema" + }, + "type": "array", + "title": "Rows" + } + }, + "type": "object", + "required": ["rows"], + "title": "TableQueryRes" + }, + "TableRowFilter": { + "properties": { + "row_digests": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Row Digests" + } + }, + "type": "object", + "title": "TableRowFilter" + }, + "TableRowSchema": { + "properties": { + "digest": { + "type": "string", + "title": "Digest" + }, + "val": { + "title": "Val" + } + }, + "type": "object", + "required": ["digest", "val"], + "title": "TableRowSchema" + }, + "TableSchemaForInsert": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "rows": { + "items": { + "type": "object" + }, + "type": "array", + "title": "Rows" + } + }, + "type": "object", + "required": ["project_id", "rows"], + "title": "TableSchemaForInsert" + }, + "TableUpdateReq": { + "properties": { + "project_id": { + "type": "string", + "title": "Project Id" + }, + "base_digest": { + "type": "string", + "title": "Base Digest" + }, + "updates": { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/TableAppendSpec" + }, + { + "$ref": "#/components/schemas/TablePopSpec" + }, + { + "$ref": "#/components/schemas/TableInsertSpec" + } + ] + }, + "type": "array", + "title": "Updates" + } + }, + "type": "object", + "required": ["project_id", "base_digest", "updates"], + "title": "TableUpdateReq" + }, + "TableUpdateRes": { + "properties": { + "digest": { + "type": "string", + "title": "Digest" + } + }, + "type": "object", + "required": ["digest"], + "title": "TableUpdateRes" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError" + } + }, + "securitySchemes": { + "HTTPBasic": { + "type": "http", + "scheme": "basic" + } + } + } +} From abbe2e0832a4ed3a21515cd39d926daf927e4c1e Mon Sep 17 00:00:00 2001 From: Andrew Truong Date: Mon, 28 Oct 2024 23:45:36 -0400 Subject: [PATCH 5/5] feat(ui): Add simple language detection for code browser (#2780) --- .../Home/Browse2/Browse2OpDefCode.tsx | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/weave-js/src/components/PagePanelComponents/Home/Browse2/Browse2OpDefCode.tsx b/weave-js/src/components/PagePanelComponents/Home/Browse2/Browse2OpDefCode.tsx index 38aa07823d8..87b3c8339d8 100644 --- a/weave-js/src/components/PagePanelComponents/Home/Browse2/Browse2OpDefCode.tsx +++ b/weave-js/src/components/PagePanelComponents/Home/Browse2/Browse2OpDefCode.tsx @@ -6,6 +6,23 @@ import React, {FC} from 'react'; import {Alert} from '../../../Alert'; import {useWFHooks} from '../Browse3/pages/wfReactInterface/context'; +function detectLanguage(uri: string, code: string) { + // Simple language detection based on file extension or content + if (uri.endsWith('.py')) { + return 'python'; + } + if (uri.endsWith('.js') || uri.endsWith('.ts')) { + return 'javascript'; + } + if (code.includes('def ') || code.includes('import ')) { + return 'python'; + } + if (code.includes('function ') || code.includes('const ')) { + return 'javascript'; + } + return 'plaintext'; +} + export const Browse2OpDefCode: FC<{uri: string; maxRowsInView?: number}> = ({ uri, maxRowsInView, @@ -37,10 +54,12 @@ export const Browse2OpDefCode: FC<{uri: string; maxRowsInView?: number}> = ({ ); } + const detectedLanguage = detectLanguage(uri, text.result ?? ''); + const inner = (