@shop-os/modelkit 0.1.11 → 0.2.2

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/catalog.js CHANGED
@@ -93,6 +93,14 @@ var Catalog = class _Catalog {
93
93
  ...rows
94
94
  ]);
95
95
  }
96
+ /** Atomically swap every row. Builds the maps first so a bad batch throws before anything changes. */
97
+ replace(rows) {
98
+ const next = new _Catalog(rows);
99
+ this.byId.clear();
100
+ this.byAlias.clear();
101
+ for (const [id, model] of next.byId) this.byId.set(id, model);
102
+ for (const [alias, id] of next.byAlias) this.byAlias.set(alias, id);
103
+ }
96
104
  };
97
105
 
98
106
  // src/catalog/image.ts
@@ -110,7 +118,7 @@ var GEMINI_RATIOS = [
110
118
  "5:4",
111
119
  "adaptive"
112
120
  ];
113
- function geminiImage(id, label, aliases, sizes, display, exposure) {
121
+ function geminiImage(id, label, aliases, sizes, display, exposure, references) {
114
122
  const model = {
115
123
  id,
116
124
  aliases,
@@ -123,7 +131,8 @@ function geminiImage(id, label, aliases, sizes, display, exposure) {
123
131
  constraints: {
124
132
  aspectRatios: [...GEMINI_RATIOS],
125
133
  sizes,
126
- supports: ["safetySettings", "temperature"]
134
+ supports: ["safetySettings", "temperature"],
135
+ ...references ? { references } : {}
127
136
  },
128
137
  defaults: { size: "1K", aspectRatio: "1:1" },
129
138
  usage: { ...IMAGE_USAGE, slug: "gemini" },
@@ -143,7 +152,8 @@ var IMAGE_MODELS = [
143
152
  description: "Create stunning, production-quality visuals with exceptional realism, detail, and typography. Best for campaigns, e-commerce, product photography, and hero creatives.",
144
153
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
145
154
  },
146
- "catalog"
155
+ "catalog",
156
+ { images: 14 }
147
157
  ),
148
158
  geminiImage(
149
159
  "gemini-3.1-flash-image-preview",
@@ -156,7 +166,8 @@ var IMAGE_MODELS = [
156
166
  description: "Generate high-quality images at speed. Perfect for brainstorming, moodboards, concept testing, creative exploration, and large batch workflows.",
157
167
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
158
168
  },
159
- "catalog"
169
+ "catalog",
170
+ { images: 14 }
160
171
  ),
161
172
  geminiImage(
162
173
  "gemini-3.1-flash-lite-image",
@@ -168,14 +179,15 @@ var IMAGE_MODELS = [
168
179
  description: "The fastest and most affordable image model. Ideal for rapid drafts, bulk exploration, and iterative concept testing at 1K resolution.",
169
180
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
170
181
  },
171
- "catalog"
182
+ "catalog",
183
+ { images: 14 }
172
184
  ),
173
185
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
174
186
  geminiImage(
175
187
  "gemini-2.5-flash-image",
176
188
  "Gemini 2.5 Flash Image",
177
189
  [],
178
- ["1K", "2K", "4K"],
190
+ ["1K"],
179
191
  {
180
192
  provider: "google",
181
193
  description: "Nano Banana (legacy Gemini 2.5 Flash Image) native image generation and editing model for high-volume, low-latency creative workflows."
@@ -206,7 +218,7 @@ var IMAGE_MODELS = [
206
218
  sizes: ["1K", "2K", "4K"],
207
219
  qualityOptions: ["auto", "low", "medium", "high"],
208
220
  references: { images: 16 },
209
- supports: ["quality", "count"]
221
+ supports: ["quality", "count", "outputFormat"]
210
222
  },
211
223
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
212
224
  usage: { ...IMAGE_USAGE, slug: "gpt" },
@@ -226,7 +238,7 @@ var IMAGE_MODELS = [
226
238
  adapter: "openai-image",
227
239
  providerModelId: "gpt-image-2.5-sunburst",
228
240
  lifecycle: "active",
229
- exposure: "internal",
241
+ exposure: "catalog",
230
242
  constraints: {
231
243
  exactSizes: {
232
244
  "1:1": "1024x1024",
@@ -239,9 +251,9 @@ var IMAGE_MODELS = [
239
251
  },
240
252
  aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:5", "5:4", "adaptive"],
241
253
  sizes: ["1K", "2K", "4K"],
242
- qualityOptions: ["auto", "low", "medium", "high"],
254
+ qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
243
255
  references: { images: 16 },
244
- supports: ["quality", "count"]
256
+ supports: ["quality", "count", "outputFormat"]
245
257
  },
246
258
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
247
259
  usage: { ...IMAGE_USAGE },
@@ -260,7 +272,7 @@ var IMAGE_MODELS = [
260
272
  adapter: "openai-image",
261
273
  providerModelId: "gpt-image-2.5-flare",
262
274
  lifecycle: "active",
263
- exposure: "internal",
275
+ exposure: "catalog",
264
276
  constraints: {
265
277
  exactSizes: {
266
278
  "1:1": "1024x1024",
@@ -273,9 +285,9 @@ var IMAGE_MODELS = [
273
285
  },
274
286
  aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:5", "5:4", "adaptive"],
275
287
  sizes: ["1K", "2K", "4K"],
276
- qualityOptions: ["auto", "low", "medium", "high"],
288
+ qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
277
289
  references: { images: 16 },
278
- supports: ["quality", "count"]
290
+ supports: ["quality", "count", "outputFormat"]
279
291
  },
280
292
  defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
281
293
  usage: { ...IMAGE_USAGE },
@@ -305,8 +317,6 @@ var IMAGE_MODELS = [
305
317
  "2:3",
306
318
  "3:4",
307
319
  "9:16",
308
- "4:5",
309
- "5:4",
310
320
  "9:21",
311
321
  "adaptive"
312
322
  ],
@@ -338,7 +348,7 @@ var IMAGE_MODELS = [
338
348
  references: { images: 10 },
339
349
  // Ark's Images API has no output-format, quality or count field; only
340
350
  // the seed is forwarded, so anything else must warn, not vanish.
341
- supports: ["seed"]
351
+ supports: ["seed", "outputFormat"]
342
352
  },
343
353
  defaults: { size: "1K", aspectRatio: "1:1" },
344
354
  usage: { ...IMAGE_USAGE },
@@ -374,7 +384,7 @@ var IMAGE_MODELS = [
374
384
  "21:9",
375
385
  "9:21"
376
386
  ],
377
- sizes: ["1K", "2K", "4K"],
387
+ sizes: ["2K", "4K"],
378
388
  references: { images: 10 },
379
389
  supports: ["seed"]
380
390
  },
@@ -426,7 +436,7 @@ var IMAGE_MODELS = [
426
436
  aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
427
437
  sizes: ["1K", "2K", "4K"],
428
438
  requires: ["references"],
429
- supports: ["seed"]
439
+ supports: []
430
440
  },
431
441
  defaults: { size: "1K", aspectRatio: "1:1" },
432
442
  usage: { ...IMAGE_USAGE },
@@ -451,7 +461,7 @@ var IMAGE_MODELS = [
451
461
  aspectRatios: ["1:1", "2:3", "3:4", "4:3", "9:16", "16:9", "21:9"],
452
462
  sizes: ["1K"],
453
463
  requires: ["references"],
454
- supports: ["seed"]
464
+ supports: []
455
465
  },
456
466
  defaults: { size: "1K", aspectRatio: "1:1" },
457
467
  usage: { ...IMAGE_USAGE },
@@ -503,7 +513,6 @@ var IMAGE_MODELS = [
503
513
  aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"],
504
514
  sizes: [],
505
515
  references: { images: 1 },
506
- requires: ["references"],
507
516
  supports: []
508
517
  },
509
518
  defaults: { aspectRatio: "1:1" },
@@ -1222,7 +1231,7 @@ var VIDEO_MODELS = [
1222
1231
  resolutions: ["720p", "1080p", "4k"],
1223
1232
  durations: [8],
1224
1233
  references: { images: 3 },
1225
- supports: ["lastFrame"]
1234
+ supports: ["lastFrame", "seed"]
1226
1235
  },
1227
1236
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
1228
1237
  usage: { ...VIDEO_USAGE, slug: "veo" },
@@ -1250,9 +1259,9 @@ var VIDEO_MODELS = [
1250
1259
  constraints: {
1251
1260
  aspectRatios: [...OMNI_RATIOS],
1252
1261
  resolutions: ["720p", "1080p", "4k"],
1253
- durations: [4, 8],
1262
+ durations: [4, 6, 8],
1254
1263
  references: { images: 3 },
1255
- supports: ["lastFrame"]
1264
+ supports: ["lastFrame", "seed"]
1256
1265
  },
1257
1266
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
1258
1267
  usage: { ...VIDEO_USAGE },
@@ -1279,9 +1288,8 @@ var VIDEO_MODELS = [
1279
1288
  constraints: {
1280
1289
  aspectRatios: [...OMNI_RATIOS],
1281
1290
  resolutions: ["720p", "1080p"],
1282
- durations: [4, 8],
1283
- references: { images: 3 },
1284
- supports: ["lastFrame"]
1291
+ durations: [4, 6, 8],
1292
+ supports: ["lastFrame", "seed"]
1285
1293
  },
1286
1294
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
1287
1295
  usage: { ...VIDEO_USAGE },
@@ -1313,7 +1321,7 @@ var VIDEO_MODELS = [
1313
1321
  // reference clip keeps its own framing.
1314
1322
  aspectRatios: [...OMNI_RATIOS],
1315
1323
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
1316
- references: { images: 6, videos: 1 },
1324
+ references: { images: 6, videos: 3 },
1317
1325
  supports: []
1318
1326
  },
1319
1327
  defaults: {},
@@ -1339,7 +1347,7 @@ var VIDEO_MODELS = [
1339
1347
  aspectRatios: [...OMNI_RATIOS],
1340
1348
  resolutions: ["360p", "720p", "1080p", "4k"],
1341
1349
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
1342
- references: { images: 6, videos: 1 },
1350
+ references: { images: 6, videos: 3 },
1343
1351
  supports: []
1344
1352
  },
1345
1353
  defaults: {},
@@ -1368,7 +1376,7 @@ var VIDEO_MODELS = [
1368
1376
  durations: { min: 4, max: 15 },
1369
1377
  durationPresets: [4, 6, 8, 10, 12, 15],
1370
1378
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
1371
- ratioIgnoredWith: ["firstFrame"],
1379
+ ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1372
1380
  supports: ["generateAudio", "seed", "lastFrame", "watermark"]
1373
1381
  },
1374
1382
  defaults: {
@@ -1400,7 +1408,7 @@ var VIDEO_MODELS = [
1400
1408
  resolutions: ["480p", "720p"],
1401
1409
  durations: { min: 4, max: 15 },
1402
1410
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
1403
- ratioIgnoredWith: ["firstFrame"],
1411
+ ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1404
1412
  supports: ["generateAudio", "seed", "lastFrame", "watermark"]
1405
1413
  },
1406
1414
  defaults: {
@@ -1433,7 +1441,7 @@ var VIDEO_MODELS = [
1433
1441
  resolutions: ["480p", "720p", "1080p"],
1434
1442
  durations: { min: 4, max: 30 },
1435
1443
  references: { images: 30, videos: 10, audios: 10 },
1436
- ratioIgnoredWith: ["firstFrame"],
1444
+ ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1437
1445
  supports: ["generateAudio", "seed", "lastFrame", "watermark"]
1438
1446
  },
1439
1447
  defaults: {
@@ -1552,18 +1560,7 @@ var VIDEO_MODELS = [
1552
1560
  lifecycle: "active",
1553
1561
  exposure: "catalog",
1554
1562
  constraints: {
1555
- aspectRatios: [
1556
- "1:1",
1557
- "16:9",
1558
- "9:16",
1559
- "4:3",
1560
- "3:4",
1561
- "3:2",
1562
- "2:3",
1563
- "21:9",
1564
- "4:5",
1565
- "adaptive"
1566
- ],
1563
+ aspectRatios: [...KLING_RATIOS],
1567
1564
  resolutions: ["720p", "1080p"],
1568
1565
  durations: { min: 3, max: 15 },
1569
1566
  references: { images: 7 },
@@ -1783,6 +1780,397 @@ var MODELS = [
1783
1780
  ...OTHER_MODELS
1784
1781
  ];
1785
1782
 
1783
+ // src/catalog/schema.ts
1784
+ import { z } from "zod";
1785
+ var ADAPTER_IDS = [
1786
+ "ai-sdk-text",
1787
+ "google-image",
1788
+ "openai-image",
1789
+ "google-genai",
1790
+ "ark",
1791
+ "fal",
1792
+ "replicate",
1793
+ "prodia",
1794
+ "higgsfield",
1795
+ "xai-video"
1796
+ ];
1797
+ var ModelTypeSchema = z.enum(["text", "image", "video", "audio", "upscale", "bg-remove"]);
1798
+ var LifecycleSchema = z.enum(["active", "deprecated", "retired"]);
1799
+ var AdapterSchema = z.enum(ADAPTER_IDS);
1800
+ var AspectRatioSchema = z.enum([
1801
+ "1:1",
1802
+ "16:9",
1803
+ "9:16",
1804
+ "4:3",
1805
+ "3:4",
1806
+ "3:2",
1807
+ "2:3",
1808
+ "4:5",
1809
+ "5:4",
1810
+ "21:9",
1811
+ "9:21",
1812
+ "97:30",
1813
+ "adaptive"
1814
+ ]);
1815
+ var ImageSizeSchema = z.enum(["512", "1K", "2K", "4K"]);
1816
+ var VideoResolutionSchema = z.enum(["360p", "480p", "720p", "768p", "1080p", "2k", "4k"]);
1817
+ var ImageQualitySchema = z.enum(["auto", "low", "medium", "high", "xhigh", "max"]);
1818
+ var OutputFormatSchema = z.enum(["png", "jpeg", "webp"]);
1819
+ var RequiredInputSchema = z.enum(["firstFrame", "sourceVideo", "references"]);
1820
+ var SupportedOptionSchema = z.enum([
1821
+ "seed",
1822
+ "quality",
1823
+ "negativePrompt",
1824
+ "cfgScale",
1825
+ "generateAudio",
1826
+ "promptExpansion",
1827
+ "lastFrame",
1828
+ "count",
1829
+ "outputFormat",
1830
+ "scaleFactor",
1831
+ "safetySettings",
1832
+ "temperature",
1833
+ "upscale",
1834
+ "watermark",
1835
+ "safetyChecker",
1836
+ "instrumental"
1837
+ ]);
1838
+ var UsageUnitSchema = z.enum([
1839
+ "tokens",
1840
+ "images",
1841
+ "seconds",
1842
+ "reported_cost_usd",
1843
+ "compute_seconds"
1844
+ ]);
1845
+ var PricingKindSchema = z.enum(["token", "image_size", "video_per_second", "flat"]);
1846
+ var ProviderOptionValueSchema = z.lazy(
1847
+ () => z.union([
1848
+ z.string(),
1849
+ z.number(),
1850
+ z.boolean(),
1851
+ z.array(ProviderOptionValueSchema),
1852
+ z.record(z.string(), ProviderOptionValueSchema)
1853
+ ])
1854
+ );
1855
+ var ProviderOptionsSchema = z.record(z.string(), z.record(z.string(), ProviderOptionValueSchema));
1856
+ var ReferenceLimitsSchema = z.looseObject({
1857
+ images: z.number().optional(),
1858
+ videos: z.number().optional(),
1859
+ audios: z.number().optional(),
1860
+ total: z.number().optional(),
1861
+ audioOnly: z.boolean().optional()
1862
+ });
1863
+ var ModelConstraintsSchema = z.looseObject({
1864
+ aspectRatios: z.array(AspectRatioSchema).optional(),
1865
+ sizes: z.array(ImageSizeSchema).optional(),
1866
+ resolutions: z.array(VideoResolutionSchema).optional(),
1867
+ durations: z.union([z.looseObject({ min: z.number(), max: z.number() }), z.array(z.number())]).optional(),
1868
+ durationPresets: z.array(z.number()).optional(),
1869
+ qualityOptions: z.array(z.string()).optional(),
1870
+ exactSizes: z.record(z.string(), z.string()).optional(),
1871
+ references: ReferenceLimitsSchema.optional(),
1872
+ requires: z.array(RequiredInputSchema).optional(),
1873
+ supports: z.array(SupportedOptionSchema).optional(),
1874
+ ratioIgnoredWith: z.array(z.enum(["firstFrame", "sourceVideo"])).optional()
1875
+ });
1876
+ var MediaRefSchema = z.union([
1877
+ z.looseObject({ url: z.string(), label: z.string().optional() }),
1878
+ z.looseObject({
1879
+ bytes: z.instanceof(Uint8Array),
1880
+ mime: z.string(),
1881
+ label: z.string().optional()
1882
+ })
1883
+ ]);
1884
+ var ImageSafetySettingSchema = z.looseObject({ category: z.string(), threshold: z.string() });
1885
+ var UpscaleOptionsSchema = z.looseObject({
1886
+ enhanceModel: z.string().optional(),
1887
+ subjectDetection: z.string().optional(),
1888
+ faceEnhancement: z.boolean().optional(),
1889
+ faceEnhancementCreativity: z.number().optional(),
1890
+ faceEnhancementStrength: z.number().optional()
1891
+ });
1892
+ var DefaultsSchema = z.looseObject({
1893
+ references: z.array(MediaRefSchema).optional(),
1894
+ aspectRatio: AspectRatioSchema.optional(),
1895
+ size: ImageSizeSchema.optional(),
1896
+ exactSize: z.string().optional(),
1897
+ quality: ImageQualitySchema.optional(),
1898
+ seed: z.number().optional(),
1899
+ count: z.number().optional(),
1900
+ outputFormat: OutputFormatSchema.optional(),
1901
+ scaleFactor: z.number().optional(),
1902
+ safetySettings: z.array(ImageSafetySettingSchema).optional(),
1903
+ temperature: z.number().optional(),
1904
+ upscale: UpscaleOptionsSchema.optional(),
1905
+ firstFrame: MediaRefSchema.optional(),
1906
+ lastFrame: MediaRefSchema.optional(),
1907
+ sourceVideo: MediaRefSchema.optional(),
1908
+ referenceImages: z.array(MediaRefSchema).optional(),
1909
+ referenceVideos: z.array(MediaRefSchema).optional(),
1910
+ referenceAudios: z.array(MediaRefSchema).optional(),
1911
+ resolution: VideoResolutionSchema.optional(),
1912
+ durationSeconds: z.number().optional(),
1913
+ generateAudio: z.boolean().optional(),
1914
+ negativePrompt: z.string().optional(),
1915
+ cfgScale: z.number().optional(),
1916
+ promptExpansion: z.enum(["fast", "balanced", "quality"]).optional(),
1917
+ watermark: z.boolean().optional(),
1918
+ safetyChecker: z.boolean().optional(),
1919
+ mode: z.enum(["standard", "pro"]).optional(),
1920
+ instrumental: z.boolean().optional()
1921
+ });
1922
+ var CatalogDisplaySchema = z.looseObject({
1923
+ provider: z.string(),
1924
+ description: z.string(),
1925
+ logo: z.string().optional(),
1926
+ costTier: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional(),
1927
+ recommended: z.boolean().optional(),
1928
+ sortOrder: z.number().optional(),
1929
+ chat: z.looseObject({
1930
+ providerOptions: ProviderOptionsSchema.optional(),
1931
+ temperature: z.number().optional()
1932
+ }).optional()
1933
+ });
1934
+ var CatalogCapabilitiesSchema = z.looseObject({
1935
+ acceptsSourceVideo: z.boolean().optional(),
1936
+ framesExcludeReferences: z.boolean().optional(),
1937
+ reasoning: z.boolean().optional(),
1938
+ vision: z.boolean().optional(),
1939
+ contextWindow: z.number().optional()
1940
+ });
1941
+ var CatalogModelSchemaBase = z.looseObject({
1942
+ id: z.string().min(1),
1943
+ aliases: z.array(z.string()).default([]),
1944
+ label: z.string().min(1),
1945
+ type: ModelTypeSchema,
1946
+ adapter: AdapterSchema,
1947
+ providerModelId: z.string().min(1),
1948
+ lifecycle: LifecycleSchema,
1949
+ replacedBy: z.string().optional(),
1950
+ exposure: z.enum(["catalog", "internal"]),
1951
+ constraints: ModelConstraintsSchema,
1952
+ defaults: DefaultsSchema,
1953
+ providerOptions: ProviderOptionsSchema.optional(),
1954
+ usage: z.looseObject({
1955
+ unit: UsageUnitSchema,
1956
+ slug: z.string().optional(),
1957
+ providerLabel: z.string().optional()
1958
+ }),
1959
+ pricingKind: PricingKindSchema,
1960
+ display: CatalogDisplaySchema.optional(),
1961
+ capabilities: CatalogCapabilitiesSchema.optional()
1962
+ }).superRefine((row, ctx) => {
1963
+ if (row.lifecycle === "active" && !row.display) {
1964
+ ctx.addIssue({
1965
+ code: "custom",
1966
+ path: ["display"],
1967
+ message: "active rows need display"
1968
+ });
1969
+ }
1970
+ if (row.lifecycle === "active" && row.type === "text" && !row.capabilities?.contextWindow) {
1971
+ ctx.addIssue({
1972
+ code: "custom",
1973
+ path: ["capabilities", "contextWindow"],
1974
+ message: "active text rows need contextWindow"
1975
+ });
1976
+ }
1977
+ if (row.replacedBy !== void 0 && row.replacedBy === row.id) {
1978
+ ctx.addIssue({
1979
+ code: "custom",
1980
+ path: ["replacedBy"],
1981
+ message: "replacedBy must not equal id"
1982
+ });
1983
+ }
1984
+ });
1985
+ var CatalogModelSchema = CatalogModelSchemaBase;
1986
+ var CatalogSchema = z.array(CatalogModelSchemaBase).superRefine((rows, ctx) => {
1987
+ const seenIds = /* @__PURE__ */ new Map();
1988
+ rows.forEach((row, index) => {
1989
+ if (seenIds.has(row.id)) {
1990
+ ctx.addIssue({
1991
+ code: "custom",
1992
+ path: [index, "id"],
1993
+ message: `Duplicate model id "${row.id}"`
1994
+ });
1995
+ } else {
1996
+ seenIds.set(row.id, index);
1997
+ }
1998
+ });
1999
+ const seenAliases = /* @__PURE__ */ new Map();
2000
+ rows.forEach((row, index) => {
2001
+ row.aliases.forEach((alias, aliasIndex) => {
2002
+ const path = [index, "aliases", aliasIndex];
2003
+ if (alias === row.id || seenIds.has(alias)) {
2004
+ ctx.addIssue({
2005
+ code: "custom",
2006
+ path,
2007
+ message: `Alias "${alias}" collides with a model id`
2008
+ });
2009
+ return;
2010
+ }
2011
+ if (seenAliases.has(alias)) {
2012
+ ctx.addIssue({
2013
+ code: "custom",
2014
+ path,
2015
+ message: `Duplicate alias "${alias}"`
2016
+ });
2017
+ } else {
2018
+ seenAliases.set(alias, index);
2019
+ }
2020
+ });
2021
+ });
2022
+ });
2023
+
2024
+ // src/catalog/live.ts
2025
+ var KEY = /* @__PURE__ */ Symbol.for("@shop-os/modelkit/live-catalog");
2026
+ var store = globalThis;
2027
+ var liveState = store[KEY] ??= {
2028
+ catalog: new Catalog(MODELS),
2029
+ memo: void 0,
2030
+ timer: void 0,
2031
+ revision: void 0,
2032
+ origin: "snapshot"
2033
+ };
2034
+ var liveCatalog = liveState.catalog;
2035
+
2036
+ // src/catalog/load.ts
2037
+ function rowCount() {
2038
+ return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
2039
+ }
2040
+ function authHeaders(auth) {
2041
+ return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
2042
+ }
2043
+ function describeError(err) {
2044
+ return err instanceof Error ? err.message : String(err);
2045
+ }
2046
+ function parseEnvelope(json) {
2047
+ if (typeof json !== "object" || json === null || !("rows" in json)) {
2048
+ throw new Error("invalid catalog response: missing rows");
2049
+ }
2050
+ const rows = CatalogSchema.parse(json.rows);
2051
+ const rev = json.revision;
2052
+ if (typeof rev === "string") return { rows, revision: rev };
2053
+ return { rows };
2054
+ }
2055
+ function logPrimed(logger, result) {
2056
+ const { origin: loggedOrigin, rowCount: loggedCount, revision: loggedRevision } = result;
2057
+ logger.info("[modelkit] catalog primed", {
2058
+ origin: loggedOrigin,
2059
+ rowCount: loggedCount,
2060
+ revision: loggedRevision
2061
+ });
2062
+ }
2063
+ async function fetchRemote(source, logger) {
2064
+ const fetchImpl = source.fetch ?? globalThis.fetch;
2065
+ const headers = authHeaders(source.auth);
2066
+ if (liveState.revision !== void 0) headers["If-None-Match"] = `"${liveState.revision}"`;
2067
+ const res = await fetchImpl(`${source.baseUrl}/models/catalog`, {
2068
+ headers,
2069
+ signal: AbortSignal.timeout(source.timeoutMs ?? 1e4)
2070
+ });
2071
+ if (res.status === 304) return { kept: true };
2072
+ if (!res.ok) throw new Error(`catalog fetch failed with status ${res.status}`);
2073
+ const json = await res.json();
2074
+ const { rows, revision: rev } = parseEnvelope(json);
2075
+ if (rev === void 0) return { rows, fromCache: false };
2076
+ return { rows, revision: rev, fromCache: false };
2077
+ }
2078
+ async function writeCache(source, logger, rows, rev) {
2079
+ if (!source.cache) return;
2080
+ try {
2081
+ await source.cache.set(JSON.stringify({ rows, revision: rev }));
2082
+ } catch (err) {
2083
+ logger.warn("[modelkit] catalog cache write failed", err);
2084
+ }
2085
+ }
2086
+ async function prime(source, logger) {
2087
+ try {
2088
+ const fetched = await fetchRemote(source, logger);
2089
+ if ("kept" in fetched) {
2090
+ const result2 = { origin: liveState.origin, rowCount: rowCount() };
2091
+ if (liveState.revision !== void 0) result2.revision = liveState.revision;
2092
+ logPrimed(logger, result2);
2093
+ return result2;
2094
+ }
2095
+ liveCatalog.replace(fetched.rows);
2096
+ liveState.origin = "remote";
2097
+ liveState.revision = fetched.revision;
2098
+ await writeCache(source, logger, fetched.rows, fetched.revision);
2099
+ const result = { origin: liveState.origin, rowCount: rowCount() };
2100
+ if (fetched.revision !== void 0) result.revision = fetched.revision;
2101
+ logPrimed(logger, result);
2102
+ return result;
2103
+ } catch (err) {
2104
+ logger.warn("[modelkit] catalog remote failed, trying cache", err);
2105
+ const error = describeError(err);
2106
+ try {
2107
+ const cached = await source.cache?.get();
2108
+ if (!cached) throw new Error("no cached catalog");
2109
+ const { rows, revision: rev } = parseEnvelope(JSON.parse(cached));
2110
+ liveCatalog.replace(rows);
2111
+ liveState.origin = "last-good";
2112
+ liveState.revision = rev;
2113
+ const result = {
2114
+ origin: liveState.origin,
2115
+ rowCount: rowCount(),
2116
+ error
2117
+ };
2118
+ if (rev !== void 0) result.revision = rev;
2119
+ logPrimed(logger, result);
2120
+ return result;
2121
+ } catch (cacheErr) {
2122
+ logger.warn("[modelkit] catalog cache failed, using snapshot", cacheErr);
2123
+ liveCatalog.replace([...MODELS]);
2124
+ liveState.origin = "snapshot";
2125
+ liveState.revision = void 0;
2126
+ const result = {
2127
+ origin: liveState.origin,
2128
+ rowCount: rowCount(),
2129
+ error
2130
+ };
2131
+ logPrimed(logger, result);
2132
+ return result;
2133
+ }
2134
+ }
2135
+ }
2136
+ async function refresh(source, logger) {
2137
+ try {
2138
+ const fetched = await fetchRemote(source, logger);
2139
+ if ("kept" in fetched) return;
2140
+ liveCatalog.replace(fetched.rows);
2141
+ liveState.origin = "remote";
2142
+ liveState.revision = fetched.revision;
2143
+ await writeCache(source, logger, fetched.rows, fetched.revision);
2144
+ } catch (err) {
2145
+ logger.warn("[modelkit] catalog refresh failed, keeping current rows", err);
2146
+ }
2147
+ }
2148
+ function primeCatalog(source) {
2149
+ const memo = liveState.memo;
2150
+ if (memo) return memo;
2151
+ const logger = source.logger ?? console;
2152
+ liveState.memo = prime(source, logger).then((result) => {
2153
+ const ttlMs = source.ttlMs ?? 3e5;
2154
+ if (ttlMs > 0 && liveState.timer === void 0) {
2155
+ liveState.timer = setInterval(() => void refresh(source, logger), ttlMs);
2156
+ const maybeUnref = liveState.timer;
2157
+ maybeUnref.unref?.();
2158
+ }
2159
+ return result;
2160
+ });
2161
+ return liveState.memo;
2162
+ }
2163
+ function resetLiveCatalog() {
2164
+ if (liveState.timer !== void 0) {
2165
+ clearInterval(liveState.timer);
2166
+ liveState.timer = void 0;
2167
+ }
2168
+ liveState.memo = void 0;
2169
+ liveState.revision = void 0;
2170
+ liveState.origin = "snapshot";
2171
+ liveCatalog.replace([...MODELS]);
2172
+ }
2173
+
1786
2174
  // src/resolve-options.ts
1787
2175
  var SIZE_ORDER = ["512", "1K", "2K", "4K"];
1788
2176
  var RESOLUTION_ORDER = ["360p", "480p", "720p", "768p", "1080p", "2k", "4k"];
@@ -2165,7 +2553,10 @@ function resolveOptions(model, request, onUnsupported = "error") {
2165
2553
  }
2166
2554
  }
2167
2555
  export {
2556
+ ADAPTER_IDS,
2168
2557
  Catalog,
2558
+ CatalogModelSchema,
2559
+ CatalogSchema,
2169
2560
  IMAGE_MODELS,
2170
2561
  MODELS,
2171
2562
  ModelKitError,
@@ -2173,7 +2564,11 @@ export {
2173
2564
  TEXT_MODELS,
2174
2565
  VIDEO_MODELS,
2175
2566
  isModelKitError,
2567
+ liveCatalog,
2568
+ liveState,
2176
2569
  nearestAspectRatio,
2570
+ primeCatalog,
2571
+ resetLiveCatalog,
2177
2572
  resolveAudioOptions,
2178
2573
  resolveImageOptions,
2179
2574
  resolveOptions,