@caelo-cms/shared 0.10.72 → 0.11.0
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.
- package/dist/ai-tools.d.ts +125 -50
- package/dist/ai-tools.d.ts.map +1 -1
- package/dist/ai-tools.js +48 -36
- package/dist/ai-tools.js.map +1 -1
- package/dist/build-page.d.ts +110 -30
- package/dist/build-page.d.ts.map +1 -1
- package/dist/build-page.js +23 -4
- package/dist/build-page.js.map +1 -1
- package/dist/content.d.ts +201 -100
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +176 -74
- package/dist/content.js.map +1 -1
- package/dist/css-var-aliases.d.ts +20 -0
- package/dist/css-var-aliases.d.ts.map +1 -0
- package/dist/css-var-aliases.js +95 -0
- package/dist/css-var-aliases.js.map +1 -0
- package/dist/css-var-scan.d.ts +0 -16
- package/dist/css-var-scan.d.ts.map +1 -1
- package/dist/css-var-scan.js +24 -0
- package/dist/css-var-scan.js.map +1 -1
- package/dist/edit-markers.d.ts +41 -5
- package/dist/edit-markers.d.ts.map +1 -1
- package/dist/edit-markers.js +47 -50
- package/dist/edit-markers.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/interactions.js +2 -2
- package/dist/interactions.js.map +1 -1
- package/dist/link-canonicalise.d.ts.map +1 -1
- package/dist/link-canonicalise.js +7 -1
- package/dist/link-canonicalise.js.map +1 -1
- package/dist/link-refs.d.ts +1 -1
- package/dist/link-refs.js +3 -3
- package/dist/lists/address.d.ts +34 -0
- package/dist/lists/address.d.ts.map +1 -0
- package/dist/lists/address.js +43 -0
- package/dist/lists/address.js.map +1 -0
- package/dist/lists/default-types.d.ts +10 -0
- package/dist/lists/default-types.d.ts.map +1 -0
- package/dist/lists/default-types.js +91 -0
- package/dist/lists/default-types.js.map +1 -0
- package/dist/lists/fields.d.ts +152 -0
- package/dist/lists/fields.d.ts.map +1 -0
- package/dist/lists/fields.js +258 -0
- package/dist/lists/fields.js.map +1 -0
- package/dist/lists/index.d.ts +14 -0
- package/dist/lists/index.d.ts.map +1 -0
- package/dist/lists/index.js +15 -0
- package/dist/lists/index.js.map +1 -0
- package/dist/lists/item-attr.d.ts +26 -0
- package/dist/lists/item-attr.d.ts.map +1 -0
- package/dist/lists/item-attr.js +31 -0
- package/dist/lists/item-attr.js.map +1 -0
- package/dist/lists/items.d.ts +105 -0
- package/dist/lists/items.d.ts.map +1 -0
- package/dist/lists/items.js +304 -0
- package/dist/lists/items.js.map +1 -0
- package/dist/lists/list-type.d.ts +401 -0
- package/dist/lists/list-type.d.ts.map +1 -0
- package/dist/lists/list-type.js +275 -0
- package/dist/lists/list-type.js.map +1 -0
- package/dist/lists/model.d.ts +14 -0
- package/dist/lists/model.d.ts.map +1 -0
- package/dist/lists/model.js +15 -0
- package/dist/lists/model.js.map +1 -0
- package/dist/lists/page-type.d.ts +115 -0
- package/dist/lists/page-type.d.ts.map +1 -0
- package/dist/lists/page-type.js +66 -0
- package/dist/lists/page-type.js.map +1 -0
- package/dist/lists/refs.d.ts +13 -0
- package/dist/lists/refs.d.ts.map +1 -0
- package/dist/lists/refs.js +18 -0
- package/dist/lists/refs.js.map +1 -0
- package/dist/lists/render-data.d.ts +114 -0
- package/dist/lists/render-data.d.ts.map +1 -0
- package/dist/lists/render-data.js +3 -0
- package/dist/lists/render-data.js.map +1 -0
- package/dist/lists/render-deps.d.ts +68 -0
- package/dist/lists/render-deps.d.ts.map +1 -0
- package/dist/lists/render-deps.js +91 -0
- package/dist/lists/render-deps.js.map +1 -0
- package/dist/lists/resolve-values.d.ts +63 -0
- package/dist/lists/resolve-values.d.ts.map +1 -0
- package/dist/lists/resolve-values.js +181 -0
- package/dist/lists/resolve-values.js.map +1 -0
- package/dist/lists/resolve.d.ts +106 -0
- package/dist/lists/resolve.d.ts.map +1 -0
- package/dist/lists/resolve.js +878 -0
- package/dist/lists/resolve.js.map +1 -0
- package/dist/lists/scope.d.ts +81 -0
- package/dist/lists/scope.d.ts.map +1 -0
- package/dist/lists/scope.js +3 -0
- package/dist/lists/scope.js.map +1 -0
- package/dist/markup-structure.d.ts +8 -0
- package/dist/markup-structure.d.ts.map +1 -1
- package/dist/markup-structure.js +2 -0
- package/dist/markup-structure.js.map +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/nested-module-render.d.ts +25 -20
- package/dist/nested-module-render.d.ts.map +1 -1
- package/dist/nested-module-render.js +122 -31
- package/dist/nested-module-render.js.map +1 -1
- package/dist/page-log.d.ts +1 -1
- package/dist/preview-compose.d.ts +24 -21
- package/dist/preview-compose.d.ts.map +1 -1
- package/dist/preview-compose.js +34 -118
- package/dist/preview-compose.js.map +1 -1
- package/dist/source-text-transport.d.ts +17 -0
- package/dist/source-text-transport.d.ts.map +1 -0
- package/dist/source-text-transport.js +71 -0
- package/dist/source-text-transport.js.map +1 -0
- package/dist/template-engine.d.ts +44 -31
- package/dist/template-engine.d.ts.map +1 -1
- package/dist/template-engine.js +75 -174
- package/dist/template-engine.js.map +1 -1
- package/dist/template-sections.d.ts +69 -0
- package/dist/template-sections.d.ts.map +1 -0
- package/dist/template-sections.js +423 -0
- package/dist/template-sections.js.map +1 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +8 -1
- package/src/__tests__/redos-hardening.test.ts +50 -16
- package/src/ai-tools-add-module-modes.test.ts +50 -0
- package/src/ai-tools.test.ts +10 -0
- package/src/ai-tools.ts +51 -37
- package/src/build-page.test.ts +34 -3
- package/src/build-page.ts +26 -3
- package/src/content.test.ts +111 -3
- package/src/content.ts +186 -75
- package/src/css-var-aliases.test.ts +61 -0
- package/src/css-var-aliases.ts +109 -0
- package/src/css-var-scan.ts +9 -0
- package/src/edit-markers.test.ts +37 -21
- package/src/edit-markers.ts +52 -20
- package/src/index.ts +3 -1
- package/src/interactions.ts +2 -2
- package/src/link-canonicalise.test.ts +36 -6
- package/src/link-canonicalise.ts +6 -1
- package/src/link-refs.test.ts +66 -42
- package/src/link-refs.ts +3 -3
- package/src/lists/address.ts +75 -0
- package/src/lists/default-types.ts +102 -0
- package/src/lists/fields.ts +285 -0
- package/src/lists/index.ts +16 -0
- package/src/lists/item-attr.test.ts +36 -0
- package/src/lists/item-attr.ts +49 -0
- package/src/lists/items.ts +402 -0
- package/src/lists/list-fixtures.test.ts +302 -0
- package/src/lists/list-type.ts +306 -0
- package/src/lists/lists.test.ts +328 -0
- package/src/lists/model.ts +16 -0
- package/src/lists/page-type.ts +71 -0
- package/src/lists/refs.ts +21 -0
- package/src/lists/render-data.ts +125 -0
- package/src/lists/render-deps.test.ts +132 -0
- package/src/lists/render-deps.ts +144 -0
- package/src/lists/resolve-values.ts +224 -0
- package/src/lists/resolve.test.ts +295 -0
- package/src/lists/resolve.ts +1148 -0
- package/src/lists/scope.ts +69 -0
- package/src/lists-compose-parity.test.ts +262 -0
- package/src/markup-structure.ts +10 -0
- package/src/nested-module-render.test.ts +111 -77
- package/src/nested-module-render.ts +85 -48
- package/src/preview-compose.test.ts +55 -19
- package/src/preview-compose.ts +59 -154
- package/src/source-text-transport.test.ts +63 -0
- package/src/source-text-transport.ts +70 -0
- package/src/template-engine-lists.test.ts +553 -0
- package/src/template-engine.test.ts +62 -80
- package/src/template-engine.ts +102 -245
- package/src/template-sections.ts +500 -0
- package/src/version.ts +1 -1
- package/dist/structured-sets.d.ts +0 -70
- package/dist/structured-sets.d.ts.map +0 -1
- package/dist/structured-sets.js +0 -92
- package/dist/structured-sets.js.map +0 -1
- package/src/structured-sets.ts +0 -114
- package/src/template-engine-data-lists.test.ts +0 -90
package/src/structured-sets.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* P6.7.5 — typed schemas for the `structured_sets` table.
|
|
5
|
-
*
|
|
6
|
-
* One database table holds N kinds of named lists (nav-menus, taxonomies,
|
|
7
|
-
* theme tokens, tag lists, footer link blocks, ...). The `kind`
|
|
8
|
-
* discriminator drives both the Zod validator at the Query API layer
|
|
9
|
-
* and the renderer that turns the items into HTML at preview / deploy.
|
|
10
|
-
*
|
|
11
|
-
* Adding a new kind: add a Zod schema below, add it to the
|
|
12
|
-
* `structuredSetItem` discriminated union, add a renderer in
|
|
13
|
-
* `packages/shared/src/structured-set-renderer.ts`. No DB migration
|
|
14
|
-
* required — the table already accepts arbitrary `kind` text.
|
|
15
|
-
*
|
|
16
|
-
* Item shapes are deliberately **structured data**, not HTML strings.
|
|
17
|
-
* That's what makes `change_page_slug` able to retarget every menu
|
|
18
|
-
* link automatically: walk the JSON, swap `href` matches.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { z } from "zod";
|
|
22
|
-
|
|
23
|
-
/** Recursive nav menu — supports arbitrary submenu nesting. */
|
|
24
|
-
export const navMenuItem: z.ZodType<{
|
|
25
|
-
label: string;
|
|
26
|
-
href: string;
|
|
27
|
-
target?: "_self" | "_blank";
|
|
28
|
-
children?: { label: string; href: string }[];
|
|
29
|
-
/** When set, the renderer delegates this slot to the named ad plugin. */
|
|
30
|
-
adSlotId?: string;
|
|
31
|
-
}> = z.lazy(() =>
|
|
32
|
-
z
|
|
33
|
-
.object({
|
|
34
|
-
label: z.string().min(1).max(120),
|
|
35
|
-
href: z.string().min(1).max(500),
|
|
36
|
-
target: z.enum(["_self", "_blank"]).optional(),
|
|
37
|
-
children: z.array(navMenuItem).optional(),
|
|
38
|
-
adSlotId: z.string().min(1).max(100).optional(),
|
|
39
|
-
})
|
|
40
|
-
.strict(),
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
/** Tree-shaped category list. Used by typed-content / tag pages (P12A). */
|
|
44
|
-
export const taxonomyItem = z
|
|
45
|
-
.object({
|
|
46
|
-
slug: z.string().min(1).max(120),
|
|
47
|
-
displayName: z.string().min(1).max(200),
|
|
48
|
-
parentSlug: z.string().min(1).max(120).optional(),
|
|
49
|
-
description: z.string().max(2000).optional(),
|
|
50
|
-
})
|
|
51
|
-
.strict();
|
|
52
|
-
|
|
53
|
-
/** Flat tag — used by post taxonomies, content filtering. */
|
|
54
|
-
export const tagItem = z
|
|
55
|
-
.object({
|
|
56
|
-
slug: z
|
|
57
|
-
.string()
|
|
58
|
-
.min(1)
|
|
59
|
-
.max(120)
|
|
60
|
-
.regex(/^[a-z0-9-]+$/, "lowercase letters/digits/hyphens"),
|
|
61
|
-
displayName: z.string().min(1).max(200),
|
|
62
|
-
color: z
|
|
63
|
-
.string()
|
|
64
|
-
.regex(/^#[0-9a-fA-F]{3,8}$/, "hex color")
|
|
65
|
-
.optional(),
|
|
66
|
-
})
|
|
67
|
-
.strict();
|
|
68
|
-
|
|
69
|
-
/** Footer "legal" / related-resources block. */
|
|
70
|
-
export const linkListItem = z
|
|
71
|
-
.object({
|
|
72
|
-
label: z.string().min(1).max(200),
|
|
73
|
-
href: z.string().min(1).max(500),
|
|
74
|
-
description: z.string().max(500).optional(),
|
|
75
|
-
})
|
|
76
|
-
.strict();
|
|
77
|
-
|
|
78
|
-
/** Discriminated kind → items array. The Query API op picks the
|
|
79
|
-
* validator at runtime by reading `kind` first.
|
|
80
|
-
*
|
|
81
|
-
* v0.11.0 (#45) — `theme` is no longer a structured-set kind. The
|
|
82
|
-
* theme primitive moved to its own `themes` table with DTCG-shaped
|
|
83
|
-
* jsonb tokens; see `themes.ts` + `theme-render.ts`. */
|
|
84
|
-
export const structuredSetKind = z.enum(["nav-menu", "taxonomy", "tags", "link-list"]);
|
|
85
|
-
export type StructuredSetKind = z.infer<typeof structuredSetKind>;
|
|
86
|
-
|
|
87
|
-
/** Validate `items` against the right schema for `kind`. Throws a
|
|
88
|
-
* ZodError on mismatch — callers wrap and return `Err(ValidationFailed)`. */
|
|
89
|
-
export function validateStructuredSetItems(kind: StructuredSetKind, items: unknown): unknown[] {
|
|
90
|
-
if (!Array.isArray(items)) {
|
|
91
|
-
throw new z.ZodError([
|
|
92
|
-
{
|
|
93
|
-
code: "custom",
|
|
94
|
-
path: ["items"],
|
|
95
|
-
message: "items must be an array",
|
|
96
|
-
},
|
|
97
|
-
]);
|
|
98
|
-
}
|
|
99
|
-
switch (kind) {
|
|
100
|
-
case "nav-menu":
|
|
101
|
-
return items.map((it) => navMenuItem.parse(it));
|
|
102
|
-
case "taxonomy":
|
|
103
|
-
return items.map((it) => taxonomyItem.parse(it));
|
|
104
|
-
case "tags":
|
|
105
|
-
return items.map((it) => tagItem.parse(it));
|
|
106
|
-
case "link-list":
|
|
107
|
-
return items.map((it) => linkListItem.parse(it));
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export type NavMenuItem = z.infer<typeof navMenuItem>;
|
|
112
|
-
export type TaxonomyItem = z.infer<typeof taxonomyItem>;
|
|
113
|
-
export type TagItem = z.infer<typeof tagItem>;
|
|
114
|
-
export type LinkListItem = z.infer<typeof linkListItem>;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Plugin data lists in the template engine: `{{#name}}…{{/name}}` over
|
|
5
|
-
* data a plugin supplies per page, with the module owning the markup.
|
|
6
|
-
*
|
|
7
|
-
* The load-bearing case is the third one. Plugin activation is a hard
|
|
8
|
-
* state, so a module written while a plugin ran keeps its
|
|
9
|
-
* `{{#language_links}}` after the plugin is switched off. Rendering
|
|
10
|
-
* that as an empty string would be a silent fallback (CLAUDE.md §2) and
|
|
11
|
-
* would read to the operator as "the switcher vanished for no reason".
|
|
12
|
-
* It has to stay visible AND say which plugin to turn back on.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { describe, expect, it } from "bun:test";
|
|
16
|
-
import { renderTemplate } from "./template-engine.js";
|
|
17
|
-
|
|
18
|
-
const SWITCHER =
|
|
19
|
-
'<nav>{{#language_links}}<a href="{{href}}">{{label}}</a>{{/language_links}}</nav>';
|
|
20
|
-
|
|
21
|
-
describe("template engine — plugin data lists", () => {
|
|
22
|
-
it("iterates a plugin list with the module's own markup", () => {
|
|
23
|
-
const r = renderTemplate({
|
|
24
|
-
html: SWITCHER,
|
|
25
|
-
fields: [],
|
|
26
|
-
dataLists: {
|
|
27
|
-
language_links: [
|
|
28
|
-
{ href: "/", label: "English", locale: "en" },
|
|
29
|
-
{ href: "/de", label: "Deutsch", locale: "de" },
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
expect(r.html).toBe('<nav><a href="/">English</a><a href="/de">Deutsch</a></nav>');
|
|
34
|
-
expect(r.missingSlots).toEqual([]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("renders nothing for an empty list — that is data, not breakage", () => {
|
|
38
|
-
// A page with no published translations legitimately has no links.
|
|
39
|
-
const r = renderTemplate({
|
|
40
|
-
html: SWITCHER,
|
|
41
|
-
fields: [],
|
|
42
|
-
dataLists: { language_links: [] },
|
|
43
|
-
});
|
|
44
|
-
expect(r.html).toBe("<nav></nav>");
|
|
45
|
-
expect(r.missingSlots).toEqual([]);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("stays loud and names the plugin when its plugin is switched off", () => {
|
|
49
|
-
const r = renderTemplate({
|
|
50
|
-
html: SWITCHER,
|
|
51
|
-
fields: [],
|
|
52
|
-
dormantDataLists: { language_links: "international-site" },
|
|
53
|
-
});
|
|
54
|
-
// Loud-raw: the section survives in the output rather than
|
|
55
|
-
// silently collapsing.
|
|
56
|
-
expect(r.html).toContain("{{#language_links}}");
|
|
57
|
-
expect(r.missingSlots).toContain(
|
|
58
|
-
"plugin-list-unavailable:language_links plugin=international-site",
|
|
59
|
-
);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("keeps an unknown name a plain undeclared field", () => {
|
|
63
|
-
// No plugin claims it — this really is a typo, and must not be
|
|
64
|
-
// reported as a deactivated plugin.
|
|
65
|
-
const r = renderTemplate({ html: "<p>{{#nope}}x{{/nope}}</p>", fields: [] });
|
|
66
|
-
expect(r.missingSlots).toContain("field-not-declared:nope");
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("lets a module's own field win over a plugin list of the same name", () => {
|
|
70
|
-
// Module fields are resolved first by construction, so a plugin can
|
|
71
|
-
// never shadow content the author declared.
|
|
72
|
-
const r = renderTemplate({
|
|
73
|
-
html: "<ul>{{#items}}<li>{{label}}</li>{{/items}}</ul>",
|
|
74
|
-
fields: [{ name: "items", kind: "link-list" }],
|
|
75
|
-
contentValues: { items: [{ label: "authored", href: "/a" }] },
|
|
76
|
-
dataLists: { items: [{ label: "from-plugin" }] },
|
|
77
|
-
});
|
|
78
|
-
expect(r.html).toContain("authored");
|
|
79
|
-
expect(r.html).not.toContain("from-plugin");
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it("leaves a key the items do not carry raw", () => {
|
|
83
|
-
const r = renderTemplate({
|
|
84
|
-
html: "{{#l}}<a href={{href}}>{{missing_key}}</a>{{/l}}",
|
|
85
|
-
fields: [],
|
|
86
|
-
dataLists: { l: [{ href: "/x" }] },
|
|
87
|
-
});
|
|
88
|
-
expect(r.html).toContain("{{missing_key}}");
|
|
89
|
-
});
|
|
90
|
-
});
|