@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 { d as SourcePath, o as PublicPath } from "./scalars-B2lgGUnj.js";
2
- import { M as DocumentSeo, c as DocumentIssue, h as DocumentSourceMapping, n as CmsMarkdownDocument, r as CompiledDocument, t as CmsDocumentAnalysis, v as DocumentValidationResult } from "./models-ChRxBChy.js";
3
- import { c as resolveCmsComarkAttributes, d as createCmsComarkRenderData, l as withCmsComarkProps, o as normalizeCmsComponentBindings, u as cmsComarkTextContent } from "./Forms-DoB56hfe.js";
4
- import { f as DOCUMENT_ENGINE_FINGERPRINT, l as validateCollectionBody, m as SOURCE_MAPPINGS_META_KEY, o as createAuthoringDocumentValidator, p as createDocumentParser } from "./Configuration-BU-UyGoH.js";
1
+ import { o as PublicPath, r as DocumentId } from "./scalars-CVZqT3L0.js";
2
+ import { P as DocumentSeo, _ as DocumentSourceMapping, b as DocumentValidationResult, l as DocumentIssue, n as CmsMarkdownDocument, r as CompiledDocument, t as CmsDocumentAnalysis } from "./models-VmGmG19-.js";
3
+ import { i as decodeManagedMediaReference, r as decodeAuthoredDocumentReference } from "./References-CCPDAPve.js";
4
+ import { c as resolveCmsComarkAttributes, d as createCmsComarkRenderData, l as withCmsComarkProps, o as normalizeCmsComponentBindings, u as cmsComarkTextContent } from "./Forms-CX-hwmJF.js";
5
+ import { h as SOURCE_MAPPINGS_META_KEY, l as validateCollectionBody, m as createDocumentParser, o as createAuthoringDocumentValidator, p as DOCUMENT_ENGINE_FINGERPRINT } from "./Configuration-JQ1qyaU0.js";
5
6
  import { DateTime, Effect, Option, Predicate, Result, Schema } from "effect";
6
7
 
7
8
  //#region ../document/src/internal/document-address.ts
@@ -12,15 +13,15 @@ function parseDocumentAddress(pattern) {
12
13
  name: match[1] ?? ""
13
14
  }));
14
15
  if (pattern.replace(addressTokenPattern, "").includes(":") || tokens.some((token) => !token.name)) return Result.fail({
15
- field: "addressPattern",
16
+ field: "routes.entry",
16
17
  message: `Document address pattern "${pattern}" contains an invalid token.`
17
18
  });
18
19
  if (tokens.filter((token) => token.catchAll).length > 1) return Result.fail({
19
- field: "addressPattern",
20
+ field: "routes.entry",
20
21
  message: `Document address pattern "${pattern}" contains more than one catch-all token.`
21
22
  });
22
23
  if (new Set(tokens.map((token) => token.name)).size !== tokens.length) return Result.fail({
23
- field: "addressPattern",
24
+ field: "routes.entry",
24
25
  message: `Document address pattern "${pattern}" contains duplicate tokens.`
25
26
  });
26
27
  return Result.succeed({
@@ -45,16 +46,6 @@ function resolveDocumentAddress(address, relativeKey, data) {
45
46
  }
46
47
  return normalizePublicPath(resolved);
47
48
  }
48
- function documentKeyFromSourcePath(relativeSourcePath) {
49
- const extension = relativeSourcePath.match(/\.(?:md|mdc)$/iu)?.[0];
50
- if (!extension) return Result.fail({
51
- field: "sourcePath",
52
- message: `Document source must be a Markdown file: "${relativeSourcePath}".`
53
- });
54
- const segments = relativeSourcePath.slice(0, -extension.length).split("/");
55
- if (segments.at(-1)?.toLocaleLowerCase() === "index") segments.pop();
56
- return normalizeDocumentRelativePath(segments.join("/")).pipe(Result.flatMap(normalizePublicPath));
57
- }
58
49
  function localizeDocumentPath(key, locale, configuration) {
59
50
  if (!configuration || !locale || locale === configuration.defaultLocale && !configuration.prefixDefaultLocale) return key;
60
51
  return PublicPath.make(`/${locale}${key === "/" ? "" : key}`);
@@ -95,7 +86,7 @@ var DocumentEngineConfigurationInvalid = class extends Schema.TaggedError()("Doc
95
86
  field: Schema.optionalKey(Schema.NonEmptyString)
96
87
  }) {};
97
88
  var DocumentParseFailed = class extends Schema.TaggedError()("DocumentEngine.DocumentParseFailed", {
98
- sourcePath: SourcePath,
89
+ documentId: DocumentId,
99
90
  cause: Schema.Defect()
100
91
  }) {};
101
92
  var DocumentCompilationFailed = class extends Schema.TaggedError()("DocumentEngine.DocumentCompilationFailed", { issues: Schema.Array(DocumentIssue) }) {};
@@ -209,12 +200,12 @@ function normalizeText(value) {
209
200
 
210
201
  //#endregion
211
202
  //#region ../document/src/internal/document-engine/frontmatter-validation.ts
212
- const validateFrontmatter = Effect.fn("DocumentEngine.validateFrontmatter")(function* (raw, schema, type, sourcePath) {
203
+ const validateFrontmatter = Effect.fn("DocumentEngine.validateFrontmatter")(function* (raw, schema, type, documentId) {
213
204
  const projection = schema.project(raw);
214
- const issues = projection.unknownPaths.toSorted().map((path) => unknownFieldIssue(path, type, sourcePath));
205
+ const issues = projection.unknownPaths.toSorted().map((path) => unknownFieldIssue(path, type, documentId));
215
206
  const result = yield* schema.validate(projection.value);
216
207
  if ("issues" in result) {
217
- issues.push(...result.issues.map((issue) => schemaIssue(issue, projection.value, type, sourcePath)));
208
+ issues.push(...result.issues.map((issue) => schemaIssue(issue, projection.value, type, documentId)));
218
209
  return {
219
210
  data: projection.value,
220
211
  issues
@@ -222,7 +213,7 @@ const validateFrontmatter = Effect.fn("DocumentEngine.validateFrontmatter")(func
222
213
  }
223
214
  const parsed = Schema.decodeUnknownOption(Schema.JsonObject)(result.value);
224
215
  if (Option.isNone(parsed)) {
225
- issues.push(issue("schema.constraint", "The collection schema output must be a JSON-compatible object.", type, sourcePath, "frontmatter"));
216
+ issues.push(issue("schema.constraint", "The collection schema output must be a JSON-compatible object.", type, documentId, "frontmatter"));
226
217
  return {
227
218
  data: projection.value,
228
219
  issues
@@ -236,13 +227,13 @@ const validateFrontmatter = Effect.fn("DocumentEngine.validateFrontmatter")(func
236
227
  issues
237
228
  };
238
229
  });
239
- function unknownFieldIssue(path, type, sourcePath) {
240
- if (path === "id") return issue("document.id", "Document identity is managed by the CMS and cannot be authored in frontmatter.", type, sourcePath, path);
241
- return issue("schema.unknown", `Frontmatter field "${path}" is not declared by collection "${type.collection}".`, type, sourcePath, path);
230
+ function unknownFieldIssue(path, type, documentId) {
231
+ if (path === "id") return issue("document.id", "Document identity is managed by the CMS and cannot be authored in frontmatter.", type, documentId, path);
232
+ return issue("schema.unknown", `Frontmatter field "${path}" is not declared by collection "${type.collection}".`, type, documentId, path);
242
233
  }
243
- function schemaIssue(error, input, type, sourcePath) {
234
+ function schemaIssue(error, input, type, documentId) {
244
235
  const field = issuePath(error.path);
245
- return issue(issueCode(error, input, field), error.message, type, sourcePath, field || void 0);
236
+ return issue(issueCode(error, input, field), error.message, type, documentId, field || void 0);
246
237
  }
247
238
  function issueCode(error, input, field) {
248
239
  if (error.code === "invalid_type") return nestedValue(input, field) === void 0 ? "schema.required" : "schema.type";
@@ -262,14 +253,14 @@ function nestedValue(value, path) {
262
253
  return Schema.is(Schema.JsonObject)(current) ? current[segment] : void 0;
263
254
  }, value);
264
255
  }
265
- function issue(code, message, type, sourcePath, field) {
256
+ function issue(code, message, type, documentId, field) {
266
257
  return {
267
258
  code,
268
259
  collection: type.collection,
269
260
  ...field === void 0 ? void 0 : { field },
270
261
  message,
271
262
  severity: "error",
272
- sourcePath
263
+ documentId
273
264
  };
274
265
  }
275
266
 
@@ -277,26 +268,27 @@ function issue(code, message, type, sourcePath, field) {
277
268
  //#region ../document/src/internal/document-engine/compilation.ts
278
269
  const isDocumentParseFailed$1 = Schema.is(DocumentParseFailed);
279
270
  const compileCompiledDocument = Effect.fn("DocumentEngine.compileDocument")(function* (configuration, parseMarkdown, source) {
280
- const location = yield* resolveSourceLocation(configuration, source);
271
+ const collection = yield* resolveCollection(configuration, source);
281
272
  const parsedBody = yield* parseBody(parseMarkdown, source);
282
273
  const mappings = yield* sourceMappings(parsedBody, source);
283
274
  const body = normalizeCmsComponentBindings(withoutSourceMappings(parsedBody), configuration.components);
284
275
  const { collectionFrontmatter, rawSeo } = splitFrontmatter(body.frontmatter);
285
- const seoProjection = projectSeo(rawSeo, source.sourcePath);
286
- const relativeKey = yield* fromAddressResult(documentKeyFromSourcePath(location.relativeSourcePath), source.sourcePath);
287
- const validation = yield* validateFrontmatter(collectionFrontmatter, location.type.schema, location.type.definition, source.sourcePath).pipe(Effect.mapError((cause) => new DocumentCompilationFailed({ issues: [{
276
+ const seoProjection = projectSeo(rawSeo, source.identity.documentId);
277
+ const validation = yield* validateFrontmatter(collectionFrontmatter, collection.schema, collection.definition, source.identity.documentId).pipe(Effect.mapError((cause) => new DocumentCompilationFailed({ issues: [{
288
278
  code: "schema.constraint",
289
- collection: location.type.definition.collection,
279
+ collection: collection.definition.collection,
280
+ documentId: source.identity.documentId,
290
281
  message: cause.message,
291
- severity: "error",
292
- sourcePath: source.sourcePath
282
+ severity: "error"
293
283
  }] })));
294
- const address = resolveDocumentAddress(location.type.address, relativeKey, validation.data);
295
- const key = Result.isSuccess(address) ? address.success : provisionalKey(source.documentId);
296
- const path = localizeDocumentPath(key, location.locale, configuration.documents.locales);
297
- const title = stringValue(validation.data.title) ?? stringValue(body.meta.title) ?? titleFromSource(location.relativeSourcePath);
284
+ const relativeKey = PublicPath.make(source.placement.kind === "home" ? "/" : `/${source.placement.slug}`);
285
+ const route = source.placement.kind === "home" ? collection.homeRoute : collection.entryRoute;
286
+ const address = resolveDocumentAddress(route, relativeKey, validation.data);
287
+ const key = Result.isSuccess(address) ? address.success : provisionalKey(source.identity.documentId);
288
+ const path = localizeDocumentPath(key, source.identity.locale, configuration.documents.locales);
289
+ const title = stringValue(validation.data.title) ?? stringValue(body.meta.title) ?? (source.placement.kind === "home" ? collection.definition.label : humanize(source.placement.slug));
298
290
  const description = stringValue(validation.data.description) ?? stringValue(body.meta.description);
299
- const issues = Result.isSuccess(address) ? validation.issues : [...validation.issues, addressIssue(address.failure, source.sourcePath)];
291
+ const issues = Result.isSuccess(address) ? validation.issues : [...validation.issues, addressIssue(address.failure, source.identity.documentId)];
300
292
  const canonicalFrontmatter = rawSeo === void 0 ? validation.data : Object.assign({ _seo: rawSeo }, validation.data);
301
293
  const canonicalBody = CmsMarkdownDocument.make({
302
294
  frontmatter: canonicalFrontmatter,
@@ -308,32 +300,35 @@ const compileCompiledDocument = Effect.fn("DocumentEngine.compileDocument")(func
308
300
  title
309
301
  });
310
302
  return CompiledDocument.make(Object.assign({
311
- id: source.documentId,
303
+ id: source.identity.documentId,
304
+ identity: source.identity,
305
+ placement: source.placement,
312
306
  key,
313
307
  path,
314
- collection: location.type.definition.collection,
308
+ collection: source.identity.collection,
315
309
  draft: source.draft,
316
310
  title,
317
311
  data: validation.data,
318
312
  issues: [
319
313
  ...issues,
320
314
  ...seoProjection.issues,
321
- ...(configuration.validateAuthoringDocument?.(canonicalBody, location.type.definition.body) ?? []).map((message) => ({
315
+ ...(configuration.validateAuthoringDocument?.(canonicalBody, collection.definition.body) ?? []).map((issue) => ({
322
316
  code: "reference.component",
323
- message,
324
- severity: "error",
325
- sourcePath: source.sourcePath
317
+ component: issue.component,
318
+ documentId: source.identity.documentId,
319
+ ...issue.field ? { field: issue.field.join(".") } : void 0,
320
+ message: issue.message,
321
+ severity: "error"
326
322
  }))
327
323
  ],
328
324
  analysis,
329
325
  body: canonicalBody,
330
326
  engineFingerprint: DOCUMENT_ENGINE_FINGERPRINT,
331
327
  source: {
332
- path: source.sourcePath,
333
328
  mappings,
334
329
  ...source.publishedAt === void 0 ? void 0 : { publishedAt: source.publishedAt }
335
330
  }
336
- }, location.locale === void 0 ? void 0 : { locale: location.locale }, description === void 0 ? void 0 : { description }, seoProjection.seo === void 0 ? void 0 : { seo: seoProjection.seo }));
331
+ }, source.identity.locale === void 0 ? void 0 : { locale: source.identity.locale }, description === void 0 ? void 0 : { description }, seoProjection.seo === void 0 ? void 0 : { seo: seoProjection.seo }));
337
332
  });
338
333
  function splitFrontmatter(frontmatter) {
339
334
  const { _seo: rawSeo, ...collectionFrontmatter } = frontmatter;
@@ -342,7 +337,7 @@ function splitFrontmatter(frontmatter) {
342
337
  rawSeo
343
338
  };
344
339
  }
345
- function projectSeo(rawSeo, sourcePath) {
340
+ function projectSeo(rawSeo, documentId) {
346
341
  if (rawSeo === void 0) return { issues: [] };
347
342
  const decoded = Schema.decodeUnknownResult(DocumentSeo)(rawSeo, { onExcessProperty: "error" });
348
343
  if (Result.isSuccess(decoded)) return {
@@ -351,83 +346,60 @@ function projectSeo(rawSeo, sourcePath) {
351
346
  };
352
347
  return { issues: [{
353
348
  code: "seo.invalid",
349
+ documentId,
354
350
  field: "_seo",
355
351
  message: `Document SEO is invalid: ${String(decoded.failure)}`,
356
- severity: "error",
357
- sourcePath
352
+ severity: "error"
358
353
  }] };
359
354
  }
360
355
  function provisionalKey(documentId) {
361
356
  return PublicPath.make(`/_cms-invalid/${documentId}`);
362
357
  }
363
- function addressIssue(error, sourcePath) {
364
- const issue = {
358
+ function addressIssue(error, documentId) {
359
+ return {
365
360
  code: "document.invalid",
361
+ documentId,
362
+ ...error.field === void 0 ? void 0 : { field: error.field },
366
363
  message: error.message,
367
- severity: "error",
368
- sourcePath
369
- };
370
- return error.field === void 0 ? issue : {
371
- ...issue,
372
- field: error.field
364
+ severity: "error"
373
365
  };
374
366
  }
375
- function resolveSourceLocation(configuration, source) {
376
- const segments = source.sourcePath.split("/");
377
- let locale;
378
- if (configuration.documents.locales) {
379
- const candidate = segments.shift();
380
- locale = configuration.documents.locales.locales.find((value) => value === candidate);
381
- if (!locale) return compilationFailure({
382
- code: "document.invalid",
383
- field: "locale",
384
- message: `Localized document "${source.sourcePath}" must begin with a configured locale.`,
385
- severity: "error",
386
- sourcePath: source.sourcePath
387
- });
388
- }
389
- const localizedSourcePath = segments.join("/");
390
- const type = configuration.types.find(({ definition }) => definition.directory === "." || localizedSourcePath.startsWith(`${definition.directory}/`));
391
- if (!type) return compilationFailure({
367
+ function resolveCollection(configuration, source) {
368
+ const collection = configuration.types.find(({ definition }) => definition.collection === source.identity.collection);
369
+ if (!collection) return compilationFailure({
392
370
  code: "document.invalid",
393
- field: "sourcePath",
394
- message: `No document collection contains "${source.sourcePath}".`,
395
- severity: "error",
396
- sourcePath: source.sourcePath
371
+ documentId: source.identity.documentId,
372
+ field: "identity.collection",
373
+ message: `Document collection "${source.identity.collection}" does not exist.`,
374
+ severity: "error"
397
375
  });
398
- const relativeSourcePath = type.definition.directory === "." ? localizedSourcePath : localizedSourcePath.slice(type.definition.directory.length + 1);
399
- if (!relativeSourcePath) return compilationFailure({
376
+ const locales = configuration.documents.locales;
377
+ const locale = source.identity.locale;
378
+ if (!locales && locale !== void 0 || locales && (locale === void 0 || !locales.locales.includes(locale))) return compilationFailure({
400
379
  code: "document.invalid",
401
- field: "sourcePath",
402
- message: `Document source "${source.sourcePath}" has no collection-relative path.`,
403
- severity: "error",
404
- sourcePath: source.sourcePath
405
- });
406
- return Effect.succeed(locale === void 0 ? {
407
- relativeSourcePath,
408
- type
409
- } : {
410
- locale,
411
- relativeSourcePath,
412
- type
380
+ documentId: source.identity.documentId,
381
+ field: "identity.locale",
382
+ message: locale ? `Document locale "${locale}" is not configured.` : "A locale is required for this document.",
383
+ severity: "error"
413
384
  });
385
+ return Effect.succeed(collection);
414
386
  }
415
387
  function parseBody(parseMarkdown, source) {
416
388
  return Effect.tryPromise({
417
389
  try: () => parseMarkdown(source.markdown),
418
390
  catch: (cause) => new DocumentParseFailed({
419
391
  cause,
420
- sourcePath: source.sourcePath
392
+ documentId: source.identity.documentId
421
393
  })
422
394
  }).pipe(Effect.flatMap(Schema.decodeUnknownEffect(CmsMarkdownDocument)), Effect.mapError((cause) => isDocumentParseFailed$1(cause) ? cause : new DocumentParseFailed({
423
395
  cause,
424
- sourcePath: source.sourcePath
396
+ documentId: source.identity.documentId
425
397
  })));
426
398
  }
427
399
  function sourceMappings(body, source) {
428
400
  return Schema.decodeUnknownEffect(Schema.Array(DocumentSourceMapping))(body.meta["__cmsSourceMappings"] ?? []).pipe(Effect.mapError((cause) => new DocumentParseFailed({
429
401
  cause,
430
- sourcePath: source.sourcePath
402
+ documentId: source.identity.documentId
431
403
  })));
432
404
  }
433
405
  function withoutSourceMappings(body) {
@@ -438,25 +410,21 @@ function withoutSourceMappings(body) {
438
410
  nodes: body.nodes
439
411
  });
440
412
  }
441
- function fromAddressResult(result, sourcePath) {
442
- if (Result.isSuccess(result)) return Effect.succeed(result.success);
443
- return compilationFailure(addressIssue(result.failure, sourcePath));
444
- }
445
413
  function compilationFailure(issue) {
446
414
  return Effect.fail(new DocumentCompilationFailed({ issues: [issue] }));
447
415
  }
448
416
  function stringValue(value) {
449
417
  return Predicate.isString(value) && value.trim() ? value.trim() : void 0;
450
418
  }
451
- function titleFromSource(sourcePath) {
452
- const stem = (sourcePath.split("/").at(-1) ?? "content").replace(/\.(?:md|mdc)$/iu, "");
453
- return (stem.toLocaleLowerCase() === "index" ? sourcePath.split("/").at(-2) ?? "home" : stem).replace(/[-_]+/gu, " ").replace(/^./u, (character) => character.toLocaleUpperCase());
419
+ function humanize(value) {
420
+ return value.replace(/[-_]+/gu, " ").replace(/^./u, (character) => character.toLocaleUpperCase());
454
421
  }
455
422
 
456
423
  //#endregion
457
424
  //#region ../document/src/internal/document-engine/catalog-lookup.ts
458
425
  function createCatalogLookup(documents, configuration) {
459
426
  const byPath = new Map(documents.map((document) => [document.path, document]));
427
+ const byId = new Map(documents.map((document) => [document.id, document]));
460
428
  const byVariant = new Map(documents.map((document) => [localeAndKey(document.locale, document.key), document]));
461
429
  const findByKey = (input, locale) => {
462
430
  const keyResult = normalizePublicPath(input);
@@ -486,6 +454,7 @@ function createCatalogLookup(documents, configuration) {
486
454
  return Option.fromNullishOr(byVariant.get(localeAndKey(locales.fallbackLocale, request.key)));
487
455
  };
488
456
  return {
457
+ findById: (documentId) => Option.fromNullishOr(byId.get(documentId)),
489
458
  findByKey,
490
459
  findByPath
491
460
  };
@@ -519,15 +488,14 @@ function collisionIssues(documents) {
519
488
  const byPath = /* @__PURE__ */ new Map();
520
489
  const byVariant = /* @__PURE__ */ new Map();
521
490
  for (const document of documents) {
522
- const identityCollision = byId.get(document.id);
523
- if (identityCollision) issues.push(collisionIssue(`Document identity collision for "${document.id}" between "${identityCollision.source.path}" and "${document.source.path}".`, document));
491
+ if (byId.get(document.id)) issues.push(collisionIssue(`Document identity collision for "${document.id}".`, document));
524
492
  else byId.set(document.id, document);
525
493
  const pathCollision = byPath.get(document.path);
526
- if (pathCollision) issues.push(collisionIssue(`Document route collision at "${document.path}" between "${pathCollision.source.path}" and "${document.source.path}".`, document, document.path));
494
+ if (pathCollision) issues.push(collisionIssue(`Document route collision at "${document.path}" between documents "${pathCollision.id}" and "${document.id}".`, document, document.path));
527
495
  else byPath.set(document.path, document);
528
496
  const variant = localeAndKey(document.locale, document.key);
529
497
  const variantCollision = byVariant.get(variant);
530
- if (variantCollision && variantCollision.path !== pathCollision?.path) issues.push(collisionIssue(`Document locale collision at "${document.key}" between "${variantCollision.source.path}" and "${document.source.path}".`, document, document.key));
498
+ if (variantCollision && variantCollision.path !== pathCollision?.path) issues.push(collisionIssue(`Document locale collision at "${document.key}" between documents "${variantCollision.id}" and "${document.id}".`, document, document.key));
531
499
  else byVariant.set(variant, document);
532
500
  }
533
501
  return issues;
@@ -549,21 +517,15 @@ function collisionIssue(message, document, key) {
549
517
  code: "document.collision",
550
518
  message,
551
519
  severity: "error",
552
- sourcePath: document.source.path
520
+ documentId: document.id
553
521
  };
554
522
  return key === void 0 ? issue : Object.assign(issue, { key });
555
523
  }
556
524
 
557
525
  //#endregion
558
526
  //#region ../document/src/internal/document-engine/reference-target.ts
559
- function referenceTarget(tag, attributes, documentPath, mediaBaseUrl) {
560
- if (tag === "a") {
561
- const path = documentTarget(attributes.get("href"), documentPath);
562
- return path ? {
563
- kind: "document",
564
- path
565
- } : void 0;
566
- }
527
+ function referenceTarget(tag, attributes, mediaBaseUrl) {
528
+ if (tag === "a") return decodeDocumentReference(attributes.get("href"));
567
529
  if (tag === "img") {
568
530
  const key = mediaKey(attributes.get("src"), mediaBaseUrl);
569
531
  return key ? {
@@ -580,10 +542,29 @@ function referenceTarget(tag, attributes, documentPath, mediaBaseUrl) {
580
542
  function targetKey(target) {
581
543
  switch (target.kind) {
582
544
  case "component": return `component:${target.name.toLocaleLowerCase()}`;
583
- case "document": return `document:${target.path}`;
545
+ case "document": return `document:${target.documentId}`;
584
546
  case "media": return `media:${target.key}`;
585
547
  }
586
548
  }
549
+ function decodeDocumentReference(value) {
550
+ if (!Predicate.isString(value)) return void 0;
551
+ const reference = decodeAuthoredDocumentReference(value);
552
+ return reference ? {
553
+ kind: "document",
554
+ ...reference
555
+ } : void 0;
556
+ }
557
+ function decodeMediaReference(value, mediaBaseUrl) {
558
+ if (!Predicate.isString(value)) return void 0;
559
+ const reference = decodeManagedMediaReference(value);
560
+ if (!reference) return void 0;
561
+ const key = mediaKey(reference.lastKnownUrl, mediaBaseUrl);
562
+ return key ? {
563
+ kind: "media",
564
+ key,
565
+ mediaId: reference.mediaId
566
+ } : void 0;
567
+ }
587
568
  function mediaKey(value, mediaBaseUrl) {
588
569
  if (!Predicate.isString(value) || mediaBaseUrl === void 0) return void 0;
589
570
  const prefix = `${mediaBaseUrl}/`;
@@ -599,25 +580,6 @@ function mediaKey(value, mediaBaseUrl) {
599
580
  return;
600
581
  }
601
582
  }
602
- function documentTarget(value, documentPath) {
603
- if (!Predicate.isString(value)) return void 0;
604
- const href = value.trim();
605
- if (!href || href.startsWith("#") || href.startsWith("?") || href.startsWith("//") || /^[a-z][a-z\d+.-]*:/iu.test(href)) return;
606
- const resolvedPath = resolveRelativePath(href.split(/[?#]/u)[0] ?? "", documentPath);
607
- if ((resolvedPath.split("/").at(-1) ?? "").includes(".")) return void 0;
608
- const normalized = normalizePublicPath(resolvedPath);
609
- return Result.isSuccess(normalized) ? normalized.success : void 0;
610
- }
611
- function resolveRelativePath(href, documentPath) {
612
- const normalizedHref = href.replace(/\\/gu, "/");
613
- const segments = normalizedHref.startsWith("/") ? [] : documentPath.split("/").filter(Boolean).slice(0, -1);
614
- for (const segment of normalizedHref.split("/")) {
615
- if (!segment || segment === ".") continue;
616
- if (segment === "..") segments.pop();
617
- else segments.push(segment);
618
- }
619
- return `/${segments.join("/")}`;
620
- }
621
583
  function hasControlCharacter(value) {
622
584
  for (const character of value) {
623
585
  const codePoint = character.codePointAt(0);
@@ -629,19 +591,24 @@ function hasControlCharacter(value) {
629
591
  //#endregion
630
592
  //#region ../document/src/internal/document-engine/references.ts
631
593
  const noReferences = Object.freeze([]);
632
- function createDocumentReferenceIndex(documents, configuration, findByPath) {
594
+ function createDocumentReferenceIndex(documents, configuration, findById) {
633
595
  const all = [];
634
596
  const byTarget = /* @__PURE__ */ new Map();
635
597
  const issues = /* @__PURE__ */ new Map();
636
598
  for (const document of documents) {
599
+ const invalidManagedMedia = (lastKnownUrl) => addIssue(issues, document.id, {
600
+ code: "reference.media",
601
+ documentId: document.id,
602
+ message: `Managed-media reference "${lastKnownUrl}" is outside the configured media library.`,
603
+ severity: "error"
604
+ });
637
605
  const indexTarget = (target, nodePath) => {
638
606
  if (!target) return;
639
607
  const reference = freezeReference({
640
608
  nodePath,
641
609
  source: {
642
610
  documentId: document.id,
643
- path: document.path,
644
- sourcePath: document.source.path
611
+ route: document.path
645
612
  },
646
613
  target
647
614
  });
@@ -650,18 +617,20 @@ function createDocumentReferenceIndex(documents, configuration, findByPath) {
650
617
  const matching = byTarget.get(key) ?? [];
651
618
  matching.push(reference);
652
619
  byTarget.set(key, matching);
653
- if (target.kind === "document" && Option.isNone(findByPath(target.path))) addIssue(issues, document.source.path, {
620
+ if (target.kind === "document" && Option.isNone(findById(target.documentId))) addIssue(issues, document.id, {
654
621
  code: "reference.document",
655
- key: target.path,
656
- message: `Document link "${target.path}" does not resolve.`,
657
- severity: "error",
658
- sourcePath: document.source.path
622
+ documentId: document.id,
623
+ key: target.lastKnownRoute,
624
+ message: `Document link target "${target.documentId}" does not resolve.`,
625
+ severity: "error"
659
626
  });
660
627
  };
661
- const seoImageTarget = document.seo?.socialImage ? referenceTarget("img", /* @__PURE__ */ new Map([["src", document.seo.socialImage.url]]), document.path, configuration.mediaBaseUrl) : void 0;
628
+ const seoImageTarget = document.seo?.socialImage ? referenceTarget("img", /* @__PURE__ */ new Map([["src", document.seo.socialImage.url]]), configuration.mediaBaseUrl) : void 0;
662
629
  indexTarget(seoImageTarget?.kind === "media" ? Object.assign({}, seoImageTarget, { mediaId: document.seo?.socialImage?.mediaId }) : seoImageTarget, []);
630
+ const collection = configuration.types.find(({ definition }) => definition.collection === document.collection);
631
+ for (const target of propertyTargets(collection?.definition.authoringFields ?? [], document.data, configuration.mediaBaseUrl, invalidManagedMedia)) indexTarget(target, []);
663
632
  visit(document.body.nodes, createCmsComarkRenderData(document.body), (tag, attributes, children, nodePath) => {
664
- const targets = [referenceTarget(tag, attributes, document.path, configuration.mediaBaseUrl), ...propertyTargets(configuration.components?.get(tag.toLowerCase())?.authoring.props ?? [], Object.fromEntries([...attributes].flatMap(([key, value]) => value === void 0 ? [] : [[key, value]])), document.path, configuration.mediaBaseUrl)];
633
+ const targets = [referenceTarget(tag, attributes, configuration.mediaBaseUrl), ...propertyTargets(configuration.components?.get(tag.toLowerCase())?.authoring.props ?? [], Object.fromEntries([...attributes].flatMap(([key, value]) => value === void 0 ? [] : [[key, value]])), configuration.mediaBaseUrl, invalidManagedMedia)];
665
634
  for (const target of targets) indexTarget(target, nodePath);
666
635
  });
667
636
  }
@@ -692,19 +661,31 @@ function freezeReference(reference) {
692
661
  target: Object.freeze({ ...reference.target })
693
662
  });
694
663
  }
695
- function addIssue(issues, sourcePath, issue) {
696
- const sourceIssues = issues.get(sourcePath) ?? [];
664
+ function addIssue(issues, documentId, issue) {
665
+ const sourceIssues = issues.get(documentId) ?? [];
697
666
  sourceIssues.push(issue);
698
- issues.set(sourcePath, sourceIssues);
667
+ issues.set(documentId, sourceIssues);
699
668
  }
700
- function propertyTargets(fields, values, path, mediaBaseUrl) {
669
+ function propertyTargets(fields, values, mediaBaseUrl, onInvalidManagedMedia) {
701
670
  return fields.flatMap((field) => {
702
671
  const value = values[field.name] === void 0 ? field.defaultValue : values[field.name];
672
+ if (field.input === "document") {
673
+ const target = decodeDocumentReference(value);
674
+ return target ? [target] : [];
675
+ }
676
+ if (field.input === "media") {
677
+ const target = decodeMediaReference(value, mediaBaseUrl);
678
+ if (!target && Predicate.isString(value)) {
679
+ const reference = decodeManagedMediaReference(value);
680
+ if (reference) onInvalidManagedMedia(reference.lastKnownUrl);
681
+ }
682
+ return target ? [target] : [];
683
+ }
703
684
  if (field.input === "link" || field.input === "image") {
704
- const target = referenceTarget(field.input === "link" ? "a" : "img", /* @__PURE__ */ new Map([[field.input === "link" ? "href" : "src", value]]), path, mediaBaseUrl);
685
+ const target = referenceTarget(field.input === "link" ? "a" : "img", /* @__PURE__ */ new Map([[field.input === "link" ? "href" : "src", value]]), mediaBaseUrl);
705
686
  return target ? [target] : [];
706
687
  }
707
- if (field.fields) return (Array.isArray(value) ? value : [value]).flatMap((entry) => Schema.is(Schema.JsonObject)(entry) ? propertyTargets(field.fields ?? [], entry, path, mediaBaseUrl) : []);
688
+ if (field.fields) return (Array.isArray(value) ? value : [value]).flatMap((entry) => Schema.is(Schema.JsonObject)(entry) ? propertyTargets(field.fields ?? [], entry, mediaBaseUrl, onInvalidManagedMedia) : []);
708
689
  return [];
709
690
  });
710
691
  }
@@ -712,44 +693,168 @@ function propertyTargets(fields, values, path, mediaBaseUrl) {
712
693
  //#endregion
713
694
  //#region ../document/src/internal/document-engine/catalog.ts
714
695
  function createDocumentCatalog(configuration, input) {
715
- const documents = [...input].sort((left, right) => left.source.path.localeCompare(right.source.path));
696
+ const resolved = resolvePlacementGraph(configuration, input);
697
+ if (Result.isFailure(resolved)) return Result.fail(resolved.failure);
698
+ const documents = resolved.success.toSorted((left, right) => left.path.localeCompare(right.path));
716
699
  const collisions = collisionIssues(documents);
717
700
  if (collisions.length > 0) return Result.fail(new DocumentCompilationFailed({ issues: collisions }));
718
701
  const lookup = createCatalogLookup(documents, configuration);
719
- const references = createDocumentReferenceIndex(documents, configuration, lookup.findByPath);
702
+ const references = createDocumentReferenceIndex(documents, configuration, lookup.findById);
720
703
  const withReferenceIssues = documents.map((document) => {
721
- const additional = references.issues.get(document.source.path) ?? [];
722
- return additional.length === 0 ? document : CompiledDocument.make(Object.assign({}, document, { issues: [...document.issues, ...additional] }));
704
+ const additional = references.issues.get(document.id) ?? [];
705
+ const projected = resolveSemanticRoutes(document, lookup.findById);
706
+ return additional.length === 0 ? projected : CompiledDocument.make(Object.assign({}, projected, { issues: [...document.issues, ...additional] }));
723
707
  });
708
+ const finalLookup = createCatalogLookup(withReferenceIssues, configuration);
724
709
  return Result.succeed({
725
710
  documents: withReferenceIssues,
726
711
  issues: [...withReferenceIssues.flatMap((document) => document.issues), ...translationIssues(withReferenceIssues, configuration)],
727
- ...createCatalogLookup(withReferenceIssues, configuration),
712
+ ...finalLookup,
728
713
  references: references.index
729
714
  });
730
715
  }
716
+ function resolveSemanticRoutes(document, findById) {
717
+ const nodes = document.body.nodes.map((node) => resolveNodeReferences(node, findById));
718
+ const data = resolveJsonObjectReferences(document.data, findById);
719
+ const frontmatter = resolveJsonObjectReferences(document.body.frontmatter, findById);
720
+ return CompiledDocument.make(Object.assign({}, document, {
721
+ data,
722
+ body: Object.assign({}, document.body, {
723
+ frontmatter,
724
+ nodes
725
+ })
726
+ }));
727
+ }
728
+ function resolveNodeReferences(node, findById) {
729
+ if (Schema.is(Schema.String)(node)) return node;
730
+ const [tag, attributes, ...children] = node;
731
+ if (tag === null) return node;
732
+ return [
733
+ tag,
734
+ resolveJsonObjectReferences(attributes, findById),
735
+ ...children.map((child) => resolveNodeReferences(child, findById))
736
+ ];
737
+ }
738
+ function resolveJsonObjectReferences(value, findById) {
739
+ return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, resolveJsonReferences(entry, findById)]));
740
+ }
741
+ function resolveJsonReferences(value, findById) {
742
+ if (Schema.is(Schema.String)(value)) {
743
+ const reference = decodeDocumentReference(value);
744
+ if (reference?.kind === "document") {
745
+ const target = findById(reference.documentId);
746
+ return Option.isSome(target) ? target.value.path : reference.lastKnownRoute;
747
+ }
748
+ return decodeManagedMediaReference(value)?.lastKnownUrl ?? value;
749
+ }
750
+ if (Array.isArray(value)) return value.map((entry) => resolveJsonReferences(entry, findById));
751
+ if (Schema.is(Schema.JsonObject)(value)) return resolveJsonObjectReferences(value, findById);
752
+ return value;
753
+ }
754
+ function resolvePlacementGraph(configuration, documents) {
755
+ const byId = /* @__PURE__ */ new Map();
756
+ const homes = /* @__PURE__ */ new Map();
757
+ const issues = [];
758
+ for (const document of documents) {
759
+ if (byId.get(document.id)) {
760
+ issues.push(invalid$1(document, `Document identity "${document.id}" appears more than once.`));
761
+ continue;
762
+ }
763
+ byId.set(document.id, document);
764
+ if (document.placement.kind === "home") {
765
+ const key = identityScope(document);
766
+ const existing = homes.get(key);
767
+ if (existing) issues.push(invalid$1(document, `Documents "${existing.id}" and "${document.id}" are both home documents for collection "${document.collection}"${document.locale ? ` in locale "${document.locale}"` : ""}.`));
768
+ else homes.set(key, document);
769
+ }
770
+ }
771
+ const segments = /* @__PURE__ */ new Map();
772
+ const visiting = /* @__PURE__ */ new Set();
773
+ const resolveSegments = (document) => {
774
+ const cached = segments.get(document.id);
775
+ if (cached) return cached;
776
+ if (visiting.has(document.id)) {
777
+ issues.push(invalid$1(document, `Document placement cycle includes "${document.id}".`, "placement"));
778
+ return;
779
+ }
780
+ visiting.add(document.id);
781
+ let result;
782
+ if (document.placement.kind === "home") result = [];
783
+ else {
784
+ const parent = byId.get(document.placement.parentDocumentId);
785
+ if (!parent) issues.push(invalid$1(document, `Parent document "${document.placement.parentDocumentId}" does not exist in this catalog.`, "placement.parentDocumentId"));
786
+ else if (parent.collection !== document.collection) issues.push(invalid$1(document, "A parent document must belong to the same collection.", "placement.parentDocumentId"));
787
+ else if (parent.locale !== document.locale) issues.push(invalid$1(document, "A parent document must use the same locale.", "placement.parentDocumentId"));
788
+ else {
789
+ const collection = configuration.types.find(({ definition }) => definition.collection === document.collection);
790
+ if (!collection) issues.push(invalid$1(document, `Collection "${document.collection}" is not configured.`));
791
+ else if (!collection.definition.hierarchy && parent.placement.kind !== "home") issues.push(invalid$1(document, "This collection allows entries only directly below its home document.", "placement"));
792
+ else {
793
+ const parentSegments = resolveSegments(parent);
794
+ if (parentSegments) result = [...parentSegments, document.placement.slug];
795
+ }
796
+ }
797
+ }
798
+ visiting.delete(document.id);
799
+ if (result) segments.set(document.id, result);
800
+ return result;
801
+ };
802
+ for (const document of documents) resolveSegments(document);
803
+ if (issues.length > 0) return Result.fail(new DocumentCompilationFailed({ issues }));
804
+ const projected = documents.map((document) => {
805
+ const collection = configuration.types.find(({ definition }) => definition.collection === document.collection);
806
+ const placementSegments = segments.get(document.id);
807
+ if (!collection || !placementSegments) return document;
808
+ const relativeKey = PublicPath.make(`/${placementSegments.join("/")}`);
809
+ const configuredRoute = document.placement.kind === "home" ? collection.homeRoute : collection.entryRoute;
810
+ const route = resolveDocumentAddress(configuredRoute, relativeKey, document.data);
811
+ if (Result.isFailure(route)) {
812
+ issues.push(invalid$1(document, route.failure.message, route.failure.field));
813
+ return document;
814
+ }
815
+ return CompiledDocument.make(Object.assign({}, document, {
816
+ key: route.success,
817
+ path: localizeDocumentPath(route.success, document.locale, configuration.documents.locales)
818
+ }));
819
+ });
820
+ return issues.length > 0 ? Result.fail(new DocumentCompilationFailed({ issues })) : Result.succeed(projected);
821
+ }
822
+ function identityScope(document) {
823
+ return `${document.collection}:${document.locale ?? ""}`;
824
+ }
825
+ function invalid$1(document, message, field) {
826
+ return {
827
+ code: "document.invalid",
828
+ collection: document.collection,
829
+ documentId: document.id,
830
+ ...field === void 0 ? void 0 : { field },
831
+ ...document.locale === void 0 ? void 0 : { locale: document.locale },
832
+ message,
833
+ severity: "error"
834
+ };
835
+ }
731
836
 
732
837
  //#endregion
733
838
  //#region ../document/src/internal/document-engine/configuration.ts
734
839
  function resolveDocumentEngineConfiguration(documents, options) {
735
840
  if (documents.types.length === 0) return invalid("Documents must define at least one collection.", "types");
736
841
  const collections = /* @__PURE__ */ new Set();
737
- const directories = /* @__PURE__ */ new Map();
738
842
  const resolved = [];
739
843
  for (const definition of documents.types) {
740
844
  if (collections.has(definition.collection)) return invalid(`Document collection "${definition.collection}" is defined more than once.`, "types");
741
845
  collections.add(definition.collection);
742
- const existing = directories.get(definition.directory);
743
- if (existing) return invalid(`Document collections "${existing}" and "${definition.collection}" use the same directory "${definition.directory}".`, "types");
744
- directories.set(definition.directory, definition.collection);
745
- const addressResult = parseDocumentAddress(definition.addressPattern);
746
- if (Result.isFailure(addressResult)) return invalid(addressResult.failure.message, addressResult.failure.field);
747
- if (definition.hierarchy && !addressResult.success.tokens.some((token) => token.catchAll)) return invalid(`Document collection "${definition.collection}" can only enable hierarchy with a catch-all address.`, "types");
846
+ const homeRoute = parseDocumentAddress(definition.routes.home);
847
+ const entryRoute = parseDocumentAddress(definition.routes.entry);
848
+ if (Result.isFailure(homeRoute)) return invalid(homeRoute.failure.message, homeRoute.failure.field);
849
+ if (Result.isFailure(entryRoute)) return invalid(entryRoute.failure.message, entryRoute.failure.field);
850
+ if (homeRoute.success.tokens.length > 0) return invalid(`Document collection "${definition.collection}" home route cannot contain tokens.`, "types");
851
+ if (definition.hierarchy && !entryRoute.success.tokens.some((token) => token.catchAll)) return invalid(`Document collection "${definition.collection}" can only enable hierarchy with a catch-all entry route.`, "types");
748
852
  const schema = options.documentModel.types[definition.collection];
749
853
  if (!schema) return invalid(`Document collection "${definition.collection}" has no executable schema.`, "documentModel");
750
854
  resolved.push({
751
- address: addressResult.success,
752
855
  definition,
856
+ entryRoute: entryRoute.success,
857
+ homeRoute: homeRoute.success,
753
858
  schema
754
859
  });
755
860
  }
@@ -772,7 +877,6 @@ function resolveDocumentEngineConfiguration(documents, options) {
772
877
  }
773
878
  }
774
879
  const mediaBaseUrl = options.mediaBaseUrl?.replace(/\/+$/u, "");
775
- resolved.sort((left, right) => directoryDepth(right.definition.directory) - directoryDepth(left.definition.directory));
776
880
  const configuration = {
777
881
  documents,
778
882
  types: resolved
@@ -798,9 +902,6 @@ function resolveComponents(definitions) {
798
902
  return invalid(cause instanceof Error ? cause.message : "The component authoring catalog is invalid.", "components");
799
903
  }
800
904
  }
801
- function directoryDepth(directory) {
802
- return directory === "." ? 0 : directory.split("/").length;
803
- }
804
905
  function invalid(message, field) {
805
906
  return Result.fail(new DocumentEngineConfigurationInvalid(field === void 0 ? { message } : {
806
907
  field,
@@ -819,14 +920,14 @@ const createDocumentEngine = Effect.fn("DocumentEngine.make")(function* (documen
819
920
  const compileAll = Effect.fn("DocumentEngine.compileAll")(function* (input) {
820
921
  const previous = input.previous ? compilations.get(input.previous) : void 0;
821
922
  const compiled = yield* Effect.forEach(input.sources, (source) => {
822
- const cached = previous?.get(source.documentId);
923
+ const cached = previous?.get(source.identity.documentId);
823
924
  return cached && sameCompilationSource(cached.source, source) ? Effect.succeed(cached.document) : compile(source);
824
925
  });
825
926
  const catalog = yield* Effect.fromResult(createDocumentCatalog(configuration, compiled));
826
927
  compilations.set(catalog, new Map(input.sources.map((source, index) => {
827
928
  const document = compiled[index];
828
- if (!document) throw new Error(`Compiled document "${source.documentId}" is missing.`);
829
- return [source.documentId, {
929
+ if (!document) throw new Error(`Compiled document "${source.identity.documentId}" is missing.`);
930
+ return [source.identity.documentId, {
830
931
  document,
831
932
  source
832
933
  }];
@@ -848,7 +949,7 @@ const createDocumentEngine = Effect.fn("DocumentEngine.make")(function* (documen
848
949
  code: "markdown.parse",
849
950
  message: errorMessage(error.cause),
850
951
  severity: "error",
851
- sourcePath: error.sourcePath
952
+ documentId: error.documentId
852
953
  }] : error.issues;
853
954
  return DocumentValidationResult.make({
854
955
  valid: false,
@@ -863,7 +964,7 @@ const createDocumentEngine = Effect.fn("DocumentEngine.make")(function* (documen
863
964
  };
864
965
  });
865
966
  function sameCompilationSource(left, right) {
866
- return left.documentId === right.documentId && left.draft === right.draft && left.markdown === right.markdown && left.sourcePath === right.sourcePath && samePublishedAt(left.publishedAt, right.publishedAt);
967
+ return left.identity.documentId === right.identity.documentId && left.identity.collection === right.identity.collection && left.identity.locale === right.identity.locale && left.placement.kind === right.placement.kind && (left.placement.kind === "home" || right.placement.kind === "entry" && left.placement.parentDocumentId === right.placement.parentDocumentId && left.placement.slug === right.placement.slug) && left.draft === right.draft && left.markdown === right.markdown && samePublishedAt(left.publishedAt, right.publishedAt);
867
968
  }
868
969
  function samePublishedAt(left, right) {
869
970
  return left === void 0 || right === void 0 ? left === right : DateTime.Equivalence(left, right);
@@ -873,5 +974,5 @@ function errorMessage(cause) {
873
974
  }
874
975
 
875
976
  //#endregion
876
- export { normalizeDocumentRelativePath as a, resolveDocumentAddress as c, localizeDocumentPath as i, referenceTarget as n, normalizePublicPath as o, DocumentCompilationFailed as r, parseDocumentAddress as s, createDocumentEngine as t };
877
- //# sourceMappingURL=DocumentEngine-ClhFXW9M.js.map
977
+ export { DocumentCompilationFailed as n, createDocumentEngine as t };
978
+ //# sourceMappingURL=DocumentEngine-j6Vi7nQo.js.map