@cms0/cms0 0.2.17 → 0.2.18

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.
@@ -1,5 +1,198 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.schemaDescriptor = void 0;
4
- var schema_descriptor_1 = require("@cms0/cms0/generated/schema-descriptor");
5
- Object.defineProperty(exports, "schemaDescriptor", { enumerable: true, get: function () { return schema_descriptor_1.schemaDescriptor; } });
4
+ exports.readLocalSchemaDescriptor = readLocalSchemaDescriptor;
5
+ exports.getActiveSchemaDescriptor = getActiveSchemaDescriptor;
6
+ exports.syncActiveSchemaDescriptorGlobals = syncActiveSchemaDescriptorGlobals;
7
+ const schema_descriptor_1 = require("@cms0/cms0/generated/schema-descriptor");
8
+ const LOCAL_DESCRIPTOR_RELATIVE_SEGMENTS = [
9
+ ".cms0",
10
+ "generated",
11
+ "schema-descriptor.json",
12
+ ];
13
+ let cachedLocalDescriptor = null;
14
+ exports.schemaDescriptor = schema_descriptor_1.schemaDescriptor;
15
+ function getBuiltinModule(specifier) {
16
+ const runtimeProcess = globalThis.process;
17
+ if (typeof runtimeProcess?.getBuiltinModule === "function") {
18
+ try {
19
+ return runtimeProcess.getBuiltinModule(specifier);
20
+ }
21
+ catch {
22
+ // Ignore missing builtins and fall through.
23
+ }
24
+ }
25
+ try {
26
+ const runtimeRequire = Function("try { return require; } catch { return undefined; }")();
27
+ if (runtimeRequire) {
28
+ return runtimeRequire(specifier);
29
+ }
30
+ }
31
+ catch {
32
+ // Ignore runtime require failures outside Node/CommonJS.
33
+ }
34
+ return null;
35
+ }
36
+ function getNodeFs() {
37
+ return (getBuiltinModule("node:fs") ??
38
+ getBuiltinModule("fs"));
39
+ }
40
+ function getNodePath() {
41
+ return (getBuiltinModule("node:path") ??
42
+ getBuiltinModule("path"));
43
+ }
44
+ function getNodeUrl() {
45
+ return (getBuiltinModule("node:url") ??
46
+ getBuiltinModule("url"));
47
+ }
48
+ function isRecord(value) {
49
+ return !!value && typeof value === "object" && !Array.isArray(value);
50
+ }
51
+ function isFullDescriptor(value) {
52
+ return isRecord(value) && isRecord(value.models) && isRecord(value.roots);
53
+ }
54
+ function dedupePaths(values) {
55
+ const seen = new Set();
56
+ const result = [];
57
+ for (const value of values) {
58
+ const normalized = value.trim();
59
+ if (!normalized || seen.has(normalized))
60
+ continue;
61
+ seen.add(normalized);
62
+ result.push(normalized);
63
+ }
64
+ return result;
65
+ }
66
+ function parseStackFramePath(line) {
67
+ const urlModule = getNodeUrl();
68
+ const directMatch = line.match(/\((file:\/\/[^)]+|\/[^)]+):\d+:\d+\)$/) ??
69
+ line.match(/at (file:\/\/\S+|\/\S+):\d+:\d+$/);
70
+ const rawPath = directMatch?.[1];
71
+ if (!rawPath)
72
+ return null;
73
+ if (rawPath.startsWith("file://")) {
74
+ if (urlModule) {
75
+ try {
76
+ return urlModule.fileURLToPath(rawPath);
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ try {
83
+ return decodeURIComponent(new URL(rawPath).pathname);
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ return rawPath;
90
+ }
91
+ function resolveSchemaDescriptorSearchRoots() {
92
+ const pathModule = getNodePath();
93
+ if (!pathModule || typeof process === "undefined") {
94
+ return [];
95
+ }
96
+ const roots = [];
97
+ const projectRootOverride = process.env.CMS0_PROJECT_ROOT;
98
+ if (typeof projectRootOverride === "string" && projectRootOverride.trim()) {
99
+ roots.push(pathModule.resolve(projectRootOverride));
100
+ }
101
+ const descriptorPathOverride = process.env.CMS0_SCHEMA_DESCRIPTOR_PATH;
102
+ if (typeof descriptorPathOverride === "string" && descriptorPathOverride.trim()) {
103
+ roots.push(pathModule.dirname(pathModule.resolve(descriptorPathOverride)));
104
+ }
105
+ const stack = new Error().stack ?? "";
106
+ for (const line of stack.split("\n").slice(1)) {
107
+ const filePath = parseStackFramePath(line);
108
+ if (!filePath)
109
+ continue;
110
+ roots.push(pathModule.dirname(pathModule.resolve(filePath)));
111
+ }
112
+ if (typeof process.cwd === "function") {
113
+ roots.push(pathModule.resolve(process.cwd()));
114
+ }
115
+ return dedupePaths(roots);
116
+ }
117
+ function resolveLocalSchemaDescriptorPath(searchRoots) {
118
+ const fs = getNodeFs();
119
+ const pathModule = getNodePath();
120
+ if (!fs || !pathModule) {
121
+ return null;
122
+ }
123
+ const directOverride = process.env.CMS0_SCHEMA_DESCRIPTOR_PATH;
124
+ if (typeof directOverride === "string" && directOverride.trim()) {
125
+ const resolvedDirectOverride = pathModule.resolve(directOverride);
126
+ if (fs.existsSync(resolvedDirectOverride)) {
127
+ return resolvedDirectOverride;
128
+ }
129
+ }
130
+ for (const root of searchRoots) {
131
+ let current = pathModule.resolve(root);
132
+ while (true) {
133
+ const candidate = pathModule.join(current, ...LOCAL_DESCRIPTOR_RELATIVE_SEGMENTS);
134
+ if (fs.existsSync(candidate)) {
135
+ return candidate;
136
+ }
137
+ const parent = pathModule.dirname(current);
138
+ if (parent === current)
139
+ break;
140
+ current = parent;
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+ function readLocalSchemaDescriptor(searchRoots = resolveSchemaDescriptorSearchRoots()) {
146
+ const fs = getNodeFs();
147
+ if (!fs) {
148
+ return null;
149
+ }
150
+ const descriptorPath = resolveLocalSchemaDescriptorPath(searchRoots);
151
+ if (!descriptorPath) {
152
+ return null;
153
+ }
154
+ try {
155
+ const stat = fs.statSync(descriptorPath);
156
+ if (cachedLocalDescriptor &&
157
+ cachedLocalDescriptor.path === descriptorPath &&
158
+ cachedLocalDescriptor.mtimeMs === stat.mtimeMs) {
159
+ return cachedLocalDescriptor.descriptor;
160
+ }
161
+ const parsed = JSON.parse(fs.readFileSync(descriptorPath, "utf8"));
162
+ if (!isFullDescriptor(parsed)) {
163
+ return null;
164
+ }
165
+ cachedLocalDescriptor = {
166
+ descriptor: parsed,
167
+ mtimeMs: stat.mtimeMs,
168
+ path: descriptorPath,
169
+ };
170
+ return parsed;
171
+ }
172
+ catch {
173
+ return null;
174
+ }
175
+ }
176
+ function readGlobalSchemaDescriptor() {
177
+ const globals = globalThis;
178
+ const activeDescriptor = globals.__CMS0_CANVAS_SCHEMA_DESCRIPTOR__ ?? globals.__CMS0_SCHEMA_DESCRIPTOR__;
179
+ return isFullDescriptor(activeDescriptor) ? activeDescriptor : null;
180
+ }
181
+ function getActiveSchemaDescriptor() {
182
+ if (typeof window !== "undefined") {
183
+ return (readGlobalSchemaDescriptor() ??
184
+ schema_descriptor_1.schemaDescriptor);
185
+ }
186
+ return resolveActiveSchemaDescriptor();
187
+ }
188
+ function resolveActiveSchemaDescriptor() {
189
+ return (readLocalSchemaDescriptor() ??
190
+ schema_descriptor_1.schemaDescriptor);
191
+ }
192
+ function syncActiveSchemaDescriptorGlobals() {
193
+ const activeDescriptor = resolveActiveSchemaDescriptor();
194
+ const globals = globalThis;
195
+ globals.__CMS0_SCHEMA_DESCRIPTOR__ = activeDescriptor;
196
+ globals.__CMS0_CANVAS_SCHEMA_DESCRIPTOR__ = activeDescriptor;
197
+ return activeDescriptor;
198
+ }
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { attachCms0ProvenanceRoot, enableCms0ProvenanceTracking, isCms0CanvasTransportEnabled, registerCms0CollectionItemIdentity, } from "@cms0/cms0/provenance";
2
- import { schemaDescriptor } from "@cms0/cms0/schema-descriptors";
2
+ import { getActiveSchemaDescriptor, } 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 context.requestJson(`models/${modelName}/${id}`, {
580
- query: {
581
+ (await (async () => {
582
+ const query = {
581
583
  raw: 1,
582
584
  ...(context.options.locale ? { locale: context.options.locale } : {}),
583
- },
584
- }));
585
- return normalizeField(modelDescriptor, `models/${modelName}/${encodeURIComponent(id)}`, rawModel, context, nextTrail, isCollectionItem);
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
- validateResult(resource, normalizedItems, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
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
- validateResult(resource, normalizedItems, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
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
- validateResult(resource, normalized, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
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,7 @@ export function createCmsClient(descriptor, config) {
1340
1386
  return rootProxy;
1341
1387
  }
1342
1388
  export function cms0(config) {
1343
- return createCmsClient(schemaDescriptor, config);
1389
+ return createCmsClient(getActiveSchemaDescriptor(), config);
1344
1390
  }
1345
- export { activateCms0CanvasTransport, attachCms0ProvenanceRoot, captureCms0Provenance, enableCms0ProvenanceTracking, readCms0CanvasTransportCollectionItemId, readCms0CollectionItemIdentity, registerCms0LiveRoot, registerCms0CollectionItemIdentity, readCms0LiveRoots, readCms0ProvenanceValueMeta, restoreCms0CanvasTransportMetadata, unwrapCms0CanvasTransportValue, } from "./provenance.js";
1391
+ export { activateCms0CanvasTransport, attachCms0ProvenanceRoot, captureCms0Provenance, dehydrateCms0CanvasTransportValue, dehydrateCms0CanvasVisibleValue, enableCms0ProvenanceTracking, readCms0CanvasTransportCollectionItemId, readCms0CollectionItemIdentity, registerCms0LiveRoot, registerCms0CollectionItemIdentity, readCms0LiveRoots, readCms0ProvenanceValueMeta, restoreCms0CanvasTransportMetadata, unwrapCms0CanvasTransportValue, } from "./provenance.js";
1346
1392
  export { resolveLocalized, toNextMetadata, toOpenGraph, toTwitter, } from "./seo.js";
@@ -4,7 +4,7 @@ import { publishDescriptor } from "./publisher.js";
4
4
  import { writeDescriptorFiles } from "./descriptor-writer.js";
5
5
  function buildOnce(resolved) {
6
6
  const descriptor = buildDescriptor(resolved);
7
- writeDescriptorFiles(descriptor);
7
+ writeDescriptorFiles(resolved, descriptor);
8
8
  publishDescriptor(resolved, descriptor);
9
9
  return descriptor;
10
10
  }
@@ -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, fileURLToPath } from "url";
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
- if (isEsmPkg) {
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
- // eslint-disable-next-line @typescript-eslint/no-var-requires
94
- const required = require(resolvedPath);
95
- const config = required?.default ?? required?.config ?? required ?? {};
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
- if (previous === undefined) {
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}.mjs`);
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 pathToFileURL(outFile).href;
153
+ return outFile;
167
154
  }
168
- function cleanupTempModule(tempHref) {
155
+ function cleanupTempModule(tempPath) {
169
156
  try {
170
- const filePath = fileURLToPath(tempHref);
171
- fs.unlinkSync(filePath);
157
+ fs.unlinkSync(tempPath);
172
158
  }
173
159
  catch {
174
160
  // ignore cleanup failures
@@ -1,6 +1,6 @@
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
4
  function safeWrite(outputPath, output) {
5
5
  try {
6
6
  fs.mkdirSync(path.dirname(outputPath), { recursive: true });
@@ -24,11 +24,17 @@ function createEsmOutput(descriptor) {
24
24
  function createCjsOutput(descriptor) {
25
25
  return `// Auto-generated schema descriptor\nexports.schemaDescriptor = ${JSON.stringify(descriptor, null, 2)};\n`;
26
26
  }
27
- function writeDescriptorFiles(descriptor) {
27
+ function createJsonOutput(descriptor) {
28
+ return `${JSON.stringify(descriptor, null, 2)}\n`;
29
+ }
30
+ function writeDescriptorFiles(resolved, descriptor) {
31
+ const localDescriptorOutputPaths = resolveLocalDescriptorOutputPaths(resolved.projectRoot);
28
32
  if (fs.existsSync(path.resolve(path.dirname(descriptorOutputPaths.sourceTs), ".."))) {
29
33
  safeWrite(descriptorOutputPaths.sourceTs, createSourceTsOutput(descriptor));
30
34
  }
31
35
  safeWrite(descriptorOutputPaths.esmJs, createEsmOutput(descriptor));
32
36
  safeWrite(descriptorOutputPaths.cjsJs, createCjsOutput(descriptor));
37
+ safeWrite(localDescriptorOutputPaths.json, createJsonOutput(descriptor));
38
+ safeWrite(localDescriptorOutputPaths.cjsJs, createCjsOutput(descriptor));
33
39
  }
34
40
  export { 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
- export { descriptorOutputPaths, packageRoot };
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 };