@caelo-cms/shared 0.11.0 → 0.11.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 (92) hide show
  1. package/dist/ai-tools.d.ts +30 -144
  2. package/dist/ai-tools.d.ts.map +1 -1
  3. package/dist/ai-tools.js +41 -141
  4. package/dist/ai-tools.js.map +1 -1
  5. package/dist/build-page.d.ts +5 -5
  6. package/dist/build-page.js +9 -9
  7. package/dist/build-page.js.map +1 -1
  8. package/dist/content.d.ts +13 -2
  9. package/dist/content.d.ts.map +1 -1
  10. package/dist/content.js +45 -12
  11. package/dist/content.js.map +1 -1
  12. package/dist/css-scope-scan.js +1 -1
  13. package/dist/escaped-line-breaks.d.ts +44 -0
  14. package/dist/escaped-line-breaks.d.ts.map +1 -0
  15. package/dist/escaped-line-breaks.js +203 -0
  16. package/dist/escaped-line-breaks.js.map +1 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/interactions.js +5 -5
  22. package/dist/interactions.js.map +1 -1
  23. package/dist/lists/default-types.d.ts +12 -0
  24. package/dist/lists/default-types.d.ts.map +1 -1
  25. package/dist/lists/default-types.js +36 -0
  26. package/dist/lists/default-types.js.map +1 -1
  27. package/dist/lists/fields.d.ts.map +1 -1
  28. package/dist/lists/fields.js +1 -0
  29. package/dist/lists/fields.js.map +1 -1
  30. package/dist/lists/render-data.d.ts +9 -0
  31. package/dist/lists/render-data.d.ts.map +1 -1
  32. package/dist/lists/render-deps.d.ts +4 -3
  33. package/dist/lists/render-deps.d.ts.map +1 -1
  34. package/dist/lists/render-deps.js +5 -3
  35. package/dist/lists/render-deps.js.map +1 -1
  36. package/dist/lists/resolve.d.ts +13 -0
  37. package/dist/lists/resolve.d.ts.map +1 -1
  38. package/dist/lists/resolve.js +95 -3
  39. package/dist/lists/resolve.js.map +1 -1
  40. package/dist/nested-module-render.d.ts +13 -0
  41. package/dist/nested-module-render.d.ts.map +1 -1
  42. package/dist/nested-module-render.js +34 -0
  43. package/dist/nested-module-render.js.map +1 -1
  44. package/dist/redirect-plan.d.ts +13 -0
  45. package/dist/redirect-plan.d.ts.map +1 -1
  46. package/dist/redirect-plan.js +22 -0
  47. package/dist/redirect-plan.js.map +1 -1
  48. package/dist/seo.d.ts +21 -0
  49. package/dist/seo.d.ts.map +1 -1
  50. package/dist/seo.js +32 -0
  51. package/dist/seo.js.map +1 -1
  52. package/dist/source-text-transport.d.ts +28 -2
  53. package/dist/source-text-transport.d.ts.map +1 -1
  54. package/dist/source-text-transport.js +12 -4
  55. package/dist/source-text-transport.js.map +1 -1
  56. package/dist/template-sections.d.ts.map +1 -1
  57. package/dist/template-sections.js +17 -4
  58. package/dist/template-sections.js.map +1 -1
  59. package/dist/version.d.ts +2 -2
  60. package/dist/version.js +1 -1
  61. package/package.json +1 -1
  62. package/src/__tests__/redos-hardening.test.ts +1 -0
  63. package/src/ai-tools-add-module-modes.test.ts +1 -1
  64. package/src/ai-tools.test.ts +7 -20
  65. package/src/ai-tools.ts +49 -153
  66. package/src/bug-intake.test.ts +1 -1
  67. package/src/build-page.ts +9 -9
  68. package/src/content.test.ts +26 -1
  69. package/src/content.ts +47 -13
  70. package/src/css-scope-scan.ts +1 -1
  71. package/src/escaped-line-breaks.ts +221 -0
  72. package/src/index.ts +1 -0
  73. package/src/interactions.ts +5 -5
  74. package/src/link-refs.test.ts +1 -0
  75. package/src/lists/default-types.ts +39 -0
  76. package/src/lists/fields.ts +1 -0
  77. package/src/lists/list-fixtures.test.ts +9 -3
  78. package/src/lists/render-data.ts +9 -0
  79. package/src/lists/render-deps.ts +6 -3
  80. package/src/lists/resolve.test.ts +182 -5
  81. package/src/lists/resolve.ts +101 -3
  82. package/src/nested-module-render.test.ts +39 -0
  83. package/src/nested-module-render.ts +35 -0
  84. package/src/redirect-plan.test.ts +25 -0
  85. package/src/redirect-plan.ts +24 -0
  86. package/src/seo.test.ts +58 -0
  87. package/src/seo.ts +49 -0
  88. package/src/source-text-transport.test.ts +98 -3
  89. package/src/source-text-transport.ts +15 -4
  90. package/src/template-engine-lists.test.ts +55 -0
  91. package/src/template-sections.ts +18 -4
  92. package/src/version.ts +1 -1
@@ -551,3 +551,58 @@ describe("lists — loud markers", () => {
551
551
  expect(r.missingSlots).toEqual(["list-context-unavailable:bullets"]);
552
552
  });
553
553
  });
554
+
555
+ describe("lists — breadcrumbs and {{site_text.*}} (#1142)", () => {
556
+ const SITE_TEXTS = "00000000-0000-4000-8000-0000000000e1";
557
+ const withTexts = (pageId: string, texts: Record<string, string>): ListPageContext =>
558
+ createListRenderContext(
559
+ fixtureData({
560
+ lists: [
561
+ {
562
+ id: SITE_TEXTS,
563
+ slug: "site-texts",
564
+ name: "Site texts",
565
+ typeSlug: "site-texts",
566
+ items: Object.entries(texts).map(([key, text], i) => ({
567
+ id: uuid(900 + i),
568
+ node: "text",
569
+ values: { key, text },
570
+ })),
571
+ },
572
+ ],
573
+ }),
574
+ ).forPage(pageId, { linkResolver: fixtureLinkIndex().forPage(pageId) });
575
+ const CRUMBS: TemplateField = {
576
+ name: "crumbs",
577
+ kind: "list",
578
+ listType: "links",
579
+ bind: "source",
580
+ source: { kind: "system", name: "breadcrumbs" },
581
+ };
582
+ const NAV =
583
+ '<nav aria-label="{{site_text.breadcrumb_label}}"><ol>{{#crumbs}}<li>{{#is_current}}<span aria-current="page">{{short_title}}</span>{{/is_current}}{{^is_current}}<a href="{{link}}">{{short_title}}</a>{{/is_current}}</li>{{/crumbs}}</ol></nav>';
584
+
585
+ it("renders a trail home → current with the aria-label from the site texts", () => {
586
+ const r = render(NAV, [CRUMBS], {}, withTexts(PAGE.team, { breadcrumb_label: "Breadcrumb" }));
587
+ expect(r.html).toBe(
588
+ '<nav aria-label="Breadcrumb"><ol><li><a href="/">Home</a></li><li><a href="/about/">About</a></li><li><span aria-current="page">Team</span></li></ol></nav>',
589
+ );
590
+ expect(r.missingSlots).toEqual([]);
591
+ });
592
+
593
+ it("a missing site text stays loud", () => {
594
+ const r = render(
595
+ '<nav aria-label="{{site_text.breadcrumb_label}}"></nav>',
596
+ [],
597
+ {},
598
+ withTexts(PAGE.team, {}),
599
+ );
600
+ expect(r.html).toBe('<nav aria-label="{{site_text.breadcrumb_label}}"></nav>');
601
+ expect(r.missingSlots).toContain("site-text-missing:breadcrumb_label");
602
+ });
603
+
604
+ it("is loud without a list context", () => {
605
+ const r = render("{{site_text.breadcrumb_label}}", [], {}, null);
606
+ expect(r.missingSlots).toContain("list-context-unavailable:site_text");
607
+ });
608
+ });
@@ -68,6 +68,17 @@ const TAG_RE = /\{\{\s*([#^/>]\s*)?([a-zA-Z][a-zA-Z0-9_]*(?:\.[a-zA-Z0-9_]+)*)\s
68
68
  const SECTION_NAME_RE = /^[a-z][a-z0-9_]*(?:\.[a-z0-9_]+)*$/;
69
69
  const DOT_RE = /\{\{\s*\.\s*\}\}/g;
70
70
 
71
+ /** Dotted heads the list context answers, not an entry: `{{page.*}}`, `{{site_text.*}}`. */
72
+ const CONTEXT_HEADS = new Set(["page", "site_text"]);
73
+
74
+ function contextHead(name: string): string {
75
+ return (name.split(".")[0] ?? "").toLowerCase();
76
+ }
77
+
78
+ function isContextHead(name: string): boolean {
79
+ return CONTEXT_HEADS.has(contextHead(name));
80
+ }
81
+
71
82
  function tokenize(html: string): Token[] {
72
83
  const out: Token[] = [];
73
84
  TAG_RE.lastIndex = 0;
@@ -201,6 +212,9 @@ class SectionRenderer {
201
212
  v = this.env.lists?.pageScope() ?? undefined;
202
213
  if (v === undefined)
203
214
  return parts.length > 1 || st.chain.length > 0 ? { missing: true } : undefined;
215
+ } else if (head === "site_text") {
216
+ v = this.env.lists?.siteTextScope();
217
+ if (v === undefined) return { missing: true };
204
218
  } else {
205
219
  for (let k = st.chain.length - 1; k >= 0 && v === undefined; k -= 1) {
206
220
  v = st.chain[k]?.entry.get(head);
@@ -251,8 +265,8 @@ class SectionRenderer {
251
265
  }
252
266
  if ("missing" in v) {
253
267
  const marker =
254
- t.name.split(".")[0]?.toLowerCase() === "page" && this.env.lists === undefined
255
- ? "list-context-unavailable:page"
268
+ isContextHead(t.name) && this.env.lists === undefined
269
+ ? `list-context-unavailable:${contextHead(t.name)}`
256
270
  : this.notDeclared(t.name, st);
257
271
  return this.raw(source, marker);
258
272
  }
@@ -321,8 +335,8 @@ class SectionRenderer {
321
335
  if (v !== undefined) {
322
336
  if ("missing" in v) {
323
337
  const marker =
324
- t.name.split(".")[0] === "page" && this.env.lists === undefined
325
- ? "list-context-unavailable:page"
338
+ isContextHead(t.name) && this.env.lists === undefined
339
+ ? `list-context-unavailable:${contextHead(t.name)}`
326
340
  : this.notDeclared(t.name, st);
327
341
  return this.raw(source, marker);
328
342
  }
package/src/version.ts CHANGED
@@ -22,7 +22,7 @@
22
22
  * follow standard SemVer.
23
23
  */
24
24
 
25
- export const CAELO_VERSION = "0.11.0";
25
+ export const CAELO_VERSION = "0.11.1";
26
26
 
27
27
  /**
28
28
  * Deprecated alias for back-compat — early P17 work spelled this