@cms0/cms0 0.2.7 → 0.2.9

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,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toTwitter = exports.toOpenGraph = exports.toNextMetadata = exports.resolveLocalized = void 0;
3
4
  exports.createCmsClient = createCmsClient;
4
5
  exports.cms0 = cms0;
5
6
  const schema_descriptors_1 = require("@cms0/cms0/schema-descriptors");
@@ -109,6 +110,9 @@ function snakeCaseModelIdKey(modelName) {
109
110
  .toLowerCase();
110
111
  return `${snake}_id`;
111
112
  }
113
+ function isUuidLikeId(value) {
114
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
115
+ }
112
116
  function extractModelRefId(raw, modelName, options) {
113
117
  if (typeof raw === "string")
114
118
  return raw;
@@ -149,6 +153,15 @@ function extractModelRefId(raw, modelName, options) {
149
153
  function isPrimitiveDescriptor(desc) {
150
154
  return desc.kind === "primitive";
151
155
  }
156
+ function isEnumDescriptor(desc) {
157
+ return desc.kind === "enum" && Array.isArray(desc.values);
158
+ }
159
+ function isUnionDescriptor(desc) {
160
+ return desc.kind === "union" && Array.isArray(desc.anyOf);
161
+ }
162
+ function isScalarDescriptor(desc) {
163
+ return isPrimitiveDescriptor(desc) || isEnumDescriptor(desc) || isUnionDescriptor(desc);
164
+ }
152
165
  function isModelRefDescriptor(desc) {
153
166
  return desc.kind === "modelRef";
154
167
  }
@@ -166,7 +179,7 @@ function attachIdIfObject(value, id) {
166
179
  }
167
180
  return value;
168
181
  }
169
- function attachIdForPrimitiveDescriptor(descriptor, value, id) {
182
+ function attachIdForScalarDescriptor(descriptor, value, id) {
170
183
  const withId = attachIdIfObject(value, id);
171
184
  if (!withId || typeof withId !== "object" || Array.isArray(withId)) {
172
185
  return withId;
@@ -362,6 +375,23 @@ function coerceLocalizedRichTextValue(value, locale, defaultLocale) {
362
375
  }
363
376
  return projectLocalizedByLocale(normalized, locale);
364
377
  }
378
+ function coerceSeoValue(value) {
379
+ if (value == null)
380
+ return value;
381
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
382
+ return {};
383
+ }
384
+ const source = value;
385
+ const keys = Object.keys(source).filter((key) => key !== "id");
386
+ if (keys.length === 1 &&
387
+ keys[0] === "value" &&
388
+ source.value &&
389
+ typeof source.value === "object" &&
390
+ !Array.isArray(source.value)) {
391
+ return source.value;
392
+ }
393
+ return source;
394
+ }
365
395
  function coerceCustomTypeValue(descriptor, value, options) {
366
396
  const customType = descriptor.customType;
367
397
  if (!customType)
@@ -377,8 +407,81 @@ function coerceCustomTypeValue(descriptor, value, options) {
377
407
  if (customType === "LocalizedRichText") {
378
408
  return coerceLocalizedRichTextValue(value, options?.locale, options?.defaultLocale);
379
409
  }
410
+ if (customType === "Seo") {
411
+ return coerceSeoValue(value);
412
+ }
413
+ return value;
414
+ }
415
+ function coercePrimitiveValueByType(type, value) {
416
+ if (type === "string") {
417
+ if (value == null)
418
+ return "";
419
+ return typeof value === "string" ? value : String(value);
420
+ }
421
+ if (type === "number") {
422
+ if (typeof value === "number")
423
+ return Number.isFinite(value) ? value : null;
424
+ const parsed = Number(value);
425
+ return Number.isFinite(parsed) ? parsed : null;
426
+ }
427
+ if (type === "boolean") {
428
+ if (typeof value === "boolean")
429
+ return value;
430
+ if (value === "true" || value === "1" || value === 1)
431
+ return true;
432
+ if (value === "false" || value === "0" || value === 0)
433
+ return false;
434
+ return Boolean(value);
435
+ }
380
436
  return value;
381
437
  }
438
+ function coerceEnumValue(descriptor, value) {
439
+ const valueType = (descriptor.valueType ?? "string");
440
+ const coerced = coercePrimitiveValueByType(valueType, value);
441
+ const values = Array.isArray(descriptor.values)
442
+ ? descriptor.values
443
+ : [];
444
+ if (!values.length)
445
+ return coerced;
446
+ if (values.some((entry) => Object.is(entry, coerced))) {
447
+ return coerced;
448
+ }
449
+ return values[0];
450
+ }
451
+ function decodeUnionEnvelope(descriptor, raw) {
452
+ const decoded = (0, shared_1.decodeTaggedUnionValue)(raw);
453
+ if (!decoded)
454
+ return null;
455
+ const branches = Array.isArray(descriptor.anyOf)
456
+ ? descriptor.anyOf
457
+ : [];
458
+ if (!branches.length)
459
+ return null;
460
+ const branchKeys = (0, shared_1.getUnionBranchKeys)(descriptor);
461
+ const branchIndex = branchKeys.findIndex((key) => key === decoded.branchKey);
462
+ if (branchIndex < 0 || branchIndex >= branches.length)
463
+ return null;
464
+ const branchDescriptor = branches[branchIndex];
465
+ if (!branchDescriptor)
466
+ return null;
467
+ return {
468
+ branchDescriptor,
469
+ branchValue: decoded.value,
470
+ };
471
+ }
472
+ function decodeUnionValueFromRawField(descriptor, raw) {
473
+ const direct = decodeUnionEnvelope(descriptor, raw);
474
+ if (direct) {
475
+ return { ...direct, rowId: extractId(raw) };
476
+ }
477
+ if (raw && typeof raw === "object" && !Array.isArray(raw) && "value" in raw) {
478
+ const nested = decodeUnionEnvelope(descriptor, raw.value);
479
+ if (nested) {
480
+ return { ...nested, rowId: extractId(raw) };
481
+ }
482
+ }
483
+ return null;
484
+ }
382
485
  function ensureCollectionEnvelope(data, path) {
383
486
  if (Array.isArray(data)) {
384
487
  return { items: data, total: data.length };
@@ -450,12 +553,8 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
450
553
  const source = raw && typeof raw === "object" ? raw : {};
451
554
  const output = {};
452
555
  for (const [propertyName, propertyDescriptor] of Object.entries(descriptor.properties)) {
453
- if (isPrimitiveDescriptor(propertyDescriptor)) {
454
- output[propertyName] = coerceCustomTypeValue(propertyDescriptor, source[propertyName], {
455
- locale: context.options.locale,
456
- defaultLocale: context.options.defaultLocale,
457
- assetUrlBuilder: context.options.assetUrlBuilder,
458
- });
556
+ if (isScalarDescriptor(propertyDescriptor)) {
557
+ output[propertyName] = await normalizeField(propertyDescriptor, `${path}/${propertyName}`, source[propertyName], context, trail, false);
459
558
  continue;
460
559
  }
461
560
  if (isModelRefDescriptor(propertyDescriptor)) {
@@ -543,16 +642,14 @@ async function normalizeObjectField(descriptor, path, raw, context, trail, isCol
543
642
  async function normalizeArrayField(descriptor, path, raw, context, trail) {
544
643
  const envelope = ensureCollectionEnvelope(raw, path);
545
644
  const itemDescriptor = descriptor.items;
546
- if (isPrimitiveDescriptor(itemDescriptor)) {
547
- return envelope.items.map((row) => {
548
- const value = row && typeof row === "object" && "value" in row
549
- ? row.value
550
- : row;
645
+ if (isScalarDescriptor(itemDescriptor)) {
646
+ return mapWithConcurrency(envelope.items, context.options.modelNormalizationConcurrency, async (row) => {
647
+ const normalizedValue = await normalizeField(itemDescriptor, path, row, context, trail, true);
551
648
  if (context.options.includeIdMode !== "all") {
552
- return value;
649
+ return normalizedValue;
553
650
  }
554
651
  const id = extractId(row);
555
- const valueWithId = attachIdForPrimitiveDescriptor(itemDescriptor, value, id);
652
+ const valueWithId = attachIdForScalarDescriptor(itemDescriptor, normalizedValue, id);
556
653
  const isObjectValue = valueWithId && typeof valueWithId === "object" && !Array.isArray(valueWithId);
557
654
  if (!id) {
558
655
  return valueWithId;
@@ -583,6 +680,60 @@ async function normalizeArrayField(descriptor, path, raw, context, trail) {
583
680
  }
584
681
  return envelope.items;
585
682
  }
683
+ async function normalizeInlineField(descriptor, raw, context, trail, isCollectionItem) {
684
+ if (isPrimitiveDescriptor(descriptor)) {
685
+ return coerceCustomTypeValue(descriptor, raw, {
686
+ locale: context.options.locale,
687
+ defaultLocale: context.options.defaultLocale,
688
+ assetUrlBuilder: context.options.assetUrlBuilder,
689
+ });
690
+ }
691
+ if (isEnumDescriptor(descriptor)) {
692
+ return coerceEnumValue(descriptor, raw);
693
+ }
694
+ if (isUnionDescriptor(descriptor)) {
695
+ if (raw == null)
696
+ return raw;
697
+ const decoded = decodeUnionEnvelope(descriptor, raw);
698
+ if (!decoded) {
699
+ return null;
700
+ }
701
+ return normalizeInlineField(decoded.branchDescriptor, decoded.branchValue, context, trail, isCollectionItem);
702
+ }
703
+ if (isModelRefDescriptor(descriptor)) {
704
+ const refId = extractModelRefId(raw, descriptor.model, {
705
+ allowObjectIdFallback: true,
706
+ });
707
+ if (!refId) {
708
+ return missingModelRefValue(descriptor);
709
+ }
710
+ return normalizeModelRef(descriptor.model, refId, context, trail, isCollectionItem, raw);
711
+ }
712
+ if (isArrayDescriptor(descriptor)) {
713
+ const source = Array.isArray(raw) ? raw : [];
714
+ const normalized = await mapWithConcurrency(source, context.options.modelNormalizationConcurrency, async (entry) => normalizeInlineField(descriptor.items, entry, context, trail, true));
715
+ return coerceCustomTypeValue(descriptor, normalized, {
716
+ locale: context.options.locale,
717
+ defaultLocale: context.options.defaultLocale,
718
+ assetUrlBuilder: context.options.assetUrlBuilder,
719
+ });
720
+ }
721
+ if (isObjectDescriptor(descriptor)) {
722
+ const source = raw && typeof raw === "object" && !Array.isArray(raw)
723
+ ? raw
724
+ : {};
725
+ const output = {};
726
+ for (const [propertyName, propertyDescriptor] of Object.entries(descriptor.properties)) {
727
+ output[propertyName] = await normalizeInlineField(propertyDescriptor, source[propertyName], context, trail, false);
728
+ }
729
+ return coerceCustomTypeValue(descriptor, output, {
730
+ locale: context.options.locale,
731
+ defaultLocale: context.options.defaultLocale,
732
+ assetUrlBuilder: context.options.assetUrlBuilder,
733
+ });
734
+ }
735
+ return raw;
736
+ }
586
737
  async function normalizeField(descriptor, path, raw, context, trail, isCollectionItem) {
587
738
  if (isPrimitiveDescriptor(descriptor)) {
588
739
  const value = raw && typeof raw === "object" && "value" in raw
@@ -595,10 +746,33 @@ async function normalizeField(descriptor, path, raw, context, trail, isCollectio
595
746
  });
596
747
  if (shouldIncludeObjectId(context.options.includeIdMode, isCollectionItem)) {
597
748
  const id = extractId(raw);
598
- coerced = attachIdForPrimitiveDescriptor(descriptor, coerced, id);
749
+ coerced = attachIdForScalarDescriptor(descriptor, coerced, id);
750
+ }
751
+ return coerced;
752
+ }
753
+ if (isEnumDescriptor(descriptor)) {
754
+ const value = raw && typeof raw === "object" && "value" in raw
755
+ ? raw.value
756
+ : raw;
757
+ let coerced = coerceEnumValue(descriptor, value);
758
+ if (shouldIncludeObjectId(context.options.includeIdMode, isCollectionItem)) {
759
+ const id = extractId(raw);
760
+ coerced = attachIdForScalarDescriptor(descriptor, coerced, id);
599
761
  }
600
762
  return coerced;
601
763
  }
764
+ if (isUnionDescriptor(descriptor)) {
765
+ const decoded = decodeUnionValueFromRawField(descriptor, raw);
766
+ if (!decoded) {
767
+ return null;
768
+ }
769
+ let normalized = await normalizeInlineField(decoded.branchDescriptor, decoded.branchValue, context, trail, isCollectionItem);
770
+ if (shouldIncludeObjectId(context.options.includeIdMode, isCollectionItem)) {
771
+ const id = decoded.rowId ?? extractId(raw);
772
+ normalized = attachIdForScalarDescriptor(descriptor, normalized, id);
773
+ }
774
+ return normalized;
775
+ }
602
776
  if (isModelRefDescriptor(descriptor)) {
603
777
  const inlineModelDescriptor = context.modelDescriptors.get(descriptor.model);
604
778
  const inlineModel = inlineModelDescriptor
@@ -698,6 +872,140 @@ function buildSearchParams(query) {
698
872
  }
699
873
  return params;
700
874
  }
875
+ function isPlainObject(value) {
876
+ return !!value && typeof value === "object" && !Array.isArray(value);
877
+ }
878
+ function cloneValue(value) {
879
+ if (Array.isArray(value)) {
880
+ return value.map((item) => cloneValue(item));
881
+ }
882
+ if (isPlainObject(value)) {
883
+ const out = {};
884
+ for (const [key, child] of Object.entries(value)) {
885
+ out[key] = cloneValue(child);
886
+ }
887
+ return out;
888
+ }
889
+ return value;
890
+ }
891
+ function normalizeProjectionPaths(input) {
892
+ if (!input)
893
+ return [];
894
+ const values = Array.isArray(input) ? input : [input];
895
+ const unique = new Set();
896
+ for (const raw of values) {
897
+ for (const candidate of raw.split(",")) {
898
+ const path = candidate.trim();
899
+ if (!path)
900
+ continue;
901
+ const normalized = path
902
+ .split(".")
903
+ .map((part) => part.trim())
904
+ .filter(Boolean)
905
+ .join(".");
906
+ if (!normalized)
907
+ continue;
908
+ unique.add(normalized);
909
+ }
910
+ }
911
+ return Array.from(unique).map((path) => path.split("."));
912
+ }
913
+ function mergeProjectedValue(existing, incoming) {
914
+ if (incoming === undefined)
915
+ return existing;
916
+ if (existing === undefined)
917
+ return cloneValue(incoming);
918
+ if (Array.isArray(existing) && Array.isArray(incoming)) {
919
+ const max = Math.max(existing.length, incoming.length);
920
+ const out = [];
921
+ for (let index = 0; index < max; index++) {
922
+ const next = mergeProjectedValue(existing[index], incoming[index]);
923
+ if (next !== undefined)
924
+ out.push(next);
925
+ }
926
+ return out;
927
+ }
928
+ if (isPlainObject(existing) && isPlainObject(incoming)) {
929
+ const out = { ...existing };
930
+ for (const [key, child] of Object.entries(incoming)) {
931
+ out[key] = mergeProjectedValue(out[key], child);
932
+ }
933
+ return out;
934
+ }
935
+ return cloneValue(incoming);
936
+ }
937
+ function projectValueByPath(value, tokens) {
938
+ if (!tokens.length)
939
+ return cloneValue(value);
940
+ if (Array.isArray(value)) {
941
+ const projectedItems = value
942
+ .map((item) => projectValueByPath(item, tokens))
943
+ .filter((item) => item !== undefined);
944
+ return projectedItems.length ? projectedItems : undefined;
945
+ }
946
+ if (!isPlainObject(value))
947
+ return undefined;
948
+ const [head, ...rest] = tokens;
949
+ if (!head || !(head in value))
950
+ return undefined;
951
+ const child = projectValueByPath(value[head], rest);
952
+ if (child === undefined)
953
+ return undefined;
954
+ return { [head]: child };
955
+ }
956
+ function excludeValueByPath(value, tokens) {
957
+ if (!tokens.length)
958
+ return undefined;
959
+ if (Array.isArray(value)) {
960
+ return value.map((item) => excludeValueByPath(item, tokens));
961
+ }
962
+ if (!isPlainObject(value))
963
+ return value;
964
+ const [head, ...rest] = tokens;
965
+ if (!head || !(head in value))
966
+ return cloneValue(value);
967
+ const out = { ...value };
968
+ if (!rest.length) {
969
+ delete out[head];
970
+ return out;
971
+ }
972
+ const next = excludeValueByPath(out[head], rest);
973
+ if (next === undefined) {
974
+ delete out[head];
975
+ }
976
+ else {
977
+ out[head] = next;
978
+ }
979
+ return out;
980
+ }
981
+ function applyFieldProjection(value, fields, exclude) {
982
+ const includePaths = normalizeProjectionPaths(fields);
983
+ const excludePaths = normalizeProjectionPaths(exclude);
984
+ let projected = value;
985
+ if (includePaths.length) {
986
+ let included = undefined;
987
+ for (const pathTokens of includePaths) {
988
+ const partial = projectValueByPath(value, pathTokens);
989
+ included = mergeProjectedValue(included, partial);
990
+ }
991
+ projected =
992
+ included === undefined
993
+ ? Array.isArray(value)
994
+ ? []
995
+ : isPlainObject(value)
996
+ ? {}
997
+ : undefined
998
+ : included;
999
+ }
1000
+ if (excludePaths.length) {
1001
+ let excluded = projected;
1002
+ for (const pathTokens of excludePaths) {
1003
+ excluded = excludeValueByPath(excluded, pathTokens);
1004
+ }
1005
+ projected = excluded;
1006
+ }
1007
+ return projected;
1008
+ }
701
1009
  async function requestJson(baseUrl, apiKey, path, options) {
702
1010
  const params = buildSearchParams(options?.query);
703
1011
  const url = `${baseUrl}/${path}${params.toString() ? `?${params.toString()}` : ""}`;
@@ -759,6 +1067,16 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
759
1067
  const query = {
760
1068
  ...(options?.query ?? {}),
761
1069
  };
1070
+ if (options?.fields !== undefined && query.fields === undefined) {
1071
+ query.fields = Array.isArray(options.fields)
1072
+ ? options.fields.join(",")
1073
+ : options.fields;
1074
+ }
1075
+ if (options?.exclude !== undefined && query.exclude === undefined) {
1076
+ query.exclude = Array.isArray(options.exclude)
1077
+ ? options.exclude.join(",")
1078
+ : options.exclude;
1079
+ }
762
1080
  const shouldNormalize = responseMode !== "raw";
763
1081
  if (shouldNormalize && query.raw === undefined) {
764
1082
  query.raw = 1;
@@ -808,7 +1126,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
808
1126
  });
809
1127
  }
810
1128
  if (!shouldNormalize) {
811
- return rawData;
1129
+ return applyFieldProjection(rawData, options?.fields, options?.exclude);
812
1130
  }
813
1131
  const modelDescriptors = new Map();
814
1132
  for (const modelName of Object.keys(descriptor.models ?? {})) {
@@ -843,9 +1161,10 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
843
1161
  };
844
1162
  const normalizedItems = await normalizeArrayField(descriptorForCollection, resource.path, envelope, context, new Set());
845
1163
  if (responseMode === "envelope") {
1164
+ const projectedItems = applyFieldProjection(normalizedItems, options?.fields, options?.exclude);
846
1165
  validateResult(resource, normalizedItems, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
847
1166
  return {
848
- items: normalizedItems,
1167
+ items: Array.isArray(projectedItems) ? projectedItems : normalizedItems,
849
1168
  total: envelope.total,
850
1169
  };
851
1170
  }
@@ -854,7 +1173,7 @@ async function runResource(resource, descriptor, baseUrl, apiKey, defaultLocale,
854
1173
  }
855
1174
  const normalized = await normalizeField(resource.descriptor, normalizePath, rawData, context, new Set(), false);
856
1175
  validateResult(resource, normalized, descriptor, includeIdMode, zodSchemas, modelZodSchemas);
857
- return normalized;
1176
+ return applyFieldProjection(normalized, options?.fields, options?.exclude);
858
1177
  }
859
1178
  function createCmsClient(descriptor, config) {
860
1179
  const baseUrl = normalizeBaseUrl(config.apiConfig?.baseUrl);
@@ -917,3 +1236,8 @@ function createCmsClient(descriptor, config) {
917
1236
  function cms0(config) {
918
1237
  return createCmsClient(schema_descriptors_1.schemaDescriptor, config);
919
1238
  }
1239
+ var seo_js_1 = require("./seo.cjs");
1240
+ Object.defineProperty(exports, "resolveLocalized", { enumerable: true, get: function () { return seo_js_1.resolveLocalized; } });
1241
+ Object.defineProperty(exports, "toNextMetadata", { enumerable: true, get: function () { return seo_js_1.toNextMetadata; } });
1242
+ Object.defineProperty(exports, "toOpenGraph", { enumerable: true, get: function () { return seo_js_1.toOpenGraph; } });
1243
+ Object.defineProperty(exports, "toTwitter", { enumerable: true, get: function () { return seo_js_1.toTwitter; } });