@cms0/cms0 0.2.17 → 0.2.19
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/descriptor-sidecar.cjs +20 -0
- package/dist/cjs/generated/schema-descriptor.cjs +3 -0
- package/dist/cjs/index.cjs +135 -29
- package/dist/cjs/libs/cli/browser-sidecar.cjs +56 -0
- package/dist/cjs/libs/cli/build.cjs +2 -2
- package/dist/cjs/libs/cli/cli.cjs +8 -3
- package/dist/cjs/libs/cli/config-loader.cjs +19 -33
- package/dist/cjs/libs/cli/descriptor-writer.cjs +21 -9
- package/dist/cjs/libs/cli/paths.cjs +7 -0
- package/dist/cjs/libs/cli/watcher.cjs +5 -3
- package/dist/cjs/provenance.cjs +80 -10
- package/dist/cjs/schema-descriptors.cjs +265 -2
- package/dist/esm/descriptor-sidecar.js +16 -0
- package/dist/esm/generated/schema-descriptor.js +3 -0
- package/dist/esm/index.js +134 -30
- package/dist/esm/libs/cli/browser-sidecar.js +51 -0
- package/dist/esm/libs/cli/build.js +2 -2
- package/dist/esm/libs/cli/cli.js +8 -3
- package/dist/esm/libs/cli/config-loader.js +20 -34
- package/dist/esm/libs/cli/descriptor-writer.js +21 -11
- package/dist/esm/libs/cli/paths.js +7 -1
- package/dist/esm/libs/cli/watcher.js +5 -3
- package/dist/esm/provenance.js +78 -10
- package/dist/esm/schema-descriptors.js +261 -1
- package/dist/types/descriptor-sidecar.d.ts +5 -0
- package/dist/types/descriptor-sidecar.d.ts.map +1 -0
- package/dist/types/generated/schema-descriptor.d.ts +3 -0
- package/dist/types/generated/schema-descriptor.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/libs/cli/browser-sidecar.d.ts +9 -0
- package/dist/types/libs/cli/browser-sidecar.d.ts.map +1 -0
- package/dist/types/libs/cli/build.d.ts +2 -1
- package/dist/types/libs/cli/build.d.ts.map +1 -1
- package/dist/types/libs/cli/cli.d.ts.map +1 -1
- package/dist/types/libs/cli/config-loader.d.ts.map +1 -1
- package/dist/types/libs/cli/descriptor-writer.d.ts +11 -2
- package/dist/types/libs/cli/descriptor-writer.d.ts.map +1 -1
- package/dist/types/libs/cli/paths.d.ts +5 -1
- package/dist/types/libs/cli/paths.d.ts.map +1 -1
- package/dist/types/libs/cli/types.d.ts +1 -0
- package/dist/types/libs/cli/types.d.ts.map +1 -1
- package/dist/types/libs/cli/watcher.d.ts +2 -1
- package/dist/types/libs/cli/watcher.d.ts.map +1 -1
- package/dist/types/provenance.d.ts +3 -0
- package/dist/types/provenance.d.ts.map +1 -1
- package/dist/types/schema-descriptors.d.ts +12 -1
- package/dist/types/schema-descriptors.d.ts.map +1 -1
- package/package.json +4 -3
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { attachCms0ProvenanceRoot, enableCms0ProvenanceTracking, isCms0CanvasTransportEnabled, registerCms0CollectionItemIdentity, } from "@cms0/cms0/provenance";
|
|
2
|
-
import {
|
|
2
|
+
import { getActiveSchemaDescriptor, resolveBrowserSchemaDescriptor, } from "@cms0/cms0/schema-descriptors";
|
|
3
3
|
import { buildZodSchemasFromDescriptor, decodeTaggedUnionValue, getUnionBranchKeys, } from "@cms0/shared";
|
|
4
4
|
import { getCustomInlineTypeMetadata } from "./custom-types/registry.js";
|
|
5
5
|
const DEFAULT_MODEL_NORMALIZATION_CONCURRENCY = 8;
|
|
@@ -547,7 +547,7 @@ async function readCanvasModelRefCollectionIdentities(path, descriptor, context)
|
|
|
547
547
|
cache.set(cacheKey, pending);
|
|
548
548
|
return pending;
|
|
549
549
|
}
|
|
550
|
-
async function normalizeModelRef(modelName, id, context, trail, isCollectionItem, inlineValue) {
|
|
550
|
+
async function normalizeModelRef(modelName, id, context, trail, isCollectionItem, inlineValue, projectionPathTokens = []) {
|
|
551
551
|
if (!id)
|
|
552
552
|
return null;
|
|
553
553
|
if (!context.options.resolveModelRefs)
|
|
@@ -575,14 +575,29 @@ async function normalizeModelRef(modelName, id, context, trail, isCollectionItem
|
|
|
575
575
|
const nextTrail = new Set(trail);
|
|
576
576
|
nextTrail.add(nodeKey);
|
|
577
577
|
const promise = (async () => {
|
|
578
|
+
const modelPath = `models/${modelName}/${id}`;
|
|
579
|
+
const resolvedModelPath = `_resolved/models/${encodeURIComponent(modelName)}/${encodeURIComponent(id)}`;
|
|
578
580
|
const rawModel = inlineModel ??
|
|
579
|
-
(await
|
|
580
|
-
query
|
|
581
|
+
(await (async () => {
|
|
582
|
+
const query = {
|
|
581
583
|
raw: 1,
|
|
582
584
|
...(context.options.locale ? { locale: context.options.locale } : {}),
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
|
|
585
|
+
};
|
|
586
|
+
try {
|
|
587
|
+
return await context.requestJson(resolvedModelPath, {
|
|
588
|
+
query: {
|
|
589
|
+
...query,
|
|
590
|
+
resolveModelRefs: 1,
|
|
591
|
+
},
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
catch {
|
|
595
|
+
return context.requestJson(modelPath, {
|
|
596
|
+
query,
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
})());
|
|
600
|
+
return normalizeField(modelDescriptor, `models/${modelName}/${encodeURIComponent(id)}`, rawModel, context, nextTrail, isCollectionItem, projectionPathTokens);
|
|
586
601
|
})();
|
|
587
602
|
context.modelCache.set(nodeKey, promise);
|
|
588
603
|
context.sharedModelInflightCache.set(nodeKey, promise);
|
|
@@ -602,12 +617,16 @@ function missingModelRefValue(descriptor) {
|
|
|
602
617
|
return undefined;
|
|
603
618
|
return null;
|
|
604
619
|
}
|
|
605
|
-
async function normalizeObjectField(descriptor, path, raw, context, trail, isCollectionItem) {
|
|
620
|
+
async function normalizeObjectField(descriptor, path, raw, context, trail, isCollectionItem, projectionPathTokens = []) {
|
|
606
621
|
const source = raw && typeof raw === "object" ? raw : {};
|
|
607
622
|
const output = {};
|
|
608
623
|
for (const [propertyName, propertyDescriptor] of Object.entries(descriptor.properties)) {
|
|
624
|
+
const childProjectionPathTokens = [...projectionPathTokens, propertyName];
|
|
625
|
+
if (!shouldTraverseProjectionPath(childProjectionPathTokens, context.options.includeProjectionPaths, context.options.excludeProjectionPaths)) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
609
628
|
if (isScalarDescriptor(propertyDescriptor)) {
|
|
610
|
-
output[propertyName] = await normalizeField(propertyDescriptor, `${path}/${propertyName}`, source[propertyName], context, trail, false);
|
|
629
|
+
output[propertyName] = await normalizeField(propertyDescriptor, `${path}/${propertyName}`, source[propertyName], context, trail, false, childProjectionPathTokens);
|
|
611
630
|
continue;
|
|
612
631
|
}
|
|
613
632
|
if (isModelRefDescriptor(propertyDescriptor)) {
|
|
@@ -631,7 +650,7 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
631
650
|
}
|
|
632
651
|
continue;
|
|
633
652
|
}
|
|
634
|
-
output[propertyName] = await normalizeModelRef(propertyDescriptor.model, refId, context, trail, false, inlineValue);
|
|
653
|
+
output[propertyName] = await normalizeModelRef(propertyDescriptor.model, refId, context, trail, false, inlineValue, childProjectionPathTokens);
|
|
635
654
|
continue;
|
|
636
655
|
}
|
|
637
656
|
if (isArrayDescriptor(propertyDescriptor)) {
|
|
@@ -649,7 +668,7 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
649
668
|
...(context.options.locale ? { locale: context.options.locale } : {}),
|
|
650
669
|
},
|
|
651
670
|
});
|
|
652
|
-
const normalizedChild = await normalizeArrayField(propertyDescriptor, childPath, childRaw, context, trail, inlineChildRaw !== undefined);
|
|
671
|
+
const normalizedChild = await normalizeArrayField(propertyDescriptor, childPath, childRaw, context, trail, inlineChildRaw !== undefined, childProjectionPathTokens);
|
|
653
672
|
output[propertyName] = coerceCustomTypeValue(propertyDescriptor, normalizedChild, {
|
|
654
673
|
locale: context.options.locale,
|
|
655
674
|
defaultLocale: context.options.defaultLocale,
|
|
@@ -672,7 +691,7 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
672
691
|
...(context.options.locale ? { locale: context.options.locale } : {}),
|
|
673
692
|
},
|
|
674
693
|
});
|
|
675
|
-
const normalizedChild = await normalizeObjectField(propertyDescriptor, childPath, childRaw, context, trail, false);
|
|
694
|
+
const normalizedChild = await normalizeObjectField(propertyDescriptor, childPath, childRaw, context, trail, false, childProjectionPathTokens);
|
|
676
695
|
output[propertyName] = coerceCustomTypeValue(propertyDescriptor, normalizedChild, {
|
|
677
696
|
locale: context.options.locale,
|
|
678
697
|
defaultLocale: context.options.defaultLocale,
|
|
@@ -692,12 +711,12 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
|
|
|
692
711
|
}
|
|
693
712
|
return maybeAttachAssetUrl(output, context.options.assetUrlBuilder);
|
|
694
713
|
}
|
|
695
|
-
async function normalizeArrayField(descriptor, path, raw, context, trail, inlineResolvedItems = false) {
|
|
714
|
+
async function normalizeArrayField(descriptor, path, raw, context, trail, inlineResolvedItems = false, projectionPathTokens = []) {
|
|
696
715
|
const envelope = ensureCollectionEnvelope(raw, path);
|
|
697
716
|
const itemDescriptor = descriptor.items;
|
|
698
717
|
if (isScalarDescriptor(itemDescriptor)) {
|
|
699
718
|
return mapWithConcurrency(envelope.items, context.options.modelNormalizationConcurrency, async (row) => {
|
|
700
|
-
const normalizedValue = await normalizeField(itemDescriptor, path, row, context, trail, true);
|
|
719
|
+
const normalizedValue = await normalizeField(itemDescriptor, path, row, context, trail, true, projectionPathTokens);
|
|
701
720
|
if (context.options.includeIdMode !== "all") {
|
|
702
721
|
return normalizedValue;
|
|
703
722
|
}
|
|
@@ -720,7 +739,7 @@ async function normalizeArrayField(descriptor, path, raw, context, trail, inline
|
|
|
720
739
|
const refId = extractModelRefId(row, itemDescriptor.model, {
|
|
721
740
|
allowObjectIdFallback: false,
|
|
722
741
|
}) ?? (inlineModel ? extractId(inlineModel) : null);
|
|
723
|
-
const normalized = await normalizeModelRef(itemDescriptor.model, refId, context, trail, true, row);
|
|
742
|
+
const normalized = await normalizeModelRef(itemDescriptor.model, refId, context, trail, true, row, projectionPathTokens);
|
|
724
743
|
const collectionItemId = collectionIdentities?.[index]?.relationId ??
|
|
725
744
|
(inlineResolvedItems ? null : extractId(row));
|
|
726
745
|
if (!collectionItemId ||
|
|
@@ -743,12 +762,12 @@ async function normalizeArrayField(descriptor, path, raw, context, trail, inline
|
|
|
743
762
|
const rowPath = rowId
|
|
744
763
|
? `${path}/${encodeURIComponent(rowId)}`
|
|
745
764
|
: path;
|
|
746
|
-
return normalizeObjectField(itemDescriptor, rowPath, row, context, trail, true);
|
|
765
|
+
return normalizeObjectField(itemDescriptor, rowPath, row, context, trail, true, projectionPathTokens);
|
|
747
766
|
});
|
|
748
767
|
}
|
|
749
768
|
return envelope.items;
|
|
750
769
|
}
|
|
751
|
-
async function normalizeInlineField(descriptor, raw, context, trail, isCollectionItem) {
|
|
770
|
+
async function normalizeInlineField(descriptor, raw, context, trail, isCollectionItem, projectionPathTokens = []) {
|
|
752
771
|
if (isPrimitiveDescriptor(descriptor)) {
|
|
753
772
|
return coerceCustomTypeValue(descriptor, raw, {
|
|
754
773
|
locale: context.options.locale,
|
|
@@ -766,7 +785,7 @@ async function normalizeInlineField(descriptor, raw, context, trail, isCollectio
|
|
|
766
785
|
if (!decoded) {
|
|
767
786
|
return null;
|
|
768
787
|
}
|
|
769
|
-
return normalizeInlineField(decoded.branchDescriptor, decoded.branchValue, context, trail, isCollectionItem);
|
|
788
|
+
return normalizeInlineField(decoded.branchDescriptor, decoded.branchValue, context, trail, isCollectionItem, projectionPathTokens);
|
|
770
789
|
}
|
|
771
790
|
if (isModelRefDescriptor(descriptor)) {
|
|
772
791
|
const refId = extractModelRefId(raw, descriptor.model, {
|
|
@@ -775,11 +794,11 @@ async function normalizeInlineField(descriptor, raw, context, trail, isCollectio
|
|
|
775
794
|
if (!refId) {
|
|
776
795
|
return missingModelRefValue(descriptor);
|
|
777
796
|
}
|
|
778
|
-
return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem, raw);
|
|
797
|
+
return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem, raw, projectionPathTokens);
|
|
779
798
|
}
|
|
780
799
|
if (isArrayDescriptor(descriptor)) {
|
|
781
800
|
const source = Array.isArray(raw) ? raw : [];
|
|
782
|
-
const normalized = await mapWithConcurrency(source, context.options.modelNormalizationConcurrency, async (entry) => normalizeInlineField(descriptor.items, entry, context, trail, true));
|
|
801
|
+
const normalized = await mapWithConcurrency(source, context.options.modelNormalizationConcurrency, async (entry) => normalizeInlineField(descriptor.items, entry, context, trail, true, projectionPathTokens));
|
|
783
802
|
return coerceCustomTypeValue(descriptor, normalized, {
|
|
784
803
|
locale: context.options.locale,
|
|
785
804
|
defaultLocale: context.options.defaultLocale,
|
|
@@ -792,7 +811,7 @@ async function normalizeInlineField(descriptor, raw, context, trail, isCollectio
|
|
|
792
811
|
: {};
|
|
793
812
|
const output = {};
|
|
794
813
|
for (const [propertyName, propertyDescriptor] of Object.entries(descriptor.properties)) {
|
|
795
|
-
output[propertyName] = await normalizeInlineField(propertyDescriptor, source[propertyName], context, trail, false);
|
|
814
|
+
output[propertyName] = await normalizeInlineField(propertyDescriptor, source[propertyName], context, trail, false, [...projectionPathTokens, propertyName]);
|
|
796
815
|
}
|
|
797
816
|
return coerceCustomTypeValue(descriptor, output, {
|
|
798
817
|
locale: context.options.locale,
|
|
@@ -802,7 +821,7 @@ async function normalizeInlineField(descriptor, raw, context, trail, isCollectio
|
|
|
802
821
|
}
|
|
803
822
|
return raw;
|
|
804
823
|
}
|
|
805
|
-
async function normalizeField(descriptor, path, raw, context, trail, isCollectionItem) {
|
|
824
|
+
async function normalizeField(descriptor, path, raw, context, trail, isCollectionItem, projectionPathTokens = []) {
|
|
806
825
|
if (isPrimitiveDescriptor(descriptor)) {
|
|
807
826
|
const value = raw && typeof raw === "object" && "value" in raw
|
|
808
827
|
? raw.value
|
|
@@ -834,7 +853,7 @@ async function normalizeField(descriptor, path, raw, context, trail, isCollectio
|
|
|
834
853
|
if (!decoded) {
|
|
835
854
|
return null;
|
|
836
855
|
}
|
|
837
|
-
let normalized = await normalizeInlineField(decoded.branchDescriptor, decoded.branchValue, context, trail, isCollectionItem);
|
|
856
|
+
let normalized = await normalizeInlineField(decoded.branchDescriptor, decoded.branchValue, context, trail, isCollectionItem, projectionPathTokens);
|
|
838
857
|
if (shouldIncludeObjectId(context.options.includeIdMode, isCollectionItem)) {
|
|
839
858
|
const id = decoded.rowId ?? extractId(raw);
|
|
840
859
|
normalized = attachIdForScalarDescriptor(descriptor, normalized, id);
|
|
@@ -855,7 +874,7 @@ async function normalizeField(descriptor, path, raw, context, trail, isCollectio
|
|
|
855
874
|
return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem, raw);
|
|
856
875
|
}
|
|
857
876
|
if (isArrayDescriptor(descriptor)) {
|
|
858
|
-
const normalized = await normalizeArrayField(descriptor, path, raw, context, trail);
|
|
877
|
+
const normalized = await normalizeArrayField(descriptor, path, raw, context, trail, false, projectionPathTokens);
|
|
859
878
|
return coerceCustomTypeValue(descriptor, normalized, {
|
|
860
879
|
locale: context.options.locale,
|
|
861
880
|
defaultLocale: context.options.defaultLocale,
|
|
@@ -863,7 +882,7 @@ async function normalizeField(descriptor, path, raw, context, trail, isCollectio
|
|
|
863
882
|
});
|
|
864
883
|
}
|
|
865
884
|
if (isObjectDescriptor(descriptor)) {
|
|
866
|
-
const normalized = await normalizeObjectField(descriptor, path, raw, context, trail, isCollectionItem);
|
|
885
|
+
const normalized = await normalizeObjectField(descriptor, path, raw, context, trail, isCollectionItem, projectionPathTokens);
|
|
867
886
|
return coerceCustomTypeValue(descriptor, normalized, {
|
|
868
887
|
locale: context.options.locale,
|
|
869
888
|
defaultLocale: context.options.defaultLocale,
|
|
@@ -978,6 +997,22 @@ function normalizeProjectionPaths(input) {
|
|
|
978
997
|
}
|
|
979
998
|
return Array.from(unique).map((path) => path.split("."));
|
|
980
999
|
}
|
|
1000
|
+
function isProjectionPathPrefix(prefix, value) {
|
|
1001
|
+
if (prefix.length > value.length)
|
|
1002
|
+
return false;
|
|
1003
|
+
return prefix.every((token, index) => value[index] === token);
|
|
1004
|
+
}
|
|
1005
|
+
function shouldTraverseProjectionPath(childTokens, includePaths, excludePaths) {
|
|
1006
|
+
if (includePaths.length > 0 &&
|
|
1007
|
+
!includePaths.some((pathTokens) => isProjectionPathPrefix(pathTokens, childTokens) ||
|
|
1008
|
+
isProjectionPathPrefix(childTokens, pathTokens))) {
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
if (excludePaths.some((pathTokens) => isProjectionPathPrefix(pathTokens, childTokens))) {
|
|
1012
|
+
return false;
|
|
1013
|
+
}
|
|
1014
|
+
return true;
|
|
1015
|
+
}
|
|
981
1016
|
function mergeProjectedValue(existing, incoming) {
|
|
982
1017
|
if (incoming === undefined)
|
|
983
1018
|
return existing;
|
|
@@ -1138,6 +1173,9 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1138
1173
|
const responseMode = options?.response ?? "normalized";
|
|
1139
1174
|
const includeIdMode = resolveIncludeIdMode(options?.includeId, globalIncludeId);
|
|
1140
1175
|
const resolveModelRefs = options?.resolveModelRefs !== false;
|
|
1176
|
+
const includeProjectionPaths = normalizeProjectionPaths(options?.fields);
|
|
1177
|
+
const excludeProjectionPaths = normalizeProjectionPaths(options?.exclude);
|
|
1178
|
+
const shouldValidateFullResult = includeProjectionPaths.length === 0 && excludeProjectionPaths.length === 0;
|
|
1141
1179
|
const requestedLocale = options?.locale ??
|
|
1142
1180
|
(typeof options?.query?.locale === "string"
|
|
1143
1181
|
? options.query.locale
|
|
@@ -1230,6 +1268,8 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1230
1268
|
defaultLocale,
|
|
1231
1269
|
assetUrlBuilder,
|
|
1232
1270
|
modelNormalizationConcurrency: DEFAULT_MODEL_NORMALIZATION_CONCURRENCY,
|
|
1271
|
+
includeProjectionPaths,
|
|
1272
|
+
excludeProjectionPaths,
|
|
1233
1273
|
},
|
|
1234
1274
|
};
|
|
1235
1275
|
if (resource.isCollection && !byId) {
|
|
@@ -1245,17 +1285,23 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
|
|
|
1245
1285
|
const normalizedItems = await normalizeArrayField(descriptorForCollection, resource.path, envelope, context, new Set());
|
|
1246
1286
|
if (responseMode === "envelope") {
|
|
1247
1287
|
const projectedItems = applyFieldProjection(normalizedItems, options?.fields, options?.exclude);
|
|
1248
|
-
|
|
1288
|
+
if (shouldValidateFullResult) {
|
|
1289
|
+
validateResult(resource, normalizedItems, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
|
|
1290
|
+
}
|
|
1249
1291
|
return {
|
|
1250
1292
|
items: attachResourceProvenanceWithOptions(resource, Array.isArray(projectedItems) ? projectedItems : normalizedItems, provenanceOptions),
|
|
1251
1293
|
total: envelope.total,
|
|
1252
1294
|
};
|
|
1253
1295
|
}
|
|
1254
|
-
|
|
1296
|
+
if (shouldValidateFullResult) {
|
|
1297
|
+
validateResult(resource, normalizedItems, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
|
|
1298
|
+
}
|
|
1255
1299
|
return attachResourceProvenanceWithOptions(resource, normalizedItems, provenanceOptions);
|
|
1256
1300
|
}
|
|
1257
1301
|
const normalized = await normalizeField(resource.descriptor, normalizePath, rawData, context, new Set(), false);
|
|
1258
|
-
|
|
1302
|
+
if (shouldValidateFullResult) {
|
|
1303
|
+
validateResult(resource, normalized, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
|
|
1304
|
+
}
|
|
1259
1305
|
return attachResourceProvenanceWithOptions(resource, applyFieldProjection(normalized, options?.fields, options?.exclude), provenanceOptions);
|
|
1260
1306
|
}
|
|
1261
1307
|
export function createCmsClient(descriptor, config) {
|
|
@@ -1340,7 +1386,65 @@ export function createCmsClient(descriptor, config) {
|
|
|
1340
1386
|
return rootProxy;
|
|
1341
1387
|
}
|
|
1342
1388
|
export function cms0(config) {
|
|
1343
|
-
|
|
1389
|
+
if (typeof window !== "undefined") {
|
|
1390
|
+
return createLazyBrowserCmsClient(config);
|
|
1391
|
+
}
|
|
1392
|
+
return createCmsClient(getActiveSchemaDescriptor(), config);
|
|
1393
|
+
}
|
|
1394
|
+
function createLazyBrowserCmsClient(config) {
|
|
1395
|
+
let clientPromise = null;
|
|
1396
|
+
const resolveClient = async () => {
|
|
1397
|
+
if (!clientPromise) {
|
|
1398
|
+
clientPromise = resolveBrowserSchemaDescriptor(config.apiConfig?.baseUrl, config.apiConfig?.key).then((descriptor) => createCmsClient(descriptor, config));
|
|
1399
|
+
}
|
|
1400
|
+
return clientPromise;
|
|
1401
|
+
};
|
|
1402
|
+
const buildModelAccessor = (property) => {
|
|
1403
|
+
const accessor = (async (options) => {
|
|
1404
|
+
const client = await resolveClient();
|
|
1405
|
+
return client.models[property](options);
|
|
1406
|
+
});
|
|
1407
|
+
accessor.byId = async (id, options) => {
|
|
1408
|
+
const client = await resolveClient();
|
|
1409
|
+
return client.models[property].byId(id, options);
|
|
1410
|
+
};
|
|
1411
|
+
return accessor;
|
|
1412
|
+
};
|
|
1413
|
+
const modelsProxy = new Proxy({}, {
|
|
1414
|
+
get(_target, property) {
|
|
1415
|
+
if (typeof property !== "string")
|
|
1416
|
+
return undefined;
|
|
1417
|
+
if (property === "then")
|
|
1418
|
+
return undefined;
|
|
1419
|
+
return buildModelAccessor(property);
|
|
1420
|
+
},
|
|
1421
|
+
});
|
|
1422
|
+
const clientMeta = {
|
|
1423
|
+
locales: config.locales ?? [],
|
|
1424
|
+
defaultLocale: config.defaultLocale,
|
|
1425
|
+
includeIdDefault: !!config.includeId,
|
|
1426
|
+
};
|
|
1427
|
+
const rootProxyTarget = {
|
|
1428
|
+
models: modelsProxy,
|
|
1429
|
+
meta: clientMeta,
|
|
1430
|
+
};
|
|
1431
|
+
const rootProxy = new Proxy(rootProxyTarget, {
|
|
1432
|
+
get(target, property, receiver) {
|
|
1433
|
+
if (typeof property !== "string") {
|
|
1434
|
+
return Reflect.get(target, property, receiver);
|
|
1435
|
+
}
|
|
1436
|
+
if (property === "then")
|
|
1437
|
+
return undefined;
|
|
1438
|
+
if (Reflect.has(target, property)) {
|
|
1439
|
+
return Reflect.get(target, property, receiver);
|
|
1440
|
+
}
|
|
1441
|
+
return async (options) => {
|
|
1442
|
+
const client = await resolveClient();
|
|
1443
|
+
return client[property](options);
|
|
1444
|
+
};
|
|
1445
|
+
},
|
|
1446
|
+
});
|
|
1447
|
+
return rootProxy;
|
|
1344
1448
|
}
|
|
1345
|
-
export { activateCms0CanvasTransport, attachCms0ProvenanceRoot, captureCms0Provenance, enableCms0ProvenanceTracking, readCms0CanvasTransportCollectionItemId, readCms0CollectionItemIdentity, registerCms0LiveRoot, registerCms0CollectionItemIdentity, readCms0LiveRoots, readCms0ProvenanceValueMeta, restoreCms0CanvasTransportMetadata, unwrapCms0CanvasTransportValue, } from "./provenance.js";
|
|
1449
|
+
export { activateCms0CanvasTransport, attachCms0ProvenanceRoot, captureCms0Provenance, dehydrateCms0CanvasTransportValue, dehydrateCms0CanvasVisibleValue, enableCms0ProvenanceTracking, readCms0CanvasTransportCollectionItemId, readCms0CollectionItemIdentity, registerCms0LiveRoot, registerCms0CollectionItemIdentity, readCms0LiveRoots, readCms0ProvenanceValueMeta, restoreCms0CanvasTransportMetadata, unwrapCms0CanvasTransportValue, } from "./provenance.js";
|
|
1346
1450
|
export { resolveLocalized, toNextMetadata, toOpenGraph, toTwitter, } from "./seo.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import { getCms0DescriptorSidecarPort } from "../../descriptor-sidecar.js";
|
|
3
|
+
function startBrowserDescriptorSidecar(resolved, initialDescriptor) {
|
|
4
|
+
const port = getCms0DescriptorSidecarPort(resolved.apiBaseUrl, resolved.apiKey);
|
|
5
|
+
let currentDescriptor = initialDescriptor;
|
|
6
|
+
const server = http.createServer((request, response) => {
|
|
7
|
+
const requestUrl = new URL(request.url ?? "/", `http://127.0.0.1:${port}`);
|
|
8
|
+
response.setHeader("Access-Control-Allow-Origin", "*");
|
|
9
|
+
response.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
10
|
+
response.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
11
|
+
if (request.method === "OPTIONS") {
|
|
12
|
+
response.writeHead(204);
|
|
13
|
+
response.end();
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (request.method !== "GET" || requestUrl.pathname !== "/schema-descriptor") {
|
|
17
|
+
response.writeHead(404, { "content-type": "application/json; charset=utf-8" });
|
|
18
|
+
response.end(JSON.stringify({ error: "Not found" }));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
response.writeHead(200, {
|
|
22
|
+
"content-type": "application/json; charset=utf-8",
|
|
23
|
+
"cache-control": "no-store",
|
|
24
|
+
});
|
|
25
|
+
response.end(JSON.stringify(currentDescriptor));
|
|
26
|
+
});
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
server.once("error", reject);
|
|
29
|
+
server.listen(port, "127.0.0.1", () => {
|
|
30
|
+
server.off("error", reject);
|
|
31
|
+
console.log(`cms0: browser descriptor sidecar listening on http://127.0.0.1:${port}/schema-descriptor`);
|
|
32
|
+
resolve({
|
|
33
|
+
updateDescriptor(descriptor) {
|
|
34
|
+
currentDescriptor = descriptor;
|
|
35
|
+
},
|
|
36
|
+
close() {
|
|
37
|
+
return new Promise((closeResolve, closeReject) => {
|
|
38
|
+
server.close((error) => {
|
|
39
|
+
if (error) {
|
|
40
|
+
closeReject(error);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
closeResolve();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export { startBrowserDescriptorSidecar };
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { buildDescriptorAlt as buildDescriptor } from "./descriptor-builder-alt.js";
|
|
3
3
|
import { publishDescriptor } from "./publisher.js";
|
|
4
4
|
import { writeDescriptorFiles } from "./descriptor-writer.js";
|
|
5
|
-
function buildOnce(resolved) {
|
|
5
|
+
function buildOnce(resolved, browserTarget = "sidecar") {
|
|
6
6
|
const descriptor = buildDescriptor(resolved);
|
|
7
|
-
writeDescriptorFiles(descriptor);
|
|
7
|
+
writeDescriptorFiles(resolved, descriptor, browserTarget);
|
|
8
8
|
publishDescriptor(resolved, descriptor);
|
|
9
9
|
return descriptor;
|
|
10
10
|
}
|
package/dist/esm/libs/cli/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// CLI entry point: parse arguments, load config, and trigger build/watch modes.
|
|
2
2
|
import { pathToFileURL } from "url";
|
|
3
3
|
import { buildOnce } from "./build.js";
|
|
4
|
+
import { startBrowserDescriptorSidecar } from "./browser-sidecar.js";
|
|
4
5
|
import { loadUserConfig, resolvePaths } from "./config-loader.js";
|
|
5
6
|
import { startWatcher } from "./watcher.js";
|
|
6
7
|
async function runFromCli() {
|
|
@@ -26,14 +27,18 @@ async function runFromCli() {
|
|
|
26
27
|
const resolved = resolvePaths(loaded.path, loaded.config);
|
|
27
28
|
console.log("resolved: ", resolved);
|
|
28
29
|
if (mode === "watch") {
|
|
29
|
-
|
|
30
|
+
const descriptor = buildOnce(resolved, "sidecar");
|
|
31
|
+
const sidecar = await startBrowserDescriptorSidecar(resolved, descriptor);
|
|
32
|
+
startWatcher(resolved, (nextDescriptor) => sidecar.updateDescriptor(nextDescriptor), "sidecar", false);
|
|
30
33
|
return;
|
|
31
34
|
}
|
|
32
35
|
if (mode === "dev") {
|
|
33
|
-
|
|
36
|
+
const descriptor = buildOnce(resolved, "sidecar");
|
|
37
|
+
const sidecar = await startBrowserDescriptorSidecar(resolved, descriptor);
|
|
38
|
+
startWatcher(resolved, (nextDescriptor) => sidecar.updateDescriptor(nextDescriptor), "sidecar", false);
|
|
34
39
|
return;
|
|
35
40
|
}
|
|
36
|
-
buildOnce(resolved);
|
|
41
|
+
buildOnce(resolved, "bundle");
|
|
37
42
|
}
|
|
38
43
|
// ESM/CJS-safe main check without import.meta syntax in the source so CJS builds succeed.
|
|
39
44
|
const isDirectRun = (() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Load cms0.config files and resolve relevant paths for the CLI.
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
|
-
import { pathToFileURL
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
5
5
|
import crypto from "crypto";
|
|
6
6
|
const DEFAULT_CONFIG_BASENAMES = [
|
|
7
7
|
"cms0.config.ts",
|
|
@@ -70,38 +70,20 @@ async function loadUserConfig(configPath) {
|
|
|
70
70
|
return { path: resolvedPath, config: JSON.parse(json) };
|
|
71
71
|
}
|
|
72
72
|
if (ext === ".ts" || ext === ".mts" || ext === ".cts" || ext === ".tsx") {
|
|
73
|
-
// If the enclosing package is ESM, use dynamic import instead of require.
|
|
74
73
|
const pkgJsonPath = findNearestPackageJson(resolvedPath);
|
|
75
74
|
const pkgType = pkgJsonPath ? readPackageType(pkgJsonPath) : undefined;
|
|
76
75
|
const isEsmPkg = pkgType === "module";
|
|
77
|
-
|
|
78
|
-
const compiledHref = await transpileTsModuleToTemp(resolvedPath);
|
|
79
|
-
try {
|
|
80
|
-
const imported = (await importByHref(compiledHref));
|
|
81
|
-
const config = imported?.default ?? imported?.config ?? imported ?? {};
|
|
82
|
-
return { path: resolvedPath, config };
|
|
83
|
-
}
|
|
84
|
-
finally {
|
|
85
|
-
cleanupTempModule(compiledHref);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const previous = process.env.TS_NODE_COMPILER_OPTIONS;
|
|
89
|
-
// register ts-node on demand so TypeScript configs can be required
|
|
90
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
91
|
-
require("ts-node/register/transpile-only");
|
|
76
|
+
const compiledPath = await transpileTsModuleToTemp(resolvedPath, isEsmPkg ? "esm" : "cjs");
|
|
92
77
|
try {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
78
|
+
const imported = isEsmPkg
|
|
79
|
+
? (await importByHref(pathToFileURL(compiledPath).href))
|
|
80
|
+
: // eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
81
|
+
require(compiledPath);
|
|
82
|
+
const config = imported?.default ?? imported?.config ?? imported ?? {};
|
|
96
83
|
return { path: resolvedPath, config };
|
|
97
84
|
}
|
|
98
85
|
finally {
|
|
99
|
-
|
|
100
|
-
delete process.env.TS_NODE_COMPILER_OPTIONS;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
process.env.TS_NODE_COMPILER_OPTIONS = previous;
|
|
104
|
-
}
|
|
86
|
+
cleanupTempModule(compiledPath);
|
|
105
87
|
}
|
|
106
88
|
}
|
|
107
89
|
const imported = (await importByHref(pathToFileURL(resolvedPath).href));
|
|
@@ -118,6 +100,10 @@ function resolvePaths(cfgPath, config) {
|
|
|
118
100
|
throw new Error("cms0: config.entry is required");
|
|
119
101
|
}
|
|
120
102
|
const baseDir = path.dirname(cfgPath);
|
|
103
|
+
const packageJsonPath = findNearestPackageJson(cfgPath);
|
|
104
|
+
const projectRoot = packageJsonPath
|
|
105
|
+
? path.dirname(packageJsonPath)
|
|
106
|
+
: baseDir;
|
|
121
107
|
const entryFile = path.resolve(baseDir, config.entry);
|
|
122
108
|
const tsconfigPath = config.tsconfig
|
|
123
109
|
? path.resolve(baseDir, config.tsconfig)
|
|
@@ -127,6 +113,7 @@ function resolvePaths(cfgPath, config) {
|
|
|
127
113
|
return {
|
|
128
114
|
configPath: cfgPath,
|
|
129
115
|
entryFile,
|
|
116
|
+
projectRoot,
|
|
130
117
|
tsconfigPath,
|
|
131
118
|
apiBaseUrl,
|
|
132
119
|
apiKey,
|
|
@@ -146,12 +133,12 @@ function findTsConfig(entryFile) {
|
|
|
146
133
|
}
|
|
147
134
|
return undefined;
|
|
148
135
|
}
|
|
149
|
-
async function transpileTsModuleToTemp(tsPath) {
|
|
136
|
+
async function transpileTsModuleToTemp(tsPath, format) {
|
|
150
137
|
const ts = await import("typescript");
|
|
151
138
|
const source = fs.readFileSync(tsPath, "utf8");
|
|
152
139
|
const transpiled = ts.transpileModule(source, {
|
|
153
140
|
compilerOptions: {
|
|
154
|
-
module: ts.ModuleKind.ESNext,
|
|
141
|
+
module: format === "esm" ? ts.ModuleKind.ESNext : ts.ModuleKind.CommonJS,
|
|
155
142
|
target: ts.ScriptTarget.ES2022,
|
|
156
143
|
esModuleInterop: true,
|
|
157
144
|
},
|
|
@@ -159,16 +146,15 @@ async function transpileTsModuleToTemp(tsPath) {
|
|
|
159
146
|
});
|
|
160
147
|
const hash = crypto
|
|
161
148
|
.createHash("sha1")
|
|
162
|
-
.update(tsPath + source + Date.now().toString())
|
|
149
|
+
.update(tsPath + format + source + Date.now().toString())
|
|
163
150
|
.digest("hex");
|
|
164
|
-
const outFile = path.join(path.dirname(tsPath), `.cms0-config.${hash}
|
|
151
|
+
const outFile = path.join(path.dirname(tsPath), `.cms0-config.${hash}.${format === "esm" ? "mjs" : "cjs"}`);
|
|
165
152
|
fs.writeFileSync(outFile, transpiled.outputText, "utf8");
|
|
166
|
-
return
|
|
153
|
+
return outFile;
|
|
167
154
|
}
|
|
168
|
-
function cleanupTempModule(
|
|
155
|
+
function cleanupTempModule(tempPath) {
|
|
169
156
|
try {
|
|
170
|
-
|
|
171
|
-
fs.unlinkSync(filePath);
|
|
157
|
+
fs.unlinkSync(tempPath);
|
|
172
158
|
}
|
|
173
159
|
catch {
|
|
174
160
|
// ignore cleanup failures
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { descriptorOutputPaths } from "./paths.js";
|
|
3
|
+
import { descriptorOutputPaths, resolveLocalDescriptorOutputPaths } from "./paths.js";
|
|
4
|
+
const FALLBACK_DESCRIPTOR = {
|
|
5
|
+
models: {},
|
|
6
|
+
roots: {},
|
|
7
|
+
metadata: {
|
|
8
|
+
__cms0Fallback: true,
|
|
9
|
+
},
|
|
10
|
+
};
|
|
4
11
|
function safeWrite(outputPath, output) {
|
|
5
12
|
try {
|
|
6
13
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
@@ -15,20 +22,23 @@ function safeWrite(outputPath, output) {
|
|
|
15
22
|
}
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
|
-
function
|
|
19
|
-
return `// Auto-generated schema descriptor\
|
|
25
|
+
function createCjsOutput(descriptor) {
|
|
26
|
+
return `// Auto-generated schema descriptor\nexports.schemaDescriptor = ${JSON.stringify(descriptor, null, 2)};\n`;
|
|
27
|
+
}
|
|
28
|
+
function createJsonOutput(descriptor) {
|
|
29
|
+
return `${JSON.stringify(descriptor, null, 2)}\n`;
|
|
20
30
|
}
|
|
21
31
|
function createEsmOutput(descriptor) {
|
|
22
32
|
return `// Auto-generated schema descriptor\nexport const schemaDescriptor = ${JSON.stringify(descriptor, null, 2)};\n`;
|
|
23
33
|
}
|
|
24
|
-
function
|
|
25
|
-
return `// Auto-generated schema descriptor\nexports.schemaDescriptor = ${JSON.stringify(descriptor, null, 2)};\n`;
|
|
26
|
-
}
|
|
27
|
-
function writeDescriptorFiles(descriptor) {
|
|
28
|
-
if (fs.existsSync(path.resolve(path.dirname(descriptorOutputPaths.sourceTs), ".."))) {
|
|
29
|
-
safeWrite(descriptorOutputPaths.sourceTs, createSourceTsOutput(descriptor));
|
|
30
|
-
}
|
|
34
|
+
function writeBundledDescriptorProjection(descriptor) {
|
|
31
35
|
safeWrite(descriptorOutputPaths.esmJs, createEsmOutput(descriptor));
|
|
32
36
|
safeWrite(descriptorOutputPaths.cjsJs, createCjsOutput(descriptor));
|
|
33
37
|
}
|
|
34
|
-
|
|
38
|
+
function writeDescriptorFiles(resolved, descriptor, browserTarget = "sidecar") {
|
|
39
|
+
const localDescriptorOutputPaths = resolveLocalDescriptorOutputPaths(resolved.projectRoot);
|
|
40
|
+
safeWrite(localDescriptorOutputPaths.json, createJsonOutput(descriptor));
|
|
41
|
+
safeWrite(localDescriptorOutputPaths.cjsJs, createCjsOutput(descriptor));
|
|
42
|
+
writeBundledDescriptorProjection(browserTarget === "bundle" ? descriptor : FALLBACK_DESCRIPTOR);
|
|
43
|
+
}
|
|
44
|
+
export { FALLBACK_DESCRIPTOR, writeDescriptorFiles };
|
|
@@ -45,4 +45,10 @@ const descriptorOutputPaths = {
|
|
|
45
45
|
esmJs: path.resolve(packageRoot, "dist/esm/generated/schema-descriptor.js"),
|
|
46
46
|
cjsJs: path.resolve(packageRoot, "dist/cjs/generated/schema-descriptor.cjs"),
|
|
47
47
|
};
|
|
48
|
-
|
|
48
|
+
function resolveLocalDescriptorOutputPaths(projectRoot) {
|
|
49
|
+
return {
|
|
50
|
+
json: path.resolve(projectRoot, ".cms0/generated/schema-descriptor.json"),
|
|
51
|
+
cjsJs: path.resolve(projectRoot, ".cms0/generated/schema-descriptor.cjs"),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export { descriptorOutputPaths, packageRoot, resolveLocalDescriptorOutputPaths };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// File watcher that rebuilds descriptors on entry changes.
|
|
2
2
|
import chokidar from "chokidar";
|
|
3
3
|
import { buildOnce } from "./build.js";
|
|
4
|
-
function startWatcher(resolved, afterBuild) {
|
|
4
|
+
function startWatcher(resolved, afterBuild, browserTarget = "sidecar", runInitialBuild = true) {
|
|
5
5
|
const watcher = chokidar.watch([resolved.entryFile], {
|
|
6
6
|
ignoreInitial: false,
|
|
7
7
|
});
|
|
8
8
|
const run = (after) => {
|
|
9
9
|
try {
|
|
10
|
-
const descriptor = buildOnce(resolved);
|
|
10
|
+
const descriptor = buildOnce(resolved, browserTarget);
|
|
11
11
|
if (after) {
|
|
12
12
|
Promise.resolve(after(descriptor)).catch((err) => {
|
|
13
13
|
console.error("cms0: post-build hook failed", err);
|
|
@@ -19,7 +19,9 @@ function startWatcher(resolved, afterBuild) {
|
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
// Trigger a build on startup and whenever the entry file changes.
|
|
22
|
-
|
|
22
|
+
if (runInitialBuild) {
|
|
23
|
+
watcher.on("ready", () => run(afterBuild));
|
|
24
|
+
}
|
|
23
25
|
watcher.on("change", () => run(afterBuild));
|
|
24
26
|
watcher.on("error", (error) => {
|
|
25
27
|
console.error("cms0: watcher error", error);
|