@ai-sdk/quiverai 2.0.43 → 2.0.45

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/dist/index.js CHANGED
@@ -11,11 +11,12 @@ import {
11
11
 
12
12
  // src/quiverai-image-model.ts
13
13
  import {
14
- InvalidArgumentError
14
+ InvalidArgumentError as InvalidArgumentError2
15
15
  } from "@ai-sdk/provider";
16
16
  import {
17
17
  combineHeaders,
18
- convertUint8ArrayToBase64,
18
+ convertBase64ToUint8Array as convertBase64ToUint8Array2,
19
+ convertUint8ArrayToBase64 as convertUint8ArrayToBase642,
19
20
  createJsonErrorResponseHandler,
20
21
  createJsonResponseHandler,
21
22
  parseProviderOptions,
@@ -41,8 +42,12 @@ var quiveraiImageModelOptionsSchema = lazySchema(
41
42
  * - `generate`: Text-to-SVG generation. Requires `prompt`.
42
43
  * - `vectorize`: Convert an input raster image into an SVG. Requires a
43
44
  * single image in `prompt.images` / `files`.
45
+ * - `animate`: Animate an input SVG. Requires a single SVG in
46
+ * `prompt.images` / `files`; the text prompt is optional.
47
+ * - `edit`: Edit a single SVG from `prompt.images` using `prompt.text`
48
+ * as the required instruction.
44
49
  */
45
- operation: z.enum(["generate", "vectorize"]).optional(),
50
+ operation: z.enum(["generate", "vectorize", "animate", "edit"]).optional(),
46
51
  /**
47
52
  * Extra style guidance for prompt-based generation.
48
53
  */
@@ -51,6 +56,20 @@ var quiveraiImageModelOptionsSchema = lazySchema(
51
56
  * Reasoning effort applied to generation or vectorization.
52
57
  */
53
58
  reasoningEffort: z.enum(["low", "medium", "high", "xhigh"]).optional(),
59
+ /**
60
+ * Optional reference images for SVG editing. Use
61
+ * `prepareQuiverAIImageReference` to convert binary inputs.
62
+ */
63
+ referenceImages: z.array(
64
+ z.union([
65
+ z.object({ url: z.string().min(1) }).strict(),
66
+ z.object({ base64: z.string().min(1).max(16777216) }).strict()
67
+ ])
68
+ ).max(4).optional(),
69
+ /**
70
+ * Maximum number of edit review and redo steps (0-5).
71
+ */
72
+ maxReviewSteps: z.number().int().min(0).max(5).optional(),
54
73
  /**
55
74
  * SVG root attributes requested for generation or vectorization.
56
75
  */
@@ -79,6 +98,14 @@ var quiveraiImageModelOptionsSchema = lazySchema(
79
98
  * The legacy upper bound of 131072 is retained for other model IDs.
80
99
  */
81
100
  maxOutputTokens: z.number().int().min(1).max(131072).optional(),
101
+ /**
102
+ * Provider orchestrator token budget for SVG editing (1-65536).
103
+ */
104
+ orchestratorMaxOutputTokens: z.number().int().min(1).max(65536).optional(),
105
+ /**
106
+ * Provider shallow edit token budget for SVG editing (1-65536).
107
+ */
108
+ shallowMaxOutputTokens: z.number().int().min(1).max(65536).optional(),
82
109
  /**
83
110
  * Whether to auto-crop the input image before vectorization.
84
111
  * Only used when `operation` is `vectorize`.
@@ -93,6 +120,113 @@ var quiveraiImageModelOptionsSchema = lazySchema(
93
120
  )
94
121
  );
95
122
 
123
+ // src/prepare-quiverai-image-reference.ts
124
+ import { InvalidArgumentError } from "@ai-sdk/provider";
125
+ import {
126
+ convertBase64ToUint8Array,
127
+ convertUint8ArrayToBase64,
128
+ detectMediaType
129
+ } from "@ai-sdk/provider-utils";
130
+ var MAX_REFERENCE_BASE64_LENGTH = 16777216;
131
+ var MAX_REFERENCE_BYTES = 12582912;
132
+ var supportedReferenceMediaTypes = /* @__PURE__ */ new Set([
133
+ "image/gif",
134
+ "image/jpeg",
135
+ "image/png",
136
+ "image/svg+xml",
137
+ "image/webp"
138
+ ]);
139
+ function prepareQuiverAIImageReference(input) {
140
+ if (input instanceof URL) {
141
+ return { url: validateQuiverAIImageUrl(input.toString()) };
142
+ }
143
+ if (typeof input === "string") {
144
+ if (/^[a-z][a-z\d+.-]*:\/\//i.test(input)) {
145
+ return { url: validateQuiverAIImageUrl(input) };
146
+ }
147
+ if (input.startsWith("data:")) {
148
+ const match = /^data:([^;,]+);base64,(.+)$/s.exec(input);
149
+ if (match == null || !supportedReferenceMediaTypes.has(match[1])) {
150
+ throw new InvalidArgumentError({
151
+ argument: "input",
152
+ message: "QuiverAI reference image data URLs must use base64 encoding and a supported image media type."
153
+ });
154
+ }
155
+ validateQuiverAIReferenceBase64(match[2], "input");
156
+ return { base64: match[2] };
157
+ }
158
+ validateQuiverAIReferenceBase64(input, "input");
159
+ return { base64: input };
160
+ }
161
+ const data = input instanceof ArrayBuffer ? new Uint8Array(input) : new Uint8Array(input);
162
+ validateQuiverAIReferenceBytes(data, "input");
163
+ return { base64: convertUint8ArrayToBase64(data) };
164
+ }
165
+ function validateQuiverAIImageUrl(url) {
166
+ let parsed;
167
+ try {
168
+ parsed = new URL(url);
169
+ } catch (e) {
170
+ throw new InvalidArgumentError({
171
+ argument: "url",
172
+ message: "QuiverAI image URLs must be valid HTTP or HTTPS URLs."
173
+ });
174
+ }
175
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
176
+ throw new InvalidArgumentError({
177
+ argument: "url",
178
+ message: "QuiverAI image URLs must use HTTP or HTTPS."
179
+ });
180
+ }
181
+ return parsed.toString();
182
+ }
183
+ function validateQuiverAIReferenceBase64(base64, argument) {
184
+ if (base64.length === 0 || base64.length > MAX_REFERENCE_BASE64_LENGTH) {
185
+ throw new InvalidArgumentError({
186
+ argument,
187
+ message: `QuiverAI reference images must contain 1-${MAX_REFERENCE_BASE64_LENGTH} base64 characters.`
188
+ });
189
+ }
190
+ let data;
191
+ try {
192
+ data = convertBase64ToUint8Array(base64);
193
+ } catch (cause) {
194
+ throw new InvalidArgumentError({
195
+ argument,
196
+ message: "QuiverAI reference image data must be valid base64.",
197
+ cause
198
+ });
199
+ }
200
+ validateQuiverAIReferenceBytes(data, argument);
201
+ }
202
+ function validateQuiverAIReferenceBytes(data, argument) {
203
+ if (data.length === 0 || data.length > MAX_REFERENCE_BYTES) {
204
+ throw new InvalidArgumentError({
205
+ argument,
206
+ message: `QuiverAI reference images must decode to 1-${MAX_REFERENCE_BYTES} bytes.`
207
+ });
208
+ }
209
+ const mediaType = isSvg(data) ? "image/svg+xml" : detectMediaType({ data, topLevelType: "image" });
210
+ if (mediaType == null || !supportedReferenceMediaTypes.has(mediaType)) {
211
+ throw new InvalidArgumentError({
212
+ argument,
213
+ message: "QuiverAI reference images must be PNG, JPEG, WebP, GIF, or SVG data."
214
+ });
215
+ }
216
+ }
217
+ function isSvg(data) {
218
+ let text;
219
+ try {
220
+ text = new TextDecoder("utf-8", { fatal: true }).decode(data);
221
+ } catch (e) {
222
+ return false;
223
+ }
224
+ const normalized = text.replace(/^\uFEFF/, "").trim();
225
+ return /^(?:<\?xml[\s\S]*?\?>\s*)?(?:<!--[\s\S]*?-->\s*)?(?:<!DOCTYPE[\s\S]*?>\s*)?<svg[\s>]/i.test(
226
+ normalized
227
+ ) && (/<\/svg>\s*$/i.test(normalized) || /<svg(?:\s[^>]*)?\/>\s*$/is.test(normalized));
228
+ }
229
+
96
230
  // src/quiverai-image-model.ts
97
231
  var QuiverAIImageModel = class _QuiverAIImageModel {
98
232
  constructor(modelId, config) {
@@ -138,6 +272,7 @@ var QuiverAIImageModel = class _QuiverAIImageModel {
138
272
  n,
139
273
  prompt,
140
274
  files,
275
+ mask,
141
276
  operation,
142
277
  options: quiveraiOptions != null ? quiveraiOptions : {}
143
278
  });
@@ -164,7 +299,13 @@ var QuiverAIImageModel = class _QuiverAIImageModel {
164
299
  ...response.credits != null && { credits: response.credits },
165
300
  images: response.data.map((image, index) => ({
166
301
  index,
167
- mimeType: image.mime_type
302
+ mimeType: image.mime_type,
303
+ ...image.loop_period_ms !== void 0 && {
304
+ loopPeriodMs: image.loop_period_ms
305
+ },
306
+ ...image.opening_animation_ms !== void 0 && {
307
+ openingAnimationMs: image.opening_animation_ms
308
+ }
168
309
  }))
169
310
  }
170
311
  },
@@ -184,7 +325,16 @@ var QuiverAIImageModel = class _QuiverAIImageModel {
184
325
  }
185
326
  };
186
327
  function getOperationPath(operation) {
187
- return operation === "generate" ? "/svgs/generations" : "/svgs/vectorizations";
328
+ switch (operation) {
329
+ case "generate":
330
+ return "/svgs/generations";
331
+ case "vectorize":
332
+ return "/svgs/vectorizations";
333
+ case "edit":
334
+ return "/svgs/edits";
335
+ case "animate":
336
+ return "/svgs/animations";
337
+ }
188
338
  }
189
339
  function getGenerateReferenceLimit(modelId) {
190
340
  return ["arrow-1", "arrow-1.0", "arrow-1.1"].includes(modelId) ? 4 : 16;
@@ -194,19 +344,79 @@ function toQuiverAIImageReference(image) {
194
344
  return { url: image.url };
195
345
  }
196
346
  return {
197
- base64: typeof image.data === "string" ? image.data : convertUint8ArrayToBase64(image.data)
347
+ base64: typeof image.data === "string" ? image.data : convertUint8ArrayToBase642(image.data)
198
348
  };
199
349
  }
350
+ var maxAnimationSourceBase64Length = 1066668;
351
+ function toQuiverAIAnimationSource(image) {
352
+ var _a;
353
+ if (image.type === "url") {
354
+ let url;
355
+ try {
356
+ url = new URL(image.url);
357
+ } catch (e) {
358
+ throw new InvalidArgumentError2({
359
+ argument: "files",
360
+ message: "QuiverAI animate requires a valid HTTP or HTTPS SVG URL."
361
+ });
362
+ }
363
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
364
+ throw new InvalidArgumentError2({
365
+ argument: "files",
366
+ message: "QuiverAI animate requires an HTTP or HTTPS SVG URL."
367
+ });
368
+ }
369
+ return { url: image.url };
370
+ }
371
+ let base64;
372
+ let bytes;
373
+ if (typeof image.data === "string") {
374
+ const dataUrlMatch = /^data:image\/svg\+xml(?:;[^,]*)?;base64,([\s\S]+)$/i.exec(image.data);
375
+ const encodedData = (_a = dataUrlMatch == null ? void 0 : dataUrlMatch[1]) != null ? _a : image.data;
376
+ try {
377
+ bytes = convertBase64ToUint8Array2(encodedData);
378
+ } catch (e) {
379
+ throw new InvalidArgumentError2({
380
+ argument: "files",
381
+ message: "QuiverAI animate requires the source SVG string to be valid base64 or an SVG data URL."
382
+ });
383
+ }
384
+ base64 = convertUint8ArrayToBase642(bytes);
385
+ } else {
386
+ bytes = image.data;
387
+ base64 = convertUint8ArrayToBase642(bytes);
388
+ }
389
+ if (!isSvg2(bytes)) {
390
+ throw new InvalidArgumentError2({
391
+ argument: "files",
392
+ message: "QuiverAI animate requires the input file to contain SVG data."
393
+ });
394
+ }
395
+ if (base64.length > maxAnimationSourceBase64Length) {
396
+ throw new InvalidArgumentError2({
397
+ argument: "files",
398
+ message: `QuiverAI animate accepts at most ${maxAnimationSourceBase64Length} base64 characters for the source SVG.`
399
+ });
400
+ }
401
+ return { base64 };
402
+ }
403
+ function isSvg2(data) {
404
+ const head = new TextDecoder("utf-8", { fatal: false }).decode(data.subarray(0, 4096)).trimStart();
405
+ return /^(?:(?:<\?xml[\s\S]*?\?>|<!--[\s\S]*?-->|<!DOCTYPE[\s\S]*?>)\s*)*<svg(?:\s|>)/i.test(
406
+ head
407
+ );
408
+ }
200
409
  function buildRequestBody({
201
410
  modelId,
202
411
  n,
203
412
  prompt,
204
413
  files,
414
+ mask,
205
415
  operation,
206
416
  options
207
417
  }) {
208
418
  if ((modelId === "arrow-2" || modelId === "arrow-2-telos") && options.maxOutputTokens != null && options.maxOutputTokens > 65536) {
209
- throw new InvalidArgumentError({
419
+ throw new InvalidArgumentError2({
210
420
  argument: "maxOutputTokens",
211
421
  message: `QuiverAI model "${modelId}" supports at most 65536 output tokens.`
212
422
  });
@@ -220,9 +430,12 @@ function buildRequestBody({
220
430
  attributes: options.attributes,
221
431
  stream: false
222
432
  };
433
+ if (operation !== "edit") {
434
+ rejectEditOnlyOptions(operation, options);
435
+ }
223
436
  if (operation === "generate") {
224
437
  if (prompt == null || prompt.trim().length === 0) {
225
- throw new InvalidArgumentError({
438
+ throw new InvalidArgumentError2({
226
439
  argument: "prompt",
227
440
  message: "QuiverAI image generation requires a non-empty prompt for generateImage."
228
441
  });
@@ -230,7 +443,7 @@ function buildRequestBody({
230
443
  const references = files == null ? void 0 : files.map(toQuiverAIImageReference);
231
444
  const maxReferences = getGenerateReferenceLimit(modelId);
232
445
  if (references != null && references.length > maxReferences) {
233
- throw new InvalidArgumentError({
446
+ throw new InvalidArgumentError2({
234
447
  argument: "files",
235
448
  message: `QuiverAI generate supports up to ${maxReferences} reference images for model "${modelId}".`
236
449
  });
@@ -244,20 +457,40 @@ function buildRequestBody({
244
457
  references
245
458
  };
246
459
  }
460
+ if (operation === "edit") {
461
+ return buildEditRequestBody({
462
+ modelId,
463
+ n,
464
+ prompt,
465
+ files,
466
+ mask,
467
+ options
468
+ });
469
+ }
470
+ if (operation === "animate") {
471
+ return buildAnimationRequestBody({
472
+ modelId,
473
+ n,
474
+ prompt,
475
+ files,
476
+ mask,
477
+ options
478
+ });
479
+ }
247
480
  if (files == null || files.length === 0) {
248
- throw new InvalidArgumentError({
481
+ throw new InvalidArgumentError2({
249
482
  argument: "files",
250
483
  message: 'QuiverAI vectorize requires an input image. Pass an image in the generateImage prompt and set providerOptions.quiverai.operation to "vectorize".'
251
484
  });
252
485
  }
253
486
  if (files.length > 1) {
254
- throw new InvalidArgumentError({
487
+ throw new InvalidArgumentError2({
255
488
  argument: "files",
256
489
  message: "QuiverAI vectorize accepts a single input image."
257
490
  });
258
491
  }
259
492
  if (n !== 1) {
260
- throw new InvalidArgumentError({
493
+ throw new InvalidArgumentError2({
261
494
  argument: "n",
262
495
  message: "QuiverAI vectorize returns one SVG per request. Set maxImagesPerCall to 1 in generateImage to vectorize multiple times."
263
496
  });
@@ -270,6 +503,454 @@ function buildRequestBody({
270
503
  target_size: options.targetSize
271
504
  };
272
505
  }
506
+ function buildAnimationRequestBody({
507
+ modelId,
508
+ n,
509
+ prompt,
510
+ files,
511
+ mask,
512
+ options
513
+ }) {
514
+ if (modelId !== "arrow-2" && modelId !== "arrow-2-telos") {
515
+ throw new InvalidArgumentError2({
516
+ argument: "modelId",
517
+ message: 'QuiverAI animate is supported by the "arrow-2" and "arrow-2-telos" models.'
518
+ });
519
+ }
520
+ if (files == null || files.length === 0) {
521
+ throw new InvalidArgumentError2({
522
+ argument: "files",
523
+ message: "QuiverAI animate requires exactly one source SVG in prompt.images."
524
+ });
525
+ }
526
+ if (files.length !== 1) {
527
+ throw new InvalidArgumentError2({
528
+ argument: "files",
529
+ message: "QuiverAI animate accepts exactly one source SVG in prompt.images."
530
+ });
531
+ }
532
+ if (n !== 1) {
533
+ throw new InvalidArgumentError2({
534
+ argument: "n",
535
+ message: "QuiverAI animate returns one SVG per request. Set maxImagesPerCall to 1 in generateImage to animate multiple times."
536
+ });
537
+ }
538
+ if (mask != null) {
539
+ throw new InvalidArgumentError2({
540
+ argument: "mask",
541
+ message: "QuiverAI animate does not support masks."
542
+ });
543
+ }
544
+ if (prompt != null && prompt.trim().length === 0) {
545
+ throw new InvalidArgumentError2({
546
+ argument: "prompt",
547
+ message: "QuiverAI animate requires a non-empty prompt when an animation instruction is provided."
548
+ });
549
+ }
550
+ const unsupportedOptions = [
551
+ ["instructions", options.instructions],
552
+ ["topP", options.topP],
553
+ ["presencePenalty", options.presencePenalty],
554
+ ["attributes", options.attributes],
555
+ ["autoCrop", options.autoCrop],
556
+ ["targetSize", options.targetSize]
557
+ ].filter((option) => {
558
+ return option[1] !== void 0;
559
+ });
560
+ if (unsupportedOptions.length > 0) {
561
+ throw new InvalidArgumentError2({
562
+ argument: `providerOptions.quiverai.${unsupportedOptions[0][0]}`,
563
+ message: `QuiverAI animate does not support providerOptions.quiverai.${unsupportedOptions[0][0]}.`
564
+ });
565
+ }
566
+ return {
567
+ model: modelId,
568
+ svg_source: toQuiverAIAnimationSource(files[0]),
569
+ prompt,
570
+ temperature: options.temperature,
571
+ max_output_tokens: options.maxOutputTokens,
572
+ reasoning_effort: options.reasoningEffort,
573
+ stream: false
574
+ };
575
+ }
576
+ var editModelIds = /* @__PURE__ */ new Set(["arrow-2", "arrow-2-telos"]);
577
+ var MAX_EDIT_SVG_BYTES = 2e5;
578
+ function buildEditRequestBody({
579
+ modelId,
580
+ n,
581
+ prompt,
582
+ files,
583
+ mask,
584
+ options
585
+ }) {
586
+ var _a;
587
+ if (!editModelIds.has(modelId)) {
588
+ throw new InvalidArgumentError2({
589
+ argument: "modelId",
590
+ message: 'QuiverAI SVG editing is supported by the "arrow-2" and "arrow-2-telos" models.'
591
+ });
592
+ }
593
+ if (prompt == null || prompt.trim().length === 0) {
594
+ throw new InvalidArgumentError2({
595
+ argument: "prompt",
596
+ message: "QuiverAI SVG editing requires a non-empty instruction in generateImage prompt.text."
597
+ });
598
+ }
599
+ if (prompt.length > 4e3) {
600
+ throw new InvalidArgumentError2({
601
+ argument: "prompt",
602
+ message: "QuiverAI SVG editing instructions must contain at most 4000 characters."
603
+ });
604
+ }
605
+ if (files == null || files.length === 0) {
606
+ throw new InvalidArgumentError2({
607
+ argument: "files",
608
+ message: "QuiverAI SVG editing requires one source SVG in generateImage prompt.images."
609
+ });
610
+ }
611
+ if (files.length !== 1) {
612
+ throw new InvalidArgumentError2({
613
+ argument: "files",
614
+ message: "QuiverAI SVG editing accepts exactly one source SVG."
615
+ });
616
+ }
617
+ if (n !== 1) {
618
+ throw new InvalidArgumentError2({
619
+ argument: "n",
620
+ message: "QuiverAI SVG editing returns exactly one SVG per request. Set maxImagesPerCall to 1 in generateImage to edit multiple times."
621
+ });
622
+ }
623
+ if (mask != null) {
624
+ throw new InvalidArgumentError2({
625
+ argument: "mask",
626
+ message: "QuiverAI SVG editing does not support masks."
627
+ });
628
+ }
629
+ const unsupportedOptions = [
630
+ ["instructions", options.instructions],
631
+ ["attributes", options.attributes],
632
+ ["topP", options.topP],
633
+ ["presencePenalty", options.presencePenalty],
634
+ ["autoCrop", options.autoCrop],
635
+ ["targetSize", options.targetSize]
636
+ ].flatMap(([name, value]) => value == null ? [] : [name]);
637
+ if (unsupportedOptions.length > 0) {
638
+ throw new InvalidArgumentError2({
639
+ argument: "providerOptions",
640
+ message: `QuiverAI SVG editing does not support these provider options: ${unsupportedOptions.join(
641
+ ", "
642
+ )}.`
643
+ });
644
+ }
645
+ const referenceImages = (_a = options.referenceImages) == null ? void 0 : _a.map((reference, index) => {
646
+ if ("url" in reference) {
647
+ return {
648
+ url: validateQuiverAIImageUrl(reference.url)
649
+ };
650
+ }
651
+ validateQuiverAIReferenceBase64(
652
+ reference.base64,
653
+ `providerOptions.quiverai.referenceImages[${index}]`
654
+ );
655
+ return { base64: reference.base64 };
656
+ });
657
+ const settings = {
658
+ max_output_tokens: options.maxOutputTokens,
659
+ orchestrator_max_output_tokens: options.orchestratorMaxOutputTokens,
660
+ shallow_max_output_tokens: options.shallowMaxOutputTokens,
661
+ temperature: options.temperature
662
+ };
663
+ const hasSettings = Object.values(settings).some((value) => value != null);
664
+ return {
665
+ model: modelId,
666
+ prompt,
667
+ ...toQuiverAIEditSource(files[0]),
668
+ reference_images: referenceImages,
669
+ max_review_steps: options.maxReviewSteps,
670
+ reasoning_effort: options.reasoningEffort,
671
+ ...hasSettings && { settings },
672
+ stream: false
673
+ };
674
+ }
675
+ function rejectEditOnlyOptions(operation, options) {
676
+ const editOnlyOptions = [
677
+ ["referenceImages", options.referenceImages],
678
+ ["maxReviewSteps", options.maxReviewSteps],
679
+ ["orchestratorMaxOutputTokens", options.orchestratorMaxOutputTokens],
680
+ ["shallowMaxOutputTokens", options.shallowMaxOutputTokens]
681
+ ].flatMap(([name, value]) => value == null ? [] : [name]);
682
+ if (editOnlyOptions.length > 0) {
683
+ throw new InvalidArgumentError2({
684
+ argument: "providerOptions",
685
+ message: `QuiverAI ${operation} does not support these edit-only provider options: ${editOnlyOptions.join(
686
+ ", "
687
+ )}.`
688
+ });
689
+ }
690
+ }
691
+ function toQuiverAIEditSource(file) {
692
+ if (file.type === "url") {
693
+ return {
694
+ svg_source: {
695
+ url: validateQuiverAIImageUrl(file.url)
696
+ }
697
+ };
698
+ }
699
+ let data;
700
+ try {
701
+ data = typeof file.data === "string" ? convertBase64ToUint8Array2(file.data) : file.data;
702
+ } catch (cause) {
703
+ throw new InvalidArgumentError2({
704
+ argument: "files",
705
+ message: "QuiverAI SVG source data must be valid base64 or binary data.",
706
+ cause
707
+ });
708
+ }
709
+ if (data.length === 0 || data.length > MAX_EDIT_SVG_BYTES) {
710
+ throw new InvalidArgumentError2({
711
+ argument: "files",
712
+ message: `QuiverAI SVG source data must contain 1-${MAX_EDIT_SVG_BYTES} bytes.`
713
+ });
714
+ }
715
+ let svg;
716
+ try {
717
+ svg = new TextDecoder("utf-8", { fatal: true }).decode(data);
718
+ } catch (cause) {
719
+ throw new InvalidArgumentError2({
720
+ argument: "files",
721
+ message: "QuiverAI SVG source data must be valid UTF-8.",
722
+ cause
723
+ });
724
+ }
725
+ if (svg.length > MAX_EDIT_SVG_BYTES || !isSvgMarkup(svg)) {
726
+ throw new InvalidArgumentError2({
727
+ argument: "files",
728
+ message: "QuiverAI SVG source data must contain a complete SVG document."
729
+ });
730
+ }
731
+ return {
732
+ svg_source: {
733
+ base64: convertUint8ArrayToBase642(data)
734
+ }
735
+ };
736
+ }
737
+ function isSvgMarkup(svg) {
738
+ var _a;
739
+ const document = svg.replace(/^\uFEFF/, "");
740
+ const elements = [];
741
+ let position = 0;
742
+ let rootSeen = false;
743
+ let rootClosed = false;
744
+ let doctypeSeen = false;
745
+ while (position < document.length) {
746
+ if (document[position] !== "<") {
747
+ const nextTag = document.indexOf("<", position);
748
+ const end = nextTag === -1 ? document.length : nextTag;
749
+ const text = document.slice(position, end);
750
+ if (elements.length === 0 && text.trim().length > 0 || text.includes("]]>") || !hasValidXmlReferences(text)) {
751
+ return false;
752
+ }
753
+ position = end;
754
+ continue;
755
+ }
756
+ if (document.startsWith("<!--", position)) {
757
+ const commentEnd = document.indexOf("-->", position + 4);
758
+ if (commentEnd === -1 || document.slice(position + 4, commentEnd).includes("--")) {
759
+ return false;
760
+ }
761
+ position = commentEnd + 3;
762
+ continue;
763
+ }
764
+ if (document.startsWith("<?", position)) {
765
+ const instructionEnd = document.indexOf("?>", position + 2);
766
+ if (instructionEnd === -1) {
767
+ return false;
768
+ }
769
+ position = instructionEnd + 2;
770
+ continue;
771
+ }
772
+ if (document.startsWith("<![CDATA[", position)) {
773
+ if (elements.length === 0) {
774
+ return false;
775
+ }
776
+ const cdataEnd = document.indexOf("]]>", position + 9);
777
+ if (cdataEnd === -1) {
778
+ return false;
779
+ }
780
+ position = cdataEnd + 3;
781
+ continue;
782
+ }
783
+ if (document.slice(position, position + 9).toUpperCase() === "<!DOCTYPE") {
784
+ if (rootSeen || doctypeSeen || elements.length > 0 || !/[\t\n\r ]/.test((_a = document[position + 9]) != null ? _a : "")) {
785
+ return false;
786
+ }
787
+ const doctypeName = readXmlName(
788
+ document,
789
+ skipXmlWhitespace(document, position + 9)
790
+ );
791
+ if ((doctypeName == null ? void 0 : doctypeName.name.toLowerCase()) !== "svg") {
792
+ return false;
793
+ }
794
+ const doctypeEnd = findDoctypeEnd(document, doctypeName.end);
795
+ if (doctypeEnd === -1) {
796
+ return false;
797
+ }
798
+ doctypeSeen = true;
799
+ position = doctypeEnd;
800
+ continue;
801
+ }
802
+ if (document.startsWith("<!", position)) {
803
+ return false;
804
+ }
805
+ if (document.startsWith("</", position)) {
806
+ const closingTag = readXmlName(document, position + 2);
807
+ if (closingTag == null) {
808
+ return false;
809
+ }
810
+ let tagEnd = skipXmlWhitespace(document, closingTag.end);
811
+ if (document[tagEnd] !== ">") {
812
+ return false;
813
+ }
814
+ const expectedTag = elements.pop();
815
+ if (expectedTag !== closingTag.name) {
816
+ return false;
817
+ }
818
+ tagEnd += 1;
819
+ if (elements.length === 0) {
820
+ rootClosed = true;
821
+ }
822
+ position = tagEnd;
823
+ continue;
824
+ }
825
+ if (rootClosed) {
826
+ return false;
827
+ }
828
+ const openingTag = readXmlName(document, position + 1);
829
+ if (openingTag == null) {
830
+ return false;
831
+ }
832
+ if (!rootSeen) {
833
+ if (openingTag.name.toLowerCase() !== "svg") {
834
+ return false;
835
+ }
836
+ rootSeen = true;
837
+ }
838
+ const attributes = /* @__PURE__ */ new Set();
839
+ let tagPosition = openingTag.end;
840
+ while (tagPosition < document.length) {
841
+ const beforeWhitespace = tagPosition;
842
+ tagPosition = skipXmlWhitespace(document, tagPosition);
843
+ if (document.startsWith("/>", tagPosition)) {
844
+ tagPosition += 2;
845
+ if (elements.length === 0) {
846
+ rootClosed = true;
847
+ }
848
+ position = tagPosition;
849
+ break;
850
+ }
851
+ if (document[tagPosition] === ">") {
852
+ elements.push(openingTag.name);
853
+ position = tagPosition + 1;
854
+ break;
855
+ }
856
+ if (tagPosition === beforeWhitespace) {
857
+ return false;
858
+ }
859
+ const attribute = readXmlName(document, tagPosition);
860
+ if (attribute == null || attributes.has(attribute.name)) {
861
+ return false;
862
+ }
863
+ attributes.add(attribute.name);
864
+ tagPosition = skipXmlWhitespace(document, attribute.end);
865
+ if (document[tagPosition] !== "=") {
866
+ return false;
867
+ }
868
+ tagPosition = skipXmlWhitespace(document, tagPosition + 1);
869
+ const quote = document[tagPosition];
870
+ if (quote !== '"' && quote !== "'") {
871
+ return false;
872
+ }
873
+ const valueEnd = document.indexOf(quote, tagPosition + 1);
874
+ if (valueEnd === -1 || document.slice(tagPosition + 1, valueEnd).includes("<") || !hasValidXmlReferences(document.slice(tagPosition + 1, valueEnd))) {
875
+ return false;
876
+ }
877
+ tagPosition = valueEnd + 1;
878
+ }
879
+ if (tagPosition >= document.length && position !== document.length) {
880
+ return false;
881
+ }
882
+ }
883
+ return rootSeen && rootClosed && elements.length === 0;
884
+ }
885
+ function isXmlNameStart(character) {
886
+ return character != null && /[A-Z_a-z:\u0080-\uFFFF]/.test(character);
887
+ }
888
+ function isXmlNameCharacter(character) {
889
+ return character != null && /[-.0-9A-Z_a-z:\u00B7\u0080-\uFFFF]/.test(character);
890
+ }
891
+ function readXmlName(value, position) {
892
+ if (!isXmlNameStart(value[position])) {
893
+ return void 0;
894
+ }
895
+ const start = position;
896
+ position += 1;
897
+ while (isXmlNameCharacter(value[position])) {
898
+ position += 1;
899
+ }
900
+ return {
901
+ name: value.slice(start, position),
902
+ end: position
903
+ };
904
+ }
905
+ function skipXmlWhitespace(value, position) {
906
+ var _a;
907
+ while (/[\t\n\r ]/.test((_a = value[position]) != null ? _a : "")) {
908
+ position += 1;
909
+ }
910
+ return position;
911
+ }
912
+ function hasValidXmlReferences(value) {
913
+ let position = value.indexOf("&");
914
+ while (position !== -1) {
915
+ const end = value.indexOf(";", position + 1);
916
+ if (end === -1) {
917
+ return false;
918
+ }
919
+ const reference = value.slice(position + 1, end);
920
+ if (!/^#\d+$/.test(reference) && !/^#x[\dA-Fa-f]+$/.test(reference) && !/^[A-Z_a-z:\u0080-\uFFFF][-.0-9A-Z_a-z:\u00B7\u0080-\uFFFF]*$/.test(
921
+ reference
922
+ )) {
923
+ return false;
924
+ }
925
+ position = value.indexOf("&", end + 1);
926
+ }
927
+ return true;
928
+ }
929
+ function findDoctypeEnd(value, position) {
930
+ let subsetDepth = 0;
931
+ let quote;
932
+ while (position < value.length) {
933
+ const character = value[position];
934
+ if (quote != null) {
935
+ if (character === quote) {
936
+ quote = void 0;
937
+ }
938
+ } else if (character === '"' || character === "'") {
939
+ quote = character;
940
+ } else if (character === "[") {
941
+ subsetDepth += 1;
942
+ } else if (character === "]") {
943
+ if (subsetDepth === 0) {
944
+ return -1;
945
+ }
946
+ subsetDepth -= 1;
947
+ } else if (character === ">" && subsetDepth === 0) {
948
+ return position + 1;
949
+ }
950
+ position += 1;
951
+ }
952
+ return -1;
953
+ }
273
954
  function collectWarnings({
274
955
  size,
275
956
  aspectRatio,
@@ -314,7 +995,9 @@ var svgUsageSchema = z2.object({
314
995
  });
315
996
  var svgDocumentSchema = z2.object({
316
997
  svg: z2.string().min(1),
317
- mime_type: z2.literal("image/svg+xml")
998
+ mime_type: z2.literal("image/svg+xml"),
999
+ loop_period_ms: z2.number().int().nonnegative().nullish(),
1000
+ opening_animation_ms: z2.number().int().nonnegative().nullish()
318
1001
  });
319
1002
  var svgGenerationResponseSchema = z2.object({
320
1003
  id: z2.string().min(1),
@@ -336,7 +1019,7 @@ var quiveraiFailedResponseHandler = createJsonErrorResponseHandler({
336
1019
  });
337
1020
 
338
1021
  // src/version.ts
339
- var VERSION = true ? "2.0.43" : "0.0.0-test";
1022
+ var VERSION = true ? "2.0.45" : "0.0.0-test";
340
1023
 
341
1024
  // src/quiverai-provider.ts
342
1025
  var defaultBaseURL = "https://api.quiver.ai/v1";
@@ -389,6 +1072,7 @@ var quiverai = createQuiverAI();
389
1072
  export {
390
1073
  VERSION,
391
1074
  createQuiverAI,
1075
+ prepareQuiverAIImageReference,
392
1076
  quiverai
393
1077
  };
394
1078
  //# sourceMappingURL=index.js.map