@phtngyn/cms 0.1.0-beta.0 → 0.1.0-beta.1

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 (123) hide show
  1. package/README.md +72 -1
  2. package/package.json +5 -5
  3. package/src/Authoring.d.ts +13 -1
  4. package/src/{AuthoringSchema-BQ5kbhVB.js → Configuration-DyKo0sLS.js} +595 -367
  5. package/src/Configuration-DyKo0sLS.js.map +1 -0
  6. package/src/{DocumentEngine-Da0kWotL.js → DocumentEngine-DYObwMDY.js} +44 -66
  7. package/src/DocumentEngine-DYObwMDY.js.map +1 -0
  8. package/src/DocumentModel.d.ts +4 -0
  9. package/src/DocumentModel.js.map +1 -1
  10. package/src/{HttpPaths-86qLGB90.js → HttpPaths-BbS21Jy8.js} +4 -2
  11. package/src/{HttpPaths-86qLGB90.js.map → HttpPaths-BbS21Jy8.js.map} +1 -1
  12. package/src/Module.js +27 -76
  13. package/src/Module.js.map +1 -1
  14. package/src/{StudioHttp-BTLLooh0.js → StudioHttp-jR2-bc01.js} +44 -242
  15. package/src/StudioHttp-jR2-bc01.js.map +1 -0
  16. package/src/{connection-ZGtL54qY.js → connection-CBTY-TzB.js} +3 -3
  17. package/src/{connection-ZGtL54qY.js.map → connection-CBTY-TzB.js.map} +1 -1
  18. package/src/{installation-BNKaVjni.js → installation-DeuBiFRE.js} +488 -236
  19. package/src/installation-DeuBiFRE.js.map +1 -0
  20. package/src/internal/authoring-catalog.js +147 -0
  21. package/src/internal/authoring-catalog.js.map +1 -0
  22. package/src/internal/authoring-compiler.js +2 -1
  23. package/src/internal/authoring-compiler.js.map +1 -1
  24. package/src/internal/authoring-metadata.js +67 -48
  25. package/src/internal/authoring-metadata.js.map +1 -1
  26. package/src/internal/configuration.js +5 -6
  27. package/src/internal/configuration.js.map +1 -1
  28. package/src/internal/document-model.js +3 -2
  29. package/src/internal/document-model.js.map +1 -1
  30. package/src/internal/studio-app.js +66 -0
  31. package/src/internal/studio-app.js.map +1 -0
  32. package/src/location-DtYv2aes.js +36 -0
  33. package/src/location-DtYv2aes.js.map +1 -0
  34. package/src/{metadata-Czvi3E4K.js → metadata-DE1Mc2td.js} +2 -2
  35. package/src/{metadata-Czvi3E4K.js.map → metadata-DE1Mc2td.js.map} +1 -1
  36. package/src/{models-Citx8jBi.js → models-BkMgeaOL.js} +252 -3
  37. package/src/models-BkMgeaOL.js.map +1 -0
  38. package/src/{models-DS5ClMsH.js → models-C3witQBK.js} +8 -31
  39. package/src/models-C3witQBK.js.map +1 -0
  40. package/src/runtime/blocks.js +17 -0
  41. package/src/runtime/blocks.js.map +1 -0
  42. package/src/runtime/collaboration.js +9 -4
  43. package/src/runtime/collaboration.js.map +1 -1
  44. package/src/runtime/delivery/DocumentRenderer.js +6 -3
  45. package/src/runtime/delivery/DocumentRenderer.js.map +1 -1
  46. package/src/runtime/delivery/blocks.js +6 -0
  47. package/src/runtime/delivery/blocks.js.map +1 -0
  48. package/src/runtime/delivery/context.js +12 -0
  49. package/src/runtime/delivery/context.js.map +1 -0
  50. package/src/runtime/delivery/document-page.js +5 -3
  51. package/src/runtime/delivery/document-page.js.map +1 -1
  52. package/src/runtime/delivery/document.js +18 -2
  53. package/src/runtime/delivery/document.js.map +1 -1
  54. package/src/runtime/delivery/error.js +11 -0
  55. package/src/runtime/delivery/error.js.map +1 -0
  56. package/src/runtime/delivery/plugin.js +21 -0
  57. package/src/runtime/delivery/plugin.js.map +1 -0
  58. package/src/runtime/delivery/useDocument.js +6 -3
  59. package/src/runtime/delivery/useDocument.js.map +1 -1
  60. package/src/runtime/delivery/useDocumentQuery.js +6 -3
  61. package/src/runtime/delivery/useDocumentQuery.js.map +1 -1
  62. package/src/runtime/installation.js +1 -1
  63. package/src/runtime/plugin.js +3 -3
  64. package/src/runtime/plugin.js.map +1 -1
  65. package/src/runtime/preview/connection.js +1 -1
  66. package/src/runtime/preview/controller.js +29 -10
  67. package/src/runtime/preview/controller.js.map +1 -1
  68. package/src/runtime/preview/overlay.js +65 -0
  69. package/src/runtime/preview/overlay.js.map +1 -0
  70. package/src/runtime/preview/plugin.client.js +21 -34
  71. package/src/runtime/preview/plugin.client.js.map +1 -1
  72. package/src/runtime/preview/preview.css +72 -4
  73. package/src/runtime/preview/store.js +1 -1
  74. package/src/runtime/studio-app.js +5 -1
  75. package/src/runtime/studio-app.js.map +1 -1
  76. package/src/runtime/studio-entry.js +36 -0
  77. package/src/runtime/studio-entry.js.map +1 -0
  78. package/src/runtime/studio-navigation.js +11 -0
  79. package/src/runtime/studio-navigation.js.map +1 -0
  80. package/src/{store-Nl4uxEXx.js → store-CfFFx9Ua.js} +3 -3
  81. package/src/{store-Nl4uxEXx.js.map → store-CfFFx9Ua.js.map} +1 -1
  82. package/studio/CreateDocumentDialog--L9lTfx6.js +256 -0
  83. package/studio/collaboration-DUuu678R.js +4565 -0
  84. package/studio/documentPlanning-BXX04Woe.js +28989 -0
  85. package/studio/dom-to-image-more.min-D6jGGdrW.js +877 -0
  86. package/studio/editor-PexnXkJY.js +30817 -0
  87. package/studio/host.css +3 -0
  88. package/studio/host.js +44179 -0
  89. package/studio/rolldown-runtime-B0aSnxlc.js +20 -0
  90. package/dist/studio/assets/ActivityEntryRow-D--fRvWI.js +0 -1
  91. package/dist/studio/assets/ActivityView-D-XBBUHb.js +0 -1
  92. package/dist/studio/assets/CreateDocumentDialog-CH8UH1QM.js +0 -1
  93. package/dist/studio/assets/DocumentsView-CxRpmAxt.js +0 -34
  94. package/dist/studio/assets/DropdownMenu-BSb1l7fr.js +0 -1
  95. package/dist/studio/assets/Input-DVao38h7.js +0 -3
  96. package/dist/studio/assets/MediaView-BHrPIjxB.js +0 -1
  97. package/dist/studio/assets/Modal-VNrXGa5C.js +0 -1
  98. package/dist/studio/assets/OverviewView-DfP6-mLZ.js +0 -1
  99. package/dist/studio/assets/Select-BJZIs9sY.js +0 -1
  100. package/dist/studio/assets/SettingsView-CerHe-ua.js +0 -1
  101. package/dist/studio/assets/Skeleton-CWJNi3nS.js +0 -1
  102. package/dist/studio/assets/StudioPageHeader-DP9HDAF2.js +0 -1
  103. package/dist/studio/assets/UsersView-cP3REkH2.js +0 -1
  104. package/dist/studio/assets/collaboration-BLnCzfvV.js +0 -4
  105. package/dist/studio/assets/context-dY4fNbn3.js +0 -1
  106. package/dist/studio/assets/editor-CE07wW83.js +0 -137
  107. package/dist/studio/assets/gateway-BAyQgCse.js +0 -10
  108. package/dist/studio/assets/index-CjoaPLhZ.js +0 -28
  109. package/dist/studio/assets/index-s2rPqCmP.css +0 -2
  110. package/dist/studio/assets/render-BkxbK2St.js +0 -83
  111. package/dist/studio/assets/rolldown-runtime-DK3Fl9T5.js +0 -1
  112. package/dist/studio/assets/useMediaLibrary-D7qR7aBf.js +0 -1
  113. package/dist/studio/index.html +0 -76
  114. package/src/AuthoringSchema-BQ5kbhVB.js.map +0 -1
  115. package/src/Configuration-BGYFD2N1.js +0 -150
  116. package/src/Configuration-BGYFD2N1.js.map +0 -1
  117. package/src/DocumentEngine-Da0kWotL.js.map +0 -1
  118. package/src/StudioHttp-BTLLooh0.js.map +0 -1
  119. package/src/installation-BNKaVjni.js.map +0 -1
  120. package/src/internal/component-metadata.js +0 -37
  121. package/src/internal/component-metadata.js.map +0 -1
  122. package/src/models-Citx8jBi.js.map +0 -1
  123. package/src/models-DS5ClMsH.js.map +0 -1
@@ -1,6 +1,8 @@
1
- import { n as CmsMarkdownDocument } from "./models-Citx8jBi.js";
2
- import { a as createEditorNodeId, c as createCmsComarkRenderData, f as toMutableComarkAttributes, l as mutableJson, n as editorMetadata, o as editorNodeId, u as mutableJsonObject } from "./metadata-Czvi3E4K.js";
1
+ import { C as MarkdownNode, dt as Locale, ft as SourceDirectory, lt as AddressPattern, n as CmsMarkdownDocument, ut as CollectionName } from "./models-BkMgeaOL.js";
2
+ import { a as createEditorNodeId, c as createCmsComarkRenderData, f as toMutableComarkAttributes, l as mutableJson, n as editorMetadata, o as editorNodeId, u as mutableJsonObject } from "./metadata-DE1Mc2td.js";
3
3
  import { Effect, Option, Predicate, Schema } from "effect";
4
+ import { Extension } from "@tiptap/core";
5
+ import { ySyncPluginKey } from "@tiptap/y-tiptap";
4
6
  import { resolveAttributes } from "comark/utils";
5
7
  import { createMarkdownParser } from "comark/parse";
6
8
  import alert from "comark/plugins/alert";
@@ -11,8 +13,6 @@ import headings from "comark/plugins/headings";
11
13
  import security from "comark/plugins/security";
12
14
  import taskList from "comark/plugins/task-list";
13
15
  import toc from "comark/plugins/toc";
14
- import { Extension } from "@tiptap/core";
15
- import { ySyncPluginKey } from "@tiptap/y-tiptap";
16
16
 
17
17
  //#region ../document/src/internal/comark/bindings.ts
18
18
  const isJsonObject$3 = Schema.is(Schema.JsonObject);
@@ -95,10 +95,10 @@ function normalizeCmsComponentBindings(document, components) {
95
95
  }
96
96
  function matchesCmsComponentPropType(value, type) {
97
97
  switch (type) {
98
+ case "object": return isJsonObject$3(value);
98
99
  case "array": return Array.isArray(value);
99
100
  case "boolean": return Predicate.isBoolean(value);
100
101
  case "number": return Predicate.isNumber(value);
101
- case "object": return isJsonObject$3(value);
102
102
  case "string": return Predicate.isString(value);
103
103
  }
104
104
  }
@@ -107,7 +107,7 @@ function normalizeComponentNode(node, components) {
107
107
  const [tag, originalAttributes, ...originalChildren] = node;
108
108
  const attributes = mutableJsonObject(originalAttributes);
109
109
  const definition = components.get(tag.toLocaleLowerCase());
110
- if (definition) for (const prop of definition.props) {
110
+ if (definition) for (const prop of definition.authoring.props) {
111
111
  if (prop.type === "string" || `:${prop.name}` in attributes || !(prop.name in attributes)) continue;
112
112
  const value = attributes[prop.name];
113
113
  if (Predicate.isUndefined(value)) continue;
@@ -137,272 +137,6 @@ function sameJson(left, right) {
137
137
  return !Predicate.isUndefined(right) && JSON.stringify(left) === JSON.stringify(right);
138
138
  }
139
139
 
140
- //#endregion
141
- //#region ../document/src/internal/comark/source-mapping.ts
142
- const SOURCE_MAPPINGS_META_KEY = "__cmsSourceMappings";
143
- const BLOCK_TAGS = /* @__PURE__ */ new Map([
144
- ["blockquote_open", "blockquote"],
145
- ["bullet_list_open", "ul"],
146
- ["list_item_open", "li"],
147
- ["ordered_list_open", "ol"],
148
- ["paragraph_open", "p"],
149
- ["table_open", "table"],
150
- ["tbody_open", "tbody"],
151
- ["td_open", "td"],
152
- ["th_open", "th"],
153
- ["thead_open", "thead"],
154
- ["tr_open", "tr"]
155
- ]);
156
- const ComarkToken = Schema.Struct({
157
- attrs: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Tuple([Schema.String, Schema.String])))),
158
- hidden: Schema.optionalKey(Schema.Boolean),
159
- level: Schema.optionalKey(Schema.Finite),
160
- map: Schema.optionalKey(Schema.NullOr(Schema.Tuple([Schema.Finite, Schema.Finite]))),
161
- nesting: Schema.optionalKey(Schema.Finite),
162
- tag: Schema.optionalKey(Schema.String),
163
- type: Schema.optionalKey(Schema.String)
164
- });
165
- const WRAPPER_TAGS = /* @__PURE__ */ new Set([
166
- "blockquote",
167
- "ol",
168
- "pre",
169
- "table",
170
- "ul"
171
- ]);
172
- /** Captures the durable source before frontmatter or other pre-parse stages mutate it. */
173
- function sourceCapturePlugin() {
174
- return {
175
- name: "cms-source-capture",
176
- pre(state) {
177
- state.__cmsOriginalMarkdown = state.markdown;
178
- }
179
- };
180
- }
181
- /** Translate Comark block token line maps into exact JavaScript source offsets. */
182
- function sourceMappingPlugin() {
183
- return {
184
- name: "cms-source-mapping",
185
- post(state) {
186
- const source = state.__cmsOriginalMarkdown ?? state.markdown;
187
- const descriptors = createBlockDescriptors(state.tokens.flatMap((token) => {
188
- const decoded = Schema.decodeUnknownOption(ComarkToken)(token);
189
- return decoded._tag === "Some" ? [decoded.value] : [];
190
- }), source, state.markdown, state.parsedLines ?? 0);
191
- const mappings = [];
192
- mapNodes(state.tree.nodes, descriptors, [], mappings);
193
- state.tree.meta[SOURCE_MAPPINGS_META_KEY] = mappings;
194
- }
195
- };
196
- }
197
- function createBlockDescriptors(tokens, source, parsedMarkdown, parsedLines) {
198
- const roots = [];
199
- const stack = [];
200
- const sourceLineStarts = indexLineStarts(source);
201
- const parsedLineStarts = indexLineStarts(parsedMarkdown);
202
- const parsedOffset = source.lastIndexOf(parsedMarkdown);
203
- const slotMaps = createSlotTokenMaps(tokens, parsedMarkdown);
204
- for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {
205
- const token = tokens[tokenIndex];
206
- if (!token) continue;
207
- const tag = blockTag(token);
208
- const tokenMap = token.map ?? slotMaps.get(tokenIndex);
209
- if (!tag || !tokenMap) continue;
210
- const level = token.level ?? 0;
211
- while (true) {
212
- const current = stack.at(-1);
213
- if (!current || current.level < level) break;
214
- stack.pop();
215
- }
216
- const descriptor = {
217
- children: [],
218
- range: rangeFromParsedLines(source, sourceLineStarts, parsedMarkdown, parsedLineStarts, parsedOffset, tokenMap[0], tokenMap[1] + (token.type === "mdc_block_open" ? 1 : 0), parsedLines),
219
- tag
220
- };
221
- ((stack.at(-1)?.descriptor)?.children ?? roots).push(descriptor);
222
- if (token.nesting === 1) stack.push({
223
- descriptor,
224
- level
225
- });
226
- }
227
- return roots.map(collapseComarkWrapper).map(groupProjectedSlotContent);
228
- }
229
- function blockTag(token) {
230
- if (token.hidden || !token.type) return void 0;
231
- if (token.type === "heading_open") return token.tag;
232
- if (token.type === "mdc_block_open" || token.type === "mdc_block_shorthand" && token.nesting !== -1) return token.tag || "component";
233
- if (token.type === "mdc_block_slot" && token.nesting === 1) return "template";
234
- if ([
235
- "fence",
236
- "fenced_code_block",
237
- "code_block"
238
- ].includes(token.type)) return "pre";
239
- if (token.type === "math_block") return "math";
240
- if (token.type === "html_block") return "*";
241
- if (token.type === "hr") return "hr";
242
- return BLOCK_TAGS.get(token.type);
243
- }
244
- function createSlotTokenMaps(tokens, parsedMarkdown) {
245
- const maps = /* @__PURE__ */ new Map();
246
- const markers = slotMarkers(parsedMarkdown);
247
- const stack = [];
248
- let nextMarker = 0;
249
- for (let index = 0; index < tokens.length; index++) {
250
- const token = tokens[index];
251
- if (!token || token.type !== "mdc_block_slot") continue;
252
- if (token.nesting === 1) {
253
- const name = token.attrs?.[0]?.[0]?.replace(/^#/u, "");
254
- let markerIndex = nextMarker;
255
- while (markerIndex < markers.length && markers[markerIndex]?.name !== name) markerIndex++;
256
- const marker = markers[markerIndex];
257
- if (marker) {
258
- stack.push({
259
- index,
260
- marker: marker.line
261
- });
262
- nextMarker = markerIndex + 1;
263
- }
264
- continue;
265
- }
266
- if (token.nesting !== -1) continue;
267
- const open = stack.pop();
268
- if (!open) continue;
269
- let endLine = open.marker + 1;
270
- for (let childIndex = open.index + 1; childIndex < index; childIndex++) {
271
- const child = tokens[childIndex];
272
- const map = child?.map ?? maps.get(childIndex);
273
- if (child && map) endLine = Math.max(endLine, map[1] + (child.type === "mdc_block_open" ? 1 : 0));
274
- }
275
- maps.set(open.index, [open.marker, endLine]);
276
- }
277
- return maps;
278
- }
279
- function slotMarkers(source) {
280
- return source.split(/\r?\n/u).flatMap((line, index) => {
281
- const name = line.match(/^\s*#([^\s{]+)/u)?.[1];
282
- return name ? [{
283
- line: index,
284
- name
285
- }] : [];
286
- });
287
- }
288
- function collapseComarkWrapper(descriptor) {
289
- const children = descriptor.children.map(collapseComarkWrapper);
290
- if (WRAPPER_TAGS.has(descriptor.tag) && children.length === 1 && children[0]?.tag === descriptor.tag) return {
291
- ...descriptor,
292
- children: children[0].children
293
- };
294
- return {
295
- ...descriptor,
296
- children
297
- };
298
- }
299
- /** Comark projects siblings after a named slot into that active slot. */
300
- function groupProjectedSlotContent(descriptor) {
301
- const grouped = [];
302
- let activeSlot;
303
- for (const rawChild of descriptor.children) {
304
- const child = groupProjectedSlotContent(rawChild);
305
- if (child.tag === "template") {
306
- activeSlot = child;
307
- grouped.push(child);
308
- continue;
309
- }
310
- if (!activeSlot) {
311
- grouped.push(child);
312
- continue;
313
- }
314
- activeSlot.children.push(child);
315
- if (child.range.end.offset > activeSlot.range.end.offset) activeSlot.range = {
316
- start: activeSlot.range.start,
317
- end: child.range.end
318
- };
319
- }
320
- return {
321
- ...descriptor,
322
- children: grouped
323
- };
324
- }
325
- function mapNodes(nodes, descriptors, parentPath, mappings) {
326
- let descriptorIndex = 0;
327
- for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex++) {
328
- const node = nodes[nodeIndex];
329
- if (!node || !isElementNode$1(node)) continue;
330
- let matchIndex = descriptorIndex;
331
- while (matchIndex < descriptors.length && descriptors[matchIndex]?.tag !== "*" && descriptors[matchIndex]?.tag !== node[0]) matchIndex++;
332
- const descriptor = descriptors[matchIndex];
333
- if (!descriptor) continue;
334
- descriptorIndex = matchIndex + 1;
335
- const nodePath = [...parentPath, nodeIndex];
336
- mappings.push({
337
- nodePath,
338
- range: descriptor.range
339
- });
340
- const [, , ...children] = node;
341
- mapNodes(children, descriptor.children, nodePath, mappings);
342
- }
343
- }
344
- function isElementNode$1(node) {
345
- return Array.isArray(node) && Predicate.isString(node[0]);
346
- }
347
- function indexLineStarts(source) {
348
- const starts = [0];
349
- for (let offset = 0; offset < source.length; offset++) if (source.charCodeAt(offset) === 10) starts.push(offset + 1);
350
- return starts;
351
- }
352
- function rangeFromParsedLines(source, sourceLineStarts, parsedMarkdown, parsedLineStarts, parsedOffset, startLine, endLine, fallbackLineOffset) {
353
- const startOffset = parsedOffset >= 0 ? parsedOffset + (parsedLineStarts[startLine] ?? parsedMarkdown.length) : sourceLineStarts[startLine + fallbackLineOffset] ?? source.length;
354
- const endOffset = parsedOffset >= 0 ? parsedOffset + (parsedLineStarts[endLine] ?? parsedMarkdown.length) : sourceLineStarts[endLine + fallbackLineOffset] ?? source.length;
355
- return {
356
- start: pointAt(source, sourceLineStarts, startOffset),
357
- end: pointAt(source, sourceLineStarts, Math.max(startOffset, endOffset))
358
- };
359
- }
360
- function pointAt(source, lineStarts, rawOffset) {
361
- const offset = Math.min(Math.max(rawOffset, 0), source.length);
362
- let low = 0;
363
- let high = lineStarts.length - 1;
364
- while (low < high) {
365
- const middle = Math.ceil((low + high) / 2);
366
- if ((lineStarts[middle] ?? source.length) <= offset) low = middle;
367
- else high = middle - 1;
368
- }
369
- return {
370
- offset,
371
- line: low + 1,
372
- column: offset - (lineStarts[low] ?? 0) + 1
373
- };
374
- }
375
-
376
- //#endregion
377
- //#region ../document/src/internal/comark/grammar.ts
378
- /**
379
- * Changes when parsing, normalization, or projection semantics change. Stored
380
- * with compiled documents so migrations can identify stale derived content.
381
- */
382
- const DOCUMENT_ENGINE_FINGERPRINT = "comark@0.6.2/frontmatter+alert+task-list+components+attributes+security+headings+toc+source-mapping/v1";
383
- /** One explicit grammar for authoring, preview, delivery, and analysis projections. */
384
- function createDocumentParser() {
385
- return createMarkdownParser({
386
- autoClose: false,
387
- registerDefaultPlugins: false,
388
- plugins: [
389
- sourceCapturePlugin(),
390
- frontmatter(),
391
- alert(),
392
- taskList(),
393
- components(),
394
- attributes(),
395
- security(),
396
- headings(),
397
- toc({
398
- depth: 5,
399
- searchDepth: 6
400
- }),
401
- sourceMappingPlugin()
402
- ]
403
- });
404
- }
405
-
406
140
  //#endregion
407
141
  //#region ../document/src/internal/editor/attributes.ts
408
142
  function compactJsonAttributes(attributes) {
@@ -578,7 +312,7 @@ function decodeBlock(node, context, renderData) {
578
312
  content: decodeBlocks(children, context, childRenderData)
579
313
  };
580
314
  case "pre": {
581
- const code = children.find(isElementNode);
315
+ const code = children.find(isElementNode$1);
582
316
  return {
583
317
  type: "codeBlock",
584
318
  attrs: mergeJsonAttributes(commonAttrs, { language: nonEmptyStringAttribute(attrs.language) ?? nonEmptyStringAttribute(code?.[1].class)?.replace(/^language-/u, "") }),
@@ -643,7 +377,7 @@ function decodeSlot(node, context, renderData) {
643
377
  };
644
378
  }
645
379
  function decodeListItems(nodes, context, renderData) {
646
- return nodes.filter(isElementNode).filter((node) => node[0] === "li").map((node) => decodeBlock(node, context, renderData));
380
+ return nodes.filter(isElementNode$1).filter((node) => node[0] === "li").map((node) => decodeBlock(node, context, renderData));
647
381
  }
648
382
  function decodeInlines(nodes, context, renderData, inheritedMarks = []) {
649
383
  const result = [];
@@ -681,105 +415,371 @@ function decodeInlines(nodes, context, renderData, inheritedMarks = []) {
681
415
  result.push(withMarks({ type: "hardBreak" }, inheritedMarks));
682
416
  continue;
683
417
  }
684
- if (node[0] === "img") {
685
- result.push(withMarks({
686
- type: "image",
687
- attrs: mergeJsonAttributes(decodeEditorImageAttributes(cleanAttrs(node[1])), { cmsNodeId: claimIdentity(context, node[1]) })
688
- }, inheritedMarks));
689
- continue;
418
+ if (node[0] === "img") {
419
+ result.push(withMarks({
420
+ type: "image",
421
+ attrs: mergeJsonAttributes(decodeEditorImageAttributes(cleanAttrs(node[1])), { cmsNodeId: claimIdentity(context, node[1]) })
422
+ }, inheritedMarks));
423
+ continue;
424
+ }
425
+ result.push(withMarks({
426
+ type: "cmsInlineOpaque",
427
+ attrs: {
428
+ label: node[0],
429
+ node: stripInternalMetadata(node)
430
+ }
431
+ }, inheritedMarks));
432
+ }
433
+ return result;
434
+ }
435
+ function claimIdentity(context, attributes) {
436
+ return claimIdentityValue(context, attributes ? editorMetadata(attributes)?.nodeId : void 0);
437
+ }
438
+ function claimIdentityValue(context, candidate) {
439
+ const existing = editorNodeId(candidate);
440
+ if (existing !== void 0 && !context.usedIds.has(existing)) {
441
+ context.usedIds.add(existing);
442
+ return existing;
443
+ }
444
+ for (let attempt = 0; attempt < 100; attempt++) {
445
+ const created = createEditorNodeId();
446
+ if (!context.usedIds.has(created)) {
447
+ context.usedIds.add(created);
448
+ return created;
449
+ }
450
+ }
451
+ throw new Error("The editor identity factory did not produce a unique identity.");
452
+ }
453
+ function opaqueBlock(node, label, cmsNodeId) {
454
+ return {
455
+ type: "cmsOpaque",
456
+ attrs: {
457
+ cmsNodeId,
458
+ label,
459
+ node: stripInternalMetadata(node)
460
+ }
461
+ };
462
+ }
463
+ function stripInternalMetadata(node) {
464
+ if (node[0] === null) return [
465
+ null,
466
+ cleanAttrs(node[1]),
467
+ node[2]
468
+ ];
469
+ const [tag, attributes, ...children] = node;
470
+ return [
471
+ tag,
472
+ cleanAttrs(attributes),
473
+ ...children.map((child) => Predicate.isString(child) ? child : stripInternalMetadata(child))
474
+ ];
475
+ }
476
+ function slotContent(content) {
477
+ return content.length ? content : [{
478
+ type: "paragraph",
479
+ attrs: { cmsSynthetic: true }
480
+ }];
481
+ }
482
+ function slotName(attrs) {
483
+ return nonEmptyStringAttribute(attrs.name) ?? "default";
484
+ }
485
+ function cleanAttrs(attrs) {
486
+ const { $: _internal, "data-cms-node": _marker, ...clean } = attrs;
487
+ return structuredClone(clean);
488
+ }
489
+ function textContent$1(nodes) {
490
+ const text = nodes.map((node) => Predicate.isString(node) ? node : node[0] === null ? node[2] : textContent$1(elementChildren(node))?.map((child) => child.text ?? "").join("") ?? "").join("");
491
+ return text ? [{
492
+ type: "text",
493
+ text
494
+ }] : void 0;
495
+ }
496
+ function isInlineElement(node) {
497
+ return node[0] !== null && (isComarkMark(node[0]) || node[0] === "br" || node[0] === "img" || jsonObject(node[1].$).block === 0);
498
+ }
499
+ function isSlotTemplate(node) {
500
+ return isElementNode$1(node) && node[0] === "template";
501
+ }
502
+ function isElementNode$1(node) {
503
+ return Array.isArray(node) && Predicate.isString(node[0]);
504
+ }
505
+ function elementChildren(node) {
506
+ const [, , ...children] = node;
507
+ return children;
508
+ }
509
+ function jsonObject(value) {
510
+ return isJsonObject$1(value) ? value : {};
511
+ }
512
+ function mergeJsonAttributes(attributes, additions) {
513
+ return compactJsonAttributes(Object.assign({}, attributes, additions));
514
+ }
515
+ function withMarks(content, marks) {
516
+ return marks.length ? Object.assign({}, content, { marks }) : content;
517
+ }
518
+
519
+ //#endregion
520
+ //#region ../document/src/internal/comark/source-mapping.ts
521
+ const SOURCE_MAPPINGS_META_KEY = "__cmsSourceMappings";
522
+ const BLOCK_TAGS = /* @__PURE__ */ new Map([
523
+ ["blockquote_open", "blockquote"],
524
+ ["bullet_list_open", "ul"],
525
+ ["list_item_open", "li"],
526
+ ["ordered_list_open", "ol"],
527
+ ["paragraph_open", "p"],
528
+ ["table_open", "table"],
529
+ ["tbody_open", "tbody"],
530
+ ["td_open", "td"],
531
+ ["th_open", "th"],
532
+ ["thead_open", "thead"],
533
+ ["tr_open", "tr"]
534
+ ]);
535
+ const ComarkToken = Schema.Struct({
536
+ attrs: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Tuple([Schema.String, Schema.String])))),
537
+ hidden: Schema.optionalKey(Schema.Boolean),
538
+ level: Schema.optionalKey(Schema.Finite),
539
+ map: Schema.optionalKey(Schema.NullOr(Schema.Tuple([Schema.Finite, Schema.Finite]))),
540
+ nesting: Schema.optionalKey(Schema.Finite),
541
+ tag: Schema.optionalKey(Schema.String),
542
+ type: Schema.optionalKey(Schema.String)
543
+ });
544
+ const WRAPPER_TAGS = /* @__PURE__ */ new Set([
545
+ "blockquote",
546
+ "ol",
547
+ "pre",
548
+ "table",
549
+ "ul"
550
+ ]);
551
+ /** Captures the durable source before frontmatter or other pre-parse stages mutate it. */
552
+ function sourceCapturePlugin() {
553
+ return {
554
+ name: "cms-source-capture",
555
+ pre(state) {
556
+ state.__cmsOriginalMarkdown = state.markdown;
557
+ }
558
+ };
559
+ }
560
+ /** Translate Comark block token line maps into exact JavaScript source offsets. */
561
+ function sourceMappingPlugin() {
562
+ return {
563
+ name: "cms-source-mapping",
564
+ post(state) {
565
+ const source = state.__cmsOriginalMarkdown ?? state.markdown;
566
+ const descriptors = createBlockDescriptors(state.tokens.flatMap((token) => {
567
+ const decoded = Schema.decodeUnknownOption(ComarkToken)(token);
568
+ return decoded._tag === "Some" ? [decoded.value] : [];
569
+ }), source, state.markdown, state.parsedLines ?? 0);
570
+ const mappings = [];
571
+ mapNodes(state.tree.nodes, descriptors, [], mappings);
572
+ state.tree.meta[SOURCE_MAPPINGS_META_KEY] = mappings;
573
+ }
574
+ };
575
+ }
576
+ function createBlockDescriptors(tokens, source, parsedMarkdown, parsedLines) {
577
+ const roots = [];
578
+ const stack = [];
579
+ const sourceLineStarts = indexLineStarts(source);
580
+ const parsedLineStarts = indexLineStarts(parsedMarkdown);
581
+ const parsedOffset = source.lastIndexOf(parsedMarkdown);
582
+ const slotMaps = createSlotTokenMaps(tokens, parsedMarkdown);
583
+ for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex++) {
584
+ const token = tokens[tokenIndex];
585
+ if (!token) continue;
586
+ const tag = blockTag(token);
587
+ const tokenMap = token.map ?? slotMaps.get(tokenIndex);
588
+ if (!tag || !tokenMap) continue;
589
+ const level = token.level ?? 0;
590
+ while (true) {
591
+ const current = stack.at(-1);
592
+ if (!current || current.level < level) break;
593
+ stack.pop();
594
+ }
595
+ const descriptor = {
596
+ children: [],
597
+ range: rangeFromParsedLines(source, sourceLineStarts, parsedMarkdown, parsedLineStarts, parsedOffset, tokenMap[0], tokenMap[1] + (token.type === "mdc_block_open" ? 1 : 0), parsedLines),
598
+ tag
599
+ };
600
+ ((stack.at(-1)?.descriptor)?.children ?? roots).push(descriptor);
601
+ if (token.nesting === 1) stack.push({
602
+ descriptor,
603
+ level
604
+ });
605
+ }
606
+ return roots.map(collapseComarkWrapper).map(groupProjectedSlotContent);
607
+ }
608
+ function blockTag(token) {
609
+ if (token.hidden || !token.type) return void 0;
610
+ if (token.type === "heading_open") return token.tag;
611
+ if (token.type === "mdc_block_open" || token.type === "mdc_block_shorthand" && token.nesting !== -1) return token.tag || "component";
612
+ if (token.type === "mdc_block_slot" && token.nesting === 1) return "template";
613
+ if ([
614
+ "fence",
615
+ "fenced_code_block",
616
+ "code_block"
617
+ ].includes(token.type)) return "pre";
618
+ if (token.type === "math_block") return "math";
619
+ if (token.type === "html_block") return "*";
620
+ if (token.type === "hr") return "hr";
621
+ return BLOCK_TAGS.get(token.type);
622
+ }
623
+ function createSlotTokenMaps(tokens, parsedMarkdown) {
624
+ const maps = /* @__PURE__ */ new Map();
625
+ const markers = slotMarkers(parsedMarkdown);
626
+ const stack = [];
627
+ let nextMarker = 0;
628
+ for (let index = 0; index < tokens.length; index++) {
629
+ const token = tokens[index];
630
+ if (!token || token.type !== "mdc_block_slot") continue;
631
+ if (token.nesting === 1) {
632
+ const name = token.attrs?.[0]?.[0]?.replace(/^#/u, "");
633
+ let markerIndex = nextMarker;
634
+ while (markerIndex < markers.length && markers[markerIndex]?.name !== name) markerIndex++;
635
+ const marker = markers[markerIndex];
636
+ if (marker) {
637
+ stack.push({
638
+ index,
639
+ marker: marker.line
640
+ });
641
+ nextMarker = markerIndex + 1;
642
+ }
643
+ continue;
644
+ }
645
+ if (token.nesting !== -1) continue;
646
+ const open = stack.pop();
647
+ if (!open) continue;
648
+ let endLine = open.marker + 1;
649
+ for (let childIndex = open.index + 1; childIndex < index; childIndex++) {
650
+ const child = tokens[childIndex];
651
+ const map = child?.map ?? maps.get(childIndex);
652
+ if (child && map) endLine = Math.max(endLine, map[1] + (child.type === "mdc_block_open" ? 1 : 0));
690
653
  }
691
- result.push(withMarks({
692
- type: "cmsInlineOpaque",
693
- attrs: {
694
- label: node[0],
695
- node: stripInternalMetadata(node)
696
- }
697
- }, inheritedMarks));
654
+ maps.set(open.index, [open.marker, endLine]);
698
655
  }
699
- return result;
656
+ return maps;
700
657
  }
701
- function claimIdentity(context, attributes) {
702
- return claimIdentityValue(context, attributes ? editorMetadata(attributes)?.nodeId : void 0);
658
+ function slotMarkers(source) {
659
+ return source.split(/\r?\n/u).flatMap((line, index) => {
660
+ const name = line.match(/^\s*#([^\s{]+)/u)?.[1];
661
+ return name ? [{
662
+ line: index,
663
+ name
664
+ }] : [];
665
+ });
703
666
  }
704
- function claimIdentityValue(context, candidate) {
705
- const existing = editorNodeId(candidate);
706
- if (existing !== void 0 && !context.usedIds.has(existing)) {
707
- context.usedIds.add(existing);
708
- return existing;
709
- }
710
- for (let attempt = 0; attempt < 100; attempt++) {
711
- const created = createEditorNodeId();
712
- if (!context.usedIds.has(created)) {
713
- context.usedIds.add(created);
714
- return created;
667
+ function collapseComarkWrapper(descriptor) {
668
+ const children = descriptor.children.map(collapseComarkWrapper);
669
+ if (WRAPPER_TAGS.has(descriptor.tag) && children.length === 1 && children[0]?.tag === descriptor.tag) return {
670
+ ...descriptor,
671
+ children: children[0].children
672
+ };
673
+ return {
674
+ ...descriptor,
675
+ children
676
+ };
677
+ }
678
+ /** Comark projects siblings after a named slot into that active slot. */
679
+ function groupProjectedSlotContent(descriptor) {
680
+ const grouped = [];
681
+ let activeSlot;
682
+ for (const rawChild of descriptor.children) {
683
+ const child = groupProjectedSlotContent(rawChild);
684
+ if (child.tag === "template") {
685
+ activeSlot = child;
686
+ grouped.push(child);
687
+ continue;
715
688
  }
689
+ if (!activeSlot) {
690
+ grouped.push(child);
691
+ continue;
692
+ }
693
+ activeSlot.children.push(child);
694
+ if (child.range.end.offset > activeSlot.range.end.offset) activeSlot.range = {
695
+ start: activeSlot.range.start,
696
+ end: child.range.end
697
+ };
716
698
  }
717
- throw new Error("The editor identity factory did not produce a unique identity.");
718
- }
719
- function opaqueBlock(node, label, cmsNodeId) {
720
699
  return {
721
- type: "cmsOpaque",
722
- attrs: {
723
- cmsNodeId,
724
- label,
725
- node: stripInternalMetadata(node)
726
- }
700
+ ...descriptor,
701
+ children: grouped
727
702
  };
728
703
  }
729
- function stripInternalMetadata(node) {
730
- if (node[0] === null) return [
731
- null,
732
- cleanAttrs(node[1]),
733
- node[2]
734
- ];
735
- const [tag, attributes, ...children] = node;
736
- return [
737
- tag,
738
- cleanAttrs(attributes),
739
- ...children.map((child) => Predicate.isString(child) ? child : stripInternalMetadata(child))
740
- ];
741
- }
742
- function slotContent(content) {
743
- return content.length ? content : [{
744
- type: "paragraph",
745
- attrs: { cmsSynthetic: true }
746
- }];
747
- }
748
- function slotName(attrs) {
749
- return nonEmptyStringAttribute(attrs.name) ?? "default";
750
- }
751
- function cleanAttrs(attrs) {
752
- const { $: _internal, "data-cms-node": _marker, ...clean } = attrs;
753
- return structuredClone(clean);
754
- }
755
- function textContent$1(nodes) {
756
- const text = nodes.map((node) => Predicate.isString(node) ? node : node[0] === null ? node[2] : textContent$1(elementChildren(node))?.map((child) => child.text ?? "").join("") ?? "").join("");
757
- return text ? [{
758
- type: "text",
759
- text
760
- }] : void 0;
761
- }
762
- function isInlineElement(node) {
763
- return node[0] !== null && (isComarkMark(node[0]) || node[0] === "br" || node[0] === "img" || jsonObject(node[1].$).block === 0);
764
- }
765
- function isSlotTemplate(node) {
766
- return isElementNode(node) && node[0] === "template";
704
+ function mapNodes(nodes, descriptors, parentPath, mappings) {
705
+ let descriptorIndex = 0;
706
+ for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex++) {
707
+ const node = nodes[nodeIndex];
708
+ if (!node || !isElementNode(node)) continue;
709
+ let matchIndex = descriptorIndex;
710
+ while (matchIndex < descriptors.length && descriptors[matchIndex]?.tag !== "*" && descriptors[matchIndex]?.tag !== node[0]) matchIndex++;
711
+ const descriptor = descriptors[matchIndex];
712
+ if (!descriptor) continue;
713
+ descriptorIndex = matchIndex + 1;
714
+ const nodePath = [...parentPath, nodeIndex];
715
+ mappings.push({
716
+ nodePath,
717
+ range: descriptor.range
718
+ });
719
+ const [, , ...children] = node;
720
+ mapNodes(children, descriptor.children, nodePath, mappings);
721
+ }
767
722
  }
768
723
  function isElementNode(node) {
769
724
  return Array.isArray(node) && Predicate.isString(node[0]);
770
725
  }
771
- function elementChildren(node) {
772
- const [, , ...children] = node;
773
- return children;
726
+ function indexLineStarts(source) {
727
+ const starts = [0];
728
+ for (let offset = 0; offset < source.length; offset++) if (source.charCodeAt(offset) === 10) starts.push(offset + 1);
729
+ return starts;
774
730
  }
775
- function jsonObject(value) {
776
- return isJsonObject$1(value) ? value : {};
731
+ function rangeFromParsedLines(source, sourceLineStarts, parsedMarkdown, parsedLineStarts, parsedOffset, startLine, endLine, fallbackLineOffset) {
732
+ const startOffset = parsedOffset >= 0 ? parsedOffset + (parsedLineStarts[startLine] ?? parsedMarkdown.length) : sourceLineStarts[startLine + fallbackLineOffset] ?? source.length;
733
+ const endOffset = parsedOffset >= 0 ? parsedOffset + (parsedLineStarts[endLine] ?? parsedMarkdown.length) : sourceLineStarts[endLine + fallbackLineOffset] ?? source.length;
734
+ return {
735
+ start: pointAt(source, sourceLineStarts, startOffset),
736
+ end: pointAt(source, sourceLineStarts, Math.max(startOffset, endOffset))
737
+ };
777
738
  }
778
- function mergeJsonAttributes(attributes, additions) {
779
- return compactJsonAttributes(Object.assign({}, attributes, additions));
739
+ function pointAt(source, lineStarts, rawOffset) {
740
+ const offset = Math.min(Math.max(rawOffset, 0), source.length);
741
+ let low = 0;
742
+ let high = lineStarts.length - 1;
743
+ while (low < high) {
744
+ const middle = Math.ceil((low + high) / 2);
745
+ if ((lineStarts[middle] ?? source.length) <= offset) low = middle;
746
+ else high = middle - 1;
747
+ }
748
+ return {
749
+ offset,
750
+ line: low + 1,
751
+ column: offset - (lineStarts[low] ?? 0) + 1
752
+ };
780
753
  }
781
- function withMarks(content, marks) {
782
- return marks.length ? Object.assign({}, content, { marks }) : content;
754
+
755
+ //#endregion
756
+ //#region ../document/src/internal/comark/grammar.ts
757
+ /**
758
+ * Changes when parsing, normalization, or projection semantics change. Stored
759
+ * with compiled documents so migrations can identify stale derived content.
760
+ */
761
+ const DOCUMENT_ENGINE_FINGERPRINT = "comark@0.6.2/frontmatter+alert+task-list+components+attributes+security+headings+toc+source-mapping/v1";
762
+ /** One explicit grammar for authoring, preview, delivery, and analysis projections. */
763
+ function createDocumentParser() {
764
+ return createMarkdownParser({
765
+ autoClose: false,
766
+ registerDefaultPlugins: false,
767
+ plugins: [
768
+ sourceCapturePlugin(),
769
+ frontmatter(),
770
+ alert(),
771
+ taskList(),
772
+ components(),
773
+ attributes(),
774
+ security(),
775
+ headings(),
776
+ toc({
777
+ depth: 5,
778
+ searchDepth: 6
779
+ }),
780
+ sourceMappingPlugin()
781
+ ]
782
+ });
783
783
  }
784
784
 
785
785
  //#endregion
@@ -804,14 +804,52 @@ const parseAuthoringContent = Effect.fn("AuthoringSchema.parseContent")(function
804
804
  return (yield* Schema.decodeUnknownEffect(CmsMarkdownDocument)(body)).nodes;
805
805
  });
806
806
  const isJsonObject = Schema.is(Schema.JsonObject);
807
- /** Validates authored blocks during single-document compilation as well as catalog publication. */
808
- function validateAuthoringDocument(body, components) {
809
- if (!components || ![...components.values()].some((component) => component.authoring)) return [];
810
- function visit(node) {
811
- const component = node.type === "cmsBlock" ? components?.get(String(node.attrs?.tag).toLowerCase()) : void 0;
812
- return [...component?.authoring ? createAuthoringSchema(component.name, component.authoring).validate(node, "publish") : [], ...(node.content ?? []).flatMap(visit)];
807
+ /** Resolves collection selectors for both insertion controls and authoritative validation. */
808
+ function collectionComponents(components, body) {
809
+ if (!body) return components;
810
+ return components.filter((component) => body.blocks?.includes(component.name) || component.authoring.group !== void 0 && body.groups?.includes(component.authoring.group));
811
+ }
812
+ /** Configuration errors belong to developers, before an installation adopts the catalog. */
813
+ function validateCollectionBody(components, body) {
814
+ return [
815
+ ...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}.`)])),
816
+ ...(body?.blocks ?? []).filter((name) => !components.some((component) => component.name === name)).map((name) => `Unknown block "${name}".`),
817
+ ...(body?.groups ?? []).filter((group) => !components.some((component) => component.authoring.group === group)).map((group) => `Unknown block group "${group}".`)
818
+ ];
819
+ }
820
+ function contentIssues(node, schemas, allowed, purpose, collectionAllowed = allowed) {
821
+ const issues = [];
822
+ if (node.type === "cmsBlock") {
823
+ const name = String(node.attrs?.tag).toLowerCase();
824
+ const schema = schemas.get(name);
825
+ if (!schema) issues.push("This block is unavailable. Ask your site developer to restore its definition.");
826
+ else {
827
+ if (!allowed.has(name)) issues.push(`${schema.definition.label} is not allowed in this content region.`);
828
+ issues.push(...schema.validate(node, purpose));
829
+ }
830
+ }
831
+ if (node.type === "cmsOpaque" || node.type === "cmsInlineOpaque") issues.push("This content is unsupported. Ask your site developer to update it before publishing.");
832
+ if (node.type === "cmsBlock") {
833
+ const schema = schemas.get(String(node.attrs?.tag).toLowerCase());
834
+ for (const region of node.content ?? []) {
835
+ const slot = schema?.definition.slots.find((candidate) => candidate.name === region.attrs?.name);
836
+ const permitted = slot?.input === "blocks" ? new Set(collectionComponents([...schemas].map(([name, schema]) => ({
837
+ name,
838
+ authoring: schema.definition
839
+ })), slot.blocks === void 0 && slot.groups === void 0 ? void 0 : slot).map((component) => component.name).filter((name) => collectionAllowed.has(name))) : collectionAllowed;
840
+ issues.push(...contentIssues(region, schemas, permitted, purpose, collectionAllowed));
841
+ }
842
+ return issues;
813
843
  }
814
- return comarkToEditor(body.nodes, createCmsComarkRenderData(body)).flatMap(visit);
844
+ return [...issues, ...(node.content ?? []).flatMap((child) => contentIssues(child, schemas, allowed, purpose, collectionAllowed))];
845
+ }
846
+ /** Validates authored blocks during single-document compilation as well as catalog publication. */
847
+ function validateAuthoringDocument(body, components, bodyPolicy) {
848
+ if (!components) return [];
849
+ const definitions = [...components.values()];
850
+ const schemas = new Map(definitions.map((component) => [component.name, createAuthoringSchema(component.name, component.authoring)]));
851
+ const allowed = new Set(collectionComponents(definitions, bodyPolicy).map((component) => component.name));
852
+ return comarkToEditor(body.nodes, createCmsComarkRenderData(body)).flatMap((node) => contentIssues(node, schemas, allowed, "publish"));
815
853
  }
816
854
  function createAuthoringSchema(name, definition) {
817
855
  const fields = new Map(definition.props.map((field) => [field.name, field]));
@@ -902,10 +940,30 @@ function createAuthoringSchema(name, definition) {
902
940
  function propertyIssue(field, value, purpose) {
903
941
  if (value === void 0) return field.required && purpose === "publish" ? `${field.label} is required.` : void 0;
904
942
  if (value === null && field.nullable) return void 0;
905
- if (!(field.type === "string" ? Predicate.isString(value) : field.type === "number" ? Predicate.isNumber(value) && Number.isFinite(value) : Predicate.isBoolean(value))) return `${field.label} must be ${field.type}.`;
943
+ if (!(field.type === "string" ? Predicate.isString(value) : field.type === "number" ? Predicate.isNumber(value) && Number.isFinite(value) : field.type === "boolean" ? Predicate.isBoolean(value) : field.type === "array" ? Array.isArray(value) : isJsonObject(value))) return `${field.label} must be ${field.type}.`;
944
+ if (field.type === "object" || field.type === "array") {
945
+ const entries = Array.isArray(value) ? value : [value];
946
+ const ids = /* @__PURE__ */ new Set();
947
+ for (const entry of entries) {
948
+ if (!isJsonObject(entry)) return `${field.label} must contain objects.`;
949
+ if (field.type === "array") {
950
+ const id = entry.id;
951
+ if (!Predicate.isString(id) || !id || ids.has(id)) return `${field.label} items need unique identities.`;
952
+ ids.add(id);
953
+ }
954
+ 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.`;
955
+ for (const child of field.fields ?? []) {
956
+ const issue = propertyIssue(child, entry[child.name], purpose);
957
+ if (issue) return `${field.label}: ${issue}`;
958
+ }
959
+ }
960
+ }
961
+ if (purpose === "publish" && field.required && value === "" && (field.input === "image" || field.input === "link")) return `${field.label} is required.`;
962
+ 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"}.`;
906
963
  if (field.options.length && !field.options.some((option) => option.value === value)) return `${field.label} must use one of its available choices.`;
907
964
  }
908
965
  function validRegion(slot, content) {
966
+ if (slot.input === "blocks") return content.every((node) => node.type === "cmsBlock" || node.type === "paragraph" && !node.content?.length);
909
967
  const types = /* @__PURE__ */ new Set([
910
968
  "paragraph",
911
969
  "text",
@@ -920,7 +978,177 @@ function validRegion(slot, content) {
920
978
  const valid = (node) => types.has(node.type ?? "") && (node.marks ?? []).every((mark) => marks.has(mark.type)) && (node.content ?? []).every(valid);
921
979
  return content.every(valid);
922
980
  }
981
+ /** URL values remain ordinary Vue strings and use the document model's public paths. */
982
+ function validAuthoringUrl(value, input) {
983
+ if (value.trim() !== value || value.includes("\\")) return false;
984
+ for (const character of value) if (character.charCodeAt(0) <= 32) return false;
985
+ if (value.startsWith("/") && !value.startsWith("//")) return true;
986
+ if (input === "link" && value.startsWith("#")) return true;
987
+ try {
988
+ const url = new URL(value);
989
+ return [
990
+ "https:",
991
+ "http:",
992
+ ...input === "link" ? ["mailto:", "tel:"] : []
993
+ ].includes(url.protocol);
994
+ } catch {
995
+ return false;
996
+ }
997
+ }
998
+
999
+ //#endregion
1000
+ //#region ../protocol/src/Authoring.ts
1001
+ /** Serializable component authoring data resolved by the framework compiler. */
1002
+ const BlockProp = Schema.Struct({
1003
+ name: Schema.NonEmptyString,
1004
+ label: Schema.NonEmptyString,
1005
+ help: Schema.optionalKey(Schema.String),
1006
+ type: Schema.Literals([
1007
+ "string",
1008
+ "number",
1009
+ "boolean",
1010
+ "object",
1011
+ "array"
1012
+ ]),
1013
+ input: Schema.Literals([
1014
+ "text",
1015
+ "textarea",
1016
+ "select",
1017
+ "number",
1018
+ "toggle",
1019
+ "image",
1020
+ "link",
1021
+ "object",
1022
+ "list"
1023
+ ]),
1024
+ required: Schema.Boolean,
1025
+ nullable: Schema.Boolean,
1026
+ defaultValue: Schema.optionalKey(Schema.Json),
1027
+ fields: Schema.optionalKey(Schema.Array(Schema.suspend(() => BlockProp))),
1028
+ options: Schema.Array(Schema.Struct({
1029
+ value: Schema.String,
1030
+ label: Schema.String
1031
+ }))
1032
+ });
1033
+ const BlockSlot = Schema.Struct({
1034
+ name: Schema.NonEmptyString,
1035
+ label: Schema.NonEmptyString,
1036
+ help: Schema.optionalKey(Schema.String),
1037
+ required: Schema.Boolean,
1038
+ input: Schema.Literals([
1039
+ "text",
1040
+ "rich-text",
1041
+ "blocks"
1042
+ ]),
1043
+ blocks: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
1044
+ groups: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
1045
+ features: Schema.Array(Schema.Literals([
1046
+ "bold",
1047
+ "italic",
1048
+ "link",
1049
+ "bullet-list",
1050
+ "code"
1051
+ ])),
1052
+ initial: Schema.optionalKey(Schema.Array(MarkdownNode))
1053
+ });
1054
+ const BlockAuthoring = Schema.Struct({
1055
+ version: Schema.Literal(1),
1056
+ label: Schema.NonEmptyString,
1057
+ description: Schema.optionalKey(Schema.String),
1058
+ group: Schema.optionalKey(Schema.String),
1059
+ icon: Schema.optionalKey(Schema.String),
1060
+ props: Schema.Array(BlockProp),
1061
+ slots: Schema.Array(BlockSlot),
1062
+ initialProps: Schema.JsonObject
1063
+ });
1064
+
1065
+ //#endregion
1066
+ //#region ../protocol/src/Configuration.ts
1067
+ /** Validated installation configuration shared by authoring, compilation, Studio, and Nuxt. */
1068
+ const fieldPresentation = {
1069
+ path: Schema.NonEmptyString,
1070
+ label: Schema.NonEmptyString,
1071
+ description: Schema.optionalKey(Schema.String),
1072
+ defaultValue: Schema.optionalKey(Schema.Json),
1073
+ required: Schema.Boolean
1074
+ };
1075
+ const BooleanAuthoringField = Schema.Struct({
1076
+ type: Schema.tag("boolean"),
1077
+ ...fieldPresentation,
1078
+ input: Schema.Literal("toggle")
1079
+ });
1080
+ const DateAuthoringField = Schema.Struct({
1081
+ type: Schema.tag("date"),
1082
+ ...fieldPresentation,
1083
+ input: Schema.Literal("date")
1084
+ });
1085
+ const NumberAuthoringField = Schema.Struct({
1086
+ type: Schema.tag("number"),
1087
+ ...fieldPresentation,
1088
+ input: Schema.Literals(["number", "select"]),
1089
+ options: Schema.Array(Schema.Finite)
1090
+ });
1091
+ const StringAuthoringField = Schema.Struct({
1092
+ type: Schema.tag("string"),
1093
+ ...fieldPresentation,
1094
+ input: Schema.Literals([
1095
+ "select",
1096
+ "text",
1097
+ "textarea"
1098
+ ]),
1099
+ options: Schema.Array(Schema.String)
1100
+ });
1101
+ const StringArrayAuthoringField = Schema.Struct({
1102
+ type: Schema.tag("string[]"),
1103
+ ...fieldPresentation,
1104
+ input: Schema.Literal("multiselect"),
1105
+ options: Schema.Array(Schema.String)
1106
+ });
1107
+ const DocumentField = Schema.Union([
1108
+ BooleanAuthoringField,
1109
+ DateAuthoringField,
1110
+ NumberAuthoringField,
1111
+ StringArrayAuthoringField,
1112
+ StringAuthoringField
1113
+ ]);
1114
+ const CollectionBody = Schema.Struct({
1115
+ blocks: Schema.optionalKey(Schema.Array(Schema.NonEmptyString)),
1116
+ groups: Schema.optionalKey(Schema.Array(Schema.NonEmptyString))
1117
+ });
1118
+ const CollectionDefinition = Schema.Struct({
1119
+ collection: CollectionName,
1120
+ body: Schema.optionalKey(CollectionBody),
1121
+ label: Schema.NonEmptyString,
1122
+ description: Schema.String,
1123
+ directory: SourceDirectory,
1124
+ addressPattern: AddressPattern,
1125
+ hierarchy: Schema.Boolean,
1126
+ canCreate: Schema.Boolean,
1127
+ authoringFields: Schema.Array(DocumentField)
1128
+ }).annotate({ identifier: "CollectionDefinition" });
1129
+ const LocaleConfiguration = Schema.Struct({
1130
+ locales: Schema.Array(Locale),
1131
+ defaultLocale: Locale,
1132
+ fallbackLocale: Schema.NullOr(Locale),
1133
+ prefixDefaultLocale: Schema.Boolean
1134
+ }).annotate({ identifier: "LocaleConfiguration" });
1135
+ const DocumentConfiguration = Schema.Struct({
1136
+ types: Schema.Array(CollectionDefinition),
1137
+ locales: Schema.optionalKey(LocaleConfiguration)
1138
+ }).annotate({ identifier: "DocumentConfiguration" });
1139
+ const ComponentDefinition = Schema.Struct({
1140
+ authoring: BlockAuthoring,
1141
+ name: Schema.NonEmptyString
1142
+ }).annotate({ identifier: "ComponentDefinition" });
1143
+ const AuthoringCatalog = Schema.Struct({
1144
+ revision: Schema.NonEmptyString,
1145
+ components: Schema.Array(ComponentDefinition)
1146
+ });
1147
+ const MediaConfiguration = Schema.Struct({
1148
+ enabled: Schema.Boolean,
1149
+ maxUploadBytes: Schema.Int.check(Schema.isGreaterThan(0))
1150
+ });
923
1151
 
924
1152
  //#endregion
925
- export { withCmsComarkProps as C, resolveCmsComarkAttributes as S, createDocumentParser as _, EditorRenderFailed as a, matchesCmsComponentPropType as b, comarkTagForEditorMark as c, encodeEditorImageAttributes as d, normalizeEditorImageLayout as f, DOCUMENT_ENGINE_FINGERPRINT as g, nonEmptyStringAttribute as h, EditorDocumentInvalid as i, isEditorMark as l, finiteNumberAttribute as m, parseAuthoringContent as n, comarkToEditor as o, compactJsonAttributes as p, validateAuthoringDocument as r, EDITOR_MARK_TYPES as s, createAuthoringSchema as t, EDITOR_IMAGE_LAYOUT_ATTRIBUTE as u, SOURCE_MAPPINGS_META_KEY as v, normalizeCmsComponentBindings as x, encodeCmsEditorComponentAttributes as y };
926
- //# sourceMappingURL=AuthoringSchema-BQ5kbhVB.js.map
1153
+ export { finiteNumberAttribute as C, projectCmsEditorComponentAttributes as D, normalizeCmsComponentBindings as E, resolveCmsComarkAttributes as O, compactJsonAttributes as S, encodeCmsEditorComponentAttributes as T, comarkTagForEditorMark as _, BlockAuthoring as a, encodeEditorImageAttributes as b, validateAuthoringDocument as c, EditorRenderFailed as d, DOCUMENT_ENGINE_FINGERPRINT as f, EDITOR_MARK_TYPES as g, comarkToEditor as h, MediaConfiguration as i, withCmsComarkProps as k, validateCollectionBody as l, SOURCE_MAPPINGS_META_KEY as m, ComponentDefinition as n, createAuthoringSchema as o, createDocumentParser as p, DocumentConfiguration as r, parseAuthoringContent as s, AuthoringCatalog as t, EditorDocumentInvalid as u, isEditorMark as v, nonEmptyStringAttribute as w, normalizeEditorImageLayout as x, EDITOR_IMAGE_LAYOUT_ATTRIBUTE as y };
1154
+ //# sourceMappingURL=Configuration-DyKo0sLS.js.map