@cms0/cms0 0.2.20 → 0.2.22
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/README.md +11 -0
- package/dist/cjs/custom-types/registry.cjs +6 -0
- package/dist/cjs/index.cjs +400 -65
- package/dist/cjs/libs/cli/config-loader.cjs +45 -4
- package/dist/cjs/libs/cli/publisher.cjs +24 -11
- package/dist/esm/custom-types/registry.js +6 -0
- package/dist/esm/index.js +401 -66
- package/dist/esm/libs/cli/config-loader.js +45 -4
- package/dist/esm/libs/cli/publisher.js +24 -11
- package/dist/types/custom-types/index.d.ts +2 -1
- package/dist/types/custom-types/index.d.ts.map +1 -1
- package/dist/types/custom-types/registry.d.ts.map +1 -1
- package/dist/types/index.d.ts +131 -7
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/libs/cli/config-loader.d.ts.map +1 -1
- package/dist/types/libs/cli/publisher.d.ts.map +1 -1
- package/package.json +13 -9
- package/dist/cjs/index-old-1.cjs +0 -866
- package/dist/cjs/index-old.cjs +0 -1016
- package/dist/cjs/libs/cli/descriptor-builder.cjs +0 -273
- package/dist/cjs/utils/index.cjs +0 -2
- package/dist/esm/index-old-1.js +0 -862
- package/dist/esm/index-old.js +0 -1012
- package/dist/esm/libs/cli/descriptor-builder.js +0 -268
- package/dist/esm/utils/index.js +0 -1
- package/dist/types/index-old-1.d.ts +0 -175
- package/dist/types/index-old-1.d.ts.map +0 -1
- package/dist/types/index-old.d.ts +0 -151
- package/dist/types/index-old.d.ts.map +0 -1
- package/dist/types/libs/cli/descriptor-builder.d.ts +0 -5
- package/dist/types/libs/cli/descriptor-builder.d.ts.map +0 -1
- package/dist/types/utils/index.d.ts +0 -2
- package/dist/types/utils/index.d.ts.map +0 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -20,7 +20,7 @@ function normalizeAssetBaseUrl(baseUrl) {
|
|
|
20
20
|
return trimmed.replace(/\/+$/, "");
|
|
21
21
|
}
|
|
22
22
|
function encodeFilenameForUrl(filename) {
|
|
23
|
-
const normalized =
|
|
23
|
+
const normalized = (0, shared_1.normalizeAssetFilename)(filename);
|
|
24
24
|
if (!normalized)
|
|
25
25
|
return "";
|
|
26
26
|
return normalized
|
|
@@ -29,11 +29,32 @@ function encodeFilenameForUrl(filename) {
|
|
|
29
29
|
.map((segment) => encodeURIComponent(segment))
|
|
30
30
|
.join("/");
|
|
31
31
|
}
|
|
32
|
+
function resolveAssetUrlSuffix(uploadsPath, asset) {
|
|
33
|
+
const storageKey = asset.storageKey?.trim();
|
|
34
|
+
const derivedSuffix = storageKey?.startsWith("uploads/")
|
|
35
|
+
? storageKey.slice("uploads/".length)
|
|
36
|
+
: (0, shared_1.getAssetLogicalPath)(asset.kind, asset.filename).slice("uploads/".length);
|
|
37
|
+
if (uploadsPath.endsWith("/uploads/images") ||
|
|
38
|
+
uploadsPath.endsWith("/uploads/videos") ||
|
|
39
|
+
uploadsPath.endsWith("/uploads/files")) {
|
|
40
|
+
return encodeFilenameForUrl(asset.filename);
|
|
41
|
+
}
|
|
42
|
+
if (uploadsPath !== "/uploads" && !uploadsPath.endsWith("/uploads")) {
|
|
43
|
+
return encodeFilenameForUrl(asset.filename);
|
|
44
|
+
}
|
|
45
|
+
return encodeFilenameForUrl(derivedSuffix);
|
|
46
|
+
}
|
|
32
47
|
function buildAssetUrlBuilder(apiBaseUrl, assetOptions) {
|
|
33
48
|
const uploadsPath = normalizeUploadsPath(assetOptions?.uploadsPath);
|
|
49
|
+
const customResolver = assetOptions?.resolveUrl;
|
|
34
50
|
const explicitBaseUrl = normalizeAssetBaseUrl(assetOptions?.baseUrl);
|
|
35
51
|
if (explicitBaseUrl) {
|
|
36
|
-
return (
|
|
52
|
+
return (asset) => {
|
|
53
|
+
const customUrl = customResolver?.(asset)?.trim();
|
|
54
|
+
if (customUrl)
|
|
55
|
+
return customUrl;
|
|
56
|
+
return `${explicitBaseUrl}${uploadsPath}/${resolveAssetUrlSuffix(uploadsPath, asset)}`;
|
|
57
|
+
};
|
|
37
58
|
}
|
|
38
59
|
let inferredBaseUrl = "";
|
|
39
60
|
try {
|
|
@@ -42,13 +63,21 @@ function buildAssetUrlBuilder(apiBaseUrl, assetOptions) {
|
|
|
42
63
|
catch {
|
|
43
64
|
inferredBaseUrl = "";
|
|
44
65
|
}
|
|
45
|
-
return (
|
|
66
|
+
return (asset) => {
|
|
67
|
+
const customUrl = customResolver?.(asset)?.trim();
|
|
68
|
+
if (customUrl)
|
|
69
|
+
return customUrl;
|
|
70
|
+
return `${inferredBaseUrl}${uploadsPath}/${resolveAssetUrlSuffix(uploadsPath, asset)}`;
|
|
71
|
+
};
|
|
46
72
|
}
|
|
47
73
|
function maybeAttachAssetUrl(value, assetUrlBuilder) {
|
|
48
74
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
49
75
|
return value;
|
|
50
76
|
const source = value;
|
|
51
|
-
const
|
|
77
|
+
const storageKey = typeof source.storageKey === "string" ? source.storageKey.trim() : "";
|
|
78
|
+
const filename = typeof source.filename === "string" && source.filename.trim().length > 0
|
|
79
|
+
? source.filename.trim()
|
|
80
|
+
: (0, shared_1.deriveAssetFilenameFromStorageKey)(storageKey) ?? "";
|
|
52
81
|
if (!filename)
|
|
53
82
|
return value;
|
|
54
83
|
const isAssetShape = typeof source.mimeType === "string" ||
|
|
@@ -62,9 +91,17 @@ function maybeAttachAssetUrl(value, assetUrlBuilder) {
|
|
|
62
91
|
const existingUrl = typeof source.url === "string" && source.url.trim().length > 0
|
|
63
92
|
? source.url
|
|
64
93
|
: null;
|
|
94
|
+
const kind = (0, shared_1.deriveAssetKindFromStorageKey)(storageKey) ?? (0, shared_1.inferAssetKindFromFilename)(filename);
|
|
65
95
|
return {
|
|
66
96
|
...source,
|
|
67
|
-
|
|
97
|
+
filename,
|
|
98
|
+
...(storageKey ? { storageKey } : {}),
|
|
99
|
+
url: existingUrl ??
|
|
100
|
+
assetUrlBuilder({
|
|
101
|
+
filename,
|
|
102
|
+
kind,
|
|
103
|
+
...(storageKey ? { storageKey } : {}),
|
|
104
|
+
}),
|
|
68
105
|
};
|
|
69
106
|
}
|
|
70
107
|
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
@@ -90,7 +127,9 @@ async function mapWithConcurrency(items, concurrency, mapper) {
|
|
|
90
127
|
function extractId(value) {
|
|
91
128
|
if (typeof value === "string" && value.length > 0)
|
|
92
129
|
return value;
|
|
93
|
-
if (value &&
|
|
130
|
+
if (value &&
|
|
131
|
+
typeof value === "object" &&
|
|
132
|
+
typeof value.id === "string") {
|
|
94
133
|
return value.id;
|
|
95
134
|
}
|
|
96
135
|
return null;
|
|
@@ -180,7 +219,9 @@ function isUnionDescriptor(desc) {
|
|
|
180
219
|
return desc.kind === "union" && Array.isArray(desc.anyOf);
|
|
181
220
|
}
|
|
182
221
|
function isScalarDescriptor(desc) {
|
|
183
|
-
return isPrimitiveDescriptor(desc) ||
|
|
222
|
+
return (isPrimitiveDescriptor(desc) ||
|
|
223
|
+
isEnumDescriptor(desc) ||
|
|
224
|
+
isUnionDescriptor(desc));
|
|
184
225
|
}
|
|
185
226
|
function isModelRefDescriptor(desc) {
|
|
186
227
|
return desc.kind === "modelRef";
|
|
@@ -194,7 +235,10 @@ function isArrayDescriptor(desc) {
|
|
|
194
235
|
function attachIdIfObject(value, id) {
|
|
195
236
|
if (!id)
|
|
196
237
|
return value;
|
|
197
|
-
if (value &&
|
|
238
|
+
if (value &&
|
|
239
|
+
typeof value === "object" &&
|
|
240
|
+
!Array.isArray(value) &&
|
|
241
|
+
!value.id) {
|
|
198
242
|
return { ...value, id };
|
|
199
243
|
}
|
|
200
244
|
return value;
|
|
@@ -326,11 +370,13 @@ function toPlainText(value) {
|
|
|
326
370
|
return "";
|
|
327
371
|
}
|
|
328
372
|
function coerceRichTextValue(value) {
|
|
329
|
-
const source = value && typeof value === "object"
|
|
373
|
+
const source = value && typeof value === "object"
|
|
374
|
+
? value
|
|
375
|
+
: {};
|
|
330
376
|
const nested = source.value && typeof source.value === "object"
|
|
331
377
|
? source.value
|
|
332
378
|
: null;
|
|
333
|
-
const normalizedValue = source.value !== undefined ? source.value : value ?? {};
|
|
379
|
+
const normalizedValue = source.value !== undefined ? source.value : (value ?? {});
|
|
334
380
|
const html = typeof source.html === "string"
|
|
335
381
|
? source.html
|
|
336
382
|
: typeof nested?.html === "string"
|
|
@@ -345,8 +391,12 @@ function normalizeLocaleTag(value) {
|
|
|
345
391
|
return typeof value === "string" ? value.trim() : "";
|
|
346
392
|
}
|
|
347
393
|
function normalizeLocalizedMap(source, normalizeValue) {
|
|
348
|
-
const record = source && typeof source === "object"
|
|
349
|
-
|
|
394
|
+
const record = source && typeof source === "object"
|
|
395
|
+
? source
|
|
396
|
+
: {};
|
|
397
|
+
const localesSource = record.locales &&
|
|
398
|
+
typeof record.locales === "object" &&
|
|
399
|
+
!Array.isArray(record.locales)
|
|
350
400
|
? record.locales
|
|
351
401
|
: {};
|
|
352
402
|
const locales = {};
|
|
@@ -418,8 +468,11 @@ function coerceCustomTypeValue(descriptor, value, options) {
|
|
|
418
468
|
return value;
|
|
419
469
|
if (customType === "RichText")
|
|
420
470
|
return coerceRichTextValue(value);
|
|
421
|
-
if (customType === "File" ||
|
|
422
|
-
|
|
471
|
+
if (customType === "File" ||
|
|
472
|
+
customType === "Image" ||
|
|
473
|
+
customType === "Video") {
|
|
474
|
+
return maybeAttachAssetUrl(value, options?.assetUrlBuilder ??
|
|
475
|
+
((asset) => asset.storageKey ?? asset.filename));
|
|
423
476
|
}
|
|
424
477
|
if (customType === "LocalizedString") {
|
|
425
478
|
return coerceLocalizedStringValue(value, options?.locale, options?.defaultLocale);
|
|
@@ -494,7 +547,10 @@ function decodeUnionValueFromRawField(descriptor, raw) {
|
|
|
494
547
|
if (direct) {
|
|
495
548
|
return { ...direct, rowId: extractId(raw) };
|
|
496
549
|
}
|
|
497
|
-
if (raw &&
|
|
550
|
+
if (raw &&
|
|
551
|
+
typeof raw === "object" &&
|
|
552
|
+
!Array.isArray(raw) &&
|
|
553
|
+
"value" in raw) {
|
|
498
554
|
const nested = decodeUnionEnvelope(descriptor, raw.value);
|
|
499
555
|
if (nested) {
|
|
500
556
|
return { ...nested, rowId: extractId(raw) };
|
|
@@ -512,17 +568,22 @@ function ensureCollectionEnvelope(data, path) {
|
|
|
512
568
|
total: Number(data.total ?? data.items.length ?? 0),
|
|
513
569
|
};
|
|
514
570
|
}
|
|
571
|
+
if (data && typeof data === "object" && Array.isArray(data.data)) {
|
|
572
|
+
const pagination = data.pagination;
|
|
573
|
+
return {
|
|
574
|
+
items: data.data,
|
|
575
|
+
total: Number((pagination && typeof pagination === "object"
|
|
576
|
+
? pagination.total
|
|
577
|
+
: undefined) ?? data.data.length ?? 0),
|
|
578
|
+
};
|
|
579
|
+
}
|
|
515
580
|
throw new Error(`${COLLECTION_SHAPE_ERROR} Path: '${path}'.`);
|
|
516
581
|
}
|
|
517
582
|
async function readCanvasModelRefCollectionIdentities(path, descriptor, context) {
|
|
518
583
|
const cache = context.canvasModelRefCollectionIdentityCache;
|
|
519
584
|
if (!cache)
|
|
520
585
|
return null;
|
|
521
|
-
const cacheKey = [
|
|
522
|
-
path,
|
|
523
|
-
descriptor.model,
|
|
524
|
-
context.options.locale ?? "",
|
|
525
|
-
].join("::");
|
|
586
|
+
const cacheKey = [path, descriptor.model, context.options.locale ?? ""].join("::");
|
|
526
587
|
const existing = cache.get(cacheKey);
|
|
527
588
|
if (existing) {
|
|
528
589
|
return existing;
|
|
@@ -581,7 +642,7 @@ async function normalizeModelRef(modelName, id, context, trail, isCollectionItem
|
|
|
581
642
|
nextTrail.add(nodeKey);
|
|
582
643
|
const promise = (async () => {
|
|
583
644
|
const modelPath = `models/${modelName}/${id}`;
|
|
584
|
-
const resolvedModelPath = `
|
|
645
|
+
const resolvedModelPath = `_graph/models/${encodeURIComponent(modelName)}/${encodeURIComponent(id)}`;
|
|
585
646
|
const rawModel = inlineModel ??
|
|
586
647
|
(await (async () => {
|
|
587
648
|
const query = {
|
|
@@ -670,7 +731,9 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
670
731
|
: await context.requestJson(childPath, {
|
|
671
732
|
query: {
|
|
672
733
|
raw: 1,
|
|
673
|
-
...(context.options.locale
|
|
734
|
+
...(context.options.locale
|
|
735
|
+
? { locale: context.options.locale }
|
|
736
|
+
: {}),
|
|
674
737
|
},
|
|
675
738
|
});
|
|
676
739
|
const normalizedChild = await normalizeArrayField(propertyDescriptor, childPath, childRaw, context, trail, inlineChildRaw !== undefined, childProjectionPathTokens);
|
|
@@ -693,7 +756,9 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
693
756
|
: await context.requestJson(childPath, {
|
|
694
757
|
query: {
|
|
695
758
|
raw: 1,
|
|
696
|
-
...(context.options.locale
|
|
759
|
+
...(context.options.locale
|
|
760
|
+
? { locale: context.options.locale }
|
|
761
|
+
: {}),
|
|
697
762
|
},
|
|
698
763
|
});
|
|
699
764
|
const normalizedChild = await normalizeObjectField(propertyDescriptor, childPath, childRaw, context, trail, false, childProjectionPathTokens);
|
|
@@ -727,11 +792,15 @@ async function normalizeArrayField(descriptor, path, raw, context, trail, inline
|
|
|
727
792
|
}
|
|
728
793
|
const id = extractId(row);
|
|
729
794
|
const valueWithId = attachIdForScalarDescriptor(itemDescriptor, normalizedValue, id);
|
|
730
|
-
const isObjectValue = valueWithId &&
|
|
795
|
+
const isObjectValue = valueWithId &&
|
|
796
|
+
typeof valueWithId === "object" &&
|
|
797
|
+
!Array.isArray(valueWithId);
|
|
731
798
|
if (!id) {
|
|
732
799
|
return valueWithId;
|
|
733
800
|
}
|
|
734
|
-
return isObjectValue
|
|
801
|
+
return isObjectValue
|
|
802
|
+
? { id, ...valueWithId }
|
|
803
|
+
: { id, value: valueWithId };
|
|
735
804
|
});
|
|
736
805
|
}
|
|
737
806
|
if (isModelRefDescriptor(itemDescriptor)) {
|
|
@@ -764,9 +833,7 @@ async function normalizeArrayField(descriptor, path, raw, context, trail, inline
|
|
|
764
833
|
if (isObjectDescriptor(itemDescriptor)) {
|
|
765
834
|
return mapWithConcurrency(envelope.items, context.options.modelNormalizationConcurrency, async (row) => {
|
|
766
835
|
const rowId = extractId(row);
|
|
767
|
-
const rowPath = rowId
|
|
768
|
-
? `${path}/${encodeURIComponent(rowId)}`
|
|
769
|
-
: path;
|
|
836
|
+
const rowPath = rowId ? `${path}/${encodeURIComponent(rowId)}` : path;
|
|
770
837
|
return normalizeObjectField(itemDescriptor, rowPath, row, context, trail, true, projectionPathTokens);
|
|
771
838
|
});
|
|
772
839
|
}
|
|
@@ -932,7 +999,7 @@ function createResourceRegistry(descriptor) {
|
|
|
932
999
|
};
|
|
933
1000
|
}
|
|
934
1001
|
function normalizeBaseUrl(baseUrl) {
|
|
935
|
-
const cleaned = baseUrl?.trim().replace(
|
|
1002
|
+
const cleaned = baseUrl?.trim().replace(/\/+$/, "") ?? "";
|
|
936
1003
|
if (!cleaned) {
|
|
937
1004
|
throw new Error("cms0: apiConfig.baseUrl is required to consume API resources.");
|
|
938
1005
|
}
|
|
@@ -945,28 +1012,39 @@ function resolveIncludeIdMode(includeId, globalIncludeId) {
|
|
|
945
1012
|
function shouldIncludeObjectId(mode, _isCollectionItem) {
|
|
946
1013
|
return mode === "all";
|
|
947
1014
|
}
|
|
948
|
-
function
|
|
1015
|
+
function isPlainObject(value) {
|
|
1016
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1017
|
+
}
|
|
1018
|
+
function appendQueryValue(params, key, rawValue) {
|
|
1019
|
+
if (rawValue === undefined || rawValue === null)
|
|
1020
|
+
return;
|
|
1021
|
+
if (Array.isArray(rawValue)) {
|
|
1022
|
+
const values = rawValue.filter((value) => value !== undefined && value !== null);
|
|
1023
|
+
if (values.length === 0)
|
|
1024
|
+
return;
|
|
1025
|
+
if (key.endsWith(".fields") ||
|
|
1026
|
+
key.endsWith(".exclude") ||
|
|
1027
|
+
key === "fields" ||
|
|
1028
|
+
key === "exclude") {
|
|
1029
|
+
params.set(key, values.map(String).join(","));
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
values.forEach((value) => params.append(key, String(value)));
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
params.set(key, String(rawValue));
|
|
1036
|
+
}
|
|
1037
|
+
function buildSearchParams(query, graph) {
|
|
949
1038
|
const params = new URLSearchParams();
|
|
950
|
-
if (
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
if (rawValue === undefined || rawValue === null)
|
|
954
|
-
continue;
|
|
955
|
-
if (Array.isArray(rawValue)) {
|
|
956
|
-
rawValue.forEach((value) => {
|
|
957
|
-
if (value === undefined || value === null)
|
|
958
|
-
return;
|
|
959
|
-
params.append(key, String(value));
|
|
960
|
-
});
|
|
961
|
-
continue;
|
|
1039
|
+
if (query) {
|
|
1040
|
+
for (const [key, rawValue] of Object.entries(query)) {
|
|
1041
|
+
appendQueryValue(params, key, rawValue);
|
|
962
1042
|
}
|
|
963
|
-
params.set(key, String(rawValue));
|
|
964
1043
|
}
|
|
1044
|
+
const graphParams = (0, shared_1.serializeGraphQueryOptions)(graph);
|
|
1045
|
+
graphParams.forEach((value, key) => params.set(key, value));
|
|
965
1046
|
return params;
|
|
966
1047
|
}
|
|
967
|
-
function isPlainObject(value) {
|
|
968
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
969
|
-
}
|
|
970
1048
|
function cloneValue(value) {
|
|
971
1049
|
if (Array.isArray(value)) {
|
|
972
1050
|
return value.map((item) => cloneValue(item));
|
|
@@ -1114,8 +1192,73 @@ function applyFieldProjection(value, fields, exclude) {
|
|
|
1114
1192
|
}
|
|
1115
1193
|
return projected;
|
|
1116
1194
|
}
|
|
1195
|
+
function escapeGraphPointerToken(token) {
|
|
1196
|
+
return String(token).replace(/~/g, "~0").replace(/\//g, "~1");
|
|
1197
|
+
}
|
|
1198
|
+
function buildGraphPointerPath(path) {
|
|
1199
|
+
return path.length === 0
|
|
1200
|
+
? "/"
|
|
1201
|
+
: `/${path.map((token) => escapeGraphPointerToken(token)).join("/")}`;
|
|
1202
|
+
}
|
|
1203
|
+
function buildProjectionPath(path) {
|
|
1204
|
+
return path
|
|
1205
|
+
.filter((token) => typeof token === "string")
|
|
1206
|
+
.map((token) => String(token))
|
|
1207
|
+
.join(".");
|
|
1208
|
+
}
|
|
1209
|
+
function readValueAtPath(value, path) {
|
|
1210
|
+
let current = value;
|
|
1211
|
+
for (const token of path) {
|
|
1212
|
+
if (Array.isArray(current)) {
|
|
1213
|
+
const index = Number(token);
|
|
1214
|
+
if (!Number.isInteger(index) || index < 0)
|
|
1215
|
+
return undefined;
|
|
1216
|
+
current = current[index];
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
if (!isPlainObject(current))
|
|
1220
|
+
return undefined;
|
|
1221
|
+
current = current[String(token)];
|
|
1222
|
+
}
|
|
1223
|
+
return current;
|
|
1224
|
+
}
|
|
1225
|
+
function buildSetOpsFromPatch(value, path = []) {
|
|
1226
|
+
if (value === undefined)
|
|
1227
|
+
return [];
|
|
1228
|
+
if (Array.isArray(value) || !isPlainObject(value)) {
|
|
1229
|
+
return [{ op: "set", path: buildGraphPointerPath(path), value }];
|
|
1230
|
+
}
|
|
1231
|
+
const entries = Object.entries(value).filter(([, child]) => child !== undefined);
|
|
1232
|
+
if (!entries.length) {
|
|
1233
|
+
return path.length
|
|
1234
|
+
? [{ op: "set", path: buildGraphPointerPath(path), value }]
|
|
1235
|
+
: [];
|
|
1236
|
+
}
|
|
1237
|
+
return entries.flatMap(([key, child]) => buildSetOpsFromPatch(child, [...path, key]));
|
|
1238
|
+
}
|
|
1239
|
+
function getModelObjectDescriptor(descriptor, modelName) {
|
|
1240
|
+
const model = descriptor.models?.[modelName];
|
|
1241
|
+
if (!model)
|
|
1242
|
+
return undefined;
|
|
1243
|
+
return asModelObjectDescriptor(modelName, descriptor);
|
|
1244
|
+
}
|
|
1245
|
+
function getDescriptorChild(descriptor, field, key) {
|
|
1246
|
+
if (!field)
|
|
1247
|
+
return undefined;
|
|
1248
|
+
if (isModelRefDescriptor(field)) {
|
|
1249
|
+
const model = getModelObjectDescriptor(descriptor, field.model);
|
|
1250
|
+
return getDescriptorChild(descriptor, model, key);
|
|
1251
|
+
}
|
|
1252
|
+
if (isArrayDescriptor(field)) {
|
|
1253
|
+
return getDescriptorChild(descriptor, field.items, key);
|
|
1254
|
+
}
|
|
1255
|
+
if (isObjectDescriptor(field)) {
|
|
1256
|
+
return field.properties[key];
|
|
1257
|
+
}
|
|
1258
|
+
return undefined;
|
|
1259
|
+
}
|
|
1117
1260
|
async function requestJson(baseUrl, apiKey, path, options) {
|
|
1118
|
-
const params = buildSearchParams(options?.query);
|
|
1261
|
+
const params = buildSearchParams(options?.query, options?.graph);
|
|
1119
1262
|
const url = `${baseUrl}/${path}${params.toString() ? `?${params.toString()}` : ""}`;
|
|
1120
1263
|
const response = await fetch(url, {
|
|
1121
1264
|
method: "GET",
|
|
@@ -1129,6 +1272,34 @@ async function requestJson(baseUrl, apiKey, path, options) {
|
|
|
1129
1272
|
}
|
|
1130
1273
|
return response.json();
|
|
1131
1274
|
}
|
|
1275
|
+
async function request(baseUrl, apiKey, method, path, body, options) {
|
|
1276
|
+
const params = buildSearchParams(options?.query, options?.graph);
|
|
1277
|
+
const url = `${baseUrl}/${path}${params.toString() ? `?${params.toString()}` : ""}`;
|
|
1278
|
+
const headers = {
|
|
1279
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
1280
|
+
};
|
|
1281
|
+
if (body !== undefined) {
|
|
1282
|
+
headers["content-type"] = "application/json";
|
|
1283
|
+
}
|
|
1284
|
+
const response = await fetch(url, {
|
|
1285
|
+
method,
|
|
1286
|
+
signal: options?.signal,
|
|
1287
|
+
headers,
|
|
1288
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
1289
|
+
});
|
|
1290
|
+
if (!response.ok) {
|
|
1291
|
+
throw new Error(`Request failed for '${path}' with status ${response.status}: ${response.statusText}`);
|
|
1292
|
+
}
|
|
1293
|
+
const text = await response.text();
|
|
1294
|
+
if (!text)
|
|
1295
|
+
return undefined;
|
|
1296
|
+
try {
|
|
1297
|
+
return JSON.parse(text);
|
|
1298
|
+
}
|
|
1299
|
+
catch {
|
|
1300
|
+
return text;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1132
1303
|
function validateResult(resource, result, descriptor, includeIdMode, zodSchemas, modelZodSchemas) {
|
|
1133
1304
|
if (includeIdMode === "all")
|
|
1134
1305
|
return;
|
|
@@ -1171,13 +1342,13 @@ function attachResourceProvenanceWithOptions(resource, value, options) {
|
|
|
1171
1342
|
(0, provenance_1.enableCms0ProvenanceTracking)(true);
|
|
1172
1343
|
const rootId = resource.kind === "root"
|
|
1173
1344
|
? resource.key
|
|
1174
|
-
: resource.modelName ?? resource.key;
|
|
1345
|
+
: (resource.modelName ?? resource.key);
|
|
1175
1346
|
return (0, provenance_1.attachCms0ProvenanceRoot)(value, rootId, options);
|
|
1176
1347
|
}
|
|
1177
1348
|
async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options, byId, provenanceOptions) {
|
|
1178
1349
|
const responseMode = options?.response ?? "normalized";
|
|
1179
1350
|
const includeIdMode = resolveIncludeIdMode(options?.includeId, globalIncludeId);
|
|
1180
|
-
const resolveModelRefs = options?.resolveModelRefs !== false;
|
|
1351
|
+
const resolveModelRefs = options?.graph?.resolveModelRefs !== false;
|
|
1181
1352
|
const includeProjectionPaths = normalizeProjectionPaths(options?.fields);
|
|
1182
1353
|
const excludeProjectionPaths = normalizeProjectionPaths(options?.exclude);
|
|
1183
1354
|
const shouldValidateFullResult = includeProjectionPaths.length === 0 && excludeProjectionPaths.length === 0;
|
|
@@ -1185,18 +1356,20 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1185
1356
|
(typeof options?.query?.locale === "string"
|
|
1186
1357
|
? options.query.locale
|
|
1187
1358
|
: undefined);
|
|
1188
|
-
const locale = requestedLocale ??
|
|
1189
|
-
(responseMode === "normalized" ? "all" : undefined);
|
|
1359
|
+
const locale = requestedLocale ?? (responseMode === "normalized" ? "all" : undefined);
|
|
1190
1360
|
const query = {
|
|
1191
1361
|
...(options?.query ?? {}),
|
|
1192
1362
|
};
|
|
1193
|
-
|
|
1194
|
-
|
|
1363
|
+
const graphQuery = {
|
|
1364
|
+
...options?.graph,
|
|
1365
|
+
};
|
|
1366
|
+
if (options?.fields !== undefined && graphQuery.fields === undefined) {
|
|
1367
|
+
graphQuery.fields = Array.isArray(options.fields)
|
|
1195
1368
|
? options.fields.join(",")
|
|
1196
1369
|
: options.fields;
|
|
1197
1370
|
}
|
|
1198
|
-
if (options?.exclude !== undefined &&
|
|
1199
|
-
|
|
1371
|
+
if (options?.exclude !== undefined && graphQuery.exclude === undefined) {
|
|
1372
|
+
graphQuery.exclude = Array.isArray(options.exclude)
|
|
1200
1373
|
? options.exclude.join(",")
|
|
1201
1374
|
: options.exclude;
|
|
1202
1375
|
}
|
|
@@ -1204,8 +1377,8 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1204
1377
|
if (shouldNormalize && query.raw === undefined) {
|
|
1205
1378
|
query.raw = 1;
|
|
1206
1379
|
}
|
|
1207
|
-
if (locale &&
|
|
1208
|
-
|
|
1380
|
+
if (locale && graphQuery.locale === undefined) {
|
|
1381
|
+
graphQuery.locale = locale;
|
|
1209
1382
|
}
|
|
1210
1383
|
const useResolvedEndpoint = shouldNormalize &&
|
|
1211
1384
|
!byId &&
|
|
@@ -1213,20 +1386,25 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1213
1386
|
!resource.isCollection &&
|
|
1214
1387
|
isObjectDescriptor(resource.descriptor) &&
|
|
1215
1388
|
resolveModelRefs;
|
|
1216
|
-
if (useResolvedEndpoint &&
|
|
1217
|
-
|
|
1389
|
+
if (useResolvedEndpoint &&
|
|
1390
|
+
graphQuery.resolveModelRefs === undefined) {
|
|
1391
|
+
graphQuery.resolveModelRefs = true;
|
|
1392
|
+
}
|
|
1393
|
+
if (useResolvedEndpoint && graphQuery.pageSize === undefined) {
|
|
1394
|
+
graphQuery.pageSize = "full";
|
|
1218
1395
|
}
|
|
1219
1396
|
const normalizePath = byId
|
|
1220
1397
|
? `${resource.path}/${encodeURIComponent(byId)}`
|
|
1221
1398
|
: resource.path;
|
|
1222
1399
|
const requestPath = useResolvedEndpoint
|
|
1223
|
-
? `
|
|
1400
|
+
? `_graph/${encodeURIComponent(resource.key)}`
|
|
1224
1401
|
: normalizePath;
|
|
1225
1402
|
let rawData;
|
|
1226
1403
|
if (useResolvedEndpoint) {
|
|
1227
1404
|
try {
|
|
1228
1405
|
rawData = await requestJson(baseUrl, apiKey, requestPath, {
|
|
1229
1406
|
query,
|
|
1407
|
+
graph: graphQuery,
|
|
1230
1408
|
signal: options?.signal,
|
|
1231
1409
|
});
|
|
1232
1410
|
}
|
|
@@ -1234,6 +1412,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1234
1412
|
try {
|
|
1235
1413
|
rawData = await requestJson(baseUrl, apiKey, normalizePath, {
|
|
1236
1414
|
query,
|
|
1415
|
+
graph: graphQuery,
|
|
1237
1416
|
signal: options?.signal,
|
|
1238
1417
|
});
|
|
1239
1418
|
}
|
|
@@ -1245,6 +1424,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1245
1424
|
else {
|
|
1246
1425
|
rawData = await requestJson(baseUrl, apiKey, requestPath, {
|
|
1247
1426
|
query,
|
|
1427
|
+
graph: graphQuery,
|
|
1248
1428
|
signal: options?.signal,
|
|
1249
1429
|
});
|
|
1250
1430
|
}
|
|
@@ -1309,6 +1489,80 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1309
1489
|
}
|
|
1310
1490
|
return attachResourceProvenanceWithOptions(resource, applyFieldProjection(normalized, options?.fields, options?.exclude), provenanceOptions);
|
|
1311
1491
|
}
|
|
1492
|
+
function createConcreteGraphAccessor(input, path = [], fieldDescriptor = input.entry.descriptor) {
|
|
1493
|
+
const read = async (options) => {
|
|
1494
|
+
const projectionPath = buildProjectionPath(path);
|
|
1495
|
+
const value = await runResource(input.entry, input.descriptor, input.baseUrl, input.apiKey, input.defaultLocale, input.assetUrlBuilder, input.zodSchemas, input.modelZodSchemas, input.globalIncludeId, input.sharedModelInflightCache, projectionPath
|
|
1496
|
+
? {
|
|
1497
|
+
...options,
|
|
1498
|
+
fields: options?.fields === undefined ? projectionPath : options.fields,
|
|
1499
|
+
}
|
|
1500
|
+
: options);
|
|
1501
|
+
return projectionPath ? readValueAtPath(value, path) : value;
|
|
1502
|
+
};
|
|
1503
|
+
const mutate = async (ops) => {
|
|
1504
|
+
if (!ops.length)
|
|
1505
|
+
return;
|
|
1506
|
+
await request(input.baseUrl, input.apiKey, "POST", `_graph/${input.entry.path}/_mutate`, { ops });
|
|
1507
|
+
};
|
|
1508
|
+
const update = async (value) => {
|
|
1509
|
+
await mutate(buildSetOpsFromPatch(value, path));
|
|
1510
|
+
};
|
|
1511
|
+
const set = async (value) => {
|
|
1512
|
+
await mutate([{ op: "set", path: buildGraphPointerPath(path), value }]);
|
|
1513
|
+
};
|
|
1514
|
+
const fieldFn = read;
|
|
1515
|
+
return new Proxy(fieldFn, {
|
|
1516
|
+
get(target, property, receiver) {
|
|
1517
|
+
if (typeof property !== "string") {
|
|
1518
|
+
return Reflect.get(target, property, receiver);
|
|
1519
|
+
}
|
|
1520
|
+
if (property === "then")
|
|
1521
|
+
return undefined;
|
|
1522
|
+
if (property === "update")
|
|
1523
|
+
return update;
|
|
1524
|
+
if (property === "set")
|
|
1525
|
+
return set;
|
|
1526
|
+
if (property === "delete") {
|
|
1527
|
+
return async () => {
|
|
1528
|
+
await mutate([{ op: "delete", path: buildGraphPointerPath(path) }]);
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
if (property === "append") {
|
|
1532
|
+
return async (value) => {
|
|
1533
|
+
await mutate([
|
|
1534
|
+
{
|
|
1535
|
+
op: "insert",
|
|
1536
|
+
path: buildGraphPointerPath([...path, "-"]),
|
|
1537
|
+
value,
|
|
1538
|
+
},
|
|
1539
|
+
]);
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
if (property === "insert") {
|
|
1543
|
+
return async (index, value) => {
|
|
1544
|
+
await mutate([
|
|
1545
|
+
{
|
|
1546
|
+
op: "insert",
|
|
1547
|
+
path: buildGraphPointerPath([...path, index]),
|
|
1548
|
+
value,
|
|
1549
|
+
},
|
|
1550
|
+
]);
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
if (property === "at") {
|
|
1554
|
+
return (index) => createConcreteGraphAccessor(input, [...path, index], isArrayDescriptor(fieldDescriptor)
|
|
1555
|
+
? fieldDescriptor.items
|
|
1556
|
+
: fieldDescriptor);
|
|
1557
|
+
}
|
|
1558
|
+
const childDescriptor = getDescriptorChild(input.descriptor, fieldDescriptor, property);
|
|
1559
|
+
if (childDescriptor) {
|
|
1560
|
+
return createConcreteGraphAccessor(input, [...path, property], childDescriptor);
|
|
1561
|
+
}
|
|
1562
|
+
return Reflect.get(target, property, receiver);
|
|
1563
|
+
},
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1312
1566
|
function createCmsClient(descriptor, config) {
|
|
1313
1567
|
const baseUrl = normalizeBaseUrl(config.apiConfig?.baseUrl);
|
|
1314
1568
|
const apiKey = config.apiConfig?.key;
|
|
@@ -1321,7 +1575,20 @@ function createCmsClient(descriptor, config) {
|
|
|
1321
1575
|
const sharedModelInflightCache = new Map();
|
|
1322
1576
|
const buildModelAccessor = (entry) => {
|
|
1323
1577
|
const accessor = (async (options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options));
|
|
1324
|
-
accessor.byId = async (id, options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options, id);
|
|
1578
|
+
accessor.byId = (async (id, options) => runResource(entry, descriptor, baseUrl, apiKey, config.defaultLocale, assetUrlBuilder, zodSchemas, modelZodSchemas, globalIncludeId, sharedModelInflightCache, options, id));
|
|
1579
|
+
accessor.list = async (options) => accessor(options);
|
|
1580
|
+
accessor.get = async (id, options) => accessor.byId(id, options);
|
|
1581
|
+
accessor.create = async (value) => {
|
|
1582
|
+
const response = await request(baseUrl, apiKey, "POST", entry.path, value);
|
|
1583
|
+
return response;
|
|
1584
|
+
};
|
|
1585
|
+
accessor.update = async (id, value) => {
|
|
1586
|
+
const response = await request(baseUrl, apiKey, "POST", `_graph/${entry.path}/${encodeURIComponent(id)}/_mutate`, { ops: buildSetOpsFromPatch(value) });
|
|
1587
|
+
return response;
|
|
1588
|
+
};
|
|
1589
|
+
accessor.delete = async (id) => {
|
|
1590
|
+
await request(baseUrl, apiKey, "DELETE", `${entry.path}?id=${encodeURIComponent(id)}`);
|
|
1591
|
+
};
|
|
1325
1592
|
return accessor;
|
|
1326
1593
|
};
|
|
1327
1594
|
const modelsProxy = new Proxy({}, {
|
|
@@ -1385,7 +1652,18 @@ function createCmsClient(descriptor, config) {
|
|
|
1385
1652
|
if (!entry) {
|
|
1386
1653
|
unknownKeyError(property, rootKeys, []);
|
|
1387
1654
|
}
|
|
1388
|
-
return
|
|
1655
|
+
return createConcreteGraphAccessor({
|
|
1656
|
+
apiKey,
|
|
1657
|
+
assetUrlBuilder,
|
|
1658
|
+
baseUrl,
|
|
1659
|
+
defaultLocale: config.defaultLocale,
|
|
1660
|
+
descriptor,
|
|
1661
|
+
entry,
|
|
1662
|
+
globalIncludeId,
|
|
1663
|
+
modelZodSchemas,
|
|
1664
|
+
sharedModelInflightCache,
|
|
1665
|
+
zodSchemas,
|
|
1666
|
+
});
|
|
1389
1667
|
},
|
|
1390
1668
|
});
|
|
1391
1669
|
return rootProxy;
|
|
@@ -1427,6 +1705,50 @@ function createLazyBrowserCmsClient(config) {
|
|
|
1427
1705
|
throw error;
|
|
1428
1706
|
}
|
|
1429
1707
|
};
|
|
1708
|
+
const getNestedAccessor = (client, rootProperty, path) => {
|
|
1709
|
+
let current = client[rootProperty];
|
|
1710
|
+
for (const token of path) {
|
|
1711
|
+
current =
|
|
1712
|
+
typeof token === "number" ? current.at(token) : current[String(token)];
|
|
1713
|
+
}
|
|
1714
|
+
return current;
|
|
1715
|
+
};
|
|
1716
|
+
const functionMemberNames = new Set([
|
|
1717
|
+
"apply",
|
|
1718
|
+
"bind",
|
|
1719
|
+
"call",
|
|
1720
|
+
"hasOwnProperty",
|
|
1721
|
+
"isPrototypeOf",
|
|
1722
|
+
"propertyIsEnumerable",
|
|
1723
|
+
"toLocaleString",
|
|
1724
|
+
"toString",
|
|
1725
|
+
"valueOf",
|
|
1726
|
+
]);
|
|
1727
|
+
const buildLazyGraphAccessor = (rootProperty, path = []) => {
|
|
1728
|
+
const accessor = (async (options) => runBrowserAccessor((client) => getNestedAccessor(client, rootProperty, path)(options)));
|
|
1729
|
+
return new Proxy(accessor, {
|
|
1730
|
+
get(target, property, receiver) {
|
|
1731
|
+
if (typeof property !== "string") {
|
|
1732
|
+
return Reflect.get(target, property, receiver);
|
|
1733
|
+
}
|
|
1734
|
+
if (property === "then")
|
|
1735
|
+
return undefined;
|
|
1736
|
+
if (functionMemberNames.has(property)) {
|
|
1737
|
+
return Reflect.get(target, property, receiver);
|
|
1738
|
+
}
|
|
1739
|
+
if (property === "update" || property === "set" || property === "delete") {
|
|
1740
|
+
return (...args) => runBrowserAccessor((client) => getNestedAccessor(client, rootProperty, path)[property](...args));
|
|
1741
|
+
}
|
|
1742
|
+
if (property === "append" || property === "insert") {
|
|
1743
|
+
return (...args) => runBrowserAccessor((client) => getNestedAccessor(client, rootProperty, path)[property](...args));
|
|
1744
|
+
}
|
|
1745
|
+
if (property === "at") {
|
|
1746
|
+
return (index) => buildLazyGraphAccessor(rootProperty, [...path, index]);
|
|
1747
|
+
}
|
|
1748
|
+
return buildLazyGraphAccessor(rootProperty, [...path, property]);
|
|
1749
|
+
},
|
|
1750
|
+
});
|
|
1751
|
+
};
|
|
1430
1752
|
const buildModelAccessor = (property) => {
|
|
1431
1753
|
const accessor = (async (options) => {
|
|
1432
1754
|
return runBrowserAccessor((client) => client.models[property](options));
|
|
@@ -1434,6 +1756,21 @@ function createLazyBrowserCmsClient(config) {
|
|
|
1434
1756
|
accessor.byId = async (id, options) => {
|
|
1435
1757
|
return runBrowserAccessor((client) => client.models[property].byId(id, options));
|
|
1436
1758
|
};
|
|
1759
|
+
accessor.list = async (options) => {
|
|
1760
|
+
return runBrowserAccessor((client) => client.models[property].list(options));
|
|
1761
|
+
};
|
|
1762
|
+
accessor.get = async (id, options) => {
|
|
1763
|
+
return runBrowserAccessor((client) => client.models[property].get(id, options));
|
|
1764
|
+
};
|
|
1765
|
+
accessor.create = async (value) => {
|
|
1766
|
+
return runBrowserAccessor((client) => client.models[property].create(value));
|
|
1767
|
+
};
|
|
1768
|
+
accessor.update = async (id, value) => {
|
|
1769
|
+
return runBrowserAccessor((client) => client.models[property].update(id, value));
|
|
1770
|
+
};
|
|
1771
|
+
accessor.delete = async (id) => {
|
|
1772
|
+
return runBrowserAccessor((client) => client.models[property].delete(id));
|
|
1773
|
+
};
|
|
1437
1774
|
return accessor;
|
|
1438
1775
|
};
|
|
1439
1776
|
const modelsProxy = new Proxy({}, {
|
|
@@ -1464,9 +1801,7 @@ function createLazyBrowserCmsClient(config) {
|
|
|
1464
1801
|
if (Reflect.has(target, property)) {
|
|
1465
1802
|
return Reflect.get(target, property, receiver);
|
|
1466
1803
|
}
|
|
1467
|
-
return
|
|
1468
|
-
return runBrowserAccessor((client) => client[property](options));
|
|
1469
|
-
};
|
|
1804
|
+
return buildLazyGraphAccessor(property);
|
|
1470
1805
|
},
|
|
1471
1806
|
});
|
|
1472
1807
|
return rootProxy;
|