@shop-os/modelkit 0.2.0 → 0.2.3
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 +40 -0
- package/dist/catalog.cjs +98 -93
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +25 -9
- package/dist/catalog.d.ts +25 -9
- package/dist/catalog.js +98 -93
- package/dist/catalog.js.map +1 -1
- package/dist/index.cjs +256 -217
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -9
- package/dist/index.d.ts +25 -9
- package/dist/index.js +255 -217
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,44 @@ header in the release PR.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.2.3] - 2026-09-15
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- `display.sortOrder` sorts descending: a higher number shows first, so a new model takes the next number up. No published row set it before.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `primeCatalog` treats a remote response with zero rows as a failure and keeps the last-good or snapshot rows, so a pro-backend whose catalog table is not seeded yet cannot empty every consumer's pickers.
|
|
19
|
+
|
|
20
|
+
## [0.2.2] - 2026-09-15
|
|
21
|
+
|
|
22
|
+
Option audit against provider docs, limited to options a product surface sets.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- Veo 3.1, 3.1 Fast and 3.1 Lite accept `seed` (sent as `config.seed`).
|
|
26
|
+
- Seedream 5.0 Pro accepts `outputFormat` png/jpeg (sent as `output_format`; webp is coerced to png with a warning).
|
|
27
|
+
- Gemini 3 image rows declare `references.images: 14`, the provider cap.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- `liveCatalog` and the prime state are one process-wide singleton (on `globalThis`). The root and `./catalog` bundles each carried their own copy, so a server that primed through the root entry still served the snapshot to code importing `@shop-os/modelkit/catalog`.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- gpt-image-2.5-sunburst/flare `qualityOptions` add `xhigh` and `max`.
|
|
34
|
+
- Veo 3.1 Fast and Lite durations add 6 s. Veo 3.1 Lite drops `references` (not supported for Lite).
|
|
35
|
+
- Gemini Omni rows allow 3 reference videos (was 1).
|
|
36
|
+
- Seedance rows declare `ratioIgnoredWith: ["firstFrame", "sourceVideo"]` to match the adapter.
|
|
37
|
+
- kling-v3-omni-video aspect ratios are 16:9, 9:16, 1:1, adaptive (provider list).
|
|
38
|
+
- seedream-4.5 drops the 1K tier (provider minimum 2K). gemini-2.5-flash-image is 1K only. muse-image-1.0 drops 4:5 and 5:4.
|
|
39
|
+
- qwen-image-2-pro no longer requires a reference image.
|
|
40
|
+
- Prodia nano-banana rows drop `seed` (the adapter never sent it).
|
|
41
|
+
|
|
42
|
+
## [0.2.1] - 2026-09-15
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
- `gpt-image-2`, `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare` declare `outputFormat`; the `openai-image` adapter sends `output_format` (png or jpeg) and labels the result mime to match.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- `outputFormat: "webp"` on GPT image models is coerced to png with an `option_ignored` warning: the API returns PNG bytes for webp (openai/openai-node#1850).
|
|
49
|
+
|
|
12
50
|
## [0.2.0] - 2026-09-14
|
|
13
51
|
|
|
14
52
|
### Added
|
|
@@ -210,6 +248,8 @@ First publish, renamed from `@shop-os/model-sdk`.
|
|
|
210
248
|
type, one usage event with the canonical id.
|
|
211
249
|
|
|
212
250
|
[Unreleased]: https://github.com/Shop-OS/shopos-backend-ts/compare/modelkit-0.2.0...dev
|
|
251
|
+
[0.2.2]: https://github.com/Shop-OS/shopos-backend-ts/compare/modelkit-0.2.1...modelkit-0.2.2
|
|
252
|
+
[0.2.1]: https://github.com/Shop-OS/shopos-backend-ts/compare/modelkit-0.2.0...modelkit-0.2.1
|
|
213
253
|
[0.2.0]: https://github.com/Shop-OS/shopos-backend-ts/compare/modelkit-0.1.11...modelkit-0.2.0
|
|
214
254
|
[0.1.11]: https://github.com/Shop-OS/shopos-backend-ts/compare/f0981d9b9...bb3b16354
|
|
215
255
|
[0.1.10]: https://github.com/Shop-OS/shopos-backend-ts/compare/f17e822e2...f0981d9b9
|
package/dist/catalog.cjs
CHANGED
|
@@ -32,6 +32,7 @@ __export(catalog_entry_exports, {
|
|
|
32
32
|
VIDEO_MODELS: () => VIDEO_MODELS,
|
|
33
33
|
isModelKitError: () => isModelKitError,
|
|
34
34
|
liveCatalog: () => liveCatalog,
|
|
35
|
+
liveState: () => liveState,
|
|
35
36
|
nearestAspectRatio: () => nearestAspectRatio,
|
|
36
37
|
primeCatalog: () => primeCatalog,
|
|
37
38
|
resetLiveCatalog: () => resetLiveCatalog,
|
|
@@ -162,7 +163,13 @@ var GEMINI_RATIOS = [
|
|
|
162
163
|
"5:4",
|
|
163
164
|
"adaptive"
|
|
164
165
|
];
|
|
165
|
-
function geminiImage(id, label, aliases, sizes, display, exposure) {
|
|
166
|
+
function geminiImage(id, label, aliases, sizes, display, exposure, references) {
|
|
167
|
+
const constraints = {
|
|
168
|
+
aspectRatios: [...GEMINI_RATIOS],
|
|
169
|
+
sizes,
|
|
170
|
+
supports: ["safetySettings", "temperature"]
|
|
171
|
+
};
|
|
172
|
+
if (references !== void 0) constraints.references = references;
|
|
166
173
|
const model = {
|
|
167
174
|
id,
|
|
168
175
|
aliases,
|
|
@@ -172,11 +179,7 @@ function geminiImage(id, label, aliases, sizes, display, exposure) {
|
|
|
172
179
|
providerModelId: id,
|
|
173
180
|
lifecycle: "active",
|
|
174
181
|
exposure,
|
|
175
|
-
constraints
|
|
176
|
-
aspectRatios: [...GEMINI_RATIOS],
|
|
177
|
-
sizes,
|
|
178
|
-
supports: ["safetySettings", "temperature"]
|
|
179
|
-
},
|
|
182
|
+
constraints,
|
|
180
183
|
defaults: { size: "1K", aspectRatio: "1:1" },
|
|
181
184
|
usage: { ...IMAGE_USAGE, slug: "gemini" },
|
|
182
185
|
pricingKind: "image_size",
|
|
@@ -195,7 +198,8 @@ var IMAGE_MODELS = [
|
|
|
195
198
|
description: "Create stunning, production-quality visuals with exceptional realism, detail, and typography. Best for campaigns, e-commerce, product photography, and hero creatives.",
|
|
196
199
|
logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
|
|
197
200
|
},
|
|
198
|
-
"catalog"
|
|
201
|
+
"catalog",
|
|
202
|
+
{ images: 14 }
|
|
199
203
|
),
|
|
200
204
|
geminiImage(
|
|
201
205
|
"gemini-3.1-flash-image-preview",
|
|
@@ -208,7 +212,8 @@ var IMAGE_MODELS = [
|
|
|
208
212
|
description: "Generate high-quality images at speed. Perfect for brainstorming, moodboards, concept testing, creative exploration, and large batch workflows.",
|
|
209
213
|
logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
|
|
210
214
|
},
|
|
211
|
-
"catalog"
|
|
215
|
+
"catalog",
|
|
216
|
+
{ images: 14 }
|
|
212
217
|
),
|
|
213
218
|
geminiImage(
|
|
214
219
|
"gemini-3.1-flash-lite-image",
|
|
@@ -220,14 +225,15 @@ var IMAGE_MODELS = [
|
|
|
220
225
|
description: "The fastest and most affordable image model. Ideal for rapid drafts, bulk exploration, and iterative concept testing at 1K resolution.",
|
|
221
226
|
logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
|
|
222
227
|
},
|
|
223
|
-
"catalog"
|
|
228
|
+
"catalog",
|
|
229
|
+
{ images: 14 }
|
|
224
230
|
),
|
|
225
231
|
// trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
|
|
226
232
|
geminiImage(
|
|
227
233
|
"gemini-2.5-flash-image",
|
|
228
234
|
"Gemini 2.5 Flash Image",
|
|
229
235
|
[],
|
|
230
|
-
["1K"
|
|
236
|
+
["1K"],
|
|
231
237
|
{
|
|
232
238
|
provider: "google",
|
|
233
239
|
description: "Nano Banana (legacy Gemini 2.5 Flash Image) native image generation and editing model for high-volume, low-latency creative workflows."
|
|
@@ -258,7 +264,7 @@ var IMAGE_MODELS = [
|
|
|
258
264
|
sizes: ["1K", "2K", "4K"],
|
|
259
265
|
qualityOptions: ["auto", "low", "medium", "high"],
|
|
260
266
|
references: { images: 16 },
|
|
261
|
-
supports: ["quality", "count"]
|
|
267
|
+
supports: ["quality", "count", "outputFormat"]
|
|
262
268
|
},
|
|
263
269
|
defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
|
|
264
270
|
usage: { ...IMAGE_USAGE, slug: "gpt" },
|
|
@@ -278,7 +284,7 @@ var IMAGE_MODELS = [
|
|
|
278
284
|
adapter: "openai-image",
|
|
279
285
|
providerModelId: "gpt-image-2.5-sunburst",
|
|
280
286
|
lifecycle: "active",
|
|
281
|
-
exposure: "
|
|
287
|
+
exposure: "catalog",
|
|
282
288
|
constraints: {
|
|
283
289
|
exactSizes: {
|
|
284
290
|
"1:1": "1024x1024",
|
|
@@ -291,15 +297,16 @@ var IMAGE_MODELS = [
|
|
|
291
297
|
},
|
|
292
298
|
aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:5", "5:4", "adaptive"],
|
|
293
299
|
sizes: ["1K", "2K", "4K"],
|
|
294
|
-
qualityOptions: ["auto", "low", "medium", "high"],
|
|
300
|
+
qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
|
|
295
301
|
references: { images: 16 },
|
|
296
|
-
supports: ["quality", "count"]
|
|
302
|
+
supports: ["quality", "count", "outputFormat"]
|
|
297
303
|
},
|
|
298
304
|
defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
|
|
299
305
|
usage: { ...IMAGE_USAGE },
|
|
300
306
|
pricingKind: "image_size",
|
|
301
307
|
display: {
|
|
302
308
|
provider: "openai",
|
|
309
|
+
sortOrder: 20,
|
|
303
310
|
description: "OpenAI's most capable GPT Image 2.5 tier, built for editing precision; takes text and image input and outputs images in low, medium, high, xhigh, or max quality."
|
|
304
311
|
}
|
|
305
312
|
},
|
|
@@ -312,7 +319,7 @@ var IMAGE_MODELS = [
|
|
|
312
319
|
adapter: "openai-image",
|
|
313
320
|
providerModelId: "gpt-image-2.5-flare",
|
|
314
321
|
lifecycle: "active",
|
|
315
|
-
exposure: "
|
|
322
|
+
exposure: "catalog",
|
|
316
323
|
constraints: {
|
|
317
324
|
exactSizes: {
|
|
318
325
|
"1:1": "1024x1024",
|
|
@@ -325,15 +332,16 @@ var IMAGE_MODELS = [
|
|
|
325
332
|
},
|
|
326
333
|
aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:5", "5:4", "adaptive"],
|
|
327
334
|
sizes: ["1K", "2K", "4K"],
|
|
328
|
-
qualityOptions: ["auto", "low", "medium", "high"],
|
|
335
|
+
qualityOptions: ["auto", "low", "medium", "high", "xhigh", "max"],
|
|
329
336
|
references: { images: 16 },
|
|
330
|
-
supports: ["quality", "count"]
|
|
337
|
+
supports: ["quality", "count", "outputFormat"]
|
|
331
338
|
},
|
|
332
339
|
defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
|
|
333
340
|
usage: { ...IMAGE_USAGE },
|
|
334
341
|
pricingKind: "image_size",
|
|
335
342
|
display: {
|
|
336
343
|
provider: "openai",
|
|
344
|
+
sortOrder: 10,
|
|
337
345
|
description: "OpenAI's fastest GPT Image 2.5 tier for high-quality everyday image generation and editing; takes text and image input and outputs images."
|
|
338
346
|
}
|
|
339
347
|
},
|
|
@@ -357,8 +365,6 @@ var IMAGE_MODELS = [
|
|
|
357
365
|
"2:3",
|
|
358
366
|
"3:4",
|
|
359
367
|
"9:16",
|
|
360
|
-
"4:5",
|
|
361
|
-
"5:4",
|
|
362
368
|
"9:21",
|
|
363
369
|
"adaptive"
|
|
364
370
|
],
|
|
@@ -390,7 +396,7 @@ var IMAGE_MODELS = [
|
|
|
390
396
|
references: { images: 10 },
|
|
391
397
|
// Ark's Images API has no output-format, quality or count field; only
|
|
392
398
|
// the seed is forwarded, so anything else must warn, not vanish.
|
|
393
|
-
supports: ["seed"]
|
|
399
|
+
supports: ["seed", "outputFormat"]
|
|
394
400
|
},
|
|
395
401
|
defaults: { size: "1K", aspectRatio: "1:1" },
|
|
396
402
|
usage: { ...IMAGE_USAGE },
|
|
@@ -426,7 +432,7 @@ var IMAGE_MODELS = [
|
|
|
426
432
|
"21:9",
|
|
427
433
|
"9:21"
|
|
428
434
|
],
|
|
429
|
-
sizes: ["
|
|
435
|
+
sizes: ["2K", "4K"],
|
|
430
436
|
references: { images: 10 },
|
|
431
437
|
supports: ["seed"]
|
|
432
438
|
},
|
|
@@ -478,7 +484,7 @@ var IMAGE_MODELS = [
|
|
|
478
484
|
aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
|
|
479
485
|
sizes: ["1K", "2K", "4K"],
|
|
480
486
|
requires: ["references"],
|
|
481
|
-
supports: [
|
|
487
|
+
supports: []
|
|
482
488
|
},
|
|
483
489
|
defaults: { size: "1K", aspectRatio: "1:1" },
|
|
484
490
|
usage: { ...IMAGE_USAGE },
|
|
@@ -503,7 +509,7 @@ var IMAGE_MODELS = [
|
|
|
503
509
|
aspectRatios: ["1:1", "2:3", "3:4", "4:3", "9:16", "16:9", "21:9"],
|
|
504
510
|
sizes: ["1K"],
|
|
505
511
|
requires: ["references"],
|
|
506
|
-
supports: [
|
|
512
|
+
supports: []
|
|
507
513
|
},
|
|
508
514
|
defaults: { size: "1K", aspectRatio: "1:1" },
|
|
509
515
|
usage: { ...IMAGE_USAGE },
|
|
@@ -555,7 +561,6 @@ var IMAGE_MODELS = [
|
|
|
555
561
|
aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"],
|
|
556
562
|
sizes: [],
|
|
557
563
|
references: { images: 1 },
|
|
558
|
-
requires: ["references"],
|
|
559
564
|
supports: []
|
|
560
565
|
},
|
|
561
566
|
defaults: { aspectRatio: "1:1" },
|
|
@@ -1274,7 +1279,7 @@ var VIDEO_MODELS = [
|
|
|
1274
1279
|
resolutions: ["720p", "1080p", "4k"],
|
|
1275
1280
|
durations: [8],
|
|
1276
1281
|
references: { images: 3 },
|
|
1277
|
-
supports: ["lastFrame"]
|
|
1282
|
+
supports: ["lastFrame", "seed"]
|
|
1278
1283
|
},
|
|
1279
1284
|
defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
|
|
1280
1285
|
usage: { ...VIDEO_USAGE, slug: "veo" },
|
|
@@ -1302,9 +1307,9 @@ var VIDEO_MODELS = [
|
|
|
1302
1307
|
constraints: {
|
|
1303
1308
|
aspectRatios: [...OMNI_RATIOS],
|
|
1304
1309
|
resolutions: ["720p", "1080p", "4k"],
|
|
1305
|
-
durations: [4, 8],
|
|
1310
|
+
durations: [4, 6, 8],
|
|
1306
1311
|
references: { images: 3 },
|
|
1307
|
-
supports: ["lastFrame"]
|
|
1312
|
+
supports: ["lastFrame", "seed"]
|
|
1308
1313
|
},
|
|
1309
1314
|
defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
|
|
1310
1315
|
usage: { ...VIDEO_USAGE },
|
|
@@ -1331,9 +1336,8 @@ var VIDEO_MODELS = [
|
|
|
1331
1336
|
constraints: {
|
|
1332
1337
|
aspectRatios: [...OMNI_RATIOS],
|
|
1333
1338
|
resolutions: ["720p", "1080p"],
|
|
1334
|
-
durations: [4, 8],
|
|
1335
|
-
|
|
1336
|
-
supports: ["lastFrame"]
|
|
1339
|
+
durations: [4, 6, 8],
|
|
1340
|
+
supports: ["lastFrame", "seed"]
|
|
1337
1341
|
},
|
|
1338
1342
|
defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
|
|
1339
1343
|
usage: { ...VIDEO_USAGE },
|
|
@@ -1365,7 +1369,7 @@ var VIDEO_MODELS = [
|
|
|
1365
1369
|
// reference clip keeps its own framing.
|
|
1366
1370
|
aspectRatios: [...OMNI_RATIOS],
|
|
1367
1371
|
durations: [3, 4, 5, 6, 7, 8, 9, 10],
|
|
1368
|
-
references: { images: 6, videos:
|
|
1372
|
+
references: { images: 6, videos: 3 },
|
|
1369
1373
|
supports: []
|
|
1370
1374
|
},
|
|
1371
1375
|
defaults: {},
|
|
@@ -1391,7 +1395,7 @@ var VIDEO_MODELS = [
|
|
|
1391
1395
|
aspectRatios: [...OMNI_RATIOS],
|
|
1392
1396
|
resolutions: ["360p", "720p", "1080p", "4k"],
|
|
1393
1397
|
durations: [3, 4, 5, 6, 7, 8, 9, 10],
|
|
1394
|
-
references: { images: 6, videos:
|
|
1398
|
+
references: { images: 6, videos: 3 },
|
|
1395
1399
|
supports: []
|
|
1396
1400
|
},
|
|
1397
1401
|
defaults: {},
|
|
@@ -1420,7 +1424,7 @@ var VIDEO_MODELS = [
|
|
|
1420
1424
|
durations: { min: 4, max: 15 },
|
|
1421
1425
|
durationPresets: [4, 6, 8, 10, 12, 15],
|
|
1422
1426
|
references: { images: 9, videos: 3, audios: 3, audioOnly: false },
|
|
1423
|
-
ratioIgnoredWith: ["firstFrame"],
|
|
1427
|
+
ratioIgnoredWith: ["firstFrame", "sourceVideo"],
|
|
1424
1428
|
supports: ["generateAudio", "seed", "lastFrame", "watermark"]
|
|
1425
1429
|
},
|
|
1426
1430
|
defaults: {
|
|
@@ -1452,7 +1456,7 @@ var VIDEO_MODELS = [
|
|
|
1452
1456
|
resolutions: ["480p", "720p"],
|
|
1453
1457
|
durations: { min: 4, max: 15 },
|
|
1454
1458
|
references: { images: 9, videos: 3, audios: 3, audioOnly: false },
|
|
1455
|
-
ratioIgnoredWith: ["firstFrame"],
|
|
1459
|
+
ratioIgnoredWith: ["firstFrame", "sourceVideo"],
|
|
1456
1460
|
supports: ["generateAudio", "seed", "lastFrame", "watermark"]
|
|
1457
1461
|
},
|
|
1458
1462
|
defaults: {
|
|
@@ -1485,7 +1489,7 @@ var VIDEO_MODELS = [
|
|
|
1485
1489
|
resolutions: ["480p", "720p", "1080p"],
|
|
1486
1490
|
durations: { min: 4, max: 30 },
|
|
1487
1491
|
references: { images: 30, videos: 10, audios: 10 },
|
|
1488
|
-
ratioIgnoredWith: ["firstFrame"],
|
|
1492
|
+
ratioIgnoredWith: ["firstFrame", "sourceVideo"],
|
|
1489
1493
|
supports: ["generateAudio", "seed", "lastFrame", "watermark"]
|
|
1490
1494
|
},
|
|
1491
1495
|
defaults: {
|
|
@@ -1604,18 +1608,7 @@ var VIDEO_MODELS = [
|
|
|
1604
1608
|
lifecycle: "active",
|
|
1605
1609
|
exposure: "catalog",
|
|
1606
1610
|
constraints: {
|
|
1607
|
-
aspectRatios: [
|
|
1608
|
-
"1:1",
|
|
1609
|
-
"16:9",
|
|
1610
|
-
"9:16",
|
|
1611
|
-
"4:3",
|
|
1612
|
-
"3:4",
|
|
1613
|
-
"3:2",
|
|
1614
|
-
"2:3",
|
|
1615
|
-
"21:9",
|
|
1616
|
-
"4:5",
|
|
1617
|
-
"adaptive"
|
|
1618
|
-
],
|
|
1611
|
+
aspectRatios: [...KLING_RATIOS],
|
|
1619
1612
|
resolutions: ["720p", "1080p"],
|
|
1620
1613
|
durations: { min: 3, max: 15 },
|
|
1621
1614
|
references: { images: 7 },
|
|
@@ -2077,30 +2070,34 @@ var CatalogSchema = import_zod.z.array(CatalogModelSchemaBase).superRefine((rows
|
|
|
2077
2070
|
});
|
|
2078
2071
|
|
|
2079
2072
|
// src/catalog/live.ts
|
|
2080
|
-
var
|
|
2073
|
+
var KEY = /* @__PURE__ */ Symbol.for("@shop-os/modelkit/live-catalog");
|
|
2074
|
+
var store = globalThis;
|
|
2075
|
+
var liveState = store[KEY] ??= {
|
|
2076
|
+
catalog: new Catalog(MODELS),
|
|
2077
|
+
memo: void 0,
|
|
2078
|
+
timer: void 0,
|
|
2079
|
+
revision: void 0,
|
|
2080
|
+
origin: "snapshot"
|
|
2081
|
+
};
|
|
2082
|
+
var liveCatalog = liveState.catalog;
|
|
2081
2083
|
|
|
2082
2084
|
// src/catalog/load.ts
|
|
2083
|
-
var
|
|
2084
|
-
var timer;
|
|
2085
|
-
var revision;
|
|
2086
|
-
var origin = "snapshot";
|
|
2085
|
+
var import_zod2 = require("zod");
|
|
2087
2086
|
function rowCount() {
|
|
2088
2087
|
return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
|
|
2089
2088
|
}
|
|
2090
2089
|
function authHeaders(auth) {
|
|
2091
2090
|
return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
|
|
2092
2091
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
}
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
if (typeof rev === "string") return { rows, revision: rev };
|
|
2103
|
-
return { rows };
|
|
2092
|
+
var EnvelopeSchema = import_zod2.z.object({
|
|
2093
|
+
rows: CatalogSchema,
|
|
2094
|
+
revision: import_zod2.z.string().optional()
|
|
2095
|
+
});
|
|
2096
|
+
function toEnvelope(data) {
|
|
2097
|
+
if (data.rows.length === 0) throw new Error("invalid catalog response: no rows");
|
|
2098
|
+
const envelope = { rows: data.rows };
|
|
2099
|
+
if (data.revision !== void 0) envelope.revision = data.revision;
|
|
2100
|
+
return envelope;
|
|
2104
2101
|
}
|
|
2105
2102
|
function logPrimed(logger, result) {
|
|
2106
2103
|
const { origin: loggedOrigin, rowCount: loggedCount, revision: loggedRevision } = result;
|
|
@@ -2113,7 +2110,7 @@ function logPrimed(logger, result) {
|
|
|
2113
2110
|
async function fetchRemote(source, logger) {
|
|
2114
2111
|
const fetchImpl = source.fetch ?? globalThis.fetch;
|
|
2115
2112
|
const headers = authHeaders(source.auth);
|
|
2116
|
-
if (revision !== void 0) headers["If-None-Match"] = `"${revision}"`;
|
|
2113
|
+
if (liveState.revision !== void 0) headers["If-None-Match"] = `"${liveState.revision}"`;
|
|
2117
2114
|
const res = await fetchImpl(`${source.baseUrl}/models/catalog`, {
|
|
2118
2115
|
headers,
|
|
2119
2116
|
signal: AbortSignal.timeout(source.timeoutMs ?? 1e4)
|
|
@@ -2121,7 +2118,9 @@ async function fetchRemote(source, logger) {
|
|
|
2121
2118
|
if (res.status === 304) return { kept: true };
|
|
2122
2119
|
if (!res.ok) throw new Error(`catalog fetch failed with status ${res.status}`);
|
|
2123
2120
|
const json = await res.json();
|
|
2124
|
-
const
|
|
2121
|
+
const parsed = EnvelopeSchema.safeParse(json);
|
|
2122
|
+
if (!parsed.success) throw new Error("invalid catalog response: missing rows");
|
|
2123
|
+
const { rows, revision: rev } = toEnvelope(parsed.data);
|
|
2125
2124
|
if (rev === void 0) return { rows, fromCache: false };
|
|
2126
2125
|
return { rows, revision: rev, fromCache: false };
|
|
2127
2126
|
}
|
|
@@ -2137,40 +2136,46 @@ async function prime(source, logger) {
|
|
|
2137
2136
|
try {
|
|
2138
2137
|
const fetched = await fetchRemote(source, logger);
|
|
2139
2138
|
if ("kept" in fetched) {
|
|
2140
|
-
const result2 = { origin, rowCount: rowCount() };
|
|
2141
|
-
if (revision !== void 0) result2.revision = revision;
|
|
2139
|
+
const result2 = { origin: liveState.origin, rowCount: rowCount() };
|
|
2140
|
+
if (liveState.revision !== void 0) result2.revision = liveState.revision;
|
|
2142
2141
|
logPrimed(logger, result2);
|
|
2143
2142
|
return result2;
|
|
2144
2143
|
}
|
|
2145
2144
|
liveCatalog.replace(fetched.rows);
|
|
2146
|
-
origin = "remote";
|
|
2147
|
-
revision = fetched.revision;
|
|
2145
|
+
liveState.origin = "remote";
|
|
2146
|
+
liveState.revision = fetched.revision;
|
|
2148
2147
|
await writeCache(source, logger, fetched.rows, fetched.revision);
|
|
2149
|
-
const result = { origin, rowCount: rowCount() };
|
|
2148
|
+
const result = { origin: liveState.origin, rowCount: rowCount() };
|
|
2150
2149
|
if (fetched.revision !== void 0) result.revision = fetched.revision;
|
|
2151
2150
|
logPrimed(logger, result);
|
|
2152
2151
|
return result;
|
|
2153
2152
|
} catch (err) {
|
|
2154
2153
|
logger.warn("[modelkit] catalog remote failed, trying cache", err);
|
|
2155
|
-
const error =
|
|
2154
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
2156
2155
|
try {
|
|
2157
2156
|
const cached = await source.cache?.get();
|
|
2158
2157
|
if (!cached) throw new Error("no cached catalog");
|
|
2159
|
-
const
|
|
2158
|
+
const cachedParsed = EnvelopeSchema.safeParse(JSON.parse(cached));
|
|
2159
|
+
if (!cachedParsed.success) throw new Error("invalid catalog response: missing rows");
|
|
2160
|
+
const { rows, revision: rev } = toEnvelope(cachedParsed.data);
|
|
2160
2161
|
liveCatalog.replace(rows);
|
|
2161
|
-
origin = "last-good";
|
|
2162
|
-
revision = rev;
|
|
2163
|
-
const result = {
|
|
2162
|
+
liveState.origin = "last-good";
|
|
2163
|
+
liveState.revision = rev;
|
|
2164
|
+
const result = {
|
|
2165
|
+
origin: liveState.origin,
|
|
2166
|
+
rowCount: rowCount(),
|
|
2167
|
+
error
|
|
2168
|
+
};
|
|
2164
2169
|
if (rev !== void 0) result.revision = rev;
|
|
2165
2170
|
logPrimed(logger, result);
|
|
2166
2171
|
return result;
|
|
2167
2172
|
} catch (cacheErr) {
|
|
2168
2173
|
logger.warn("[modelkit] catalog cache failed, using snapshot", cacheErr);
|
|
2169
2174
|
liveCatalog.replace([...MODELS]);
|
|
2170
|
-
origin = "snapshot";
|
|
2171
|
-
revision = void 0;
|
|
2175
|
+
liveState.origin = "snapshot";
|
|
2176
|
+
liveState.revision = void 0;
|
|
2172
2177
|
const result = {
|
|
2173
|
-
origin,
|
|
2178
|
+
origin: liveState.origin,
|
|
2174
2179
|
rowCount: rowCount(),
|
|
2175
2180
|
error
|
|
2176
2181
|
};
|
|
@@ -2184,35 +2189,35 @@ async function refresh(source, logger) {
|
|
|
2184
2189
|
const fetched = await fetchRemote(source, logger);
|
|
2185
2190
|
if ("kept" in fetched) return;
|
|
2186
2191
|
liveCatalog.replace(fetched.rows);
|
|
2187
|
-
origin = "remote";
|
|
2188
|
-
revision = fetched.revision;
|
|
2192
|
+
liveState.origin = "remote";
|
|
2193
|
+
liveState.revision = fetched.revision;
|
|
2189
2194
|
await writeCache(source, logger, fetched.rows, fetched.revision);
|
|
2190
2195
|
} catch (err) {
|
|
2191
2196
|
logger.warn("[modelkit] catalog refresh failed, keeping current rows", err);
|
|
2192
2197
|
}
|
|
2193
2198
|
}
|
|
2194
2199
|
function primeCatalog(source) {
|
|
2195
|
-
if (memo) return memo;
|
|
2200
|
+
if (liveState.memo) return liveState.memo;
|
|
2196
2201
|
const logger = source.logger ?? console;
|
|
2197
|
-
|
|
2202
|
+
const pending = prime(source, logger).then((result) => {
|
|
2198
2203
|
const ttlMs = source.ttlMs ?? 3e5;
|
|
2199
|
-
if (ttlMs > 0 && timer === void 0) {
|
|
2200
|
-
timer = setInterval(() => void refresh(source, logger), ttlMs);
|
|
2201
|
-
|
|
2202
|
-
maybeUnref.unref?.();
|
|
2204
|
+
if (ttlMs > 0 && liveState.timer === void 0) {
|
|
2205
|
+
liveState.timer = setInterval(() => void refresh(source, logger), ttlMs);
|
|
2206
|
+
liveState.timer.unref?.();
|
|
2203
2207
|
}
|
|
2204
2208
|
return result;
|
|
2205
2209
|
});
|
|
2206
|
-
|
|
2210
|
+
liveState.memo = pending;
|
|
2211
|
+
return pending;
|
|
2207
2212
|
}
|
|
2208
2213
|
function resetLiveCatalog() {
|
|
2209
|
-
if (timer !== void 0) {
|
|
2210
|
-
clearInterval(timer);
|
|
2211
|
-
timer = void 0;
|
|
2214
|
+
if (liveState.timer !== void 0) {
|
|
2215
|
+
clearInterval(liveState.timer);
|
|
2216
|
+
liveState.timer = void 0;
|
|
2212
2217
|
}
|
|
2213
|
-
memo = void 0;
|
|
2214
|
-
revision = void 0;
|
|
2215
|
-
origin = "snapshot";
|
|
2218
|
+
liveState.memo = void 0;
|
|
2219
|
+
liveState.revision = void 0;
|
|
2220
|
+
liveState.origin = "snapshot";
|
|
2216
2221
|
liveCatalog.replace([...MODELS]);
|
|
2217
2222
|
}
|
|
2218
2223
|
|