@cms0/cms0 0.2.1 → 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/dist/cjs/index.cjs +313 -46
- package/dist/esm/index.js +313 -46
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -4,6 +4,11 @@ exports.createCmsClient = createCmsClient;
|
|
|
4
4
|
exports.cms0 = cms0;
|
|
5
5
|
const schema_descriptors_1 = require("@cms0/cms0/schema-descriptors");
|
|
6
6
|
const shared_1 = require("@cms0/shared");
|
|
7
|
+
const DEFAULT_REQUEST_CONCURRENCY = 6;
|
|
8
|
+
const DEFAULT_REQUEST_RETRIES = 3;
|
|
9
|
+
const DEFAULT_REQUEST_RETRY_BASE_MS = 250;
|
|
10
|
+
const DEFAULT_REQUEST_RETRY_MAX_MS = 4000;
|
|
11
|
+
const DEFAULT_MODEL_NORMALIZATION_CONCURRENCY = 8;
|
|
7
12
|
const COLLECTION_SHAPE_ERROR = "Invalid collection response. Expected array or { items, total }.";
|
|
8
13
|
function normalizeUploadsPath(uploadsPath) {
|
|
9
14
|
const raw = uploadsPath?.trim() || "/uploads";
|
|
@@ -247,6 +252,146 @@ function ensureCollectionEnvelope(data, path) {
|
|
|
247
252
|
}
|
|
248
253
|
throw new Error(`${COLLECTION_SHAPE_ERROR} Path: '${path}'.`);
|
|
249
254
|
}
|
|
255
|
+
function clampInteger(value, fallback, minimum) {
|
|
256
|
+
if (!Number.isFinite(value))
|
|
257
|
+
return fallback;
|
|
258
|
+
const normalized = Math.floor(value);
|
|
259
|
+
return normalized >= minimum ? normalized : fallback;
|
|
260
|
+
}
|
|
261
|
+
function normalizeRequestOptions(options) {
|
|
262
|
+
const concurrency = clampInteger(options?.concurrency, DEFAULT_REQUEST_CONCURRENCY, 1);
|
|
263
|
+
const retries = clampInteger(options?.retries, DEFAULT_REQUEST_RETRIES, 0);
|
|
264
|
+
const baseDelayMs = clampInteger(options?.retryBaseMs, DEFAULT_REQUEST_RETRY_BASE_MS, 1);
|
|
265
|
+
const maxDelayCandidate = clampInteger(options?.retryMaxMs, DEFAULT_REQUEST_RETRY_MAX_MS, 1);
|
|
266
|
+
const maxDelayMs = Math.max(maxDelayCandidate, baseDelayMs);
|
|
267
|
+
const modelNormalizationConcurrency = Math.max(concurrency, DEFAULT_MODEL_NORMALIZATION_CONCURRENCY);
|
|
268
|
+
return {
|
|
269
|
+
concurrency,
|
|
270
|
+
retry: {
|
|
271
|
+
retries,
|
|
272
|
+
baseDelayMs,
|
|
273
|
+
maxDelayMs,
|
|
274
|
+
},
|
|
275
|
+
modelNormalizationConcurrency,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function createAbortError() {
|
|
279
|
+
const error = new Error("The operation was aborted.");
|
|
280
|
+
error.name = "AbortError";
|
|
281
|
+
return error;
|
|
282
|
+
}
|
|
283
|
+
function isAbortError(error) {
|
|
284
|
+
return !!error && typeof error === "object" && error.name === "AbortError";
|
|
285
|
+
}
|
|
286
|
+
function waitForDelay(ms, signal) {
|
|
287
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
288
|
+
if (signal?.aborted)
|
|
289
|
+
return Promise.reject(createAbortError());
|
|
290
|
+
return Promise.resolve();
|
|
291
|
+
}
|
|
292
|
+
return new Promise((resolve, reject) => {
|
|
293
|
+
if (signal?.aborted) {
|
|
294
|
+
reject(createAbortError());
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const timer = setTimeout(() => {
|
|
298
|
+
signal?.removeEventListener("abort", onAbort);
|
|
299
|
+
resolve();
|
|
300
|
+
}, ms);
|
|
301
|
+
function onAbort() {
|
|
302
|
+
clearTimeout(timer);
|
|
303
|
+
signal?.removeEventListener("abort", onAbort);
|
|
304
|
+
reject(createAbortError());
|
|
305
|
+
}
|
|
306
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function parseRetryAfterMs(retryAfterValue) {
|
|
310
|
+
if (!retryAfterValue)
|
|
311
|
+
return null;
|
|
312
|
+
const value = retryAfterValue.trim();
|
|
313
|
+
if (!value)
|
|
314
|
+
return null;
|
|
315
|
+
const seconds = Number(value);
|
|
316
|
+
if (Number.isFinite(seconds)) {
|
|
317
|
+
return Math.max(0, Math.round(seconds * 1000));
|
|
318
|
+
}
|
|
319
|
+
const at = Date.parse(value);
|
|
320
|
+
if (Number.isNaN(at))
|
|
321
|
+
return null;
|
|
322
|
+
return Math.max(0, at - Date.now());
|
|
323
|
+
}
|
|
324
|
+
function shouldRetryStatus(status) {
|
|
325
|
+
return status === 429 || (status >= 500 && status <= 599);
|
|
326
|
+
}
|
|
327
|
+
function createRequestScheduler(maxConcurrency) {
|
|
328
|
+
let active = 0;
|
|
329
|
+
const queue = [];
|
|
330
|
+
const pump = () => {
|
|
331
|
+
while (active < maxConcurrency && queue.length > 0) {
|
|
332
|
+
const next = queue.shift();
|
|
333
|
+
if (!next)
|
|
334
|
+
continue;
|
|
335
|
+
next();
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
return (task, signal) => new Promise((resolve, reject) => {
|
|
339
|
+
if (signal?.aborted) {
|
|
340
|
+
reject(createAbortError());
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
let queued = true;
|
|
344
|
+
let settled = false;
|
|
345
|
+
const run = () => {
|
|
346
|
+
if (settled)
|
|
347
|
+
return;
|
|
348
|
+
queued = false;
|
|
349
|
+
active += 1;
|
|
350
|
+
Promise.resolve()
|
|
351
|
+
.then(task)
|
|
352
|
+
.then(resolve, reject)
|
|
353
|
+
.finally(() => {
|
|
354
|
+
settled = true;
|
|
355
|
+
active = Math.max(0, active - 1);
|
|
356
|
+
signal?.removeEventListener("abort", onAbort);
|
|
357
|
+
pump();
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
const onAbort = () => {
|
|
361
|
+
if (!queued || settled)
|
|
362
|
+
return;
|
|
363
|
+
settled = true;
|
|
364
|
+
const index = queue.indexOf(run);
|
|
365
|
+
if (index >= 0) {
|
|
366
|
+
queue.splice(index, 1);
|
|
367
|
+
}
|
|
368
|
+
reject(createAbortError());
|
|
369
|
+
};
|
|
370
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
371
|
+
queue.push(run);
|
|
372
|
+
pump();
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
376
|
+
if (items.length === 0)
|
|
377
|
+
return [];
|
|
378
|
+
const limit = Math.max(1, Math.floor(concurrency));
|
|
379
|
+
if (limit >= items.length) {
|
|
380
|
+
return Promise.all(items.map((item, index) => mapper(item, index)));
|
|
381
|
+
}
|
|
382
|
+
const results = new Array(items.length);
|
|
383
|
+
let nextIndex = 0;
|
|
384
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
385
|
+
while (true) {
|
|
386
|
+
const current = nextIndex++;
|
|
387
|
+
if (current >= items.length)
|
|
388
|
+
break;
|
|
389
|
+
results[current] = await mapper(items[current], current);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
await Promise.all(workers);
|
|
393
|
+
return results;
|
|
394
|
+
}
|
|
250
395
|
function extractId(value) {
|
|
251
396
|
if (typeof value === "string" && value.length > 0)
|
|
252
397
|
return value;
|
|
@@ -278,15 +423,19 @@ function extractModelRefId(raw, modelName, options) {
|
|
|
278
423
|
return null;
|
|
279
424
|
const object = raw;
|
|
280
425
|
const prop = options?.propertyName ?? "";
|
|
281
|
-
const
|
|
282
|
-
camelCaseModelIdKey(modelName),
|
|
283
|
-
snakeCaseModelIdKey(modelName),
|
|
426
|
+
const propertyCandidates = [
|
|
284
427
|
prop ? `${prop}Id` : "",
|
|
285
428
|
prop
|
|
286
429
|
? `${prop.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase()}_id`
|
|
287
430
|
: "",
|
|
288
|
-
"value",
|
|
289
431
|
].filter(Boolean);
|
|
432
|
+
const modelCandidates = [
|
|
433
|
+
camelCaseModelIdKey(modelName),
|
|
434
|
+
snakeCaseModelIdKey(modelName),
|
|
435
|
+
];
|
|
436
|
+
// Prefer property-specific FKs (e.g., logoId) over model-level defaults
|
|
437
|
+
// (e.g., imageId) when multiple refs share the same model in one object row.
|
|
438
|
+
const candidates = Array.from(new Set([...propertyCandidates, ...modelCandidates, "value"]));
|
|
290
439
|
for (const key of candidates) {
|
|
291
440
|
const found = extractId(object[key]);
|
|
292
441
|
if (found)
|
|
@@ -304,6 +453,34 @@ function extractModelRefId(raw, modelName, options) {
|
|
|
304
453
|
}
|
|
305
454
|
return null;
|
|
306
455
|
}
|
|
456
|
+
function buildModelRefCacheKey(modelName, id, context, isCollectionItem) {
|
|
457
|
+
const locale = context.options.locale ?? "";
|
|
458
|
+
const defaultLocale = context.options.defaultLocale ?? "";
|
|
459
|
+
const includeIdMode = context.options.includeIdMode;
|
|
460
|
+
const refsMode = context.options.resolveModelRefs ? "resolve" : "ids";
|
|
461
|
+
const collectionFlag = isCollectionItem ? "collection" : "single";
|
|
462
|
+
return [
|
|
463
|
+
modelName,
|
|
464
|
+
id,
|
|
465
|
+
locale,
|
|
466
|
+
defaultLocale,
|
|
467
|
+
includeIdMode,
|
|
468
|
+
refsMode,
|
|
469
|
+
collectionFlag,
|
|
470
|
+
].join("|");
|
|
471
|
+
}
|
|
472
|
+
function extractInlineModelObject(raw, modelDescriptor) {
|
|
473
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
474
|
+
return null;
|
|
475
|
+
if (!isObjectDescriptor(modelDescriptor))
|
|
476
|
+
return null;
|
|
477
|
+
const source = raw;
|
|
478
|
+
const propertyKeys = Object.keys(modelDescriptor.properties ?? {});
|
|
479
|
+
const hasModelField = propertyKeys.some((key) => key in source);
|
|
480
|
+
if (!hasModelField)
|
|
481
|
+
return null;
|
|
482
|
+
return source;
|
|
483
|
+
}
|
|
307
484
|
function formatValidationError(name, error) {
|
|
308
485
|
const payload = error && typeof error === "object" && "format" in error
|
|
309
486
|
? error.format()
|
|
@@ -352,12 +529,12 @@ function unknownKeyError(key, roots, models) {
|
|
|
352
529
|
: "";
|
|
353
530
|
throw new Error(`Unknown schema key '${key}'. ${rootsText}${modelsText}.${suggestionText}`);
|
|
354
531
|
}
|
|
355
|
-
async function normalizeModelRef(modelName, id, context, trail, isCollectionItem) {
|
|
532
|
+
async function normalizeModelRef(modelName, id, context, trail, isCollectionItem, inlineValue) {
|
|
356
533
|
if (!id)
|
|
357
534
|
return null;
|
|
358
535
|
if (!context.options.resolveModelRefs)
|
|
359
536
|
return id;
|
|
360
|
-
const nodeKey =
|
|
537
|
+
const nodeKey = buildModelRefCacheKey(modelName, id, context, isCollectionItem);
|
|
361
538
|
if (trail.has(nodeKey)) {
|
|
362
539
|
return shouldIncludeObjectId(context.options.includeIdMode, isCollectionItem)
|
|
363
540
|
? { id }
|
|
@@ -367,25 +544,37 @@ async function normalizeModelRef(modelName, id, context, trail, isCollectionItem
|
|
|
367
544
|
if (!modelDescriptor) {
|
|
368
545
|
return id;
|
|
369
546
|
}
|
|
547
|
+
const inlineModel = extractInlineModelObject(inlineValue, modelDescriptor);
|
|
370
548
|
const cached = context.modelCache.get(nodeKey);
|
|
371
549
|
if (cached) {
|
|
372
550
|
return cached;
|
|
373
551
|
}
|
|
552
|
+
const sharedInflight = context.sharedModelInflightCache.get(nodeKey);
|
|
553
|
+
if (sharedInflight) {
|
|
554
|
+
context.modelCache.set(nodeKey, sharedInflight);
|
|
555
|
+
return sharedInflight;
|
|
556
|
+
}
|
|
374
557
|
const nextTrail = new Set(trail);
|
|
375
558
|
nextTrail.add(nodeKey);
|
|
376
559
|
const promise = (async () => {
|
|
377
|
-
const rawModel =
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
560
|
+
const rawModel = inlineModel ??
|
|
561
|
+
(await context.requestJson(`models/${modelName}/${id}`, {
|
|
562
|
+
query: {
|
|
563
|
+
raw: 1,
|
|
564
|
+
...(context.options.locale ? { locale: context.options.locale } : {}),
|
|
565
|
+
},
|
|
566
|
+
}));
|
|
383
567
|
return normalizeField(modelDescriptor, `models/${modelName}/${encodeURIComponent(id)}`, rawModel, context, nextTrail, isCollectionItem);
|
|
384
|
-
})()
|
|
568
|
+
})();
|
|
569
|
+
context.modelCache.set(nodeKey, promise);
|
|
570
|
+
context.sharedModelInflightCache.set(nodeKey, promise);
|
|
571
|
+
promise
|
|
572
|
+
.then(() => undefined, () => {
|
|
385
573
|
context.modelCache.delete(nodeKey);
|
|
386
|
-
|
|
574
|
+
})
|
|
575
|
+
.finally(() => {
|
|
576
|
+
context.sharedModelInflightCache.delete(nodeKey);
|
|
387
577
|
});
|
|
388
|
-
context.modelCache.set(nodeKey, promise);
|
|
389
578
|
return promise;
|
|
390
579
|
}
|
|
391
580
|
function missingModelRefValue(descriptor) {
|
|
@@ -408,10 +597,19 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
408
597
|
continue;
|
|
409
598
|
}
|
|
410
599
|
if (isModelRefDescriptor(propertyDescriptor)) {
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
600
|
+
const inlineValue = source[propertyName];
|
|
601
|
+
const inlineModelDescriptor = context.modelDescriptors.get(propertyDescriptor.model);
|
|
602
|
+
const inlineModel = inlineModelDescriptor
|
|
603
|
+
? extractInlineModelObject(inlineValue, inlineModelDescriptor)
|
|
604
|
+
: null;
|
|
605
|
+
const refId = extractModelRefId(inlineValue, propertyDescriptor.model, {
|
|
606
|
+
allowObjectIdFallback: true,
|
|
607
|
+
}) ??
|
|
608
|
+
(inlineModel ? extractId(inlineModel) : null) ??
|
|
609
|
+
extractModelRefId(source, propertyDescriptor.model, {
|
|
610
|
+
propertyName,
|
|
611
|
+
allowObjectIdFallback: false,
|
|
612
|
+
});
|
|
415
613
|
if (!refId) {
|
|
416
614
|
const missing = missingModelRefValue(propertyDescriptor);
|
|
417
615
|
if (missing !== undefined) {
|
|
@@ -419,7 +617,7 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
419
617
|
}
|
|
420
618
|
continue;
|
|
421
619
|
}
|
|
422
|
-
output[propertyName] = await normalizeModelRef(propertyDescriptor.model, refId, context, trail, false);
|
|
620
|
+
output[propertyName] = await normalizeModelRef(propertyDescriptor.model, refId, context, trail, false, inlineValue);
|
|
423
621
|
continue;
|
|
424
622
|
}
|
|
425
623
|
if (isArrayDescriptor(propertyDescriptor)) {
|
|
@@ -482,22 +680,25 @@ async function normalizeArrayField(descriptor, path, raw, context, trail) {
|
|
|
482
680
|
});
|
|
483
681
|
}
|
|
484
682
|
if (isModelRefDescriptor(itemDescriptor)) {
|
|
485
|
-
const
|
|
683
|
+
const inlineModelDescriptor = context.modelDescriptors.get(itemDescriptor.model);
|
|
684
|
+
return mapWithConcurrency(envelope.items, context.options.modelNormalizationConcurrency, async (row) => {
|
|
685
|
+
const inlineModel = inlineModelDescriptor
|
|
686
|
+
? extractInlineModelObject(row, inlineModelDescriptor)
|
|
687
|
+
: null;
|
|
486
688
|
const refId = extractModelRefId(row, itemDescriptor.model, {
|
|
487
689
|
allowObjectIdFallback: false,
|
|
488
|
-
});
|
|
489
|
-
return normalizeModelRef(itemDescriptor.model, refId, context, trail, true);
|
|
490
|
-
})
|
|
491
|
-
return resolved;
|
|
690
|
+
}) ?? (inlineModel ? extractId(inlineModel) : null);
|
|
691
|
+
return normalizeModelRef(itemDescriptor.model, refId, context, trail, true, row);
|
|
692
|
+
});
|
|
492
693
|
}
|
|
493
694
|
if (isObjectDescriptor(itemDescriptor)) {
|
|
494
|
-
return
|
|
695
|
+
return mapWithConcurrency(envelope.items, context.options.modelNormalizationConcurrency, async (row) => {
|
|
495
696
|
const rowId = extractId(row);
|
|
496
697
|
const rowPath = rowId
|
|
497
698
|
? `${path}/${encodeURIComponent(rowId)}`
|
|
498
699
|
: path;
|
|
499
700
|
return normalizeObjectField(itemDescriptor, rowPath, row, context, trail, true);
|
|
500
|
-
})
|
|
701
|
+
});
|
|
501
702
|
}
|
|
502
703
|
return envelope.items;
|
|
503
704
|
}
|
|
@@ -513,11 +714,17 @@ async function normalizeField(descriptor, path, raw, context, trail, isCollectio
|
|
|
513
714
|
});
|
|
514
715
|
}
|
|
515
716
|
if (isModelRefDescriptor(descriptor)) {
|
|
516
|
-
const
|
|
717
|
+
const inlineModelDescriptor = context.modelDescriptors.get(descriptor.model);
|
|
718
|
+
const inlineModel = inlineModelDescriptor
|
|
719
|
+
? extractInlineModelObject(raw, inlineModelDescriptor)
|
|
720
|
+
: null;
|
|
721
|
+
const refId = extractModelRefId(raw, descriptor.model, {
|
|
722
|
+
allowObjectIdFallback: true,
|
|
723
|
+
}) ?? (inlineModel ? extractId(inlineModel) : null);
|
|
517
724
|
if (!refId) {
|
|
518
725
|
return missingModelRefValue(descriptor);
|
|
519
726
|
}
|
|
520
|
-
return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem);
|
|
727
|
+
return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem, raw);
|
|
521
728
|
}
|
|
522
729
|
if (isArrayDescriptor(descriptor)) {
|
|
523
730
|
const normalized = await normalizeArrayField(descriptor, path, raw, context, trail);
|
|
@@ -572,20 +779,71 @@ function createResourceRegistry(descriptor) {
|
|
|
572
779
|
modelsCaseInsensitive,
|
|
573
780
|
};
|
|
574
781
|
}
|
|
575
|
-
async function requestJson(baseUrl, apiKey, path, options) {
|
|
782
|
+
async function requestJson(baseUrl, apiKey, path, options, runtime) {
|
|
576
783
|
const params = buildSearchParams(options?.query);
|
|
577
784
|
const url = `${baseUrl}/${path}${params.toString() ? `?${params.toString()}` : ""}`;
|
|
578
|
-
const
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
785
|
+
const execute = async () => {
|
|
786
|
+
const retry = runtime?.retry ?? {
|
|
787
|
+
retries: DEFAULT_REQUEST_RETRIES,
|
|
788
|
+
baseDelayMs: DEFAULT_REQUEST_RETRY_BASE_MS,
|
|
789
|
+
maxDelayMs: DEFAULT_REQUEST_RETRY_MAX_MS,
|
|
790
|
+
};
|
|
791
|
+
let attempt = 0;
|
|
792
|
+
while (true) {
|
|
793
|
+
let response;
|
|
794
|
+
try {
|
|
795
|
+
response = runtime?.scheduler
|
|
796
|
+
? await runtime.scheduler(() => fetch(url, {
|
|
797
|
+
method: "GET",
|
|
798
|
+
signal: options?.signal,
|
|
799
|
+
headers: {
|
|
800
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
801
|
+
},
|
|
802
|
+
}), options?.signal)
|
|
803
|
+
: await fetch(url, {
|
|
804
|
+
method: "GET",
|
|
805
|
+
signal: options?.signal,
|
|
806
|
+
headers: {
|
|
807
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
808
|
+
},
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
catch (error) {
|
|
812
|
+
if (isAbortError(error))
|
|
813
|
+
throw error;
|
|
814
|
+
if (attempt >= retry.retries)
|
|
815
|
+
throw error;
|
|
816
|
+
const delayMs = Math.min(retry.baseDelayMs * 2 ** attempt, retry.maxDelayMs);
|
|
817
|
+
attempt += 1;
|
|
818
|
+
await waitForDelay(delayMs, options?.signal);
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (response.ok) {
|
|
822
|
+
return response.json();
|
|
823
|
+
}
|
|
824
|
+
const canRetry = shouldRetryStatus(response.status) && attempt < retry.retries;
|
|
825
|
+
if (!canRetry) {
|
|
826
|
+
throw new Error(`Request failed for '${path}' with status ${response.status}: ${response.statusText}`);
|
|
827
|
+
}
|
|
828
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
829
|
+
const fallbackDelay = Math.min(retry.baseDelayMs * 2 ** attempt, retry.maxDelayMs);
|
|
830
|
+
const delayMs = retryAfterMs ?? fallbackDelay;
|
|
831
|
+
attempt += 1;
|
|
832
|
+
await waitForDelay(delayMs, options?.signal);
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
const useInflightDedup = !options?.signal && !!runtime;
|
|
836
|
+
if (!useInflightDedup || !runtime) {
|
|
837
|
+
return execute();
|
|
587
838
|
}
|
|
588
|
-
|
|
839
|
+
const cached = runtime.inflightRequestCache.get(url);
|
|
840
|
+
if (cached)
|
|
841
|
+
return cached;
|
|
842
|
+
const promise = execute().finally(() => {
|
|
843
|
+
runtime.inflightRequestCache.delete(url);
|
|
844
|
+
});
|
|
845
|
+
runtime.inflightRequestCache.set(url, promise);
|
|
846
|
+
return promise;
|
|
589
847
|
}
|
|
590
848
|
function validateResult(resource, result, descriptor, includeIdMode, zodSchemas, modelZodSchemas) {
|
|
591
849
|
if (includeIdMode === "all")
|
|
@@ -620,7 +878,7 @@ function validateResult(resource, result, descriptor, includeIdMode, zodSchemas,
|
|
|
620
878
|
throw new Error(formatValidationError(modelName, parsed.error));
|
|
621
879
|
}
|
|
622
880
|
}
|
|
623
|
-
async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options, byId) {
|
|
881
|
+
async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options, byId) {
|
|
624
882
|
const responseMode = options?.response ?? "normalized";
|
|
625
883
|
const includeIdMode = resolveIncludeIdMode(options?.includeId);
|
|
626
884
|
const resolveModelRefs = options?.resolveModelRefs !== false;
|
|
@@ -646,7 +904,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
646
904
|
const rawData = await requestJson(baseUrl, apiKey, resourcePath, {
|
|
647
905
|
query,
|
|
648
906
|
signal: options?.signal,
|
|
649
|
-
});
|
|
907
|
+
}, requestRuntime);
|
|
650
908
|
if (!shouldNormalize) {
|
|
651
909
|
return rawData;
|
|
652
910
|
}
|
|
@@ -658,15 +916,17 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
658
916
|
requestJson: (path, requestOptions) => requestJson(baseUrl, apiKey, path, {
|
|
659
917
|
...requestOptions,
|
|
660
918
|
signal: options?.signal,
|
|
661
|
-
}),
|
|
919
|
+
}, requestRuntime),
|
|
662
920
|
modelDescriptors,
|
|
663
921
|
modelCache: new Map(),
|
|
922
|
+
sharedModelInflightCache,
|
|
664
923
|
options: {
|
|
665
924
|
includeIdMode,
|
|
666
925
|
resolveModelRefs,
|
|
667
926
|
locale,
|
|
668
927
|
defaultLocale,
|
|
669
928
|
assetUrlBuilder,
|
|
929
|
+
modelNormalizationConcurrency,
|
|
670
930
|
},
|
|
671
931
|
};
|
|
672
932
|
if (resource.isCollection && !byId) {
|
|
@@ -702,9 +962,16 @@ function createCmsClient(descriptor, config) {
|
|
|
702
962
|
const rootKeys = Array.from(registry.roots.keys());
|
|
703
963
|
const modelKeys = Array.from(registry.models.keys());
|
|
704
964
|
const { zodSchemas, modelZodSchemas } = (0, shared_1.buildZodSchemasFromDescriptor)(descriptor);
|
|
965
|
+
const requestOptions = normalizeRequestOptions(config.requests);
|
|
966
|
+
const requestRuntime = {
|
|
967
|
+
scheduler: createRequestScheduler(requestOptions.concurrency),
|
|
968
|
+
inflightRequestCache: new Map(),
|
|
969
|
+
retry: requestOptions.retry,
|
|
970
|
+
};
|
|
971
|
+
const sharedModelInflightCache = new Map();
|
|
705
972
|
const buildModelAccessor = (entry) => {
|
|
706
|
-
const accessor = (async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options));
|
|
707
|
-
accessor.byId = async (id, options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options, id);
|
|
973
|
+
const accessor = (async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, requestOptions.modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options));
|
|
974
|
+
accessor.byId = async (id, options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, requestOptions.modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options, id);
|
|
708
975
|
return accessor;
|
|
709
976
|
};
|
|
710
977
|
const modelsProxy = new Proxy({}, {
|
|
@@ -734,7 +1001,7 @@ function createCmsClient(descriptor, config) {
|
|
|
734
1001
|
if (!entry) {
|
|
735
1002
|
unknownKeyError(property, rootKeys, []);
|
|
736
1003
|
}
|
|
737
|
-
return async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options);
|
|
1004
|
+
return async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, requestOptions.modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options);
|
|
738
1005
|
},
|
|
739
1006
|
});
|
|
740
1007
|
return rootProxy;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { schemaDescriptor } from "@cms0/cms0/schema-descriptors";
|
|
2
2
|
import { buildZodSchemasFromDescriptor, } from "@cms0/shared";
|
|
3
|
+
const DEFAULT_REQUEST_CONCURRENCY = 6;
|
|
4
|
+
const DEFAULT_REQUEST_RETRIES = 3;
|
|
5
|
+
const DEFAULT_REQUEST_RETRY_BASE_MS = 250;
|
|
6
|
+
const DEFAULT_REQUEST_RETRY_MAX_MS = 4000;
|
|
7
|
+
const DEFAULT_MODEL_NORMALIZATION_CONCURRENCY = 8;
|
|
3
8
|
const COLLECTION_SHAPE_ERROR = "Invalid collection response. Expected array or { items, total }.";
|
|
4
9
|
function normalizeUploadsPath(uploadsPath) {
|
|
5
10
|
const raw = uploadsPath?.trim() || "/uploads";
|
|
@@ -243,6 +248,146 @@ function ensureCollectionEnvelope(data, path) {
|
|
|
243
248
|
}
|
|
244
249
|
throw new Error(`${COLLECTION_SHAPE_ERROR} Path: '${path}'.`);
|
|
245
250
|
}
|
|
251
|
+
function clampInteger(value, fallback, minimum) {
|
|
252
|
+
if (!Number.isFinite(value))
|
|
253
|
+
return fallback;
|
|
254
|
+
const normalized = Math.floor(value);
|
|
255
|
+
return normalized >= minimum ? normalized : fallback;
|
|
256
|
+
}
|
|
257
|
+
function normalizeRequestOptions(options) {
|
|
258
|
+
const concurrency = clampInteger(options?.concurrency, DEFAULT_REQUEST_CONCURRENCY, 1);
|
|
259
|
+
const retries = clampInteger(options?.retries, DEFAULT_REQUEST_RETRIES, 0);
|
|
260
|
+
const baseDelayMs = clampInteger(options?.retryBaseMs, DEFAULT_REQUEST_RETRY_BASE_MS, 1);
|
|
261
|
+
const maxDelayCandidate = clampInteger(options?.retryMaxMs, DEFAULT_REQUEST_RETRY_MAX_MS, 1);
|
|
262
|
+
const maxDelayMs = Math.max(maxDelayCandidate, baseDelayMs);
|
|
263
|
+
const modelNormalizationConcurrency = Math.max(concurrency, DEFAULT_MODEL_NORMALIZATION_CONCURRENCY);
|
|
264
|
+
return {
|
|
265
|
+
concurrency,
|
|
266
|
+
retry: {
|
|
267
|
+
retries,
|
|
268
|
+
baseDelayMs,
|
|
269
|
+
maxDelayMs,
|
|
270
|
+
},
|
|
271
|
+
modelNormalizationConcurrency,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function createAbortError() {
|
|
275
|
+
const error = new Error("The operation was aborted.");
|
|
276
|
+
error.name = "AbortError";
|
|
277
|
+
return error;
|
|
278
|
+
}
|
|
279
|
+
function isAbortError(error) {
|
|
280
|
+
return !!error && typeof error === "object" && error.name === "AbortError";
|
|
281
|
+
}
|
|
282
|
+
function waitForDelay(ms, signal) {
|
|
283
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
284
|
+
if (signal?.aborted)
|
|
285
|
+
return Promise.reject(createAbortError());
|
|
286
|
+
return Promise.resolve();
|
|
287
|
+
}
|
|
288
|
+
return new Promise((resolve, reject) => {
|
|
289
|
+
if (signal?.aborted) {
|
|
290
|
+
reject(createAbortError());
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const timer = setTimeout(() => {
|
|
294
|
+
signal?.removeEventListener("abort", onAbort);
|
|
295
|
+
resolve();
|
|
296
|
+
}, ms);
|
|
297
|
+
function onAbort() {
|
|
298
|
+
clearTimeout(timer);
|
|
299
|
+
signal?.removeEventListener("abort", onAbort);
|
|
300
|
+
reject(createAbortError());
|
|
301
|
+
}
|
|
302
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
function parseRetryAfterMs(retryAfterValue) {
|
|
306
|
+
if (!retryAfterValue)
|
|
307
|
+
return null;
|
|
308
|
+
const value = retryAfterValue.trim();
|
|
309
|
+
if (!value)
|
|
310
|
+
return null;
|
|
311
|
+
const seconds = Number(value);
|
|
312
|
+
if (Number.isFinite(seconds)) {
|
|
313
|
+
return Math.max(0, Math.round(seconds * 1000));
|
|
314
|
+
}
|
|
315
|
+
const at = Date.parse(value);
|
|
316
|
+
if (Number.isNaN(at))
|
|
317
|
+
return null;
|
|
318
|
+
return Math.max(0, at - Date.now());
|
|
319
|
+
}
|
|
320
|
+
function shouldRetryStatus(status) {
|
|
321
|
+
return status === 429 || (status >= 500 && status <= 599);
|
|
322
|
+
}
|
|
323
|
+
function createRequestScheduler(maxConcurrency) {
|
|
324
|
+
let active = 0;
|
|
325
|
+
const queue = [];
|
|
326
|
+
const pump = () => {
|
|
327
|
+
while (active < maxConcurrency && queue.length > 0) {
|
|
328
|
+
const next = queue.shift();
|
|
329
|
+
if (!next)
|
|
330
|
+
continue;
|
|
331
|
+
next();
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
return (task, signal) => new Promise((resolve, reject) => {
|
|
335
|
+
if (signal?.aborted) {
|
|
336
|
+
reject(createAbortError());
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
let queued = true;
|
|
340
|
+
let settled = false;
|
|
341
|
+
const run = () => {
|
|
342
|
+
if (settled)
|
|
343
|
+
return;
|
|
344
|
+
queued = false;
|
|
345
|
+
active += 1;
|
|
346
|
+
Promise.resolve()
|
|
347
|
+
.then(task)
|
|
348
|
+
.then(resolve, reject)
|
|
349
|
+
.finally(() => {
|
|
350
|
+
settled = true;
|
|
351
|
+
active = Math.max(0, active - 1);
|
|
352
|
+
signal?.removeEventListener("abort", onAbort);
|
|
353
|
+
pump();
|
|
354
|
+
});
|
|
355
|
+
};
|
|
356
|
+
const onAbort = () => {
|
|
357
|
+
if (!queued || settled)
|
|
358
|
+
return;
|
|
359
|
+
settled = true;
|
|
360
|
+
const index = queue.indexOf(run);
|
|
361
|
+
if (index >= 0) {
|
|
362
|
+
queue.splice(index, 1);
|
|
363
|
+
}
|
|
364
|
+
reject(createAbortError());
|
|
365
|
+
};
|
|
366
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
367
|
+
queue.push(run);
|
|
368
|
+
pump();
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
372
|
+
if (items.length === 0)
|
|
373
|
+
return [];
|
|
374
|
+
const limit = Math.max(1, Math.floor(concurrency));
|
|
375
|
+
if (limit >= items.length) {
|
|
376
|
+
return Promise.all(items.map((item, index) => mapper(item, index)));
|
|
377
|
+
}
|
|
378
|
+
const results = new Array(items.length);
|
|
379
|
+
let nextIndex = 0;
|
|
380
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
381
|
+
while (true) {
|
|
382
|
+
const current = nextIndex++;
|
|
383
|
+
if (current >= items.length)
|
|
384
|
+
break;
|
|
385
|
+
results[current] = await mapper(items[current], current);
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
await Promise.all(workers);
|
|
389
|
+
return results;
|
|
390
|
+
}
|
|
246
391
|
function extractId(value) {
|
|
247
392
|
if (typeof value === "string" && value.length > 0)
|
|
248
393
|
return value;
|
|
@@ -274,15 +419,19 @@ function extractModelRefId(raw, modelName, options) {
|
|
|
274
419
|
return null;
|
|
275
420
|
const object = raw;
|
|
276
421
|
const prop = options?.propertyName ?? "";
|
|
277
|
-
const
|
|
278
|
-
camelCaseModelIdKey(modelName),
|
|
279
|
-
snakeCaseModelIdKey(modelName),
|
|
422
|
+
const propertyCandidates = [
|
|
280
423
|
prop ? `${prop}Id` : "",
|
|
281
424
|
prop
|
|
282
425
|
? `${prop.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase()}_id`
|
|
283
426
|
: "",
|
|
284
|
-
"value",
|
|
285
427
|
].filter(Boolean);
|
|
428
|
+
const modelCandidates = [
|
|
429
|
+
camelCaseModelIdKey(modelName),
|
|
430
|
+
snakeCaseModelIdKey(modelName),
|
|
431
|
+
];
|
|
432
|
+
// Prefer property-specific FKs (e.g., logoId) over model-level defaults
|
|
433
|
+
// (e.g., imageId) when multiple refs share the same model in one object row.
|
|
434
|
+
const candidates = Array.from(new Set([...propertyCandidates, ...modelCandidates, "value"]));
|
|
286
435
|
for (const key of candidates) {
|
|
287
436
|
const found = extractId(object[key]);
|
|
288
437
|
if (found)
|
|
@@ -300,6 +449,34 @@ function extractModelRefId(raw, modelName, options) {
|
|
|
300
449
|
}
|
|
301
450
|
return null;
|
|
302
451
|
}
|
|
452
|
+
function buildModelRefCacheKey(modelName, id, context, isCollectionItem) {
|
|
453
|
+
const locale = context.options.locale ?? "";
|
|
454
|
+
const defaultLocale = context.options.defaultLocale ?? "";
|
|
455
|
+
const includeIdMode = context.options.includeIdMode;
|
|
456
|
+
const refsMode = context.options.resolveModelRefs ? "resolve" : "ids";
|
|
457
|
+
const collectionFlag = isCollectionItem ? "collection" : "single";
|
|
458
|
+
return [
|
|
459
|
+
modelName,
|
|
460
|
+
id,
|
|
461
|
+
locale,
|
|
462
|
+
defaultLocale,
|
|
463
|
+
includeIdMode,
|
|
464
|
+
refsMode,
|
|
465
|
+
collectionFlag,
|
|
466
|
+
].join("|");
|
|
467
|
+
}
|
|
468
|
+
function extractInlineModelObject(raw, modelDescriptor) {
|
|
469
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
470
|
+
return null;
|
|
471
|
+
if (!isObjectDescriptor(modelDescriptor))
|
|
472
|
+
return null;
|
|
473
|
+
const source = raw;
|
|
474
|
+
const propertyKeys = Object.keys(modelDescriptor.properties ?? {});
|
|
475
|
+
const hasModelField = propertyKeys.some((key) => key in source);
|
|
476
|
+
if (!hasModelField)
|
|
477
|
+
return null;
|
|
478
|
+
return source;
|
|
479
|
+
}
|
|
303
480
|
function formatValidationError(name, error) {
|
|
304
481
|
const payload = error && typeof error === "object" && "format" in error
|
|
305
482
|
? error.format()
|
|
@@ -348,12 +525,12 @@ function unknownKeyError(key, roots, models) {
|
|
|
348
525
|
: "";
|
|
349
526
|
throw new Error(`Unknown schema key '${key}'. ${rootsText}${modelsText}.${suggestionText}`);
|
|
350
527
|
}
|
|
351
|
-
async function normalizeModelRef(modelName, id, context, trail, isCollectionItem) {
|
|
528
|
+
async function normalizeModelRef(modelName, id, context, trail, isCollectionItem, inlineValue) {
|
|
352
529
|
if (!id)
|
|
353
530
|
return null;
|
|
354
531
|
if (!context.options.resolveModelRefs)
|
|
355
532
|
return id;
|
|
356
|
-
const nodeKey =
|
|
533
|
+
const nodeKey = buildModelRefCacheKey(modelName, id, context, isCollectionItem);
|
|
357
534
|
if (trail.has(nodeKey)) {
|
|
358
535
|
return shouldIncludeObjectId(context.options.includeIdMode, isCollectionItem)
|
|
359
536
|
? { id }
|
|
@@ -363,25 +540,37 @@ async function normalizeModelRef(modelName, id, context, trail, isCollectionItem
|
|
|
363
540
|
if (!modelDescriptor) {
|
|
364
541
|
return id;
|
|
365
542
|
}
|
|
543
|
+
const inlineModel = extractInlineModelObject(inlineValue, modelDescriptor);
|
|
366
544
|
const cached = context.modelCache.get(nodeKey);
|
|
367
545
|
if (cached) {
|
|
368
546
|
return cached;
|
|
369
547
|
}
|
|
548
|
+
const sharedInflight = context.sharedModelInflightCache.get(nodeKey);
|
|
549
|
+
if (sharedInflight) {
|
|
550
|
+
context.modelCache.set(nodeKey, sharedInflight);
|
|
551
|
+
return sharedInflight;
|
|
552
|
+
}
|
|
370
553
|
const nextTrail = new Set(trail);
|
|
371
554
|
nextTrail.add(nodeKey);
|
|
372
555
|
const promise = (async () => {
|
|
373
|
-
const rawModel =
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
556
|
+
const rawModel = inlineModel ??
|
|
557
|
+
(await context.requestJson(`models/${modelName}/${id}`, {
|
|
558
|
+
query: {
|
|
559
|
+
raw: 1,
|
|
560
|
+
...(context.options.locale ? { locale: context.options.locale } : {}),
|
|
561
|
+
},
|
|
562
|
+
}));
|
|
379
563
|
return normalizeField(modelDescriptor, `models/${modelName}/${encodeURIComponent(id)}`, rawModel, context, nextTrail, isCollectionItem);
|
|
380
|
-
})()
|
|
564
|
+
})();
|
|
565
|
+
context.modelCache.set(nodeKey, promise);
|
|
566
|
+
context.sharedModelInflightCache.set(nodeKey, promise);
|
|
567
|
+
promise
|
|
568
|
+
.then(() => undefined, () => {
|
|
381
569
|
context.modelCache.delete(nodeKey);
|
|
382
|
-
|
|
570
|
+
})
|
|
571
|
+
.finally(() => {
|
|
572
|
+
context.sharedModelInflightCache.delete(nodeKey);
|
|
383
573
|
});
|
|
384
|
-
context.modelCache.set(nodeKey, promise);
|
|
385
574
|
return promise;
|
|
386
575
|
}
|
|
387
576
|
function missingModelRefValue(descriptor) {
|
|
@@ -404,10 +593,19 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
404
593
|
continue;
|
|
405
594
|
}
|
|
406
595
|
if (isModelRefDescriptor(propertyDescriptor)) {
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
596
|
+
const inlineValue = source[propertyName];
|
|
597
|
+
const inlineModelDescriptor = context.modelDescriptors.get(propertyDescriptor.model);
|
|
598
|
+
const inlineModel = inlineModelDescriptor
|
|
599
|
+
? extractInlineModelObject(inlineValue, inlineModelDescriptor)
|
|
600
|
+
: null;
|
|
601
|
+
const refId = extractModelRefId(inlineValue, propertyDescriptor.model, {
|
|
602
|
+
allowObjectIdFallback: true,
|
|
603
|
+
}) ??
|
|
604
|
+
(inlineModel ? extractId(inlineModel) : null) ??
|
|
605
|
+
extractModelRefId(source, propertyDescriptor.model, {
|
|
606
|
+
propertyName,
|
|
607
|
+
allowObjectIdFallback: false,
|
|
608
|
+
});
|
|
411
609
|
if (!refId) {
|
|
412
610
|
const missing = missingModelRefValue(propertyDescriptor);
|
|
413
611
|
if (missing !== undefined) {
|
|
@@ -415,7 +613,7 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
415
613
|
}
|
|
416
614
|
continue;
|
|
417
615
|
}
|
|
418
|
-
output[propertyName] = await normalizeModelRef(propertyDescriptor.model, refId, context, trail, false);
|
|
616
|
+
output[propertyName] = await normalizeModelRef(propertyDescriptor.model, refId, context, trail, false, inlineValue);
|
|
419
617
|
continue;
|
|
420
618
|
}
|
|
421
619
|
if (isArrayDescriptor(propertyDescriptor)) {
|
|
@@ -478,22 +676,25 @@ async function normalizeArrayField(descriptor, path, raw, context, trail) {
|
|
|
478
676
|
});
|
|
479
677
|
}
|
|
480
678
|
if (isModelRefDescriptor(itemDescriptor)) {
|
|
481
|
-
const
|
|
679
|
+
const inlineModelDescriptor = context.modelDescriptors.get(itemDescriptor.model);
|
|
680
|
+
return mapWithConcurrency(envelope.items, context.options.modelNormalizationConcurrency, async (row) => {
|
|
681
|
+
const inlineModel = inlineModelDescriptor
|
|
682
|
+
? extractInlineModelObject(row, inlineModelDescriptor)
|
|
683
|
+
: null;
|
|
482
684
|
const refId = extractModelRefId(row, itemDescriptor.model, {
|
|
483
685
|
allowObjectIdFallback: false,
|
|
484
|
-
});
|
|
485
|
-
return normalizeModelRef(itemDescriptor.model, refId, context, trail, true);
|
|
486
|
-
})
|
|
487
|
-
return resolved;
|
|
686
|
+
}) ?? (inlineModel ? extractId(inlineModel) : null);
|
|
687
|
+
return normalizeModelRef(itemDescriptor.model, refId, context, trail, true, row);
|
|
688
|
+
});
|
|
488
689
|
}
|
|
489
690
|
if (isObjectDescriptor(itemDescriptor)) {
|
|
490
|
-
return
|
|
691
|
+
return mapWithConcurrency(envelope.items, context.options.modelNormalizationConcurrency, async (row) => {
|
|
491
692
|
const rowId = extractId(row);
|
|
492
693
|
const rowPath = rowId
|
|
493
694
|
? `${path}/${encodeURIComponent(rowId)}`
|
|
494
695
|
: path;
|
|
495
696
|
return normalizeObjectField(itemDescriptor, rowPath, row, context, trail, true);
|
|
496
|
-
})
|
|
697
|
+
});
|
|
497
698
|
}
|
|
498
699
|
return envelope.items;
|
|
499
700
|
}
|
|
@@ -509,11 +710,17 @@ async function normalizeField(descriptor, path, raw, context, trail, isCollectio
|
|
|
509
710
|
});
|
|
510
711
|
}
|
|
511
712
|
if (isModelRefDescriptor(descriptor)) {
|
|
512
|
-
const
|
|
713
|
+
const inlineModelDescriptor = context.modelDescriptors.get(descriptor.model);
|
|
714
|
+
const inlineModel = inlineModelDescriptor
|
|
715
|
+
? extractInlineModelObject(raw, inlineModelDescriptor)
|
|
716
|
+
: null;
|
|
717
|
+
const refId = extractModelRefId(raw, descriptor.model, {
|
|
718
|
+
allowObjectIdFallback: true,
|
|
719
|
+
}) ?? (inlineModel ? extractId(inlineModel) : null);
|
|
513
720
|
if (!refId) {
|
|
514
721
|
return missingModelRefValue(descriptor);
|
|
515
722
|
}
|
|
516
|
-
return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem);
|
|
723
|
+
return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem, raw);
|
|
517
724
|
}
|
|
518
725
|
if (isArrayDescriptor(descriptor)) {
|
|
519
726
|
const normalized = await normalizeArrayField(descriptor, path, raw, context, trail);
|
|
@@ -568,20 +775,71 @@ function createResourceRegistry(descriptor) {
|
|
|
568
775
|
modelsCaseInsensitive,
|
|
569
776
|
};
|
|
570
777
|
}
|
|
571
|
-
async function requestJson(baseUrl, apiKey, path, options) {
|
|
778
|
+
async function requestJson(baseUrl, apiKey, path, options, runtime) {
|
|
572
779
|
const params = buildSearchParams(options?.query);
|
|
573
780
|
const url = `${baseUrl}/${path}${params.toString() ? `?${params.toString()}` : ""}`;
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
781
|
+
const execute = async () => {
|
|
782
|
+
const retry = runtime?.retry ?? {
|
|
783
|
+
retries: DEFAULT_REQUEST_RETRIES,
|
|
784
|
+
baseDelayMs: DEFAULT_REQUEST_RETRY_BASE_MS,
|
|
785
|
+
maxDelayMs: DEFAULT_REQUEST_RETRY_MAX_MS,
|
|
786
|
+
};
|
|
787
|
+
let attempt = 0;
|
|
788
|
+
while (true) {
|
|
789
|
+
let response;
|
|
790
|
+
try {
|
|
791
|
+
response = runtime?.scheduler
|
|
792
|
+
? await runtime.scheduler(() => fetch(url, {
|
|
793
|
+
method: "GET",
|
|
794
|
+
signal: options?.signal,
|
|
795
|
+
headers: {
|
|
796
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
797
|
+
},
|
|
798
|
+
}), options?.signal)
|
|
799
|
+
: await fetch(url, {
|
|
800
|
+
method: "GET",
|
|
801
|
+
signal: options?.signal,
|
|
802
|
+
headers: {
|
|
803
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
804
|
+
},
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
catch (error) {
|
|
808
|
+
if (isAbortError(error))
|
|
809
|
+
throw error;
|
|
810
|
+
if (attempt >= retry.retries)
|
|
811
|
+
throw error;
|
|
812
|
+
const delayMs = Math.min(retry.baseDelayMs * 2 ** attempt, retry.maxDelayMs);
|
|
813
|
+
attempt += 1;
|
|
814
|
+
await waitForDelay(delayMs, options?.signal);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (response.ok) {
|
|
818
|
+
return response.json();
|
|
819
|
+
}
|
|
820
|
+
const canRetry = shouldRetryStatus(response.status) && attempt < retry.retries;
|
|
821
|
+
if (!canRetry) {
|
|
822
|
+
throw new Error(`Request failed for '${path}' with status ${response.status}: ${response.statusText}`);
|
|
823
|
+
}
|
|
824
|
+
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
825
|
+
const fallbackDelay = Math.min(retry.baseDelayMs * 2 ** attempt, retry.maxDelayMs);
|
|
826
|
+
const delayMs = retryAfterMs ?? fallbackDelay;
|
|
827
|
+
attempt += 1;
|
|
828
|
+
await waitForDelay(delayMs, options?.signal);
|
|
829
|
+
}
|
|
830
|
+
};
|
|
831
|
+
const useInflightDedup = !options?.signal && !!runtime;
|
|
832
|
+
if (!useInflightDedup || !runtime) {
|
|
833
|
+
return execute();
|
|
583
834
|
}
|
|
584
|
-
|
|
835
|
+
const cached = runtime.inflightRequestCache.get(url);
|
|
836
|
+
if (cached)
|
|
837
|
+
return cached;
|
|
838
|
+
const promise = execute().finally(() => {
|
|
839
|
+
runtime.inflightRequestCache.delete(url);
|
|
840
|
+
});
|
|
841
|
+
runtime.inflightRequestCache.set(url, promise);
|
|
842
|
+
return promise;
|
|
585
843
|
}
|
|
586
844
|
function validateResult(resource, result, descriptor, includeIdMode, zodSchemas, modelZodSchemas) {
|
|
587
845
|
if (includeIdMode === "all")
|
|
@@ -616,7 +874,7 @@ function validateResult(resource, result, descriptor, includeIdMode, zodSchemas,
|
|
|
616
874
|
throw new Error(formatValidationError(modelName, parsed.error));
|
|
617
875
|
}
|
|
618
876
|
}
|
|
619
|
-
async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options, byId) {
|
|
877
|
+
async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options, byId) {
|
|
620
878
|
const responseMode = options?.response ?? "normalized";
|
|
621
879
|
const includeIdMode = resolveIncludeIdMode(options?.includeId);
|
|
622
880
|
const resolveModelRefs = options?.resolveModelRefs !== false;
|
|
@@ -642,7 +900,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
642
900
|
const rawData = await requestJson(baseUrl, apiKey, resourcePath, {
|
|
643
901
|
query,
|
|
644
902
|
signal: options?.signal,
|
|
645
|
-
});
|
|
903
|
+
}, requestRuntime);
|
|
646
904
|
if (!shouldNormalize) {
|
|
647
905
|
return rawData;
|
|
648
906
|
}
|
|
@@ -654,15 +912,17 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
654
912
|
requestJson: (path, requestOptions) => requestJson(baseUrl, apiKey, path, {
|
|
655
913
|
...requestOptions,
|
|
656
914
|
signal: options?.signal,
|
|
657
|
-
}),
|
|
915
|
+
}, requestRuntime),
|
|
658
916
|
modelDescriptors,
|
|
659
917
|
modelCache: new Map(),
|
|
918
|
+
sharedModelInflightCache,
|
|
660
919
|
options: {
|
|
661
920
|
includeIdMode,
|
|
662
921
|
resolveModelRefs,
|
|
663
922
|
locale,
|
|
664
923
|
defaultLocale,
|
|
665
924
|
assetUrlBuilder,
|
|
925
|
+
modelNormalizationConcurrency,
|
|
666
926
|
},
|
|
667
927
|
};
|
|
668
928
|
if (resource.isCollection && !byId) {
|
|
@@ -698,9 +958,16 @@ export function createCmsClient(descriptor, config) {
|
|
|
698
958
|
const rootKeys = Array.from(registry.roots.keys());
|
|
699
959
|
const modelKeys = Array.from(registry.models.keys());
|
|
700
960
|
const { zodSchemas, modelZodSchemas } = buildZodSchemasFromDescriptor(descriptor);
|
|
961
|
+
const requestOptions = normalizeRequestOptions(config.requests);
|
|
962
|
+
const requestRuntime = {
|
|
963
|
+
scheduler: createRequestScheduler(requestOptions.concurrency),
|
|
964
|
+
inflightRequestCache: new Map(),
|
|
965
|
+
retry: requestOptions.retry,
|
|
966
|
+
};
|
|
967
|
+
const sharedModelInflightCache = new Map();
|
|
701
968
|
const buildModelAccessor = (entry) => {
|
|
702
|
-
const accessor = (async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options));
|
|
703
|
-
accessor.byId = async (id, options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options, id);
|
|
969
|
+
const accessor = (async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, requestOptions.modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options));
|
|
970
|
+
accessor.byId = async (id, options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, requestOptions.modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options, id);
|
|
704
971
|
return accessor;
|
|
705
972
|
};
|
|
706
973
|
const modelsProxy = new Proxy({}, {
|
|
@@ -730,7 +997,7 @@ export function createCmsClient(descriptor, config) {
|
|
|
730
997
|
if (!entry) {
|
|
731
998
|
unknownKeyError(property, rootKeys, []);
|
|
732
999
|
}
|
|
733
|
-
return async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, options);
|
|
1000
|
+
return async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, requestOptions.modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options);
|
|
734
1001
|
},
|
|
735
1002
|
});
|
|
736
1003
|
return rootProxy;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export interface CmsAssetOptions {
|
|
|
10
10
|
baseUrl?: string;
|
|
11
11
|
uploadsPath?: string;
|
|
12
12
|
}
|
|
13
|
+
export interface CmsRequestOptions {
|
|
14
|
+
concurrency?: number;
|
|
15
|
+
retries?: number;
|
|
16
|
+
retryBaseMs?: number;
|
|
17
|
+
retryMaxMs?: number;
|
|
18
|
+
}
|
|
13
19
|
export interface CmsAccessorOptions {
|
|
14
20
|
query?: CmsQuery;
|
|
15
21
|
response?: CmsResponseMode;
|
|
@@ -70,6 +76,7 @@ export interface Cms0Options {
|
|
|
70
76
|
locales?: string[];
|
|
71
77
|
defaultLocale?: string;
|
|
72
78
|
assets?: CmsAssetOptions;
|
|
79
|
+
requests?: CmsRequestOptions;
|
|
73
80
|
}
|
|
74
81
|
type PrimitiveValueFromDescriptor<TypeName extends string> = TypeName extends "string" ? string : TypeName extends "number" ? number : TypeName extends "boolean" ? boolean : any;
|
|
75
82
|
type CustomTypeValueFromDescriptor<TypeName extends string> = TypeName extends "RichText" ? CmsRichText : TypeName extends "LocalizedString" ? CmsLocalizedString : TypeName extends "LocalizedRichText" ? CmsLocalizedRichText : never;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|