@phtngyn/cms 0.2.0-beta.3 → 0.2.0-beta.5

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.
Files changed (121) hide show
  1. package/README.md +31 -13
  2. package/package.json +7 -7
  3. package/src/ApplicationTypes.d.ts +3 -3
  4. package/src/Authoring.d.ts +11 -3
  5. package/src/{Configuration-BU-UyGoH.js → Configuration-JQ1qyaU0.js} +347 -189
  6. package/src/Configuration-JQ1qyaU0.js.map +1 -0
  7. package/src/Configuration.d.ts +1 -1
  8. package/src/Content.d.ts +7 -6
  9. package/src/Content.js +106 -47
  10. package/src/Content.js.map +1 -1
  11. package/src/ContentTransfer-BxwswWn3.js +86 -0
  12. package/src/ContentTransfer-BxwswWn3.js.map +1 -0
  13. package/src/{ContentTransfer-CRQet3QJ.d.ts → ContentTransfer-Z9FHyLez.d.ts} +33 -9
  14. package/src/{DocumentEngine-ClhFXW9M.js → DocumentEngine-j6Vi7nQo.js} +279 -178
  15. package/src/DocumentEngine-j6Vi7nQo.js.map +1 -0
  16. package/src/DocumentModel.d.ts +9 -2
  17. package/src/DocumentModel.js +17 -4
  18. package/src/DocumentModel.js.map +1 -1
  19. package/src/{Forms-DoB56hfe.js → Forms-CX-hwmJF.js} +18 -11
  20. package/src/Forms-CX-hwmJF.js.map +1 -0
  21. package/src/{Forms-BbkE7QfC.js → Forms-CfNPTiCr.js} +2 -2
  22. package/src/{Forms-BbkE7QfC.js.map → Forms-CfNPTiCr.js.map} +1 -1
  23. package/src/Media-j1bTritO.js +112 -0
  24. package/src/Media-j1bTritO.js.map +1 -0
  25. package/src/Module.js +0 -4
  26. package/src/Module.js.map +1 -1
  27. package/src/References-CCPDAPve.js +45 -0
  28. package/src/References-CCPDAPve.js.map +1 -0
  29. package/src/{S3MediaObjects-CR3p1WTg.js → S3MediaObjects-BRuPQbxl.js} +454 -516
  30. package/src/S3MediaObjects-BRuPQbxl.js.map +1 -0
  31. package/src/cli/content.js +6 -5
  32. package/src/cli/content.js.map +1 -1
  33. package/src/cli.js +16 -3
  34. package/src/cli.js.map +1 -1
  35. package/src/{connection-D5S9Iph-.js → connection-BP80q4d-.js} +2 -2
  36. package/src/{connection-D5S9Iph-.js.map → connection-BP80q4d-.js.map} +1 -1
  37. package/src/{dev-lifecycle-D3f9-upa.js → dev-lifecycle-DdBluSiR.js} +26 -6
  38. package/src/dev-lifecycle-DdBluSiR.js.map +1 -0
  39. package/src/{installation-fZtNxtoW.js → installation-BoG8E3nJ.js} +7982 -7155
  40. package/src/installation-BoG8E3nJ.js.map +1 -0
  41. package/src/internal/authoring-catalog.js +2 -2
  42. package/src/internal/authoring-compiler.js +35 -9
  43. package/src/internal/authoring-compiler.js.map +1 -1
  44. package/src/internal/authoring-metadata.js +14 -5
  45. package/src/internal/authoring-metadata.js.map +1 -1
  46. package/src/internal/configuration.js +27 -3
  47. package/src/internal/configuration.js.map +1 -1
  48. package/src/internal/dev-lifecycle.js +2 -2
  49. package/src/internal/document-model.js +134 -43
  50. package/src/internal/document-model.js.map +1 -1
  51. package/src/internal/field-metadata.js +2 -1
  52. package/src/internal/field-metadata.js.map +1 -1
  53. package/src/internal/form-capabilities.js +1 -1
  54. package/src/{location-CPX7oj0o.js → location-CMNGO3lU.js} +2 -2
  55. package/src/{location-CPX7oj0o.js.map → location-CMNGO3lU.js.map} +1 -1
  56. package/src/{metadata-BEZPWh-3.js → metadata-UzZmjwxM.js} +2 -2
  57. package/src/{metadata-BEZPWh-3.js.map → metadata-UzZmjwxM.js.map} +1 -1
  58. package/src/{models-C9kF9JlQ.js → models-5j67LMqW.js} +4 -4
  59. package/src/{models-C9kF9JlQ.js.map → models-5j67LMqW.js.map} +1 -1
  60. package/src/{models-ChRxBChy.js → models-VmGmG19-.js} +42 -130
  61. package/src/models-VmGmG19-.js.map +1 -0
  62. package/src/runtime/delivery/DocumentRenderer.js +5 -1
  63. package/src/runtime/delivery/DocumentRenderer.js.map +1 -1
  64. package/src/runtime/delivery/context.js +1 -1
  65. package/src/runtime/delivery/document-page.js +1 -5
  66. package/src/runtime/delivery/document-page.js.map +1 -1
  67. package/src/runtime/delivery/plugin.js +34 -5
  68. package/src/runtime/delivery/plugin.js.map +1 -1
  69. package/src/runtime/delivery/preview.d.ts +1 -1
  70. package/src/runtime/delivery/types.d.ts +1 -1
  71. package/src/runtime/delivery/useCmsDocumentSeo.d.ts +2 -1
  72. package/src/runtime/delivery/useCmsDocumentSeo.js +5 -4
  73. package/src/runtime/delivery/useCmsDocumentSeo.js.map +1 -1
  74. package/src/runtime/forms/document.js +2 -2
  75. package/src/runtime/forms/useCmsForm.js +2 -2
  76. package/src/runtime/installation.js +1 -1
  77. package/src/runtime/plugin.js +1 -1
  78. package/src/runtime/preview/CmsStudioPreviewMarker.js +18 -4
  79. package/src/runtime/preview/CmsStudioPreviewMarker.js.map +1 -1
  80. package/src/runtime/preview/connection.js +1 -1
  81. package/src/runtime/preview/controller.js +65 -74
  82. package/src/runtime/preview/controller.js.map +1 -1
  83. package/src/runtime/preview/overlay.js +85 -28
  84. package/src/runtime/preview/overlay.js.map +1 -1
  85. package/src/runtime/preview/plugin.client.js +4 -4
  86. package/src/runtime/preview/preview.css +50 -36
  87. package/src/runtime/preview/protocol.d.ts +11 -5
  88. package/src/runtime/preview/protocol.js +17 -5
  89. package/src/runtime/preview/protocol.js.map +1 -1
  90. package/src/runtime/preview/regions.js +142 -0
  91. package/src/runtime/preview/regions.js.map +1 -0
  92. package/src/runtime/preview/store.js +1 -1
  93. package/src/runtime/sitemap.js +9 -10
  94. package/src/runtime/sitemap.js.map +1 -1
  95. package/src/{scalars-B2lgGUnj.js → scalars-CVZqT3L0.js} +2 -6
  96. package/src/scalars-CVZqT3L0.js.map +1 -0
  97. package/src/{store-CCsjHaZm.js → store-PKKB8xa2.js} +49 -66
  98. package/src/store-PKKB8xa2.js.map +1 -0
  99. package/src/{types-DutFRtaG.d.ts → types-DZ0IV-Ow.d.ts} +14 -3
  100. package/studio/CreateDocumentDialog-vQ8hDs2C.js +253 -0
  101. package/studio/{documentPlanning-CDb31mnl.js → documentPlanning-Bx3W9KkD.js} +12054 -12085
  102. package/studio/{editor-Bzlh3zXt.js → editor-P2rB7Csp.js} +11228 -11927
  103. package/studio/host.css +1 -1
  104. package/studio/host.js +23595 -24903
  105. package/studio/rolldown-runtime-Dy4uBu1J.js +11 -0
  106. package/src/Configuration-BU-UyGoH.js.map +0 -1
  107. package/src/ContentTransfer-BhF9G56s.js +0 -57
  108. package/src/ContentTransfer-BhF9G56s.js.map +0 -1
  109. package/src/DocumentEngine-ClhFXW9M.js.map +0 -1
  110. package/src/Forms-DoB56hfe.js.map +0 -1
  111. package/src/S3MediaObjects-CR3p1WTg.js.map +0 -1
  112. package/src/dev-lifecycle-D3f9-upa.js.map +0 -1
  113. package/src/installation-fZtNxtoW.js.map +0 -1
  114. package/src/models-ChRxBChy.js.map +0 -1
  115. package/src/runtime/preview/targets.js +0 -101
  116. package/src/runtime/preview/targets.js.map +0 -1
  117. package/src/scalars-B2lgGUnj.js.map +0 -1
  118. package/src/store-CCsjHaZm.js.map +0 -1
  119. package/studio/CreateDocumentDialog-DJ8c0WZb.js +0 -256
  120. package/studio/dom-to-image-more.min-D6jGGdrW.js +0 -877
  121. package/studio/rolldown-runtime-B0aSnxlc.js +0 -20
@@ -1,7 +1,8 @@
1
- import { Ct as CollectionName, St as AddressPattern, Tt as SourceDirectory, b as MarkdownNode, n as CmsMarkdownDocument, wt as Locale } from "./models-ChRxBChy.js";
2
- import { r as FormCapabilities } from "./Forms-BbkE7QfC.js";
3
- import { c as resolveCmsComarkAttributes, d as createCmsComarkRenderData, l as withCmsComarkProps, r as formStructureIssues, s as projectCmsEditorComponentAttributes } from "./Forms-DoB56hfe.js";
4
- import { a as createEditorNodeId, n as editorMetadata, o as editorNodeId } from "./metadata-BEZPWh-3.js";
1
+ import { S as MarkdownNode, at as Locale, it as CollectionName, n as CmsMarkdownDocument, ot as RoutePattern } from "./models-VmGmG19-.js";
2
+ import { i as decodeManagedMediaReference, r as decodeAuthoredDocumentReference } from "./References-CCPDAPve.js";
3
+ import { r as FormCapabilities } from "./Forms-CfNPTiCr.js";
4
+ import { c as resolveCmsComarkAttributes, d as createCmsComarkRenderData, l as withCmsComarkProps, r as formStructureIssues, s as projectCmsEditorComponentAttributes } from "./Forms-CX-hwmJF.js";
5
+ import { a as createEditorNodeId, n as editorMetadata, o as editorNodeId } from "./metadata-UzZmjwxM.js";
5
6
  import { Effect, Option, Predicate, Schema } from "effect";
6
7
  import { Extension } from "@tiptap/core";
7
8
  import { ySyncPluginKey } from "@tiptap/y-tiptap";
@@ -29,7 +30,7 @@ function finiteNumberAttribute(value, fallback) {
29
30
 
30
31
  //#endregion
31
32
  //#region ../document/src/internal/editor/image.ts
32
- const isJsonObject$2 = Schema.is(Schema.JsonObject);
33
+ const isJsonObject$3 = Schema.is(Schema.JsonObject);
33
34
  const EditorImageLayout = Schema.Literals([
34
35
  "natural",
35
36
  "content",
@@ -71,7 +72,7 @@ function normalizeEditorImageLayout(value) {
71
72
  return isEditorImageLayout(value) ? value : "natural";
72
73
  }
73
74
  function jsonObject$1(value) {
74
- return isJsonObject$2(value) ? value : {};
75
+ return isJsonObject$3(value) ? value : {};
75
76
  }
76
77
  function stringValue(value) {
77
78
  return Predicate.isString(value) || Predicate.isNumber(value) ? String(value) : "";
@@ -105,7 +106,7 @@ function isComarkMark(tag) {
105
106
 
106
107
  //#endregion
107
108
  //#region ../document/src/internal/comark/to-editor.ts
108
- const isJsonObject$1 = Schema.is(Schema.JsonObject);
109
+ const isJsonObject$2 = Schema.is(Schema.JsonObject);
109
110
  const OPAQUE_BLOCK_TAGS = /* @__PURE__ */ new Set([
110
111
  "math",
111
112
  "table",
@@ -384,7 +385,7 @@ function elementChildren(node) {
384
385
  return children;
385
386
  }
386
387
  function jsonObject(value) {
387
- return isJsonObject$1(value) ? value : {};
388
+ return isJsonObject$2(value) ? value : {};
388
389
  }
389
390
  function mergeJsonAttributes(attributes, additions) {
390
391
  return compactJsonAttributes(Object.assign({}, attributes, additions));
@@ -664,6 +665,181 @@ var EditorDocumentInvalid = class extends Schema.TaggedError()("EditorDocument.I
664
665
  }) {};
665
666
  var EditorRenderFailed = class extends Schema.TaggedError()("EditorDocument.RenderFailed", { cause: Schema.Defect() }) {};
666
667
 
668
+ //#endregion
669
+ //#region ../document/src/StructuredFields.ts
670
+ function compileStructuredFields(definitions) {
671
+ const fields = new Map(definitions.map((field) => [field.name, field]));
672
+ if (fields.size !== definitions.length) throw new Error("Structured field names must be unique.");
673
+ for (const field of definitions) {
674
+ if (field.defaultValue === void 0) continue;
675
+ const issue = fieldIssue(field, field.defaultValue, "working");
676
+ if (issue) throw new Error(`Invalid default for ${field.label}: ${issue.message}`);
677
+ }
678
+ const listField = (name) => {
679
+ const field = fields.get(name);
680
+ if (!field || field.type !== "array" || field.input !== "list") throw new Error(`Structured field "${name}" is not a repeatable object.`);
681
+ return field;
682
+ };
683
+ const validateDetailed = (value, purpose) => {
684
+ const issues = [];
685
+ for (const name of Object.keys(value)) if (!fields.has(name)) issues.push({
686
+ message: `${name} is not editable.`,
687
+ path: [name]
688
+ });
689
+ for (const field of definitions) {
690
+ const issue = fieldIssue(field, value[field.name] ?? (value[field.name] === null ? null : field.defaultValue), purpose, [field.name]);
691
+ if (issue) issues.push(issue);
692
+ }
693
+ return issues;
694
+ };
695
+ return {
696
+ create: () => createObject(definitions),
697
+ set(current, name, value) {
698
+ const field = fields.get(name);
699
+ if (!field) throw new Error(`Structured field "${name}" is unavailable.`);
700
+ const issue = fieldIssue(field, value, "working");
701
+ if (issue) throw new Error(issue.message);
702
+ const next = Object.fromEntries(Object.entries(current));
703
+ if (value === void 0) delete next[name];
704
+ else next[name] = structuredClone(value);
705
+ return next;
706
+ },
707
+ validate: (value, purpose) => validateDetailed(value, purpose).map((issue) => issue.message),
708
+ validateDetailed,
709
+ validateIdentities: (value) => identityIssues(definitions, value),
710
+ createListItem(name) {
711
+ return createObject(listField(name).fields ?? [], true);
712
+ },
713
+ duplicateListItem(name, item) {
714
+ const field = listField(name);
715
+ const copied = cloneWithFreshIdentities(item, field.fields ?? []);
716
+ return Object.assign(createObject(field.fields ?? [], true), copied, { id: createEditorNodeId() });
717
+ },
718
+ moveListItem(items, id, offset) {
719
+ const index = items.findIndex((item) => item.id === id);
720
+ const target = index + offset;
721
+ if (index < 0 || target < 0 || target >= items.length) return items;
722
+ const next = [...items];
723
+ const item = next.splice(index, 1)[0];
724
+ if (!item) return items;
725
+ next.splice(target, 0, item);
726
+ return next;
727
+ },
728
+ removeListItem: (items, id) => items.filter((item) => item.id !== id)
729
+ };
730
+ }
731
+ function identityIssues(fields, value, parent = []) {
732
+ return fields.flatMap((field) => {
733
+ const current = value[field.name];
734
+ const path = [...parent, field.name];
735
+ if (field.type === "object" && isJsonObject$1(current)) return identityIssues(field.fields ?? [], current, path);
736
+ if (field.type !== "array" || !Array.isArray(current)) return [];
737
+ const seen = /* @__PURE__ */ new Set();
738
+ return current.flatMap((item, index) => {
739
+ if (!isJsonObject$1(item)) return [];
740
+ const id = item.id;
741
+ const issue = !Predicate.isString(id) || !id || seen.has(id) ? [{
742
+ message: `${field.label} items need unique identities.`,
743
+ path: [
744
+ ...path,
745
+ index,
746
+ "id"
747
+ ]
748
+ }] : [];
749
+ if (Predicate.isString(id) && id) seen.add(id);
750
+ return [...issue, ...identityIssues(field.fields ?? [], item, [...path, index])];
751
+ });
752
+ });
753
+ }
754
+ function createObject(fields, listItem = false) {
755
+ return {
756
+ ...listItem ? { id: createEditorNodeId() } : void 0,
757
+ ...Object.fromEntries(fields.flatMap((field) => field.required || field.defaultValue !== void 0 ? [[field.name, structuredClone(field.defaultValue ?? defaultValue(field))]] : []))
758
+ };
759
+ }
760
+ function defaultValue(field) {
761
+ if (field.type === "object") return createObject(field.fields ?? []);
762
+ if (field.type === "array") return [];
763
+ if (field.type === "string[]") return [];
764
+ if (field.type === "number") return 0;
765
+ if (field.type === "boolean") return false;
766
+ return field.options[0]?.value ?? "";
767
+ }
768
+ function cloneWithFreshIdentities(value, fields) {
769
+ const byName = new Map(fields.map((field) => [field.name, field]));
770
+ const copy = {};
771
+ for (const [name, child] of Object.entries(value)) {
772
+ if (name === "id") continue;
773
+ const field = byName.get(name);
774
+ if (field?.type === "object" && isJsonObject$1(child)) copy[name] = cloneWithFreshIdentities(child, field.fields ?? []);
775
+ else if (field?.type === "array" && Array.isArray(child)) copy[name] = child.map((item) => isJsonObject$1(item) ? Object.assign(cloneWithFreshIdentities(item, field.fields ?? []), { id: createEditorNodeId() }) : structuredClone(item));
776
+ else copy[name] = structuredClone(child);
777
+ }
778
+ return copy;
779
+ }
780
+ function fieldIssue(field, value, purpose, path = [field.name]) {
781
+ const issue = (message, issuePath = path) => ({
782
+ message,
783
+ path: issuePath
784
+ });
785
+ if (value === void 0) return field.required && purpose === "publish" ? issue(`${field.label} is required.`) : void 0;
786
+ if (value === null && field.nullable) return void 0;
787
+ if (!(field.type === "string" || field.type === "date" ? Predicate.isString(value) : field.type === "number" ? Predicate.isNumber(value) && Number.isFinite(value) : field.type === "boolean" ? Predicate.isBoolean(value) : field.type === "string[]" ? Array.isArray(value) && value.every(Predicate.isString) : field.type === "array" ? Array.isArray(value) : isJsonObject$1(value))) return issue(`${field.label} must be ${field.type}.`);
788
+ if (field.type === "object" || field.type === "array") {
789
+ const entries = Array.isArray(value) ? value : [value];
790
+ const ids = /* @__PURE__ */ new Set();
791
+ for (const [index, entry] of entries.entries()) {
792
+ const entryPath = field.type === "array" ? [...path, index] : path;
793
+ if (!isJsonObject$1(entry)) return issue(`${field.label} must contain objects.`, entryPath);
794
+ if (field.type === "array") {
795
+ const id = entry.id;
796
+ if (!Predicate.isString(id) || !id || ids.has(id)) return issue(`${field.label} items need unique identities.`, [...entryPath, "id"]);
797
+ ids.add(id);
798
+ }
799
+ for (const key of Object.keys(entry)) if (!(field.type === "array" && key === "id") && !field.fields?.some((child) => child.name === key)) return issue(`${field.label} contains an unavailable field.`, [...entryPath, key]);
800
+ for (const child of field.fields ?? []) {
801
+ const childIssue = fieldIssue(child, entry[child.name], purpose, [...entryPath, child.name]);
802
+ if (childIssue) return {
803
+ ...childIssue,
804
+ message: `${field.label}: ${childIssue.message}`
805
+ };
806
+ }
807
+ }
808
+ }
809
+ if (purpose === "publish" && field.required && value === "" && [
810
+ "image",
811
+ "link",
812
+ "document",
813
+ "media"
814
+ ].includes(field.input)) return issue(`${field.label} is required.`);
815
+ if (purpose === "publish" && Predicate.isString(value) && value && (field.input === "image" || field.input === "link") && !(field.input === "link" && decodeAuthoredDocumentReference(value)) && !validAuthoringUrl(value, field.input)) return issue(`${field.label} needs a valid ${field.input === "image" ? "image URL" : "link"}.`);
816
+ if (purpose === "publish" && Predicate.isString(value) && value && field.input === "document" && !decodeAuthoredDocumentReference(value)) return issue(`${field.label} needs a valid document reference.`);
817
+ if (purpose === "publish" && Predicate.isString(value) && value && field.input === "media" && !decodeManagedMediaReference(value)) return issue(`${field.label} needs a valid managed-media reference.`);
818
+ if (field.options.length) {
819
+ if ((Array.isArray(value) ? value : [value]).some((entry) => !field.options.some((option) => option.value === entry))) return issue(`${field.label} must use only available choices.`);
820
+ }
821
+ }
822
+ function isJsonObject$1(value) {
823
+ return Schema.is(Schema.JsonObject)(value);
824
+ }
825
+ /** URL values remain ordinary strings; identity-backed references use dedicated field inputs. */
826
+ function validAuthoringUrl(value, input) {
827
+ if (value.trim() !== value || value.includes("\\")) return false;
828
+ for (const character of value) if (character.charCodeAt(0) <= 32) return false;
829
+ if (value.startsWith("/") && !value.startsWith("//")) return true;
830
+ if (input === "link" && value.startsWith("#")) return true;
831
+ try {
832
+ const url = new URL(value);
833
+ return [
834
+ "https:",
835
+ "http:",
836
+ ...input === "link" ? ["mailto:", "tel:"] : []
837
+ ].includes(url.protocol);
838
+ } catch {
839
+ return false;
840
+ }
841
+ }
842
+
667
843
  //#endregion
668
844
  //#region ../document/src/AuthoringSchema.ts
669
845
  const parseAuthoringContent = Effect.fn("AuthoringSchema.parseContent")(function* (markdown) {
@@ -684,7 +860,7 @@ function collectionComponents(components, body) {
684
860
  /** Configuration errors belong to developers, before an installation adopts the catalog. */
685
861
  function validateCollectionBody(components, body) {
686
862
  return [
687
- ...components.flatMap((component) => component.authoring.slots.flatMap((slot) => [...(slot.blocks ?? []).filter((name) => !components.some((candidate) => candidate.name === name)).map((name) => `Unknown block "${name}" in ${slot.label}.`), ...(slot.groups ?? []).filter((group) => !components.some((candidate) => candidate.authoring.group === group)).map((group) => `Unknown block group "${group}" in ${slot.label}.`)])),
863
+ ...components.flatMap((component) => component.authoring.slots.flatMap((slot) => slot.input === "blocks" ? [...(slot.blocks ?? []).filter((name) => !components.some((candidate) => candidate.name === name)).map((name) => `Unknown block "${name}" in ${slot.label}.`), ...(slot.groups ?? []).filter((group) => !components.some((candidate) => candidate.authoring.group === group)).map((group) => `Unknown block group "${group}" in ${slot.label}.`)] : [])),
688
864
  ...(body?.blocks ?? []).filter((name) => !components.some((component) => component.name === name)).map((name) => `Unknown block "${name}".`),
689
865
  ...(body?.groups ?? []).filter((group) => !components.some((component) => component.authoring.group === group)).map((group) => `Unknown block group "${group}".`)
690
866
  ];
@@ -692,16 +868,28 @@ function validateCollectionBody(components, body) {
692
868
  function contentIssues(node, schemas, allowed, purpose, collectionAllowed = allowed) {
693
869
  const issues = [];
694
870
  if (node.type === "cmsBlock") {
695
- const name = String(node.attrs?.tag).toLowerCase();
871
+ const name = String(node.attrs?.tag ?? "unknown").toLowerCase() || "unknown";
696
872
  const schema = schemas.get(name);
697
- if (!schema) issues.push("This block is unavailable. Ask your site developer to restore its definition.");
873
+ if (!schema) issues.push({
874
+ component: name,
875
+ message: "This block is unavailable. Ask your site developer to restore its definition."
876
+ });
698
877
  else {
699
- if (!allowed.has(name)) issues.push(`${schema.definition.label} is not allowed in this content region.`);
700
- issues.push(...schema.validate(node, purpose));
701
- if (purpose === "publish" && schema.definition.form) issues.push(...formStructureIssues(node.attrs?.props?.steps, schema.definition.form, name));
878
+ if (!allowed.has(name)) issues.push({
879
+ component: name,
880
+ message: `${schema.definition.label} is not allowed in this content region.`
881
+ });
882
+ issues.push(...schema.validateDetailed(node, purpose));
883
+ if (purpose === "publish" && schema.definition.form) issues.push(...formStructureIssues(node.attrs?.props?.steps, schema.definition.form, name).map((message) => ({
884
+ component: name,
885
+ message
886
+ })));
702
887
  }
703
888
  }
704
- if (node.type === "cmsOpaque" || node.type === "cmsInlineOpaque") issues.push("This content is unsupported. Ask your site developer to update it before publishing.");
889
+ if (node.type === "cmsOpaque" || node.type === "cmsInlineOpaque") issues.push({
890
+ component: String(node.attrs?.tag ?? node.type),
891
+ message: "This content is unsupported. Ask your site developer to update it before publishing."
892
+ });
705
893
  if (node.type === "cmsBlock") {
706
894
  const schema = schemas.get(String(node.attrs?.tag).toLowerCase());
707
895
  for (const region of node.content ?? []) {
@@ -726,18 +914,11 @@ function createAuthoringDocumentValidator(components) {
726
914
  };
727
915
  }
728
916
  function createAuthoringSchema(name, definition) {
917
+ const structuredFields = compileStructuredFields(definition.props);
729
918
  const fields = new Map(definition.props.map((field) => [field.name, field]));
730
919
  const slots = new Map(definition.slots.map((slot) => [slot.name, slot]));
731
920
  if (fields.size !== definition.props.length || slots.size !== definition.slots.length) throw new Error(`${definition.label}: authoring names must be unique.`);
732
- const initialProps = {
733
- ...Object.fromEntries(definition.props.flatMap((field) => field.defaultValue === void 0 ? [] : [[field.name, field.defaultValue]])),
734
- ...definition.initialProps
735
- };
736
- for (const field of definition.props) {
737
- if (field.defaultValue === void 0) continue;
738
- const issue = propertyIssue(field, field.defaultValue, "working");
739
- if (issue) throw new Error(`Invalid default for ${definition.label}: ${issue}`);
740
- }
921
+ const initialProps = Object.assign({}, structuredFields.create(), definition.initialProps);
741
922
  function createSlot(slotName) {
742
923
  const slot = slots.get(slotName);
743
924
  if (!slot) throw new Error(`${definition.label}: unknown content region ${slotName}.`);
@@ -767,81 +948,57 @@ function createAuthoringSchema(name, definition) {
767
948
  content: definition.slots.filter((slot) => slot.required || slot.initial !== void 0).map((slot) => createSlot(slot.name))
768
949
  };
769
950
  }
770
- function validate(node, purpose) {
951
+ function validateDetailed(node, purpose) {
771
952
  const issues = [];
772
953
  const attributes = node.attrs?.props;
773
954
  const values = isJsonObject(attributes) ? attributes : {};
774
- for (const key of Object.keys(values)) if (!fields.has(key)) issues.push(`${definition.label}: ${key} is not editable.`);
775
- for (const field of definition.props) {
776
- const issue = propertyIssue(field, values[field.name] ?? (values[field.name] === null ? null : field.defaultValue), purpose);
777
- if (issue) issues.push(issue);
778
- }
955
+ issues.push(...structuredFields.validateDetailed(values, purpose).map((issue) => ({
956
+ component: name,
957
+ field: issue.path,
958
+ message: issue.message
959
+ })));
779
960
  const used = /* @__PURE__ */ new Set();
780
961
  for (const region of node.content ?? []) {
781
962
  const key = String(region.attrs?.name ?? "default");
782
963
  const slot = slots.get(key);
783
964
  if (!slot || region.type !== "cmsSlot") {
784
- issues.push(`${definition.label}: unsupported content region ${key}.`);
965
+ issues.push({
966
+ component: name,
967
+ message: `${definition.label}: unsupported content region ${key}.`
968
+ });
785
969
  continue;
786
970
  }
787
- if (used.has(key)) issues.push(`${slot.label} can only appear once.`);
971
+ if (used.has(key)) issues.push({
972
+ component: name,
973
+ message: `${slot.label} can only appear once.`
974
+ });
788
975
  used.add(key);
789
- if (!validRegion(slot, region.content ?? [])) issues.push(`${slot.label} contains unsupported formatting or blocks.`);
976
+ if (!validRegion(slot, region.content ?? [])) issues.push({
977
+ component: name,
978
+ message: `${slot.label} contains unsupported formatting or blocks.`
979
+ });
790
980
  }
791
- for (const slot of definition.slots) if (slot.required && !used.has(slot.name)) issues.push(`${slot.label} is required.`);
981
+ for (const slot of definition.slots) if (slot.required && !used.has(slot.name)) issues.push({
982
+ component: name,
983
+ message: `${slot.label} is required.`
984
+ });
792
985
  return issues;
793
986
  }
987
+ const validate = (node, purpose) => validateDetailed(node, purpose).map((issue) => issue.message);
794
988
  const schema = {
795
989
  definition,
796
990
  createContent,
797
991
  createSlot,
798
992
  validate,
993
+ validateDetailed,
799
994
  setProperty(current, key, value) {
800
- const field = fields.get(key);
801
- if (!field) throw new Error(`${definition.label}: ${key} is not editable.`);
802
- const issue = propertyIssue(field, value, "working");
803
- if (issue) throw new Error(issue);
804
- const next = Object.fromEntries(Object.entries(current));
805
- if (value === void 0) delete next[key];
806
- else next[key] = value;
807
- return next;
995
+ return structuredFields.set(current, key, value);
808
996
  }
809
997
  };
810
998
  const issues = validate(createContent(), "publish");
811
999
  if (issues.length) throw new Error(`Invalid initial content for ${definition.label}: ${issues.join(" ")}`);
812
1000
  return schema;
813
1001
  }
814
- function propertyIssue(field, value, purpose) {
815
- if (value === void 0) return field.required && purpose === "publish" ? `${field.label} is required.` : void 0;
816
- if (value === null && field.nullable) return void 0;
817
- let valid;
818
- if (field.type === "string") valid = Predicate.isString(value);
819
- else if (field.type === "number") valid = Predicate.isNumber(value) && Number.isFinite(value);
820
- else if (field.type === "boolean") valid = Predicate.isBoolean(value);
821
- else if (field.type === "array") valid = Array.isArray(value);
822
- else valid = isJsonObject(value);
823
- if (!valid) return `${field.label} must be ${field.type}.`;
824
- if (field.type === "object" || field.type === "array") {
825
- const entries = Array.isArray(value) ? value : [value];
826
- const ids = /* @__PURE__ */ new Set();
827
- for (const entry of entries) {
828
- if (!isJsonObject(entry)) return `${field.label} must contain objects.`;
829
- if (field.type === "array") {
830
- const id = entry.id;
831
- if (!Predicate.isString(id) || !id || ids.has(id)) return `${field.label} items need unique identities.`;
832
- ids.add(id);
833
- }
834
- for (const key of Object.keys(entry)) if (!(field.type === "array" && key === "id") && !field.fields?.some((child) => child.name === key)) return `${field.label} contains an unavailable field.`;
835
- for (const child of field.fields ?? []) {
836
- const issue = propertyIssue(child, entry[child.name], purpose);
837
- if (issue) return `${field.label}: ${issue}`;
838
- }
839
- }
840
- }
841
- if (purpose === "publish" && field.required && value === "" && (field.input === "image" || field.input === "link")) return `${field.label} is required.`;
842
- if (purpose === "publish" && Predicate.isString(value) && value && (field.input === "image" || field.input === "link") && !validAuthoringUrl(value, field.input)) return `${field.label} needs a valid ${field.input === "image" ? "image URL" : "link"}.`;
843
- if (field.options.length && !field.options.some((option) => option.value === value)) return `${field.label} must use one of its available choices.`;
844
- }
845
1002
  function validRegion(slot, content) {
846
1003
  if (slot.input === "blocks") return content.every((node) => node.type === "cmsBlock" || node.type === "paragraph" && !node.content?.length);
847
1004
  const types = /* @__PURE__ */ new Set([
@@ -850,86 +1007,130 @@ function validRegion(slot, content) {
850
1007
  "hardBreak"
851
1008
  ]);
852
1009
  const marks = /* @__PURE__ */ new Set();
853
- if (slot.input === "text" && content.length > 1) return false;
854
- if (slot.input === "rich-text") for (const feature of slot.features) if (feature === "bullet-list") {
1010
+ if (slot.input !== "rich-text" && content.length > 1) return false;
1011
+ if (slot.input === "inline-text" || slot.input === "rich-text") for (const feature of slot.features) if (feature === "bullet-list") {
855
1012
  types.add("bulletList");
856
1013
  types.add("listItem");
857
1014
  } else marks.add(feature);
858
1015
  const valid = (node) => types.has(node.type ?? "") && (node.marks ?? []).every((mark) => marks.has(mark.type)) && (node.content ?? []).every(valid);
859
1016
  return content.every(valid);
860
1017
  }
861
- /** URL values remain ordinary Vue strings and use the document model's public paths. */
862
- function validAuthoringUrl(value, input) {
863
- if (value.trim() !== value || value.includes("\\")) return false;
864
- for (const character of value) if (character.charCodeAt(0) <= 32) return false;
865
- if (value.startsWith("/") && !value.startsWith("//")) return true;
866
- if (input === "link" && value.startsWith("#")) return true;
867
- try {
868
- const url = new URL(value);
869
- return [
870
- "https:",
871
- "http:",
872
- ...input === "link" ? ["mailto:", "tel:"] : []
873
- ].includes(url.protocol);
874
- } catch {
875
- return false;
876
- }
877
- }
878
1018
 
879
1019
  //#endregion
880
- //#region ../protocol/src/Authoring.ts
881
- const BlockProp = Schema.Struct({
1020
+ //#region ../protocol/src/StructuredFields.ts
1021
+ const presentation = {
882
1022
  name: Schema.NonEmptyString,
883
1023
  label: Schema.NonEmptyString,
884
- help: Schema.optionalKey(Schema.String),
885
- type: Schema.Literals([
886
- "string",
887
- "number",
888
- "boolean",
889
- "object",
890
- "array"
891
- ]),
892
- input: Schema.Literals([
893
- "text",
894
- "textarea",
895
- "select",
896
- "number",
897
- "toggle",
898
- "image",
899
- "link",
900
- "object",
901
- "list"
902
- ]),
903
- required: Schema.Boolean,
904
- nullable: Schema.Boolean,
1024
+ description: Schema.optionalKey(Schema.String),
905
1025
  defaultValue: Schema.optionalKey(Schema.Json),
906
- fields: Schema.optionalKey(Schema.Array(Schema.suspend(() => BlockProp))),
907
- options: Schema.Array(Schema.Struct({
908
- value: Schema.String,
909
- label: Schema.String
910
- }))
911
- });
912
- const BlockSlot = Schema.Struct({
1026
+ required: Schema.Boolean,
1027
+ nullable: Schema.Boolean
1028
+ };
1029
+ const stringOptions = Schema.Array(Schema.Struct({
1030
+ value: Schema.String,
1031
+ label: Schema.String
1032
+ }));
1033
+ const numberOptions = Schema.Array(Schema.Struct({
1034
+ value: Schema.Finite,
1035
+ label: Schema.String
1036
+ }));
1037
+ const noOptions = Schema.Tuple([]);
1038
+ const StructuredField = Schema.Union([
1039
+ Schema.Struct({
1040
+ ...presentation,
1041
+ type: Schema.tag("boolean"),
1042
+ input: Schema.Literal("toggle"),
1043
+ options: noOptions
1044
+ }),
1045
+ Schema.Struct({
1046
+ ...presentation,
1047
+ type: Schema.tag("date"),
1048
+ input: Schema.Literal("date"),
1049
+ options: noOptions
1050
+ }),
1051
+ Schema.Struct({
1052
+ ...presentation,
1053
+ type: Schema.tag("number"),
1054
+ input: Schema.Literals(["number", "select"]),
1055
+ options: numberOptions
1056
+ }),
1057
+ Schema.Struct({
1058
+ ...presentation,
1059
+ type: Schema.tag("string"),
1060
+ input: Schema.Literals([
1061
+ "select",
1062
+ "text",
1063
+ "textarea",
1064
+ "image",
1065
+ "link",
1066
+ "document",
1067
+ "media"
1068
+ ]),
1069
+ options: stringOptions
1070
+ }),
1071
+ Schema.Struct({
1072
+ ...presentation,
1073
+ type: Schema.tag("string[]"),
1074
+ input: Schema.Literal("multiselect"),
1075
+ options: stringOptions
1076
+ }),
1077
+ Schema.Struct({
1078
+ ...presentation,
1079
+ type: Schema.tag("object"),
1080
+ input: Schema.Literal("object"),
1081
+ options: noOptions,
1082
+ fields: Schema.Array(Schema.suspend(() => StructuredField))
1083
+ }),
1084
+ Schema.Struct({
1085
+ ...presentation,
1086
+ type: Schema.tag("array"),
1087
+ input: Schema.Literal("list"),
1088
+ options: noOptions,
1089
+ fields: Schema.Array(Schema.suspend(() => StructuredField))
1090
+ })
1091
+ ]);
1092
+
1093
+ //#endregion
1094
+ //#region ../protocol/src/Authoring.ts
1095
+ const inlineTextFeature = Schema.Literals([
1096
+ "bold",
1097
+ "italic",
1098
+ "link",
1099
+ "code"
1100
+ ]);
1101
+ const richTextFeature = Schema.Union([inlineTextFeature, Schema.Literal("bullet-list")]);
1102
+ const slotBase = {
913
1103
  name: Schema.NonEmptyString,
914
1104
  label: Schema.NonEmptyString,
915
1105
  help: Schema.optionalKey(Schema.String),
916
1106
  required: Schema.Boolean,
917
- input: Schema.Literals([
918
- "text",
919
- "rich-text",
920
- "blocks"
921
- ]),
922
- blocks: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
923
- groups: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
924
- features: Schema.Array(Schema.Literals([
925
- "bold",
926
- "italic",
927
- "link",
928
- "bullet-list",
929
- "code"
930
- ])),
931
1107
  initial: Schema.optionalKey(Schema.Array(MarkdownNode))
932
- });
1108
+ };
1109
+ const noFeatures = Schema.Tuple([]);
1110
+ const BlockSlot = Schema.Union([
1111
+ Schema.Struct({
1112
+ ...slotBase,
1113
+ input: Schema.Literal("text"),
1114
+ features: noFeatures
1115
+ }),
1116
+ Schema.Struct({
1117
+ ...slotBase,
1118
+ input: Schema.Literal("inline-text"),
1119
+ features: Schema.Array(inlineTextFeature)
1120
+ }),
1121
+ Schema.Struct({
1122
+ ...slotBase,
1123
+ input: Schema.Literal("rich-text"),
1124
+ features: Schema.Array(richTextFeature)
1125
+ }),
1126
+ Schema.Struct({
1127
+ ...slotBase,
1128
+ input: Schema.Literal("blocks"),
1129
+ features: noFeatures,
1130
+ blocks: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
1131
+ groups: Schema.optionalKey(Schema.Array(Schema.NonEmptyString))
1132
+ })
1133
+ ]);
933
1134
  const BlockAuthoring = Schema.Struct({
934
1135
  version: Schema.Literal(1),
935
1136
  form: Schema.optionalKey(FormCapabilities),
@@ -937,73 +1138,30 @@ const BlockAuthoring = Schema.Struct({
937
1138
  description: Schema.optionalKey(Schema.String),
938
1139
  group: Schema.optionalKey(Schema.String),
939
1140
  icon: Schema.optionalKey(Schema.String),
940
- props: Schema.Array(BlockProp),
1141
+ props: Schema.Array(StructuredField),
941
1142
  slots: Schema.Array(BlockSlot),
942
1143
  initialProps: Schema.JsonObject
943
1144
  });
944
1145
 
945
1146
  //#endregion
946
1147
  //#region ../protocol/src/Configuration.ts
947
- const fieldPresentation = {
948
- path: Schema.NonEmptyString,
949
- label: Schema.NonEmptyString,
950
- description: Schema.optionalKey(Schema.String),
951
- defaultValue: Schema.optionalKey(Schema.Json),
952
- required: Schema.Boolean
953
- };
954
- const BooleanAuthoringField = Schema.Struct({
955
- type: Schema.tag("boolean"),
956
- ...fieldPresentation,
957
- input: Schema.Literal("toggle")
958
- });
959
- const DateAuthoringField = Schema.Struct({
960
- type: Schema.tag("date"),
961
- ...fieldPresentation,
962
- input: Schema.Literal("date")
963
- });
964
- const NumberAuthoringField = Schema.Struct({
965
- type: Schema.tag("number"),
966
- ...fieldPresentation,
967
- input: Schema.Literals(["number", "select"]),
968
- options: Schema.Array(Schema.Finite)
969
- });
970
- const StringAuthoringField = Schema.Struct({
971
- type: Schema.tag("string"),
972
- ...fieldPresentation,
973
- input: Schema.Literals([
974
- "select",
975
- "text",
976
- "textarea"
977
- ]),
978
- options: Schema.Array(Schema.String)
979
- });
980
- const StringArrayAuthoringField = Schema.Struct({
981
- type: Schema.tag("string[]"),
982
- ...fieldPresentation,
983
- input: Schema.Literal("multiselect"),
984
- options: Schema.Array(Schema.String)
985
- });
986
- const DocumentField = Schema.Union([
987
- BooleanAuthoringField,
988
- DateAuthoringField,
989
- NumberAuthoringField,
990
- StringArrayAuthoringField,
991
- StringAuthoringField
992
- ]);
993
1148
  const CollectionBody = Schema.Struct({
994
1149
  blocks: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
995
1150
  groups: Schema.optionalKey(Schema.Array(Schema.NonEmptyString))
996
1151
  });
1152
+ const CollectionRoutes = Schema.Struct({
1153
+ home: RoutePattern,
1154
+ entry: RoutePattern
1155
+ }).annotate({ identifier: "CollectionRoutes" });
997
1156
  const CollectionDefinition = Schema.Struct({
998
1157
  collection: CollectionName,
999
1158
  body: Schema.optionalKey(CollectionBody),
1000
1159
  label: Schema.NonEmptyString,
1001
1160
  description: Schema.String,
1002
- directory: SourceDirectory,
1003
- addressPattern: AddressPattern,
1161
+ routes: CollectionRoutes,
1004
1162
  hierarchy: Schema.Boolean,
1005
1163
  canCreate: Schema.Boolean,
1006
- authoringFields: Schema.Array(DocumentField)
1164
+ authoringFields: Schema.Array(StructuredField)
1007
1165
  }).annotate({ identifier: "CollectionDefinition" });
1008
1166
  const LocaleConfiguration = Schema.Struct({
1009
1167
  locales: Schema.Array(Locale),
@@ -1029,5 +1187,5 @@ const MediaConfiguration = Schema.Struct({
1029
1187
  });
1030
1188
 
1031
1189
  //#endregion
1032
- export { finiteNumberAttribute as C, compactJsonAttributes as S, comarkTagForEditorMark as _, BlockAuthoring as a, encodeEditorImageAttributes as b, parseAuthoringContent as c, EditorRenderFailed as d, DOCUMENT_ENGINE_FINGERPRINT as f, EDITOR_MARK_TYPES as g, comarkToEditor as h, MediaConfiguration as i, validateCollectionBody as l, SOURCE_MAPPINGS_META_KEY as m, ComponentDefinition as n, createAuthoringDocumentValidator as o, createDocumentParser as p, DocumentConfiguration as r, createAuthoringSchema as s, AuthoringCatalog as t, EditorDocumentInvalid as u, isEditorMark as v, nonEmptyStringAttribute as w, normalizeEditorImageLayout as x, EDITOR_IMAGE_LAYOUT_ATTRIBUTE as y };
1033
- //# sourceMappingURL=Configuration-BU-UyGoH.js.map
1190
+ export { compactJsonAttributes as C, normalizeEditorImageLayout as S, nonEmptyStringAttribute as T, EDITOR_MARK_TYPES as _, BlockAuthoring as a, EDITOR_IMAGE_LAYOUT_ATTRIBUTE as b, parseAuthoringContent as c, EditorDocumentInvalid as d, EditorRenderFailed as f, comarkToEditor as g, SOURCE_MAPPINGS_META_KEY as h, MediaConfiguration as i, validateCollectionBody as l, createDocumentParser as m, ComponentDefinition as n, createAuthoringDocumentValidator as o, DOCUMENT_ENGINE_FINGERPRINT as p, DocumentConfiguration as r, createAuthoringSchema as s, AuthoringCatalog as t, compileStructuredFields as u, comarkTagForEditorMark as v, finiteNumberAttribute as w, encodeEditorImageAttributes as x, isEditorMark as y };
1191
+ //# sourceMappingURL=Configuration-JQ1qyaU0.js.map