@cms0/cms0 0.2.3 → 0.2.4

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.
@@ -4,10 +4,7 @@ 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;
7
+ const registry_js_1 = require("./custom-types/registry.cjs");
11
8
  const DEFAULT_MODEL_NORMALIZATION_CONCURRENCY = 8;
12
9
  const COLLECTION_SHAPE_ERROR = "Invalid collection response. Expected array or { items, total }.";
13
10
  function normalizeUploadsPath(uploadsPath) {
@@ -68,310 +65,6 @@ function maybeAttachAssetUrl(value, assetUrlBuilder) {
68
65
  url: existingUrl ?? assetUrlBuilder(filename),
69
66
  };
70
67
  }
71
- function toPlainText(value) {
72
- if (typeof value === "string")
73
- return value;
74
- if (typeof value === "number" || typeof value === "boolean")
75
- return String(value);
76
- if (!value || typeof value !== "object")
77
- return "";
78
- const node = value;
79
- if (typeof node.text === "string")
80
- return node.text;
81
- if ("value" in node)
82
- return toPlainText(node.value);
83
- if (Array.isArray(node.content)) {
84
- return node.content
85
- .map((child) => toPlainText(child))
86
- .filter(Boolean)
87
- .join(" ")
88
- .trim();
89
- }
90
- return "";
91
- }
92
- function coerceRichTextValue(value) {
93
- const source = value && typeof value === "object" ? value : {};
94
- const nested = source.value && typeof source.value === "object"
95
- ? source.value
96
- : null;
97
- const normalizedValue = source.value !== undefined ? source.value : value ?? {};
98
- const html = typeof source.html === "string"
99
- ? source.html
100
- : typeof nested?.html === "string"
101
- ? nested.html
102
- : "";
103
- return {
104
- value: normalizedValue ?? {},
105
- html,
106
- };
107
- }
108
- function normalizeLocaleTag(value) {
109
- return typeof value === "string" ? value.trim() : "";
110
- }
111
- function normalizeLocalizedMap(source, normalizeValue) {
112
- const record = source && typeof source === "object" ? source : {};
113
- const localesSource = record.locales && typeof record.locales === "object" && !Array.isArray(record.locales)
114
- ? record.locales
115
- : {};
116
- const locales = {};
117
- for (const [locale, raw] of Object.entries(localesSource)) {
118
- const localeKey = normalizeLocaleTag(locale);
119
- if (!localeKey)
120
- continue;
121
- locales[localeKey] = normalizeValue(raw);
122
- }
123
- const explicitDefault = normalizeLocaleTag(record.defaultLocale);
124
- const firstLocale = Object.keys(locales)[0] ?? "";
125
- const defaultLocale = explicitDefault || firstLocale;
126
- return { defaultLocale, locales };
127
- }
128
- function projectLocalizedByLocale(localized, locale) {
129
- if (!locale || locale === "all")
130
- return localized;
131
- const requested = normalizeLocaleTag(locale);
132
- if (!requested)
133
- return localized;
134
- const requestedValue = localized.locales[requested];
135
- if (requestedValue === undefined) {
136
- return {
137
- defaultLocale: requested,
138
- locales: {},
139
- };
140
- }
141
- return {
142
- defaultLocale: requested,
143
- locales: {
144
- [requested]: requestedValue,
145
- },
146
- };
147
- }
148
- function coerceLocalizedStringValue(value, locale, defaultLocale) {
149
- const normalized = normalizeLocalizedMap(value, (entry) => toPlainText(entry));
150
- if (!normalized.defaultLocale && defaultLocale) {
151
- normalized.defaultLocale = defaultLocale;
152
- }
153
- return projectLocalizedByLocale(normalized, locale);
154
- }
155
- function coerceLocalizedRichTextValue(value, locale, defaultLocale) {
156
- const normalized = normalizeLocalizedMap(value, (entry) => coerceRichTextValue(entry));
157
- if (!normalized.defaultLocale && defaultLocale) {
158
- normalized.defaultLocale = defaultLocale;
159
- }
160
- return projectLocalizedByLocale(normalized, locale);
161
- }
162
- function coerceCustomTypeValue(descriptor, value, options) {
163
- const customType = descriptor.customType;
164
- if (!customType)
165
- return value;
166
- if (customType === "RichText")
167
- return coerceRichTextValue(value);
168
- if (customType === "File" || customType === "Image" || customType === "Video") {
169
- return maybeAttachAssetUrl(value, options?.assetUrlBuilder ?? ((filename) => filename));
170
- }
171
- if (customType === "LocalizedString") {
172
- return coerceLocalizedStringValue(value, options?.locale, options?.defaultLocale);
173
- }
174
- if (customType === "LocalizedRichText") {
175
- return coerceLocalizedRichTextValue(value, options?.locale, options?.defaultLocale);
176
- }
177
- return value;
178
- }
179
- function isPrimitiveDescriptor(desc) {
180
- return desc.kind === "primitive";
181
- }
182
- function isModelRefDescriptor(desc) {
183
- return desc.kind === "modelRef";
184
- }
185
- function isObjectDescriptor(desc) {
186
- return desc.type === "object" && !!desc.properties;
187
- }
188
- function isArrayDescriptor(desc) {
189
- return desc.type === "array" && !!desc.items;
190
- }
191
- function asModelObjectDescriptor(modelName, descriptor) {
192
- const model = descriptor.models?.[modelName];
193
- if (!model) {
194
- throw new Error(`Unknown model '${modelName}' in schema descriptor.`);
195
- }
196
- return {
197
- type: "object",
198
- properties: model.properties,
199
- optional: false,
200
- nullable: false,
201
- };
202
- }
203
- function normalizeBaseUrl(baseUrl) {
204
- const cleaned = baseUrl?.trim().replace(/\/$/, "") ?? "";
205
- if (!cleaned) {
206
- throw new Error("cms0: apiConfig.baseUrl is required to consume API resources.");
207
- }
208
- return cleaned;
209
- }
210
- function resolveIncludeIdMode(includeId) {
211
- if (includeId === true)
212
- return "all";
213
- if (includeId === false)
214
- return "none";
215
- return "collections";
216
- }
217
- function shouldIncludeObjectId(mode, isCollectionItem) {
218
- if (mode === "all")
219
- return true;
220
- if (mode === "collections")
221
- return isCollectionItem;
222
- return false;
223
- }
224
- function buildSearchParams(query) {
225
- const params = new URLSearchParams();
226
- if (!query)
227
- return params;
228
- for (const [key, rawValue] of Object.entries(query)) {
229
- if (rawValue === undefined || rawValue === null)
230
- continue;
231
- if (Array.isArray(rawValue)) {
232
- rawValue.forEach((value) => {
233
- if (value === undefined || value === null)
234
- return;
235
- params.append(key, String(value));
236
- });
237
- continue;
238
- }
239
- params.set(key, String(rawValue));
240
- }
241
- return params;
242
- }
243
- function ensureCollectionEnvelope(data, path) {
244
- if (Array.isArray(data)) {
245
- return { items: data, total: data.length };
246
- }
247
- if (data && typeof data === "object" && Array.isArray(data.items)) {
248
- return {
249
- items: data.items,
250
- total: Number(data.total ?? data.items.length ?? 0),
251
- };
252
- }
253
- throw new Error(`${COLLECTION_SHAPE_ERROR} Path: '${path}'.`);
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
68
  async function mapWithConcurrency(items, concurrency, mapper) {
376
69
  if (items.length === 0)
377
70
  return [];
@@ -453,6 +146,43 @@ function extractModelRefId(raw, modelName, options) {
453
146
  }
454
147
  return null;
455
148
  }
149
+ function isPrimitiveDescriptor(desc) {
150
+ return desc.kind === "primitive";
151
+ }
152
+ function isModelRefDescriptor(desc) {
153
+ return desc.kind === "modelRef";
154
+ }
155
+ function isObjectDescriptor(desc) {
156
+ return desc.type === "object" && !!desc.properties;
157
+ }
158
+ function isArrayDescriptor(desc) {
159
+ return desc.type === "array" && !!desc.items;
160
+ }
161
+ function attachIdIfObject(value, id) {
162
+ if (!id)
163
+ return value;
164
+ if (value && typeof value === "object" && !Array.isArray(value) && !value.id) {
165
+ return { ...value, id };
166
+ }
167
+ return value;
168
+ }
169
+ function attachIdForPrimitiveDescriptor(descriptor, value, id) {
170
+ const withId = attachIdIfObject(value, id);
171
+ if (!withId || typeof withId !== "object" || Array.isArray(withId)) {
172
+ return withId;
173
+ }
174
+ const customType = descriptor.customType;
175
+ if (typeof customType !== "string") {
176
+ return withId;
177
+ }
178
+ const metadata = (0, registry_js_1.getCustomInlineTypeMetadata)(customType);
179
+ if (!metadata?.includeId?.mapLikeFields?.length) {
180
+ return withId;
181
+ }
182
+ // Map-like objects (e.g., locales) are data containers, not entity rows.
183
+ // Keep them untouched by id propagation.
184
+ return { ...withId };
185
+ }
456
186
  function buildModelRefCacheKey(modelName, id, context, isCollectionItem) {
457
187
  const locale = context.options.locale ?? "";
458
188
  const defaultLocale = context.options.defaultLocale ?? "";
@@ -481,6 +211,18 @@ function extractInlineModelObject(raw, modelDescriptor) {
481
211
  return null;
482
212
  return source;
483
213
  }
214
+ function asModelObjectDescriptor(modelName, descriptor) {
215
+ const model = descriptor.models?.[modelName];
216
+ if (!model) {
217
+ throw new Error(`Unknown model '${modelName}' in schema descriptor.`);
218
+ }
219
+ return {
220
+ type: "object",
221
+ properties: model.properties,
222
+ optional: false,
223
+ nullable: false,
224
+ };
225
+ }
484
226
  function formatValidationError(name, error) {
485
227
  const payload = error && typeof error === "object" && "format" in error
486
228
  ? error.format()
@@ -529,6 +271,126 @@ function unknownKeyError(key, roots, models) {
529
271
  : "";
530
272
  throw new Error(`Unknown schema key '${key}'. ${rootsText}${modelsText}.${suggestionText}`);
531
273
  }
274
+ function toPlainText(value) {
275
+ if (typeof value === "string")
276
+ return value;
277
+ if (typeof value === "number" || typeof value === "boolean")
278
+ return String(value);
279
+ if (!value || typeof value !== "object")
280
+ return "";
281
+ const node = value;
282
+ if (typeof node.text === "string")
283
+ return node.text;
284
+ if ("value" in node)
285
+ return toPlainText(node.value);
286
+ if (Array.isArray(node.content)) {
287
+ return node.content
288
+ .map((child) => toPlainText(child))
289
+ .filter(Boolean)
290
+ .join(" ")
291
+ .trim();
292
+ }
293
+ return "";
294
+ }
295
+ function coerceRichTextValue(value) {
296
+ const source = value && typeof value === "object" ? value : {};
297
+ const nested = source.value && typeof source.value === "object"
298
+ ? source.value
299
+ : null;
300
+ const normalizedValue = source.value !== undefined ? source.value : value ?? {};
301
+ const html = typeof source.html === "string"
302
+ ? source.html
303
+ : typeof nested?.html === "string"
304
+ ? nested.html
305
+ : "";
306
+ return {
307
+ value: normalizedValue ?? {},
308
+ html,
309
+ };
310
+ }
311
+ function normalizeLocaleTag(value) {
312
+ return typeof value === "string" ? value.trim() : "";
313
+ }
314
+ function normalizeLocalizedMap(source, normalizeValue) {
315
+ const record = source && typeof source === "object" ? source : {};
316
+ const localesSource = record.locales && typeof record.locales === "object" && !Array.isArray(record.locales)
317
+ ? record.locales
318
+ : {};
319
+ const locales = {};
320
+ for (const [locale, raw] of Object.entries(localesSource)) {
321
+ const localeKey = normalizeLocaleTag(locale);
322
+ if (!localeKey)
323
+ continue;
324
+ locales[localeKey] = normalizeValue(raw);
325
+ }
326
+ const explicitDefault = normalizeLocaleTag(record.defaultLocale);
327
+ const firstLocale = Object.keys(locales)[0] ?? "";
328
+ const defaultLocale = explicitDefault || firstLocale;
329
+ return { defaultLocale, locales };
330
+ }
331
+ function projectLocalizedByLocale(localized, locale) {
332
+ if (!locale || locale === "all")
333
+ return localized;
334
+ const requested = normalizeLocaleTag(locale);
335
+ if (!requested)
336
+ return localized;
337
+ const requestedValue = localized.locales[requested];
338
+ if (requestedValue === undefined) {
339
+ return {
340
+ defaultLocale: requested,
341
+ locales: {},
342
+ };
343
+ }
344
+ return {
345
+ defaultLocale: requested,
346
+ locales: {
347
+ [requested]: requestedValue,
348
+ },
349
+ };
350
+ }
351
+ function coerceLocalizedStringValue(value, locale, defaultLocale) {
352
+ const normalized = normalizeLocalizedMap(value, (entry) => toPlainText(entry));
353
+ if (!normalized.defaultLocale && defaultLocale) {
354
+ normalized.defaultLocale = defaultLocale;
355
+ }
356
+ return projectLocalizedByLocale(normalized, locale);
357
+ }
358
+ function coerceLocalizedRichTextValue(value, locale, defaultLocale) {
359
+ const normalized = normalizeLocalizedMap(value, (entry) => coerceRichTextValue(entry));
360
+ if (!normalized.defaultLocale && defaultLocale) {
361
+ normalized.defaultLocale = defaultLocale;
362
+ }
363
+ return projectLocalizedByLocale(normalized, locale);
364
+ }
365
+ function coerceCustomTypeValue(descriptor, value, options) {
366
+ const customType = descriptor.customType;
367
+ if (!customType)
368
+ return value;
369
+ if (customType === "RichText")
370
+ return coerceRichTextValue(value);
371
+ if (customType === "File" || customType === "Image" || customType === "Video") {
372
+ return maybeAttachAssetUrl(value, options?.assetUrlBuilder ?? ((filename) => filename));
373
+ }
374
+ if (customType === "LocalizedString") {
375
+ return coerceLocalizedStringValue(value, options?.locale, options?.defaultLocale);
376
+ }
377
+ if (customType === "LocalizedRichText") {
378
+ return coerceLocalizedRichTextValue(value, options?.locale, options?.defaultLocale);
379
+ }
380
+ return value;
381
+ }
382
+ function ensureCollectionEnvelope(data, path) {
383
+ if (Array.isArray(data)) {
384
+ return { items: data, total: data.length };
385
+ }
386
+ if (data && typeof data === "object" && Array.isArray(data.items)) {
387
+ return {
388
+ items: data.items,
389
+ total: Number(data.total ?? data.items.length ?? 0),
390
+ };
391
+ }
392
+ throw new Error(`${COLLECTION_SHAPE_ERROR} Path: '${path}'.`);
393
+ }
532
394
  async function normalizeModelRef(modelName, id, context, trail, isCollectionItem, inlineValue) {
533
395
  if (!id)
534
396
  return null;
@@ -676,7 +538,12 @@ async function normalizeArrayField(descriptor, path, raw, context, trail) {
676
538
  return value;
677
539
  }
678
540
  const id = extractId(row);
679
- return id ? { id, value } : value;
541
+ const valueWithId = attachIdForPrimitiveDescriptor(itemDescriptor, value, id);
542
+ const isObjectValue = valueWithId && typeof valueWithId === "object" && !Array.isArray(valueWithId);
543
+ if (!id) {
544
+ return valueWithId;
545
+ }
546
+ return isObjectValue ? { id, ...valueWithId } : { id, value: valueWithId };
680
547
  });
681
548
  }
682
549
  if (isModelRefDescriptor(itemDescriptor)) {
@@ -707,11 +574,16 @@ async function normalizeField(descriptor, path, raw, context, trail, isCollectio
707
574
  const value = raw && typeof raw === "object" && "value" in raw
708
575
  ? raw.value
709
576
  : raw;
710
- return coerceCustomTypeValue(descriptor, value, {
577
+ let coerced = coerceCustomTypeValue(descriptor, value, {
711
578
  locale: context.options.locale,
712
579
  defaultLocale: context.options.defaultLocale,
713
580
  assetUrlBuilder: context.options.assetUrlBuilder,
714
581
  });
582
+ if (shouldIncludeObjectId(context.options.includeIdMode, isCollectionItem)) {
583
+ const id = extractId(raw);
584
+ coerced = attachIdForPrimitiveDescriptor(descriptor, coerced, id);
585
+ }
586
+ return coerced;
715
587
  }
716
588
  if (isModelRefDescriptor(descriptor)) {
717
589
  const inlineModelDescriptor = context.modelDescriptors.get(descriptor.model);
@@ -779,71 +651,53 @@ function createResourceRegistry(descriptor) {
779
651
  modelsCaseInsensitive,
780
652
  };
781
653
  }
782
- async function requestJson(baseUrl, apiKey, path, options, runtime) {
783
- const params = buildSearchParams(options?.query);
784
- const url = `${baseUrl}/${path}${params.toString() ? `?${params.toString()}` : ""}`;
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);
654
+ function normalizeBaseUrl(baseUrl) {
655
+ const cleaned = baseUrl?.trim().replace(/\/$/, "") ?? "";
656
+ if (!cleaned) {
657
+ throw new Error("cms0: apiConfig.baseUrl is required to consume API resources.");
658
+ }
659
+ return cleaned;
660
+ }
661
+ function resolveIncludeIdMode(includeId, globalIncludeId) {
662
+ const enabled = includeId ?? globalIncludeId ?? false;
663
+ return enabled ? "all" : "none";
664
+ }
665
+ function shouldIncludeObjectId(mode, _isCollectionItem) {
666
+ return mode === "all";
667
+ }
668
+ function buildSearchParams(query) {
669
+ const params = new URLSearchParams();
670
+ if (!query)
671
+ return params;
672
+ for (const [key, rawValue] of Object.entries(query)) {
673
+ if (rawValue === undefined || rawValue === null)
674
+ continue;
675
+ if (Array.isArray(rawValue)) {
676
+ rawValue.forEach((value) => {
677
+ if (value === undefined || value === null)
678
+ return;
679
+ params.append(key, String(value));
680
+ });
681
+ continue;
833
682
  }
834
- };
835
- const useInflightDedup = !options?.signal && !!runtime;
836
- if (!useInflightDedup || !runtime) {
837
- return execute();
683
+ params.set(key, String(rawValue));
838
684
  }
839
- const cached = runtime.inflightRequestCache.get(url);
840
- if (cached)
841
- return cached;
842
- const promise = execute().finally(() => {
843
- runtime.inflightRequestCache.delete(url);
685
+ return params;
686
+ }
687
+ async function requestJson(baseUrl, apiKey, path, options) {
688
+ const params = buildSearchParams(options?.query);
689
+ const url = `${baseUrl}/${path}${params.toString() ? `?${params.toString()}` : ""}`;
690
+ const response = await fetch(url, {
691
+ method: "GET",
692
+ signal: options?.signal,
693
+ headers: {
694
+ ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
695
+ },
844
696
  });
845
- runtime.inflightRequestCache.set(url, promise);
846
- return promise;
697
+ if (!response.ok) {
698
+ throw new Error(`Request failed for '${path}' with status ${response.status}: ${response.statusText}`);
699
+ }
700
+ return response.json();
847
701
  }
848
702
  function validateResult(resource, result, descriptor, includeIdMode, zodSchemas, modelZodSchemas) {
849
703
  if (includeIdMode === "all")
@@ -878,9 +732,9 @@ function validateResult(resource, result, descriptor, includeIdMode, zodSchemas,
878
732
  throw new Error(formatValidationError(modelName, parsed.error));
879
733
  }
880
734
  }
881
- async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options, byId) {
735
+ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options, byId) {
882
736
  const responseMode = options?.response ?? "normalized";
883
- const includeIdMode = resolveIncludeIdMode(options?.includeId);
737
+ const includeIdMode = resolveIncludeIdMode(options?.includeId, globalIncludeId);
884
738
  const resolveModelRefs = options?.resolveModelRefs !== false;
885
739
  const requestedLocale = options?.locale ??
886
740
  (typeof options?.query?.locale === "string"
@@ -904,7 +758,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
904
758
  const rawData = await requestJson(baseUrl, apiKey, resourcePath, {
905
759
  query,
906
760
  signal: options?.signal,
907
- }, requestRuntime);
761
+ });
908
762
  if (!shouldNormalize) {
909
763
  return rawData;
910
764
  }
@@ -916,7 +770,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
916
770
  requestJson: (path, requestOptions) => requestJson(baseUrl, apiKey, path, {
917
771
  ...requestOptions,
918
772
  signal: options?.signal,
919
- }, requestRuntime),
773
+ }),
920
774
  modelDescriptors,
921
775
  modelCache: new Map(),
922
776
  sharedModelInflightCache,
@@ -926,7 +780,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
926
780
  locale,
927
781
  defaultLocale,
928
782
  assetUrlBuilder,
929
- modelNormalizationConcurrency,
783
+ modelNormalizationConcurrency: DEFAULT_MODEL_NORMALIZATION_CONCURRENCY,
930
784
  },
931
785
  };
932
786
  if (resource.isCollection && !byId) {
@@ -962,16 +816,11 @@ function createCmsClient(descriptor, config) {
962
816
  const rootKeys = Array.from(registry.roots.keys());
963
817
  const modelKeys = Array.from(registry.models.keys());
964
818
  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
- };
819
+ const globalIncludeId = !!config.includeId;
971
820
  const sharedModelInflightCache = new Map();
972
821
  const buildModelAccessor = (entry) => {
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);
822
+ const accessor = (async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options));
823
+ accessor.byId = async (id, options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options, id);
975
824
  return accessor;
976
825
  };
977
826
  const modelsProxy = new Proxy({}, {
@@ -1001,10 +850,16 @@ function createCmsClient(descriptor, config) {
1001
850
  if (!entry) {
1002
851
  unknownKeyError(property, rootKeys, []);
1003
852
  }
1004
- return async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, requestRuntime, sharedModelInflightCache, requestOptions.modelNormalizationConcurrency, zodSchemas, modelZodSchemas, options);
853
+ return async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options);
1005
854
  },
1006
855
  });
1007
- return rootProxy;
856
+ const client = Object.assign(rootProxy, {
857
+ models: modelsProxy,
858
+ locales: config.locales ?? [],
859
+ defaultLocale: config.defaultLocale,
860
+ includeIdDefault: globalIncludeId,
861
+ });
862
+ return client;
1008
863
  }
1009
864
  /**
1010
865
  * Initializes the CMS SDK for a given schema.