@caelo-cms/shared 0.10.21 → 0.10.23

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 (241) hide show
  1. package/dist/ai-tools.d.ts +291 -231
  2. package/dist/ai-tools.d.ts.map +1 -1
  3. package/dist/ai-tools.js +349 -281
  4. package/dist/ai-tools.js.map +1 -1
  5. package/dist/auth-forms.d.ts.map +1 -1
  6. package/dist/auth-forms.js +4 -1
  7. package/dist/auth-forms.js.map +1 -1
  8. package/dist/base-css.d.ts +24 -0
  9. package/dist/base-css.d.ts.map +1 -0
  10. package/dist/base-css.js +28 -0
  11. package/dist/base-css.js.map +1 -0
  12. package/dist/build-page.d.ts +330 -0
  13. package/dist/build-page.d.ts.map +1 -0
  14. package/dist/build-page.js +282 -0
  15. package/dist/build-page.js.map +1 -0
  16. package/dist/content.d.ts +322 -9
  17. package/dist/content.d.ts.map +1 -1
  18. package/dist/content.js +354 -11
  19. package/dist/content.js.map +1 -1
  20. package/dist/css-gradient-scan.d.ts +14 -0
  21. package/dist/css-gradient-scan.d.ts.map +1 -0
  22. package/dist/css-gradient-scan.js +81 -0
  23. package/dist/css-gradient-scan.js.map +1 -0
  24. package/dist/css-var-scan.d.ts +56 -0
  25. package/dist/css-var-scan.d.ts.map +1 -0
  26. package/dist/css-var-scan.js +97 -0
  27. package/dist/css-var-scan.js.map +1 -0
  28. package/dist/design-manifest.d.ts +36 -0
  29. package/dist/design-manifest.d.ts.map +1 -0
  30. package/dist/design-manifest.js +90 -0
  31. package/dist/design-manifest.js.map +1 -0
  32. package/dist/fonts.d.ts +89 -0
  33. package/dist/fonts.d.ts.map +1 -0
  34. package/dist/fonts.js +241 -0
  35. package/dist/fonts.js.map +1 -0
  36. package/dist/genesis-inventory.d.ts +32 -0
  37. package/dist/genesis-inventory.d.ts.map +1 -0
  38. package/dist/genesis-inventory.js +186 -0
  39. package/dist/genesis-inventory.js.map +1 -0
  40. package/dist/genesis.d.ts +62 -0
  41. package/dist/genesis.d.ts.map +1 -0
  42. package/dist/genesis.js +78 -0
  43. package/dist/genesis.js.map +1 -0
  44. package/dist/i18n.d.ts +44 -1
  45. package/dist/i18n.d.ts.map +1 -1
  46. package/dist/i18n.js +72 -6
  47. package/dist/i18n.js.map +1 -1
  48. package/dist/index.d.ts +27 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +27 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/interactions.d.ts +23 -0
  53. package/dist/interactions.d.ts.map +1 -0
  54. package/dist/interactions.js +44 -0
  55. package/dist/interactions.js.map +1 -0
  56. package/dist/media.d.ts +101 -16
  57. package/dist/media.d.ts.map +1 -1
  58. package/dist/media.js +126 -15
  59. package/dist/media.js.map +1 -1
  60. package/dist/page-log.d.ts +94 -0
  61. package/dist/page-log.d.ts.map +1 -0
  62. package/dist/page-log.js +111 -0
  63. package/dist/page-log.js.map +1 -0
  64. package/dist/preview-compose.d.ts +79 -0
  65. package/dist/preview-compose.d.ts.map +1 -1
  66. package/dist/preview-compose.js +155 -25
  67. package/dist/preview-compose.js.map +1 -1
  68. package/dist/proposal-status.d.ts +40 -0
  69. package/dist/proposal-status.d.ts.map +1 -0
  70. package/dist/proposal-status.js +34 -0
  71. package/dist/proposal-status.js.map +1 -0
  72. package/dist/responsive-images.d.ts +64 -0
  73. package/dist/responsive-images.d.ts.map +1 -0
  74. package/dist/responsive-images.js +98 -0
  75. package/dist/responsive-images.js.map +1 -0
  76. package/dist/safe-keys.d.ts +9 -0
  77. package/dist/safe-keys.d.ts.map +1 -0
  78. package/dist/safe-keys.js +20 -0
  79. package/dist/safe-keys.js.map +1 -0
  80. package/dist/seo.d.ts +8 -0
  81. package/dist/seo.d.ts.map +1 -1
  82. package/dist/seo.js +3 -1
  83. package/dist/seo.js.map +1 -1
  84. package/dist/skills.d.ts +14 -68
  85. package/dist/skills.d.ts.map +1 -1
  86. package/dist/skills.js +19 -113
  87. package/dist/skills.js.map +1 -1
  88. package/dist/strip-cdata.d.ts +7 -0
  89. package/dist/strip-cdata.d.ts.map +1 -0
  90. package/dist/strip-cdata.js +48 -0
  91. package/dist/strip-cdata.js.map +1 -0
  92. package/dist/structured-sets.d.ts +6 -16
  93. package/dist/structured-sets.d.ts.map +1 -1
  94. package/dist/structured-sets.js +5 -16
  95. package/dist/structured-sets.js.map +1 -1
  96. package/dist/subagents.d.ts +105 -3
  97. package/dist/subagents.d.ts.map +1 -1
  98. package/dist/subagents.js +224 -41
  99. package/dist/subagents.js.map +1 -1
  100. package/dist/template-engine.d.ts +85 -0
  101. package/dist/template-engine.d.ts.map +1 -0
  102. package/dist/template-engine.js +403 -0
  103. package/dist/template-engine.js.map +1 -0
  104. package/dist/theme-importers/auto-detect.d.ts +26 -0
  105. package/dist/theme-importers/auto-detect.d.ts.map +1 -0
  106. package/dist/theme-importers/auto-detect.js +42 -0
  107. package/dist/theme-importers/auto-detect.js.map +1 -0
  108. package/dist/theme-importers/css-comments.d.ts +12 -0
  109. package/dist/theme-importers/css-comments.d.ts.map +1 -0
  110. package/dist/theme-importers/css-comments.js +15 -0
  111. package/dist/theme-importers/css-comments.js.map +1 -0
  112. package/dist/theme-importers/dtcg.d.ts +46 -0
  113. package/dist/theme-importers/dtcg.d.ts.map +1 -0
  114. package/dist/theme-importers/dtcg.js +111 -0
  115. package/dist/theme-importers/dtcg.js.map +1 -0
  116. package/dist/theme-importers/loose.d.ts +3 -0
  117. package/dist/theme-importers/loose.d.ts.map +1 -0
  118. package/dist/theme-importers/loose.js +76 -0
  119. package/dist/theme-importers/loose.js.map +1 -0
  120. package/dist/theme-importers/shadcn.d.ts +24 -0
  121. package/dist/theme-importers/shadcn.d.ts.map +1 -0
  122. package/dist/theme-importers/shadcn.js +135 -0
  123. package/dist/theme-importers/shadcn.js.map +1 -0
  124. package/dist/theme-importers/style-dictionary.d.ts +17 -0
  125. package/dist/theme-importers/style-dictionary.d.ts.map +1 -0
  126. package/dist/theme-importers/style-dictionary.js +125 -0
  127. package/dist/theme-importers/style-dictionary.js.map +1 -0
  128. package/dist/theme-importers/tailwind.d.ts +3 -0
  129. package/dist/theme-importers/tailwind.d.ts.map +1 -0
  130. package/dist/theme-importers/tailwind.js +218 -0
  131. package/dist/theme-importers/tailwind.js.map +1 -0
  132. package/dist/theme-literal-binding.d.ts +37 -0
  133. package/dist/theme-literal-binding.d.ts.map +1 -0
  134. package/dist/theme-literal-binding.js +138 -0
  135. package/dist/theme-literal-binding.js.map +1 -0
  136. package/dist/theme-normalize.d.ts +31 -0
  137. package/dist/theme-normalize.d.ts.map +1 -0
  138. package/dist/theme-normalize.js +587 -0
  139. package/dist/theme-normalize.js.map +1 -0
  140. package/dist/theme-ramp.d.ts +55 -0
  141. package/dist/theme-ramp.d.ts.map +1 -0
  142. package/dist/theme-ramp.js +149 -0
  143. package/dist/theme-ramp.js.map +1 -0
  144. package/dist/theme-render.d.ts +105 -0
  145. package/dist/theme-render.d.ts.map +1 -0
  146. package/dist/theme-render.js +441 -0
  147. package/dist/theme-render.js.map +1 -0
  148. package/dist/themes-errors.d.ts +109 -0
  149. package/dist/themes-errors.d.ts.map +1 -0
  150. package/dist/themes-errors.js +170 -0
  151. package/dist/themes-errors.js.map +1 -0
  152. package/dist/themes.d.ts +343 -0
  153. package/dist/themes.d.ts.map +1 -0
  154. package/dist/themes.js +697 -0
  155. package/dist/themes.js.map +1 -0
  156. package/dist/version.d.ts +7 -4
  157. package/dist/version.d.ts.map +1 -1
  158. package/dist/version.js +6 -3
  159. package/dist/version.js.map +1 -1
  160. package/package.json +10 -2
  161. package/src/__tests__/redos-hardening.test.ts +160 -0
  162. package/src/ai-tools-add-module-modes.test.ts +106 -0
  163. package/src/ai-tools-position.test.ts +134 -0
  164. package/src/ai-tools.test.ts +81 -0
  165. package/src/ai-tools.ts +1179 -0
  166. package/src/auth-forms.ts +36 -0
  167. package/src/base-css.ts +30 -0
  168. package/src/build-page.test.ts +228 -0
  169. package/src/build-page.ts +319 -0
  170. package/src/cap-failures.ts +67 -0
  171. package/src/content.test.ts +170 -0
  172. package/src/content.ts +620 -0
  173. package/src/context.ts +43 -0
  174. package/src/css-gradient-scan.ts +88 -0
  175. package/src/css-var-scan.test.ts +96 -0
  176. package/src/css-var-scan.ts +144 -0
  177. package/src/derive-module-type.test.ts +80 -0
  178. package/src/design-manifest.ts +93 -0
  179. package/src/fonts.test.ts +157 -0
  180. package/src/fonts.ts +296 -0
  181. package/src/genesis-inventory.test.ts +86 -0
  182. package/src/genesis-inventory.ts +215 -0
  183. package/src/genesis-sanitize.test.ts +35 -0
  184. package/src/genesis.ts +87 -0
  185. package/src/i18n.test.ts +274 -0
  186. package/src/i18n.ts +269 -0
  187. package/src/index.test.ts +10 -0
  188. package/src/index.ts +59 -0
  189. package/src/interactions.ts +48 -0
  190. package/src/logger.ts +147 -0
  191. package/src/media.test.ts +160 -0
  192. package/src/media.ts +355 -0
  193. package/src/page-log.test.ts +163 -0
  194. package/src/page-log.ts +124 -0
  195. package/src/preview-compose.test.ts +602 -0
  196. package/src/preview-compose.ts +709 -0
  197. package/src/preview-scanner.test.ts +96 -0
  198. package/src/preview-scanner.ts +214 -0
  199. package/src/proposal-status.test.ts +69 -0
  200. package/src/proposal-status.ts +40 -0
  201. package/src/responsive-images.test.ts +104 -0
  202. package/src/responsive-images.ts +151 -0
  203. package/src/result.ts +29 -0
  204. package/src/safe-keys.ts +21 -0
  205. package/src/seo.test.ts +234 -0
  206. package/src/seo.ts +261 -0
  207. package/src/skills.ts +48 -0
  208. package/src/snapshots.test.ts +80 -0
  209. package/src/snapshots.ts +81 -0
  210. package/src/strip-cdata.test.ts +41 -0
  211. package/src/strip-cdata.ts +50 -0
  212. package/src/structured-sets.ts +180 -0
  213. package/src/subagents.test.ts +262 -0
  214. package/src/subagents.ts +432 -0
  215. package/src/template-engine.test.ts +379 -0
  216. package/src/template-engine.ts +520 -0
  217. package/src/theme-gradient.test.ts +92 -0
  218. package/src/theme-importers/__tests__/proto-pollution.test.ts +54 -0
  219. package/src/theme-importers/auto-detect.ts +84 -0
  220. package/src/theme-importers/css-comments.ts +15 -0
  221. package/src/theme-importers/dtcg.ts +106 -0
  222. package/src/theme-importers/loose.ts +76 -0
  223. package/src/theme-importers/shadcn.ts +133 -0
  224. package/src/theme-importers/style-dictionary.ts +125 -0
  225. package/src/theme-importers/tailwind.ts +217 -0
  226. package/src/theme-literal-binding.test.ts +71 -0
  227. package/src/theme-literal-binding.ts +159 -0
  228. package/src/theme-motion.test.ts +115 -0
  229. package/src/theme-normalize-envelope.test.ts +43 -0
  230. package/src/theme-normalize-gradient.test.ts +135 -0
  231. package/src/theme-normalize.ts +661 -0
  232. package/src/theme-ramp.ts +187 -0
  233. package/src/theme-render-sanitize.test.ts +45 -0
  234. package/src/theme-render.test.ts +119 -0
  235. package/src/theme-render.ts +487 -0
  236. package/src/theme-shadow.test.ts +56 -0
  237. package/src/themes-errors.ts +199 -0
  238. package/src/themes.ts +842 -0
  239. package/src/translation.test.ts +160 -0
  240. package/src/translation.ts +295 -0
  241. package/src/version.ts +66 -0
@@ -0,0 +1,97 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ const VAR_REFERENCE_RE = /var\(\s*(--[a-zA-Z0-9_-]+)\s*(,)?/g;
3
+ const VAR_DEFINITION_RE = /(?:^|[{;\s])(--[a-zA-Z0-9_-]+)\s*:/g;
4
+ /** Extract every `var(--x[, fallback])` reference (deduped by name+fallback). */
5
+ export function extractCssVarReferences(css) {
6
+ const seen = new Map();
7
+ for (const m of css.matchAll(VAR_REFERENCE_RE)) {
8
+ const name = m[1] ?? "";
9
+ const hasFallback = m[2] === ",";
10
+ const key = `${name}|${hasFallback}`;
11
+ if (!seen.has(key))
12
+ seen.set(key, { name, hasFallback });
13
+ }
14
+ return [...seen.values()];
15
+ }
16
+ /** Custom properties DEFINED in the CSS (`--x: value;`). */
17
+ export function extractCssVarDefinitions(css) {
18
+ const out = new Set();
19
+ for (const m of css.matchAll(VAR_DEFINITION_RE)) {
20
+ out.add(m[1] ?? "");
21
+ }
22
+ return out;
23
+ }
24
+ /** Levenshtein distance, small-string use only (var names). */
25
+ function editDistance(a, b) {
26
+ const prev = new Array(b.length + 1);
27
+ for (let j = 0; j <= b.length; j++)
28
+ prev[j] = j;
29
+ for (let i = 1; i <= a.length; i++) {
30
+ let diag = prev[0];
31
+ prev[0] = i;
32
+ for (let j = 1; j <= b.length; j++) {
33
+ const tmp = prev[j];
34
+ prev[j] = Math.min(prev[j] + 1, prev[j - 1] + 1, diag + (a[i - 1] === b[j - 1] ? 0 : 1));
35
+ diag = tmp;
36
+ }
37
+ }
38
+ return prev[b.length];
39
+ }
40
+ /**
41
+ * Report `var()` references that neither the theme emits nor the
42
+ * bundle itself defines, with a nearest-match suggestion so the fix is
43
+ * one edit, not a research task (CLAUDE.md §11: AI-actionable).
44
+ */
45
+ export function scanCssVars(input) {
46
+ const known = new Set(input.knownVars);
47
+ for (const def of extractCssVarDefinitions(input.css))
48
+ known.add(def);
49
+ const unknown = [];
50
+ const reported = new Set();
51
+ for (const ref of extractCssVarReferences(input.css)) {
52
+ if (known.has(ref.name) || reported.has(ref.name))
53
+ continue;
54
+ reported.add(ref.name);
55
+ let best = null;
56
+ let bestDist = Number.POSITIVE_INFINITY;
57
+ for (const candidate of known) {
58
+ const d = editDistance(ref.name, candidate);
59
+ if (d < bestDist) {
60
+ bestDist = d;
61
+ best = candidate;
62
+ }
63
+ }
64
+ const threshold = Math.max(2, Math.floor(ref.name.length * 0.4));
65
+ unknown.push({
66
+ name: ref.name,
67
+ hasFallback: ref.hasFallback,
68
+ suggestion: best !== null && bestDist <= threshold ? best : null,
69
+ });
70
+ }
71
+ return unknown;
72
+ }
73
+ /**
74
+ * Marker for the preview op's missing-content surface — same
75
+ * convention as `theme-asset-unbound:<slot>` / `theme-font-unresolvable`.
76
+ */
77
+ export function unknownCssVarMarker(name) {
78
+ return `unknown-css-var:${name}`;
79
+ }
80
+ /**
81
+ * One-line, AI-actionable warning for authoring-tool results. Returns
82
+ * null when nothing is unknown (no noise on clean writes).
83
+ */
84
+ export function formatUnknownCssVarWarning(unknown) {
85
+ if (unknown.length === 0)
86
+ return null;
87
+ const parts = unknown.map((u) => {
88
+ const hint = u.suggestion !== null ? ` (did you mean \`${u.suggestion}\`?)` : "";
89
+ const fallback = u.hasFallback
90
+ ? " — its hardcoded fallback is what visitors will see, detached from the theme"
91
+ : " — the declaration is invalid and the property won't apply";
92
+ return `\`${u.name}\`${hint}${fallback}`;
93
+ });
94
+ return (`⚠️ Unknown CSS vars (the active theme does not emit them): ${parts.join("; ")}. ` +
95
+ "Use the exact names from the `## Theme` inventory, or define the custom property in this CSS if it's intentional.");
96
+ }
97
+ //# sourceMappingURL=css-var-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"css-var-scan.js","sourceRoot":"","sources":["../src/css-var-scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * issue #165 — the Design Manifest: a generated, versioned description
3
+ * of THIS site's design system (epic #149, growth-time convergence).
4
+ *
5
+ * The operator requirement is asymmetric: pages of the SAME site share
6
+ * one visual line, while different sites are free to look nothing
7
+ * alike. A global stylesheet solves the first at the cost of the
8
+ * second; the per-site manifest solves both — it captures what this
9
+ * site's tokens/patterns MEAN so the AI converges toward its own
10
+ * design, not a Caelo default.
11
+ *
12
+ * Written at Genesis materialisation (or curated later via chat);
13
+ * rendered as the `## Design system` system-prompt block.
14
+ */
15
+ import { z } from "zod";
16
+ export declare const designManifestPattern: z.ZodObject<{
17
+ name: z.ZodString;
18
+ moduleType: z.ZodOptional<z.ZodString>;
19
+ spec: z.ZodString;
20
+ }, z.core.$strict>;
21
+ export declare const designManifestSchema: z.ZodObject<{
22
+ tokenRoles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
23
+ typography: z.ZodOptional<z.ZodString>;
24
+ rhythm: z.ZodOptional<z.ZodString>;
25
+ patterns: z.ZodOptional<z.ZodArray<z.ZodObject<{
26
+ name: z.ZodString;
27
+ moduleType: z.ZodOptional<z.ZodString>;
28
+ spec: z.ZodString;
29
+ }, z.core.$strict>>>;
30
+ imagery: z.ZodOptional<z.ZodString>;
31
+ avoid: z.ZodOptional<z.ZodString>;
32
+ }, z.core.$strict>;
33
+ export type DesignManifest = z.infer<typeof designManifestSchema>;
34
+ /** Render the `## Design system` prompt block. Null when empty. */
35
+ export declare function formatDesignSystemBlock(manifest: DesignManifest | null): string | null;
36
+ //# sourceMappingURL=design-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-manifest.d.ts","sourceRoot":"","sources":["../src/design-manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,qBAAqB;;;;kBASvB,CAAC;AAEZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;kBAYtB,CAAC;AACZ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,mEAAmE;AACnE,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CA8CtF"}
@@ -0,0 +1,90 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * issue #165 — the Design Manifest: a generated, versioned description
4
+ * of THIS site's design system (epic #149, growth-time convergence).
5
+ *
6
+ * The operator requirement is asymmetric: pages of the SAME site share
7
+ * one visual line, while different sites are free to look nothing
8
+ * alike. A global stylesheet solves the first at the cost of the
9
+ * second; the per-site manifest solves both — it captures what this
10
+ * site's tokens/patterns MEAN so the AI converges toward its own
11
+ * design, not a Caelo default.
12
+ *
13
+ * Written at Genesis materialisation (or curated later via chat);
14
+ * rendered as the `## Design system` system-prompt block.
15
+ */
16
+ import { z } from "zod";
17
+ export const designManifestPattern = z
18
+ .object({
19
+ /** Pattern name the AI reasons in ("hero", "card grid", "cta band"). */
20
+ name: z.string().min(1).max(80),
21
+ /** Stable module `type` implementing it — reuse target (#159 place mode). */
22
+ moduleType: z.string().max(64).optional(),
23
+ /** One-line spec: what makes this pattern THIS site's version of it. */
24
+ spec: z.string().min(1).max(500),
25
+ })
26
+ .strict();
27
+ export const designManifestSchema = z
28
+ .object({
29
+ /** `--color-primary` → "CTAs and links only" — usage semantics per var. */
30
+ tokenRoles: z.record(z.string().min(3).max(64), z.string().min(1).max(300)).optional(),
31
+ /** Scale ratio, casing, measure rules. */
32
+ typography: z.string().min(1).max(1000).optional(),
33
+ /** Section padding, container width, grid gaps. */
34
+ rhythm: z.string().min(1).max(1000).optional(),
35
+ patterns: z.array(designManifestPattern).max(24).optional(),
36
+ imagery: z.string().min(1).max(500).optional(),
37
+ avoid: z.string().min(1).max(500).optional(),
38
+ })
39
+ .strict();
40
+ /** Render the `## Design system` prompt block. Null when empty. */
41
+ export function formatDesignSystemBlock(manifest) {
42
+ if (manifest === null)
43
+ return null;
44
+ const lines = [
45
+ "## Design system",
46
+ "",
47
+ "THIS site's own design language — derived from the chosen design. Every module you author or restyle conforms to it unless the operator explicitly asks to break the line. Reuse the pattern's module type (place mode) before minting lookalikes.",
48
+ "",
49
+ ];
50
+ let hasContent = false;
51
+ const roles = manifest.tokenRoles ?? {};
52
+ const roleEntries = Object.entries(roles);
53
+ if (roleEntries.length > 0) {
54
+ hasContent = true;
55
+ lines.push("Token roles:");
56
+ for (const [token, role] of roleEntries.slice(0, 24))
57
+ lines.push(`- \`${token}\` — ${role}`);
58
+ lines.push("");
59
+ }
60
+ if (manifest.typography) {
61
+ hasContent = true;
62
+ lines.push(`Typography: ${manifest.typography}`, "");
63
+ }
64
+ if (manifest.rhythm) {
65
+ hasContent = true;
66
+ lines.push(`Rhythm: ${manifest.rhythm}`, "");
67
+ }
68
+ if (manifest.patterns && manifest.patterns.length > 0) {
69
+ hasContent = true;
70
+ lines.push("Patterns:");
71
+ for (const p of manifest.patterns) {
72
+ lines.push(`- **${p.name}**${p.moduleType ? ` (module type \`${p.moduleType}\`)` : ""} — ${p.spec}`);
73
+ }
74
+ lines.push("");
75
+ }
76
+ if (manifest.imagery) {
77
+ hasContent = true;
78
+ lines.push(`Imagery: ${manifest.imagery}`, "");
79
+ }
80
+ if (manifest.avoid) {
81
+ hasContent = true;
82
+ lines.push(`Never: ${manifest.avoid}`, "");
83
+ }
84
+ if (!hasContent)
85
+ return null;
86
+ while (lines[lines.length - 1] === "")
87
+ lines.pop();
88
+ return lines.join("\n");
89
+ }
90
+ //# sourceMappingURL=design-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-manifest.js","sourceRoot":"","sources":["../src/design-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * issue #150 — web-font resolution primitives.
3
+ *
4
+ * Theme typography tokens name font families (`--font-heading:
5
+ * "Poppins"`), but until #150 nothing delivered the font files to
6
+ * rendered pages — every site silently fell back to system fonts, the
7
+ * exact silent-degradation CLAUDE.md §2 forbids. These pure helpers are
8
+ * the shared half of the fix: extract the families a theme actually
9
+ * uses, classify system stacks (nothing to deliver) vs web fonts
10
+ * (self-hosted at deploy — hotlinking fonts.googleapis.com is both a
11
+ * GDPR liability, LG München I 3 O 17493/20, and a third-party request
12
+ * on the critical path), and build/parse the CSS involved.
13
+ *
14
+ * Network + disk live in `@caelo-cms/admin-core`'s font resolver; the
15
+ * static generator and the preview op share that resolver so preview
16
+ * and production render the same `@font-face` set (parity contract,
17
+ * apps/static-generator/src/generate.ts).
18
+ */
19
+ import type { ThemeDocument } from "./themes.js";
20
+ /** One family the theme needs delivered, with the weights it uses. */
21
+ export interface FontRequest {
22
+ /** Primary family name, unquoted (`Poppins`), first entry of the stack. */
23
+ readonly family: string;
24
+ /** Distinct numeric weights, ascending. Always non-empty. */
25
+ readonly weights: readonly number[];
26
+ /** Typography token names that reference the family (`body`, `heading`). */
27
+ readonly roles: readonly string[];
28
+ }
29
+ /** A single @font-face declaration parsed from a fonts-CSS payload. */
30
+ export interface ParsedFontFace {
31
+ readonly family: string;
32
+ readonly style: string;
33
+ readonly weight: string;
34
+ readonly unicodeRange: string | null;
35
+ /** Remote woff2 URL as served by the fonts source. */
36
+ readonly srcUrl: string;
37
+ }
38
+ /** A parsed face whose bytes now live at a self-hosted public URL. */
39
+ export interface ResolvedFontFace extends Omit<ParsedFontFace, "srcUrl"> {
40
+ readonly publicUrl: string;
41
+ }
42
+ /** True when the family resolves locally on visitors' machines. */
43
+ export declare function isSystemFontFamily(family: string): boolean;
44
+ /**
45
+ * Walk `typography.*` composites and collect every non-system family
46
+ * with the weights the theme declares for it. Alias entries (`$value`
47
+ * as `{other.token}` string) are skipped — they point at a concrete
48
+ * composite that is collected on its own.
49
+ *
50
+ * Only the FIRST family of a declared stack is requested: the rest of
51
+ * the stack is the author's fallback chain, which by definition is not
52
+ * delivered.
53
+ */
54
+ export declare function extractThemeFontRequests(tokens: ThemeDocument): FontRequest[];
55
+ /** css2 request URL for one family (woff2 negotiated via the UA header). */
56
+ export declare function googleFontsCssUrl(request: FontRequest): string;
57
+ /**
58
+ * Parse a fonts-CSS payload (css2 response shape) into face records.
59
+ * Faces without a woff2/woff URL are dropped; a payload yielding zero
60
+ * faces is the caller's loud-failure signal, not a silent empty set.
61
+ * Oversized payloads short-circuit to [] for the same loud path —
62
+ * a legitimate css2 response is orders of magnitude smaller.
63
+ */
64
+ export declare function parseFontsCss(cssText: string): ParsedFontFace[];
65
+ /**
66
+ * Emit self-hosted @font-face CSS. `font-display: swap` always — text
67
+ * must render in the fallback while the woff2 loads.
68
+ */
69
+ export declare function buildFontFaceCss(faces: readonly ResolvedFontFace[]): string;
70
+ /**
71
+ * Pick the faces worth a `<link rel="preload">`: one per family — the
72
+ * latin, normal-style face at the lowest declared weight (the body-copy
73
+ * face; heavier display cuts arrive via normal loading). Capped at two
74
+ * families so the preload budget stays sane on multi-family themes.
75
+ */
76
+ export declare function selectPreloadFaces(faces: readonly ResolvedFontFace[]): ResolvedFontFace[];
77
+ /**
78
+ * Structured marker for a family that could not be resolved. Lands in
79
+ * `missingSlots` (preview, soft) or a thrown deploy error (hard) — the
80
+ * same loud-signal convention as `theme-asset-unbound:<slot>`.
81
+ */
82
+ export declare function fontUnresolvableMarker(family: string): string;
83
+ /**
84
+ * Filesystem/URL-safe slug for a family name. Shared between the font
85
+ * resolver (writes `<cacheDir>/<slug>/…`) and the admin serve route
86
+ * (reads the same path from the URL) so the two can't drift.
87
+ */
88
+ export declare function fontFamilySlug(family: string): string;
89
+ //# sourceMappingURL=fonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,4EAA4E;IAC5E,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,sEAAsE;AACtE,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC;IACtE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAqDD,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE1D;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,EAAE,CAiC7E;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAI9D;AAmBD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAyB/D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAW3E;AAOD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAezF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAUrD"}
package/dist/fonts.js ADDED
@@ -0,0 +1,241 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ /**
3
+ * Families that ship with operating systems — nothing to download, no
4
+ * @font-face to emit. Generic CSS keywords included. Lowercased for
5
+ * case-insensitive membership tests.
6
+ */
7
+ const SYSTEM_FONT_FAMILIES = new Set([
8
+ // Generic keywords + UI stacks
9
+ "serif",
10
+ "sans-serif",
11
+ "monospace",
12
+ "cursive",
13
+ "fantasy",
14
+ "system-ui",
15
+ "ui-sans-serif",
16
+ "ui-serif",
17
+ "ui-monospace",
18
+ "ui-rounded",
19
+ "-apple-system",
20
+ "blinkmacsystemfont",
21
+ // Cross-platform classics
22
+ "arial",
23
+ "arial black",
24
+ "helvetica",
25
+ "helvetica neue",
26
+ "georgia",
27
+ "times",
28
+ "times new roman",
29
+ "courier",
30
+ "courier new",
31
+ "verdana",
32
+ "tahoma",
33
+ "trebuchet ms",
34
+ "impact",
35
+ "palatino",
36
+ "garamond",
37
+ // Platform UI/mono staples browsers resolve locally
38
+ "segoe ui",
39
+ "sf pro",
40
+ "sf pro text",
41
+ "sf pro display",
42
+ "sf mono",
43
+ "sfmono-regular",
44
+ "menlo",
45
+ "monaco",
46
+ "consolas",
47
+ "lucida console",
48
+ "lucida sans",
49
+ ].map((f) => f.toLowerCase()));
50
+ /** True when the family resolves locally on visitors' machines. */
51
+ export function isSystemFontFamily(family) {
52
+ return SYSTEM_FONT_FAMILIES.has(family.trim().toLowerCase());
53
+ }
54
+ /** Strip quotes + whitespace from one family in a font stack. */
55
+ function cleanFamilyName(raw) {
56
+ return raw
57
+ .trim()
58
+ .replace(/^["']|["']$/g, "")
59
+ .trim();
60
+ }
61
+ const KEYWORD_WEIGHTS = {
62
+ normal: 400,
63
+ bold: 700,
64
+ // Relative keywords have no absolute value; anchor to the common pair
65
+ // so the request still covers a usable range.
66
+ lighter: 300,
67
+ bolder: 700,
68
+ };
69
+ const DEFAULT_WEIGHTS = [400, 700];
70
+ /**
71
+ * Walk `typography.*` composites and collect every non-system family
72
+ * with the weights the theme declares for it. Alias entries (`$value`
73
+ * as `{other.token}` string) are skipped — they point at a concrete
74
+ * composite that is collected on its own.
75
+ *
76
+ * Only the FIRST family of a declared stack is requested: the rest of
77
+ * the stack is the author's fallback chain, which by definition is not
78
+ * delivered.
79
+ */
80
+ export function extractThemeFontRequests(tokens) {
81
+ const typography = tokens.typography;
82
+ if (typography === undefined || typography === null || typeof typography !== "object") {
83
+ return [];
84
+ }
85
+ const byFamily = new Map();
86
+ for (const [role, entry] of Object.entries(typography)) {
87
+ if (entry === null || typeof entry !== "object")
88
+ continue;
89
+ const value = entry.$value;
90
+ if (value === null || typeof value !== "object")
91
+ continue; // alias or malformed
92
+ const composite = value;
93
+ if (typeof composite.fontFamily !== "string")
94
+ continue;
95
+ const primary = cleanFamilyName(composite.fontFamily.split(",")[0] ?? "");
96
+ if (primary === "" || isSystemFontFamily(primary))
97
+ continue;
98
+ const bucket = byFamily.get(primary) ?? { weights: new Set(), roles: [] };
99
+ if (typeof composite.fontWeight === "number") {
100
+ bucket.weights.add(composite.fontWeight);
101
+ }
102
+ else if (typeof composite.fontWeight === "string") {
103
+ const mapped = KEYWORD_WEIGHTS[composite.fontWeight];
104
+ if (mapped !== undefined)
105
+ bucket.weights.add(mapped);
106
+ }
107
+ bucket.roles.push(role);
108
+ byFamily.set(primary, bucket);
109
+ }
110
+ return [...byFamily.entries()].map(([family, b]) => ({
111
+ family,
112
+ weights: b.weights.size > 0
113
+ ? [...b.weights].sort((a, z) => a - z)
114
+ : [...DEFAULT_WEIGHTS],
115
+ roles: b.roles,
116
+ }));
117
+ }
118
+ /** css2 request URL for one family (woff2 negotiated via the UA header). */
119
+ export function googleFontsCssUrl(request) {
120
+ const family = encodeURIComponent(request.family).replace(/%20/g, "+");
121
+ const weights = request.weights.join(";");
122
+ return `https://fonts.googleapis.com/css2?family=${family}:wght@${weights}&display=swap`;
123
+ }
124
+ /**
125
+ * `[^{}]*` (not `[^}]*`): the char class excluding BOTH braces makes the
126
+ * pattern unambiguous — on adversarial payloads full of unclosed
127
+ * `@font-face{` starts, a failed match aborts at the next `{` instead of
128
+ * rescanning to end-of-input per start position (the polynomial-ReDoS
129
+ * shape CodeQL flags; this file parses remotely-served CSS).
130
+ */
131
+ const FONT_FACE_BLOCK_RE = /@font-face\s*\{([^{}]*)\}/g;
132
+ /** css2 responses are ~10–50 KB; anything past this is not a fonts CSS. */
133
+ const MAX_FONTS_CSS_BYTES = 1_000_000;
134
+ function declValue(block, prop) {
135
+ const m = new RegExp(`${prop}\\s*:\\s*([^;]+);`, "i").exec(block);
136
+ return m ? (m[1] ?? "").trim() : null;
137
+ }
138
+ /**
139
+ * Parse a fonts-CSS payload (css2 response shape) into face records.
140
+ * Faces without a woff2/woff URL are dropped; a payload yielding zero
141
+ * faces is the caller's loud-failure signal, not a silent empty set.
142
+ * Oversized payloads short-circuit to [] for the same loud path —
143
+ * a legitimate css2 response is orders of magnitude smaller.
144
+ */
145
+ export function parseFontsCss(cssText) {
146
+ if (cssText.length > MAX_FONTS_CSS_BYTES)
147
+ return [];
148
+ const faces = [];
149
+ for (const m of cssText.matchAll(FONT_FACE_BLOCK_RE)) {
150
+ const block = m[1] ?? "";
151
+ const familyRaw = declValue(block, "font-family");
152
+ const src = declValue(block, "src");
153
+ if (familyRaw === null || src === null)
154
+ continue;
155
+ // Capture the whole url(...) argument with an unambiguous class,
156
+ // then check the extension in code — `[^)]+\.woff2?` is the classic
157
+ // ambiguous-suffix polynomial pattern.
158
+ const urlMatch = /url\(([^()]*)\)/i.exec(src);
159
+ if (!urlMatch)
160
+ continue;
161
+ const srcUrl = (urlMatch[1] ?? "").trim().replace(/^["']|["']$/g, "");
162
+ const lower = srcUrl.toLowerCase();
163
+ if (!(lower.endsWith(".woff2") || lower.endsWith(".woff")))
164
+ continue;
165
+ faces.push({
166
+ family: cleanFamilyName(familyRaw),
167
+ style: declValue(block, "font-style") ?? "normal",
168
+ weight: declValue(block, "font-weight") ?? "400",
169
+ unicodeRange: declValue(block, "unicode-range"),
170
+ srcUrl,
171
+ });
172
+ }
173
+ return faces;
174
+ }
175
+ /**
176
+ * Emit self-hosted @font-face CSS. `font-display: swap` always — text
177
+ * must render in the fallback while the woff2 loads.
178
+ */
179
+ export function buildFontFaceCss(faces) {
180
+ return faces
181
+ .map((f) => {
182
+ const range = f.unicodeRange !== null ? `unicode-range:${f.unicodeRange};` : "";
183
+ return (`@font-face{font-family:${JSON.stringify(f.family)};` +
184
+ `font-style:${f.style};font-weight:${f.weight};font-display:swap;` +
185
+ `src:url(${f.publicUrl}) format('woff2');${range}}`);
186
+ })
187
+ .join("\n");
188
+ }
189
+ /** Latin-subset heuristic: css2 marks it with a range starting U+0000. */
190
+ function isLatinFace(f) {
191
+ return f.unicodeRange === null || f.unicodeRange.toLowerCase().includes("u+0000");
192
+ }
193
+ /**
194
+ * Pick the faces worth a `<link rel="preload">`: one per family — the
195
+ * latin, normal-style face at the lowest declared weight (the body-copy
196
+ * face; heavier display cuts arrive via normal loading). Capped at two
197
+ * families so the preload budget stays sane on multi-family themes.
198
+ */
199
+ export function selectPreloadFaces(faces) {
200
+ const byFamily = new Map();
201
+ for (const f of faces) {
202
+ if (f.style !== "normal" || !isLatinFace(f))
203
+ continue;
204
+ const list = byFamily.get(f.family) ?? [];
205
+ list.push(f);
206
+ byFamily.set(f.family, list);
207
+ }
208
+ const picks = [];
209
+ for (const list of byFamily.values()) {
210
+ list.sort((a, z) => Number.parseInt(a.weight, 10) - Number.parseInt(z.weight, 10));
211
+ const first = list[0];
212
+ if (first !== undefined)
213
+ picks.push(first);
214
+ }
215
+ return picks.slice(0, 2);
216
+ }
217
+ /**
218
+ * Structured marker for a family that could not be resolved. Lands in
219
+ * `missingSlots` (preview, soft) or a thrown deploy error (hard) — the
220
+ * same loud-signal convention as `theme-asset-unbound:<slot>`.
221
+ */
222
+ export function fontUnresolvableMarker(family) {
223
+ return `theme-font-unresolvable:${family}`;
224
+ }
225
+ /**
226
+ * Filesystem/URL-safe slug for a family name. Shared between the font
227
+ * resolver (writes `<cacheDir>/<slug>/…`) and the admin serve route
228
+ * (reads the same path from the URL) so the two can't drift.
229
+ */
230
+ export function fontFamilySlug(family) {
231
+ // After the collapse pass, dash RUNS no longer exist — at most one
232
+ // leading + one trailing dash remain, so the trim pattern needs no
233
+ // quantifier (a `-+$` here is the polynomial-ReDoS shape on
234
+ // adversarial all-dash input; this runs on library-supplied strings).
235
+ return family
236
+ .trim()
237
+ .toLowerCase()
238
+ .replace(/[^a-z0-9]+/g, "-")
239
+ .replace(/^-|-$/g, "");
240
+ }
241
+ //# sourceMappingURL=fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /** One distinct color literal with where/how often the draft uses it. */
2
+ export interface ColorUsage {
3
+ readonly value: string;
4
+ readonly count: number;
5
+ /** Distinct declaration properties using it (background, color, …). */
6
+ readonly properties: readonly string[];
7
+ }
8
+ export interface GenesisDraftInventory {
9
+ readonly colors: readonly ColorUsage[];
10
+ readonly gradients: readonly string[];
11
+ readonly fontFamilies: readonly string[];
12
+ readonly fontSizes: readonly string[];
13
+ readonly spacingValues: readonly string[];
14
+ readonly radiusValues: readonly string[];
15
+ readonly shadows: readonly string[];
16
+ /** Ordered structural outline of the body (tag + text snippet). */
17
+ readonly outline: readonly {
18
+ tag: string;
19
+ text: string;
20
+ }[];
21
+ }
22
+ /** Linear extraction of every <style> block's contents. */
23
+ export declare function extractDraftCss(html: string): string;
24
+ /**
25
+ * Build the inventory. Caps guard pathological drafts: the boundary
26
+ * already limits draft size (GENESIS_DRAFT_MAX_HTML_BYTES), and each
27
+ * result list is truncated to what a decision-maker can actually use.
28
+ */
29
+ export declare function inventoryGenesisDraft(html: string): GenesisDraftInventory;
30
+ /** Compact, prompt-friendly rendering of the inventory. */
31
+ export declare function formatGenesisInventory(inv: GenesisDraftInventory): string;
32
+ //# sourceMappingURL=genesis-inventory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"genesis-inventory.d.ts","sourceRoot":"","sources":["../src/genesis-inventory.ts"],"names":[],"mappings":"AAsBA,yEAAyE;AACzE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5D;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAepD;AAiDD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAkFzE;AAED,2DAA2D;AAC3D,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAiBzE"}