ai 6.0.33 → 6.0.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/internal/index.js +1 -1
- package/dist/internal/index.mjs +1 -1
- package/docs/02-foundations/03-prompts.mdx +2 -2
- package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +1 -1
- package/docs/07-reference/01-ai-sdk-core/28-output.mdx +1 -1
- package/package.json +6 -4
- package/src/agent/agent.ts +116 -0
- package/src/agent/create-agent-ui-stream-response.test.ts +258 -0
- package/src/agent/create-agent-ui-stream-response.ts +50 -0
- package/src/agent/create-agent-ui-stream.ts +73 -0
- package/src/agent/index.ts +33 -0
- package/src/agent/infer-agent-tools.ts +7 -0
- package/src/agent/infer-agent-ui-message.test-d.ts +54 -0
- package/src/agent/infer-agent-ui-message.ts +11 -0
- package/src/agent/pipe-agent-ui-stream-to-response.ts +52 -0
- package/src/agent/tool-loop-agent-on-finish-callback.ts +31 -0
- package/src/agent/tool-loop-agent-on-step-finish-callback.ts +11 -0
- package/src/agent/tool-loop-agent-settings.ts +182 -0
- package/src/agent/tool-loop-agent.test-d.ts +114 -0
- package/src/agent/tool-loop-agent.test.ts +442 -0
- package/src/agent/tool-loop-agent.ts +114 -0
- package/src/embed/__snapshots__/embed-many.test.ts.snap +191 -0
- package/src/embed/__snapshots__/embed.test.ts.snap +81 -0
- package/src/embed/embed-many-result.ts +53 -0
- package/src/embed/embed-many.test.ts +653 -0
- package/src/embed/embed-many.ts +378 -0
- package/src/embed/embed-result.ts +50 -0
- package/src/embed/embed.test.ts +298 -0
- package/src/embed/embed.ts +211 -0
- package/src/embed/index.ts +4 -0
- package/src/error/index.ts +34 -0
- package/src/error/invalid-argument-error.ts +34 -0
- package/src/error/invalid-stream-part-error.ts +28 -0
- package/src/error/invalid-tool-approval-error.ts +26 -0
- package/src/error/invalid-tool-input-error.ts +33 -0
- package/src/error/no-image-generated-error.ts +39 -0
- package/src/error/no-object-generated-error.ts +70 -0
- package/src/error/no-output-generated-error.ts +26 -0
- package/src/error/no-speech-generated-error.ts +18 -0
- package/src/error/no-such-tool-error.ts +35 -0
- package/src/error/no-transcript-generated-error.ts +20 -0
- package/src/error/tool-call-not-found-for-approval-error.ts +32 -0
- package/src/error/tool-call-repair-error.ts +30 -0
- package/src/error/unsupported-model-version-error.ts +23 -0
- package/src/error/verify-no-object-generated-error.ts +27 -0
- package/src/generate-image/generate-image-result.ts +42 -0
- package/src/generate-image/generate-image.test.ts +1420 -0
- package/src/generate-image/generate-image.ts +360 -0
- package/src/generate-image/index.ts +18 -0
- package/src/generate-object/__snapshots__/generate-object.test.ts.snap +133 -0
- package/src/generate-object/__snapshots__/stream-object.test.ts.snap +297 -0
- package/src/generate-object/generate-object-result.ts +67 -0
- package/src/generate-object/generate-object.test-d.ts +49 -0
- package/src/generate-object/generate-object.test.ts +1191 -0
- package/src/generate-object/generate-object.ts +518 -0
- package/src/generate-object/index.ts +9 -0
- package/src/generate-object/inject-json-instruction.test.ts +181 -0
- package/src/generate-object/inject-json-instruction.ts +30 -0
- package/src/generate-object/output-strategy.ts +415 -0
- package/src/generate-object/parse-and-validate-object-result.ts +111 -0
- package/src/generate-object/repair-text.ts +12 -0
- package/src/generate-object/stream-object-result.ts +120 -0
- package/src/generate-object/stream-object.test-d.ts +74 -0
- package/src/generate-object/stream-object.test.ts +1950 -0
- package/src/generate-object/stream-object.ts +986 -0
- package/src/generate-object/validate-object-generation-input.ts +144 -0
- package/src/generate-speech/generate-speech-result.ts +30 -0
- package/src/generate-speech/generate-speech.test.ts +300 -0
- package/src/generate-speech/generate-speech.ts +190 -0
- package/src/generate-speech/generated-audio-file.ts +65 -0
- package/src/generate-speech/index.ts +3 -0
- package/src/generate-text/__snapshots__/generate-text.test.ts.snap +1872 -0
- package/src/generate-text/__snapshots__/stream-text.test.ts.snap +1255 -0
- package/src/generate-text/collect-tool-approvals.test.ts +553 -0
- package/src/generate-text/collect-tool-approvals.ts +116 -0
- package/src/generate-text/content-part.ts +25 -0
- package/src/generate-text/execute-tool-call.ts +129 -0
- package/src/generate-text/extract-reasoning-content.ts +17 -0
- package/src/generate-text/extract-text-content.ts +15 -0
- package/src/generate-text/generate-text-result.ts +168 -0
- package/src/generate-text/generate-text.test-d.ts +68 -0
- package/src/generate-text/generate-text.test.ts +7011 -0
- package/src/generate-text/generate-text.ts +1223 -0
- package/src/generate-text/generated-file.ts +70 -0
- package/src/generate-text/index.ts +57 -0
- package/src/generate-text/is-approval-needed.ts +29 -0
- package/src/generate-text/output-utils.ts +23 -0
- package/src/generate-text/output.test.ts +698 -0
- package/src/generate-text/output.ts +590 -0
- package/src/generate-text/parse-tool-call.test.ts +570 -0
- package/src/generate-text/parse-tool-call.ts +188 -0
- package/src/generate-text/prepare-step.ts +103 -0
- package/src/generate-text/prune-messages.test.ts +720 -0
- package/src/generate-text/prune-messages.ts +167 -0
- package/src/generate-text/reasoning-output.ts +20 -0
- package/src/generate-text/reasoning.ts +8 -0
- package/src/generate-text/response-message.ts +10 -0
- package/src/generate-text/run-tools-transformation.test.ts +1143 -0
- package/src/generate-text/run-tools-transformation.ts +420 -0
- package/src/generate-text/smooth-stream.test.ts +2101 -0
- package/src/generate-text/smooth-stream.ts +162 -0
- package/src/generate-text/step-result.ts +238 -0
- package/src/generate-text/stop-condition.ts +29 -0
- package/src/generate-text/stream-text-result.ts +463 -0
- package/src/generate-text/stream-text.test-d.ts +200 -0
- package/src/generate-text/stream-text.test.ts +19979 -0
- package/src/generate-text/stream-text.ts +2505 -0
- package/src/generate-text/to-response-messages.test.ts +922 -0
- package/src/generate-text/to-response-messages.ts +163 -0
- package/src/generate-text/tool-approval-request-output.ts +21 -0
- package/src/generate-text/tool-call-repair-function.ts +27 -0
- package/src/generate-text/tool-call.ts +47 -0
- package/src/generate-text/tool-error.ts +34 -0
- package/src/generate-text/tool-output-denied.ts +21 -0
- package/src/generate-text/tool-output.ts +7 -0
- package/src/generate-text/tool-result.ts +36 -0
- package/src/generate-text/tool-set.ts +14 -0
- package/src/global.ts +24 -0
- package/src/index.ts +50 -0
- package/src/logger/index.ts +6 -0
- package/src/logger/log-warnings.test.ts +351 -0
- package/src/logger/log-warnings.ts +119 -0
- package/src/middleware/__snapshots__/simulate-streaming-middleware.test.ts.snap +64 -0
- package/src/middleware/add-tool-input-examples-middleware.test.ts +476 -0
- package/src/middleware/add-tool-input-examples-middleware.ts +90 -0
- package/src/middleware/default-embedding-settings-middleware.test.ts +126 -0
- package/src/middleware/default-embedding-settings-middleware.ts +22 -0
- package/src/middleware/default-settings-middleware.test.ts +388 -0
- package/src/middleware/default-settings-middleware.ts +33 -0
- package/src/middleware/extract-json-middleware.test.ts +827 -0
- package/src/middleware/extract-json-middleware.ts +197 -0
- package/src/middleware/extract-reasoning-middleware.test.ts +1028 -0
- package/src/middleware/extract-reasoning-middleware.ts +238 -0
- package/src/middleware/index.ts +10 -0
- package/src/middleware/simulate-streaming-middleware.test.ts +911 -0
- package/src/middleware/simulate-streaming-middleware.ts +79 -0
- package/src/middleware/wrap-embedding-model.test.ts +358 -0
- package/src/middleware/wrap-embedding-model.ts +86 -0
- package/src/middleware/wrap-image-model.test.ts +423 -0
- package/src/middleware/wrap-image-model.ts +85 -0
- package/src/middleware/wrap-language-model.test.ts +518 -0
- package/src/middleware/wrap-language-model.ts +104 -0
- package/src/middleware/wrap-provider.test.ts +120 -0
- package/src/middleware/wrap-provider.ts +51 -0
- package/src/model/as-embedding-model-v3.test.ts +319 -0
- package/src/model/as-embedding-model-v3.ts +24 -0
- package/src/model/as-image-model-v3.test.ts +409 -0
- package/src/model/as-image-model-v3.ts +24 -0
- package/src/model/as-language-model-v3.test.ts +508 -0
- package/src/model/as-language-model-v3.ts +103 -0
- package/src/model/as-provider-v3.ts +36 -0
- package/src/model/as-speech-model-v3.test.ts +356 -0
- package/src/model/as-speech-model-v3.ts +24 -0
- package/src/model/as-transcription-model-v3.test.ts +529 -0
- package/src/model/as-transcription-model-v3.ts +24 -0
- package/src/model/resolve-model.test.ts +244 -0
- package/src/model/resolve-model.ts +126 -0
- package/src/prompt/call-settings.ts +148 -0
- package/src/prompt/content-part.ts +209 -0
- package/src/prompt/convert-to-language-model-prompt.test.ts +2018 -0
- package/src/prompt/convert-to-language-model-prompt.ts +442 -0
- package/src/prompt/create-tool-model-output.test.ts +508 -0
- package/src/prompt/create-tool-model-output.ts +34 -0
- package/src/prompt/data-content.test.ts +15 -0
- package/src/prompt/data-content.ts +134 -0
- package/src/prompt/index.ts +27 -0
- package/src/prompt/invalid-data-content-error.ts +29 -0
- package/src/prompt/invalid-message-role-error.ts +27 -0
- package/src/prompt/message-conversion-error.ts +28 -0
- package/src/prompt/message.ts +68 -0
- package/src/prompt/prepare-call-settings.test.ts +159 -0
- package/src/prompt/prepare-call-settings.ts +108 -0
- package/src/prompt/prepare-tools-and-tool-choice.test.ts +461 -0
- package/src/prompt/prepare-tools-and-tool-choice.ts +86 -0
- package/src/prompt/prompt.ts +43 -0
- package/src/prompt/split-data-url.ts +17 -0
- package/src/prompt/standardize-prompt.test.ts +82 -0
- package/src/prompt/standardize-prompt.ts +99 -0
- package/src/prompt/wrap-gateway-error.ts +29 -0
- package/src/registry/custom-provider.test.ts +211 -0
- package/src/registry/custom-provider.ts +155 -0
- package/src/registry/index.ts +7 -0
- package/src/registry/no-such-provider-error.ts +41 -0
- package/src/registry/provider-registry.test.ts +691 -0
- package/src/registry/provider-registry.ts +328 -0
- package/src/rerank/index.ts +2 -0
- package/src/rerank/rerank-result.ts +70 -0
- package/src/rerank/rerank.test.ts +516 -0
- package/src/rerank/rerank.ts +237 -0
- package/src/telemetry/assemble-operation-name.ts +21 -0
- package/src/telemetry/get-base-telemetry-attributes.ts +53 -0
- package/src/telemetry/get-tracer.ts +20 -0
- package/src/telemetry/noop-tracer.ts +69 -0
- package/src/telemetry/record-span.ts +63 -0
- package/src/telemetry/select-telemetry-attributes.ts +78 -0
- package/src/telemetry/select-temetry-attributes.test.ts +114 -0
- package/src/telemetry/stringify-for-telemetry.test.ts +114 -0
- package/src/telemetry/stringify-for-telemetry.ts +33 -0
- package/src/telemetry/telemetry-settings.ts +44 -0
- package/src/test/mock-embedding-model-v2.ts +35 -0
- package/src/test/mock-embedding-model-v3.ts +48 -0
- package/src/test/mock-image-model-v2.ts +28 -0
- package/src/test/mock-image-model-v3.ts +28 -0
- package/src/test/mock-language-model-v2.ts +72 -0
- package/src/test/mock-language-model-v3.ts +77 -0
- package/src/test/mock-provider-v2.ts +68 -0
- package/src/test/mock-provider-v3.ts +80 -0
- package/src/test/mock-reranking-model-v3.ts +25 -0
- package/src/test/mock-server-response.ts +69 -0
- package/src/test/mock-speech-model-v2.ts +24 -0
- package/src/test/mock-speech-model-v3.ts +24 -0
- package/src/test/mock-tracer.ts +156 -0
- package/src/test/mock-transcription-model-v2.ts +24 -0
- package/src/test/mock-transcription-model-v3.ts +24 -0
- package/src/test/mock-values.ts +4 -0
- package/src/test/not-implemented.ts +3 -0
- package/src/text-stream/create-text-stream-response.test.ts +38 -0
- package/src/text-stream/create-text-stream-response.ts +18 -0
- package/src/text-stream/index.ts +2 -0
- package/src/text-stream/pipe-text-stream-to-response.test.ts +38 -0
- package/src/text-stream/pipe-text-stream-to-response.ts +26 -0
- package/src/transcribe/index.ts +2 -0
- package/src/transcribe/transcribe-result.ts +60 -0
- package/src/transcribe/transcribe.test.ts +313 -0
- package/src/transcribe/transcribe.ts +173 -0
- package/src/types/embedding-model-middleware.ts +3 -0
- package/src/types/embedding-model.ts +18 -0
- package/src/types/image-model-middleware.ts +3 -0
- package/src/types/image-model-response-metadata.ts +16 -0
- package/src/types/image-model.ts +19 -0
- package/src/types/index.ts +29 -0
- package/src/types/json-value.ts +15 -0
- package/src/types/language-model-middleware.ts +3 -0
- package/src/types/language-model-request-metadata.ts +6 -0
- package/src/types/language-model-response-metadata.ts +21 -0
- package/src/types/language-model.ts +104 -0
- package/src/types/provider-metadata.ts +16 -0
- package/src/types/provider.ts +55 -0
- package/src/types/reranking-model.ts +6 -0
- package/src/types/speech-model-response-metadata.ts +21 -0
- package/src/types/speech-model.ts +6 -0
- package/src/types/transcription-model-response-metadata.ts +16 -0
- package/src/types/transcription-model.ts +9 -0
- package/src/types/usage.ts +200 -0
- package/src/types/warning.ts +7 -0
- package/src/ui/__snapshots__/append-response-messages.test.ts.snap +416 -0
- package/src/ui/__snapshots__/convert-to-model-messages.test.ts.snap +419 -0
- package/src/ui/__snapshots__/process-chat-text-response.test.ts.snap +142 -0
- package/src/ui/call-completion-api.ts +157 -0
- package/src/ui/chat-transport.ts +83 -0
- package/src/ui/chat.test-d.ts +233 -0
- package/src/ui/chat.test.ts +2695 -0
- package/src/ui/chat.ts +716 -0
- package/src/ui/convert-file-list-to-file-ui-parts.ts +36 -0
- package/src/ui/convert-to-model-messages.test.ts +2775 -0
- package/src/ui/convert-to-model-messages.ts +373 -0
- package/src/ui/default-chat-transport.ts +36 -0
- package/src/ui/direct-chat-transport.test.ts +446 -0
- package/src/ui/direct-chat-transport.ts +118 -0
- package/src/ui/http-chat-transport.test.ts +185 -0
- package/src/ui/http-chat-transport.ts +292 -0
- package/src/ui/index.ts +71 -0
- package/src/ui/last-assistant-message-is-complete-with-approval-responses.ts +44 -0
- package/src/ui/last-assistant-message-is-complete-with-tool-calls.test.ts +371 -0
- package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +39 -0
- package/src/ui/process-text-stream.test.ts +38 -0
- package/src/ui/process-text-stream.ts +16 -0
- package/src/ui/process-ui-message-stream.test.ts +8052 -0
- package/src/ui/process-ui-message-stream.ts +713 -0
- package/src/ui/text-stream-chat-transport.ts +23 -0
- package/src/ui/transform-text-to-ui-message-stream.test.ts +124 -0
- package/src/ui/transform-text-to-ui-message-stream.ts +27 -0
- package/src/ui/ui-messages.test.ts +48 -0
- package/src/ui/ui-messages.ts +534 -0
- package/src/ui/use-completion.ts +84 -0
- package/src/ui/validate-ui-messages.test.ts +1428 -0
- package/src/ui/validate-ui-messages.ts +476 -0
- package/src/ui-message-stream/create-ui-message-stream-response.test.ts +266 -0
- package/src/ui-message-stream/create-ui-message-stream-response.ts +32 -0
- package/src/ui-message-stream/create-ui-message-stream.test.ts +639 -0
- package/src/ui-message-stream/create-ui-message-stream.ts +124 -0
- package/src/ui-message-stream/get-response-ui-message-id.test.ts +55 -0
- package/src/ui-message-stream/get-response-ui-message-id.ts +24 -0
- package/src/ui-message-stream/handle-ui-message-stream-finish.test.ts +429 -0
- package/src/ui-message-stream/handle-ui-message-stream-finish.ts +135 -0
- package/src/ui-message-stream/index.ts +13 -0
- package/src/ui-message-stream/json-to-sse-transform-stream.ts +12 -0
- package/src/ui-message-stream/pipe-ui-message-stream-to-response.test.ts +90 -0
- package/src/ui-message-stream/pipe-ui-message-stream-to-response.ts +40 -0
- package/src/ui-message-stream/read-ui-message-stream.test.ts +122 -0
- package/src/ui-message-stream/read-ui-message-stream.ts +87 -0
- package/src/ui-message-stream/ui-message-chunks.test-d.ts +18 -0
- package/src/ui-message-stream/ui-message-chunks.ts +344 -0
- package/src/ui-message-stream/ui-message-stream-headers.ts +7 -0
- package/src/ui-message-stream/ui-message-stream-on-finish-callback.ts +32 -0
- package/src/ui-message-stream/ui-message-stream-response-init.ts +5 -0
- package/src/ui-message-stream/ui-message-stream-writer.ts +24 -0
- package/src/util/as-array.ts +3 -0
- package/src/util/async-iterable-stream.test.ts +241 -0
- package/src/util/async-iterable-stream.ts +94 -0
- package/src/util/consume-stream.ts +29 -0
- package/src/util/cosine-similarity.test.ts +57 -0
- package/src/util/cosine-similarity.ts +47 -0
- package/src/util/create-resolvable-promise.ts +30 -0
- package/src/util/create-stitchable-stream.test.ts +239 -0
- package/src/util/create-stitchable-stream.ts +112 -0
- package/src/util/data-url.ts +17 -0
- package/src/util/deep-partial.ts +84 -0
- package/src/util/detect-media-type.test.ts +670 -0
- package/src/util/detect-media-type.ts +184 -0
- package/src/util/download/download-function.ts +45 -0
- package/src/util/download/download.test.ts +69 -0
- package/src/util/download/download.ts +46 -0
- package/src/util/error-handler.ts +1 -0
- package/src/util/fix-json.test.ts +279 -0
- package/src/util/fix-json.ts +401 -0
- package/src/util/get-potential-start-index.test.ts +34 -0
- package/src/util/get-potential-start-index.ts +30 -0
- package/src/util/index.ts +11 -0
- package/src/util/is-deep-equal-data.test.ts +119 -0
- package/src/util/is-deep-equal-data.ts +48 -0
- package/src/util/is-non-empty-object.ts +5 -0
- package/src/util/job.ts +1 -0
- package/src/util/log-v2-compatibility-warning.ts +21 -0
- package/src/util/merge-abort-signals.test.ts +155 -0
- package/src/util/merge-abort-signals.ts +43 -0
- package/src/util/merge-objects.test.ts +118 -0
- package/src/util/merge-objects.ts +79 -0
- package/src/util/now.ts +4 -0
- package/src/util/parse-partial-json.test.ts +80 -0
- package/src/util/parse-partial-json.ts +30 -0
- package/src/util/prepare-headers.test.ts +51 -0
- package/src/util/prepare-headers.ts +14 -0
- package/src/util/prepare-retries.test.ts +10 -0
- package/src/util/prepare-retries.ts +47 -0
- package/src/util/retry-error.ts +41 -0
- package/src/util/retry-with-exponential-backoff.test.ts +446 -0
- package/src/util/retry-with-exponential-backoff.ts +154 -0
- package/src/util/serial-job-executor.test.ts +162 -0
- package/src/util/serial-job-executor.ts +36 -0
- package/src/util/simulate-readable-stream.test.ts +98 -0
- package/src/util/simulate-readable-stream.ts +39 -0
- package/src/util/split-array.test.ts +60 -0
- package/src/util/split-array.ts +20 -0
- package/src/util/value-of.ts +65 -0
- package/src/util/write-to-server-response.test.ts +266 -0
- package/src/util/write-to-server-response.ts +49 -0
- package/src/version.ts +5 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { convertBase64ToUint8Array } from '@ai-sdk/provider-utils';
|
|
2
|
+
|
|
3
|
+
export const imageMediaTypeSignatures = [
|
|
4
|
+
{
|
|
5
|
+
mediaType: 'image/gif' as const,
|
|
6
|
+
bytesPrefix: [0x47, 0x49, 0x46], // GIF
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
mediaType: 'image/png' as const,
|
|
10
|
+
bytesPrefix: [0x89, 0x50, 0x4e, 0x47], // PNG
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
mediaType: 'image/jpeg' as const,
|
|
14
|
+
bytesPrefix: [0xff, 0xd8], // JPEG
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
mediaType: 'image/webp' as const,
|
|
18
|
+
bytesPrefix: [
|
|
19
|
+
0x52,
|
|
20
|
+
0x49,
|
|
21
|
+
0x46,
|
|
22
|
+
0x46, // "RIFF"
|
|
23
|
+
null,
|
|
24
|
+
null,
|
|
25
|
+
null,
|
|
26
|
+
null, // file size (variable)
|
|
27
|
+
0x57,
|
|
28
|
+
0x45,
|
|
29
|
+
0x42,
|
|
30
|
+
0x50, // "WEBP"
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
mediaType: 'image/bmp' as const,
|
|
35
|
+
bytesPrefix: [0x42, 0x4d],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
mediaType: 'image/tiff' as const,
|
|
39
|
+
bytesPrefix: [0x49, 0x49, 0x2a, 0x00],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
mediaType: 'image/tiff' as const,
|
|
43
|
+
bytesPrefix: [0x4d, 0x4d, 0x00, 0x2a],
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
mediaType: 'image/avif' as const,
|
|
47
|
+
bytesPrefix: [
|
|
48
|
+
0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66,
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
mediaType: 'image/heic' as const,
|
|
53
|
+
bytesPrefix: [
|
|
54
|
+
0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63,
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
] as const;
|
|
58
|
+
|
|
59
|
+
export const audioMediaTypeSignatures = [
|
|
60
|
+
{
|
|
61
|
+
mediaType: 'audio/mpeg' as const,
|
|
62
|
+
bytesPrefix: [0xff, 0xfb],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
mediaType: 'audio/mpeg' as const,
|
|
66
|
+
bytesPrefix: [0xff, 0xfa],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
mediaType: 'audio/mpeg' as const,
|
|
70
|
+
bytesPrefix: [0xff, 0xf3],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
mediaType: 'audio/mpeg' as const,
|
|
74
|
+
bytesPrefix: [0xff, 0xf2],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
mediaType: 'audio/mpeg' as const,
|
|
78
|
+
bytesPrefix: [0xff, 0xe3],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
mediaType: 'audio/mpeg' as const,
|
|
82
|
+
bytesPrefix: [0xff, 0xe2],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
mediaType: 'audio/wav' as const,
|
|
86
|
+
bytesPrefix: [
|
|
87
|
+
0x52, // R
|
|
88
|
+
0x49, // I
|
|
89
|
+
0x46, // F
|
|
90
|
+
0x46, // F
|
|
91
|
+
null,
|
|
92
|
+
null,
|
|
93
|
+
null,
|
|
94
|
+
null,
|
|
95
|
+
0x57, // W
|
|
96
|
+
0x41, // A
|
|
97
|
+
0x56, // V
|
|
98
|
+
0x45, // E
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
mediaType: 'audio/ogg' as const,
|
|
103
|
+
bytesPrefix: [0x4f, 0x67, 0x67, 0x53],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
mediaType: 'audio/flac' as const,
|
|
107
|
+
bytesPrefix: [0x66, 0x4c, 0x61, 0x43],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
mediaType: 'audio/aac' as const,
|
|
111
|
+
bytesPrefix: [0x40, 0x15, 0x00, 0x00],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
mediaType: 'audio/mp4' as const,
|
|
115
|
+
bytesPrefix: [0x66, 0x74, 0x79, 0x70],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
mediaType: 'audio/webm',
|
|
119
|
+
bytesPrefix: [0x1a, 0x45, 0xdf, 0xa3],
|
|
120
|
+
},
|
|
121
|
+
] as const;
|
|
122
|
+
|
|
123
|
+
const stripID3 = (data: Uint8Array | string) => {
|
|
124
|
+
const bytes =
|
|
125
|
+
typeof data === 'string' ? convertBase64ToUint8Array(data) : data;
|
|
126
|
+
const id3Size =
|
|
127
|
+
((bytes[6] & 0x7f) << 21) |
|
|
128
|
+
((bytes[7] & 0x7f) << 14) |
|
|
129
|
+
((bytes[8] & 0x7f) << 7) |
|
|
130
|
+
(bytes[9] & 0x7f);
|
|
131
|
+
|
|
132
|
+
// The raw MP3 starts here
|
|
133
|
+
return bytes.slice(id3Size + 10);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
function stripID3TagsIfPresent(data: Uint8Array | string): Uint8Array | string {
|
|
137
|
+
const hasId3 =
|
|
138
|
+
(typeof data === 'string' && data.startsWith('SUQz')) ||
|
|
139
|
+
(typeof data !== 'string' &&
|
|
140
|
+
data.length > 10 &&
|
|
141
|
+
data[0] === 0x49 && // 'I'
|
|
142
|
+
data[1] === 0x44 && // 'D'
|
|
143
|
+
data[2] === 0x33); // '3'
|
|
144
|
+
|
|
145
|
+
return hasId3 ? stripID3(data) : data;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Detect the media IANA media type of a file using a list of signatures.
|
|
150
|
+
*
|
|
151
|
+
* @param data - The file data.
|
|
152
|
+
* @param signatures - The signatures to use for detection.
|
|
153
|
+
* @returns The media type of the file.
|
|
154
|
+
*/
|
|
155
|
+
export function detectMediaType({
|
|
156
|
+
data,
|
|
157
|
+
signatures,
|
|
158
|
+
}: {
|
|
159
|
+
data: Uint8Array | string;
|
|
160
|
+
signatures: typeof audioMediaTypeSignatures | typeof imageMediaTypeSignatures;
|
|
161
|
+
}): (typeof signatures)[number]['mediaType'] | undefined {
|
|
162
|
+
const processedData = stripID3TagsIfPresent(data);
|
|
163
|
+
|
|
164
|
+
// Convert the first ~18 bytes (24 base64 chars) for consistent detection logic:
|
|
165
|
+
const bytes =
|
|
166
|
+
typeof processedData === 'string'
|
|
167
|
+
? convertBase64ToUint8Array(
|
|
168
|
+
processedData.substring(0, Math.min(processedData.length, 24)),
|
|
169
|
+
)
|
|
170
|
+
: processedData;
|
|
171
|
+
|
|
172
|
+
for (const signature of signatures) {
|
|
173
|
+
if (
|
|
174
|
+
bytes.length >= signature.bytesPrefix.length &&
|
|
175
|
+
signature.bytesPrefix.every(
|
|
176
|
+
(byte, index) => byte === null || bytes[index] === byte,
|
|
177
|
+
)
|
|
178
|
+
) {
|
|
179
|
+
return signature.mediaType;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { download as originalDownload } from './download';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Experimental. Can change in patch versions without warning.
|
|
5
|
+
*
|
|
6
|
+
* Download function. Called with the array of URLs and a boolean indicating
|
|
7
|
+
* whether the URL is supported by the model.
|
|
8
|
+
*
|
|
9
|
+
* The download function can decide for each URL:
|
|
10
|
+
* - to return null (which means that the URL should be passed to the model)
|
|
11
|
+
* - to download the asset and return the data (incl. retries, authentication, etc.)
|
|
12
|
+
*
|
|
13
|
+
* Should throw DownloadError if the download fails.
|
|
14
|
+
*
|
|
15
|
+
* Should return an array of objects sorted by the order of the requested downloads.
|
|
16
|
+
* For each object, the data should be a Uint8Array if the URL was downloaded.
|
|
17
|
+
* For each object, the mediaType should be the media type of the downloaded asset.
|
|
18
|
+
* For each object, the data should be null if the URL should be passed through as is.
|
|
19
|
+
*/
|
|
20
|
+
export type DownloadFunction = (
|
|
21
|
+
options: Array<{
|
|
22
|
+
url: URL;
|
|
23
|
+
isUrlSupportedByModel: boolean;
|
|
24
|
+
}>,
|
|
25
|
+
) => PromiseLike<
|
|
26
|
+
Array<{
|
|
27
|
+
data: Uint8Array;
|
|
28
|
+
mediaType: string | undefined;
|
|
29
|
+
} | null>
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default download function.
|
|
34
|
+
* Downloads the file if it is not supported by the model.
|
|
35
|
+
*/
|
|
36
|
+
export const createDefaultDownloadFunction =
|
|
37
|
+
(download: typeof originalDownload = originalDownload): DownloadFunction =>
|
|
38
|
+
requestedDownloads =>
|
|
39
|
+
Promise.all(
|
|
40
|
+
requestedDownloads.map(async requestedDownload =>
|
|
41
|
+
requestedDownload.isUrlSupportedByModel
|
|
42
|
+
? null
|
|
43
|
+
: download(requestedDownload),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createTestServer } from '@ai-sdk/test-server/with-vitest';
|
|
2
|
+
import { DownloadError } from '@ai-sdk/provider-utils';
|
|
3
|
+
import { download } from './download';
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
|
|
6
|
+
const server = createTestServer({
|
|
7
|
+
'http://example.com/file': {},
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('download', () => {
|
|
11
|
+
it('should download data successfully and match expected bytes', async () => {
|
|
12
|
+
const expectedBytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
|
|
13
|
+
|
|
14
|
+
server.urls['http://example.com/file'].response = {
|
|
15
|
+
type: 'binary',
|
|
16
|
+
headers: {
|
|
17
|
+
'content-type': 'application/octet-stream',
|
|
18
|
+
},
|
|
19
|
+
body: Buffer.from(expectedBytes),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const result = await download({
|
|
23
|
+
url: new URL('http://example.com/file'),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
expect(result).not.toBeNull();
|
|
27
|
+
expect(result!.data).toEqual(expectedBytes);
|
|
28
|
+
expect(result!.mediaType).toBe('application/octet-stream');
|
|
29
|
+
|
|
30
|
+
// UA header assertion
|
|
31
|
+
expect(server.calls[0].requestUserAgent).toContain('ai-sdk/');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should throw DownloadError when response is not ok', async () => {
|
|
35
|
+
server.urls['http://example.com/file'].response = {
|
|
36
|
+
type: 'error',
|
|
37
|
+
status: 404,
|
|
38
|
+
body: 'Not Found',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
await download({
|
|
43
|
+
url: new URL('http://example.com/file'),
|
|
44
|
+
});
|
|
45
|
+
expect.fail('Expected download to throw');
|
|
46
|
+
} catch (error: unknown) {
|
|
47
|
+
expect(error).toBeInstanceOf(DownloadError);
|
|
48
|
+
expect((error as DownloadError).statusCode).toBe(404);
|
|
49
|
+
expect((error as DownloadError).statusText).toBe('Not Found');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should throw DownloadError when fetch throws an error', async () => {
|
|
54
|
+
server.urls['http://example.com/file'].response = {
|
|
55
|
+
type: 'error',
|
|
56
|
+
status: 500,
|
|
57
|
+
body: 'Network error',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
await download({
|
|
62
|
+
url: new URL('http://example.com/file'),
|
|
63
|
+
});
|
|
64
|
+
expect.fail('Expected download to throw');
|
|
65
|
+
} catch (error: unknown) {
|
|
66
|
+
expect(error).toBeInstanceOf(DownloadError);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { DownloadError } from '@ai-sdk/provider-utils';
|
|
2
|
+
import {
|
|
3
|
+
withUserAgentSuffix,
|
|
4
|
+
getRuntimeEnvironmentUserAgent,
|
|
5
|
+
} from '@ai-sdk/provider-utils';
|
|
6
|
+
import { VERSION } from '../../version';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Download a file from a URL.
|
|
10
|
+
*
|
|
11
|
+
* @param url - The URL to download from.
|
|
12
|
+
* @returns The downloaded data and media type.
|
|
13
|
+
*
|
|
14
|
+
* @throws DownloadError if the download fails.
|
|
15
|
+
*/
|
|
16
|
+
export const download = async ({ url }: { url: URL }) => {
|
|
17
|
+
const urlText = url.toString();
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(urlText, {
|
|
20
|
+
headers: withUserAgentSuffix(
|
|
21
|
+
{},
|
|
22
|
+
`ai-sdk/${VERSION}`,
|
|
23
|
+
getRuntimeEnvironmentUserAgent(),
|
|
24
|
+
),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!response.ok) {
|
|
28
|
+
throw new DownloadError({
|
|
29
|
+
url: urlText,
|
|
30
|
+
statusCode: response.status,
|
|
31
|
+
statusText: response.statusText,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
data: new Uint8Array(await response.arrayBuffer()),
|
|
37
|
+
mediaType: response.headers.get('content-type') ?? undefined,
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (DownloadError.isInstance(error)) {
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new DownloadError({ url: urlText, cause: error });
|
|
45
|
+
}
|
|
46
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ErrorHandler = (error: unknown) => void;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { describe, test } from 'vitest';
|
|
3
|
+
import { fixJson } from './fix-json';
|
|
4
|
+
|
|
5
|
+
test('should handle empty input', () => {
|
|
6
|
+
assert.strictEqual(fixJson(''), '');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
describe('literals', () => {
|
|
10
|
+
test('should handle incomplete null', () => {
|
|
11
|
+
assert.strictEqual(fixJson('nul'), 'null');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('should handle incomplete true', () => {
|
|
15
|
+
assert.strictEqual(fixJson('t'), 'true');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should handle incomplete false', () => {
|
|
19
|
+
assert.strictEqual(fixJson('fals'), 'false');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('number', () => {
|
|
24
|
+
test('should handle incomplete numbers', () => {
|
|
25
|
+
assert.strictEqual(fixJson('12.'), '12');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('should handle numbers with dot', () => {
|
|
29
|
+
assert.strictEqual(fixJson('12.2'), '12.2');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('should handle negative numbers', () => {
|
|
33
|
+
assert.strictEqual(fixJson('-12'), '-12');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('should handle incomplete negative numbers', () => {
|
|
37
|
+
assert.strictEqual(fixJson('-'), '');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should handle e-notation numbers', () => {
|
|
41
|
+
assert.strictEqual(fixJson('2.5e'), '2.5');
|
|
42
|
+
assert.strictEqual(fixJson('2.5e-'), '2.5');
|
|
43
|
+
assert.strictEqual(fixJson('2.5e3'), '2.5e3');
|
|
44
|
+
assert.strictEqual(fixJson('-2.5e3'), '-2.5e3');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('should handle uppercase e-notation numbers', () => {
|
|
48
|
+
assert.strictEqual(fixJson('2.5E'), '2.5');
|
|
49
|
+
assert.strictEqual(fixJson('2.5E-'), '2.5');
|
|
50
|
+
assert.strictEqual(fixJson('2.5E3'), '2.5E3');
|
|
51
|
+
assert.strictEqual(fixJson('-2.5E3'), '-2.5E3');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('should handle incomplete numbers', () => {
|
|
55
|
+
assert.strictEqual(fixJson('12.e'), '12');
|
|
56
|
+
assert.strictEqual(fixJson('12.34e'), '12.34');
|
|
57
|
+
assert.strictEqual(fixJson('5e'), '5');
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('string', () => {
|
|
62
|
+
test('should handle incomplete strings', () => {
|
|
63
|
+
assert.strictEqual(fixJson('"abc'), '"abc"');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('should handle escape sequences', () => {
|
|
67
|
+
assert.strictEqual(
|
|
68
|
+
fixJson('"value with \\"quoted\\" text and \\\\ escape'),
|
|
69
|
+
'"value with \\"quoted\\" text and \\\\ escape"',
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('should handle incomplete escape sequences', () => {
|
|
74
|
+
assert.strictEqual(fixJson('"value with \\'), '"value with "');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('should handle unicode characters', () => {
|
|
78
|
+
assert.strictEqual(
|
|
79
|
+
fixJson('"value with unicode \u003C"'),
|
|
80
|
+
'"value with unicode \u003C"',
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('array', () => {
|
|
86
|
+
test('should handle incomplete array', () => {
|
|
87
|
+
assert.strictEqual(fixJson('['), '[]');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('should handle closing bracket after number in array', () => {
|
|
91
|
+
assert.strictEqual(fixJson('[[1], [2'), '[[1], [2]]');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('should handle closing bracket after string in array', () => {
|
|
95
|
+
assert.strictEqual(fixJson(`[["1"], ["2`), `[["1"], ["2"]]`);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('should handle closing bracket after literal in array', () => {
|
|
99
|
+
assert.strictEqual(fixJson('[[false], [nu'), '[[false], [null]]');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('should handle closing bracket after array in array', () => {
|
|
103
|
+
assert.strictEqual(fixJson('[[[]], [[]'), '[[[]], [[]]]');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('should handle closing bracket after object in array', () => {
|
|
107
|
+
assert.strictEqual(fixJson('[[{}], [{'), '[[{}], [{}]]');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('should handle trailing comma', () => {
|
|
111
|
+
assert.strictEqual(fixJson('[1, '), '[1]');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('should handle closing array', () => {
|
|
115
|
+
assert.strictEqual(fixJson('[[], 123'), '[[], 123]');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('object', () => {
|
|
120
|
+
test('should handle keys without values', () => {
|
|
121
|
+
assert.strictEqual(fixJson('{"key":'), '{}');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('should handle closing brace after number in object', () => {
|
|
125
|
+
assert.strictEqual(
|
|
126
|
+
fixJson('{"a": {"b": 1}, "c": {"d": 2'),
|
|
127
|
+
'{"a": {"b": 1}, "c": {"d": 2}}',
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('should handle closing brace after string in object', () => {
|
|
132
|
+
assert.strictEqual(
|
|
133
|
+
fixJson('{"a": {"b": "1"}, "c": {"d": 2'),
|
|
134
|
+
'{"a": {"b": "1"}, "c": {"d": 2}}',
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('should handle closing brace after literal in object', () => {
|
|
139
|
+
assert.strictEqual(
|
|
140
|
+
fixJson('{"a": {"b": false}, "c": {"d": 2'),
|
|
141
|
+
'{"a": {"b": false}, "c": {"d": 2}}',
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('should handle closing brace after array in object', () => {
|
|
146
|
+
assert.strictEqual(
|
|
147
|
+
fixJson('{"a": {"b": []}, "c": {"d": 2'),
|
|
148
|
+
'{"a": {"b": []}, "c": {"d": 2}}',
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('should handle closing brace after object in object', () => {
|
|
153
|
+
assert.strictEqual(
|
|
154
|
+
fixJson('{"a": {"b": {}}, "c": {"d": 2'),
|
|
155
|
+
'{"a": {"b": {}}, "c": {"d": 2}}',
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('should handle partial keys (first key)', () => {
|
|
160
|
+
assert.strictEqual(fixJson('{"ke'), '{}');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('should handle partial keys (second key)', () => {
|
|
164
|
+
assert.strictEqual(fixJson('{"k1": 1, "k2'), '{"k1": 1}');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('should handle partial keys with colon (second key)', () => {
|
|
168
|
+
assert.strictEqual(fixJson('{"k1": 1, "k2":'), '{"k1": 1}');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('should handle trailing whitespace', () => {
|
|
172
|
+
assert.strictEqual(fixJson('{"key": "value" '), '{"key": "value"}');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('should handle closing after empty object', () => {
|
|
176
|
+
assert.strictEqual(fixJson('{"a": {"b": {}'), '{"a": {"b": {}}}');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('nesting', () => {
|
|
181
|
+
test('should handle nested arrays with numbers', () => {
|
|
182
|
+
assert.strictEqual(fixJson('[1, [2, 3, ['), '[1, [2, 3, []]]');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('should handle nested arrays with literals', () => {
|
|
186
|
+
assert.strictEqual(fixJson('[false, [true, ['), '[false, [true, []]]');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('should handle nested objects', () => {
|
|
190
|
+
assert.strictEqual(fixJson('{"key": {"subKey":'), '{"key": {}}');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('should handle nested objects with numbers', () => {
|
|
194
|
+
assert.strictEqual(
|
|
195
|
+
fixJson('{"key": 123, "key2": {"subKey":'),
|
|
196
|
+
'{"key": 123, "key2": {}}',
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('should handle nested objects with literals', () => {
|
|
201
|
+
assert.strictEqual(
|
|
202
|
+
fixJson('{"key": null, "key2": {"subKey":'),
|
|
203
|
+
'{"key": null, "key2": {}}',
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('should handle arrays within objects', () => {
|
|
208
|
+
assert.strictEqual(fixJson('{"key": [1, 2, {'), '{"key": [1, 2, {}]}');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('should handle objects within arrays', () => {
|
|
212
|
+
assert.strictEqual(
|
|
213
|
+
fixJson('[1, 2, {"key": "value",'),
|
|
214
|
+
'[1, 2, {"key": "value"}]',
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test('should handle nested arrays and objects', () => {
|
|
219
|
+
assert.strictEqual(
|
|
220
|
+
fixJson('{"a": {"b": ["c", {"d": "e",'),
|
|
221
|
+
'{"a": {"b": ["c", {"d": "e"}]}}',
|
|
222
|
+
);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('should handle deeply nested objects', () => {
|
|
226
|
+
assert.strictEqual(
|
|
227
|
+
fixJson('{"a": {"b": {"c": {"d":'),
|
|
228
|
+
'{"a": {"b": {"c": {}}}}',
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('should handle potential nested arrays or objects', () => {
|
|
233
|
+
assert.strictEqual(fixJson('{"a": 1, "b": ['), '{"a": 1, "b": []}');
|
|
234
|
+
assert.strictEqual(fixJson('{"a": 1, "b": {'), '{"a": 1, "b": {}}');
|
|
235
|
+
assert.strictEqual(fixJson('{"a": 1, "b": "'), '{"a": 1, "b": ""}');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('regression', () => {
|
|
240
|
+
test('should handle complex nesting 1', () => {
|
|
241
|
+
assert.strictEqual(
|
|
242
|
+
fixJson(
|
|
243
|
+
[
|
|
244
|
+
'{',
|
|
245
|
+
' "a": [',
|
|
246
|
+
' {',
|
|
247
|
+
' "a1": "v1",',
|
|
248
|
+
' "a2": "v2",',
|
|
249
|
+
` "a3": "v3"`,
|
|
250
|
+
' }',
|
|
251
|
+
' ],',
|
|
252
|
+
' "b": [',
|
|
253
|
+
' {',
|
|
254
|
+
' "b1": "n',
|
|
255
|
+
].join('\n'),
|
|
256
|
+
),
|
|
257
|
+
[
|
|
258
|
+
'{',
|
|
259
|
+
' "a": [',
|
|
260
|
+
' {',
|
|
261
|
+
' "a1": "v1",',
|
|
262
|
+
' "a2": "v2",',
|
|
263
|
+
` "a3": "v3"`,
|
|
264
|
+
' }',
|
|
265
|
+
' ],',
|
|
266
|
+
' "b": [',
|
|
267
|
+
' {',
|
|
268
|
+
' "b1": "n"}]}',
|
|
269
|
+
].join('\n'),
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('should handle empty objects inside nested objects and arrays', () => {
|
|
274
|
+
assert.strictEqual(
|
|
275
|
+
fixJson(`{"type":"div","children":[{"type":"Card","props":{}`),
|
|
276
|
+
`{"type":"div","children":[{"type":"Card","props":{}}]}`,
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
});
|