@stacksjs/stx 0.2.342 → 0.2.344

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 (185) hide show
  1. package/dist/binding-manifest.d.ts +32 -0
  2. package/dist/binding-manifest.js +64 -0
  3. package/dist/caching.js +1 -0
  4. package/dist/chunk-020hpw7m.js +2 -0
  5. package/dist/{chunk-5g9vbhbt.js → chunk-05zfez2g.js} +1 -1
  6. package/dist/{chunk-6nvm939g.js → chunk-0k1mtwby.js} +1 -1
  7. package/dist/chunk-1bct7pfj.js +2 -0
  8. package/dist/{chunk-xec226zb.js → chunk-1c9453w0.js} +1 -1
  9. package/dist/chunk-1exk54qt.js +2 -0
  10. package/dist/{chunk-r46cg4qa.js → chunk-1vt1h19d.js} +1 -1
  11. package/dist/{chunk-cqw3c1xg.js → chunk-1yh6pq6b.js} +1 -1
  12. package/dist/{chunk-0sq5mdrh.js → chunk-34f8c48f.js} +1 -1
  13. package/dist/chunk-39qy76ek.js +2 -0
  14. package/dist/{chunk-hrmxxs0w.js → chunk-3n8avn17.js} +1 -1
  15. package/dist/chunk-3xpk99z4.js +3 -0
  16. package/dist/{chunk-4wpxbkbe.js → chunk-4kbaky4w.js} +1 -1
  17. package/dist/{chunk-c33y19bn.js → chunk-59906baj.js} +1 -1
  18. package/dist/chunk-5apmxb4e.js +2 -0
  19. package/dist/{chunk-j82hfkcf.js → chunk-5atmahe4.js} +319 -321
  20. package/dist/{chunk-mkgbrbr9.js → chunk-5yhv1pe5.js} +1 -1
  21. package/dist/{chunk-p52cm7ch.js → chunk-64n10kpa.js} +1 -1
  22. package/dist/{chunk-anf2y2h5.js → chunk-6h3r8v55.js} +1 -1
  23. package/dist/chunk-6z9pm31g.js +2 -0
  24. package/dist/{chunk-25f0s8t1.js → chunk-72x5v5an.js} +1 -1
  25. package/dist/{chunk-wv8h3d68.js → chunk-72x62kch.js} +2 -2
  26. package/dist/chunk-7d2gfyk8.js +2 -0
  27. package/dist/{chunk-s4mmmwj4.js → chunk-7h9fajgs.js} +1 -1
  28. package/dist/{chunk-gwc54hjc.js → chunk-7kcfbc0j.js} +2 -2
  29. package/dist/{chunk-74r1zw2h.js → chunk-7qp3zphs.js} +1 -1
  30. package/dist/{chunk-1xwfws06.js → chunk-7t6h8crt.js} +2 -2
  31. package/dist/{chunk-sytwy0mg.js → chunk-86c461ca.js} +1 -1
  32. package/dist/chunk-86q2h34b.js +2 -0
  33. package/dist/{chunk-25b8z7xa.js → chunk-89mqnh67.js} +1 -1
  34. package/dist/{chunk-k5b9471y.js → chunk-8cc4vdkh.js} +1 -1
  35. package/dist/chunk-8tjg4wbr.js +2 -0
  36. package/dist/{chunk-1gwbw81h.js → chunk-8v2k21n4.js} +1 -1
  37. package/dist/{chunk-rh7mwwb4.js → chunk-8y1ve8tc.js} +2 -2
  38. package/dist/chunk-94sxzp5x.js +2 -0
  39. package/dist/{chunk-mznr9nyp.js → chunk-99b5tfxc.js} +1 -1
  40. package/dist/{chunk-saqbtgqr.js → chunk-9jbssgem.js} +197 -88
  41. package/dist/{chunk-yr7camcd.js → chunk-9nrgjtah.js} +1 -1
  42. package/dist/chunk-9p5gbhwp.js +2 -0
  43. package/dist/chunk-9yqt0nf2.js +2 -0
  44. package/dist/{chunk-ebd3z4j9.js → chunk-a1ghxrb1.js} +1 -1
  45. package/dist/chunk-a29x23g7.js +2 -0
  46. package/dist/chunk-at622hg0.js +2 -0
  47. package/dist/{chunk-bt8w146r.js → chunk-avwywzsj.js} +2 -2
  48. package/dist/{chunk-m39hb2a2.js → chunk-b5hank5e.js} +1 -1
  49. package/dist/{chunk-ts3wfjh9.js → chunk-bk1zb1xp.js} +1 -1
  50. package/dist/{chunk-ytx17bg2.js → chunk-bpb3xkg4.js} +1 -1
  51. package/dist/{chunk-01ggk8k8.js → chunk-bqdfbwdf.js} +1 -1
  52. package/dist/{chunk-yzv702kk.js → chunk-c30qnb6z.js} +1 -1
  53. package/dist/chunk-cappe4hd.js +2 -0
  54. package/dist/{chunk-drs93sry.js → chunk-cdq28mek.js} +1 -1
  55. package/dist/chunk-cmccdayf.js +2 -0
  56. package/dist/chunk-csv48xrx.js +2 -0
  57. package/dist/{chunk-e0kzyf1g.js → chunk-d86q27j8.js} +1 -1
  58. package/dist/{chunk-yhvrq2qd.js → chunk-dgv43ccd.js} +1 -1
  59. package/dist/{chunk-yq7fc8sa.js → chunk-dsztk2kp.js} +1 -1
  60. package/dist/{chunk-ys56ecvx.js → chunk-dx2s51rr.js} +1 -1
  61. package/dist/{chunk-mbstfjnj.js → chunk-dxkjvajv.js} +1 -1
  62. package/dist/chunk-e4yskgtb.js +2 -0
  63. package/dist/{chunk-8tsr51hj.js → chunk-e631svvs.js} +1 -1
  64. package/dist/{chunk-3b0mgxbk.js → chunk-e7np0h2q.js} +1 -1
  65. package/dist/chunk-efx05ah0.js +2 -0
  66. package/dist/{chunk-p5d8sb50.js → chunk-ejqpq0wc.js} +1 -1
  67. package/dist/chunk-erb294sv.js +2 -0
  68. package/dist/{chunk-k3w3gwm3.js → chunk-evfr2zgg.js} +1 -1
  69. package/dist/chunk-ewpbxy5m.js +2 -0
  70. package/dist/chunk-fs2t4rs3.js +33 -0
  71. package/dist/{chunk-ghd80w8a.js → chunk-g28fs4aa.js} +2 -2
  72. package/dist/chunk-gmm82dst.js +2 -0
  73. package/dist/{chunk-kznb3hdj.js → chunk-gsc2vds7.js} +1 -1
  74. package/dist/chunk-hke0k33t.js +2 -0
  75. package/dist/{chunk-da0tfpa2.js → chunk-hsbbfp0c.js} +1 -1
  76. package/dist/chunk-j5qygdy9.js +2 -0
  77. package/dist/{chunk-c73pyeak.js → chunk-jekrtm2j.js} +1 -1
  78. package/dist/chunk-jzfk5ect.js +2 -0
  79. package/dist/{chunk-rr49zrz6.js → chunk-kfkf9v2d.js} +1 -1
  80. package/dist/chunk-kq30wqp6.js +2 -0
  81. package/dist/{chunk-ntm1761b.js → chunk-krfx5562.js} +10 -10
  82. package/dist/{chunk-48x5bsqv.js → chunk-kw9cgyfc.js} +1 -1
  83. package/dist/{chunk-evtrctj9.js → chunk-m2j5skez.js} +1 -1
  84. package/dist/{chunk-q4cemvkw.js → chunk-mph46gkj.js} +1 -1
  85. package/dist/{chunk-k9ep24bd.js → chunk-mpyjnwje.js} +1 -1
  86. package/dist/{chunk-y956wapy.js → chunk-msywnv5n.js} +1 -1
  87. package/dist/{chunk-d389vyyz.js → chunk-mvat8js5.js} +2 -2
  88. package/dist/{chunk-gkq323fg.js → chunk-mz4breg8.js} +1 -1
  89. package/dist/{chunk-reskzmsb.js → chunk-n5657hm3.js} +1 -1
  90. package/dist/{chunk-4ecv1e3w.js → chunk-nexpxxsa.js} +1 -1
  91. package/dist/{chunk-1t3j8a0q.js → chunk-njx93b4z.js} +1 -1
  92. package/dist/{chunk-fx29n4vc.js → chunk-nm60wh6r.js} +1 -1
  93. package/dist/{chunk-thrv2nbz.js → chunk-ppzz953j.js} +1 -1
  94. package/dist/chunk-q5gzpn1n.js +2 -0
  95. package/dist/{chunk-fm5211ec.js → chunk-rwwfzkz4.js} +1 -1
  96. package/dist/chunk-san1eq0r.js +7 -0
  97. package/dist/chunk-tc4pj651.js +64 -0
  98. package/dist/chunk-tscr6bs8.js +2 -0
  99. package/dist/{chunk-jcbj3xp4.js → chunk-tthrw91n.js} +1 -1
  100. package/dist/{chunk-6nxq8rcw.js → chunk-v6e35j4g.js} +1 -1
  101. package/dist/{chunk-byjy9far.js → chunk-v7aa7v85.js} +1 -1
  102. package/dist/{chunk-r1rfwm01.js → chunk-vhta1be7.js} +1 -1
  103. package/dist/{chunk-hnffkdzc.js → chunk-w5j9tbsk.js} +1 -1
  104. package/dist/{chunk-567g1ttv.js → chunk-wfwj4390.js} +1 -1
  105. package/dist/{chunk-x7dv5rq7.js → chunk-wj21f1e0.js} +1 -1
  106. package/dist/{chunk-tmj45xq5.js → chunk-ws0282jr.js} +1 -1
  107. package/dist/chunk-x054qzma.js +2 -0
  108. package/dist/{chunk-4vcyf1kh.js → chunk-xh9paa2g.js} +2 -2
  109. package/dist/{chunk-f2qbjr3z.js → chunk-xj6bcwqa.js} +1 -1
  110. package/dist/{chunk-hk8j3eps.js → chunk-xvh1k1a4.js} +1 -1
  111. package/dist/{chunk-1spzqjve.js → chunk-y2f41kaq.js} +1 -1
  112. package/dist/{chunk-2swnc8kg.js → chunk-y6xzr30x.js} +1 -1
  113. package/dist/{chunk-beextc84.js → chunk-yg5r7bnm.js} +2 -2
  114. package/dist/{chunk-gbkq5qnw.js → chunk-zkwk82ss.js} +2 -2
  115. package/dist/chunk-zkxnarrh.js +2 -0
  116. package/dist/chunk-zp14ksx1.js +2 -0
  117. package/dist/{chunk-zv4nf0x2.js → chunk-zwhnkxgy.js} +1 -1
  118. package/dist/cli.js +14 -14
  119. package/dist/craft-entry.js +1 -1
  120. package/dist/craft.js +1 -1
  121. package/dist/custom-directives.js +21 -8
  122. package/dist/database.js +1 -1
  123. package/dist/directive-arguments.d.ts +107 -0
  124. package/dist/directive-arguments.js +171 -0
  125. package/dist/expressions.d.ts +1 -1
  126. package/dist/expressions.js +15 -3
  127. package/dist/head.d.ts +7 -30
  128. package/dist/head.js +15 -5
  129. package/dist/includes.js +2 -2
  130. package/dist/meta-arguments.d.ts +52 -0
  131. package/dist/meta-arguments.js +55 -0
  132. package/dist/process.js +1 -1
  133. package/dist/pwa.js +2 -2
  134. package/dist/runtime-globals.d.ts +22 -0
  135. package/dist/runtime-globals.js +46 -0
  136. package/dist/seo.d.ts +39 -17
  137. package/dist/seo.js +221 -255
  138. package/dist/signals.js +193 -84
  139. package/dist/slots.js +3 -3
  140. package/dist/ssg.js +2 -2
  141. package/dist/theme-tokens.d.ts +59 -0
  142. package/dist/theme-tokens.js +77 -17
  143. package/dist/ts-css-config.js +4 -2
  144. package/dist/types/directive-types.d.ts +12 -0
  145. package/dist/utils.d.ts +10 -0
  146. package/dist/utils.js +77 -12
  147. package/dist/variable-extractor.d.ts +1 -1
  148. package/dist/variable-extractor.js +23 -3
  149. package/dist/visual-testing.js +1 -1
  150. package/package.json +5 -5
  151. package/dist/chunk-025z2btp.js +0 -2
  152. package/dist/chunk-19fp0z9s.js +0 -2
  153. package/dist/chunk-23sv24cm.js +0 -2
  154. package/dist/chunk-2p68bkza.js +0 -2
  155. package/dist/chunk-5grrqaqs.js +0 -2
  156. package/dist/chunk-6jx4r94x.js +0 -2
  157. package/dist/chunk-6p6b01kp.js +0 -4
  158. package/dist/chunk-a1rt1fms.js +0 -2
  159. package/dist/chunk-ahpmjg3f.js +0 -64
  160. package/dist/chunk-akp89n3w.js +0 -2
  161. package/dist/chunk-bqhrryqs.js +0 -2
  162. package/dist/chunk-czvzaa42.js +0 -3
  163. package/dist/chunk-de2cfcf5.js +0 -2
  164. package/dist/chunk-exhbc57n.js +0 -2
  165. package/dist/chunk-fhhp6pmx.js +0 -2
  166. package/dist/chunk-fqsrft0a.js +0 -2
  167. package/dist/chunk-gqzccw30.js +0 -2
  168. package/dist/chunk-h47e1c03.js +0 -2
  169. package/dist/chunk-kbemcvp1.js +0 -2
  170. package/dist/chunk-ks4j8fjz.js +0 -2
  171. package/dist/chunk-pbct6nxk.js +0 -2
  172. package/dist/chunk-peerbcwj.js +0 -2
  173. package/dist/chunk-pt1w6vq2.js +0 -2
  174. package/dist/chunk-rgda0gwf.js +0 -2
  175. package/dist/chunk-rwa10jm4.js +0 -2
  176. package/dist/chunk-snsh9ye4.js +0 -2
  177. package/dist/chunk-td39mtts.js +0 -2
  178. package/dist/chunk-trncnwem.js +0 -2
  179. package/dist/chunk-vk2j3vvs.js +0 -2
  180. package/dist/chunk-vph6zpeq.js +0 -2
  181. package/dist/chunk-wa3ks5zv.js +0 -2
  182. package/dist/chunk-xe93ae3r.js +0 -32
  183. package/dist/chunk-xer5kn26.js +0 -2
  184. package/dist/chunk-zv4gb3wb.js +0 -2
  185. package/dist/chunk-zx2krbqf.js +0 -2
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Argument handling for directives whose arguments are expressions.
3
+ *
4
+ * Written for the SEO directives (`@seo`, `@meta`, `@metaTag`,
5
+ * `@structuredData`), and kept free of anything SEO-specific so a custom
6
+ * directive can use it too (`CustomDirective.rawParams`).
7
+ *
8
+ * Every one of these used to fail the same way: an argument that was not the
9
+ * one shape its regex expected was either left in the page as literal text or
10
+ * replaced with nothing, and no one was told. `@seo(seo)` never matched a
11
+ * pattern that insisted on `{`; `@meta('author', post.author)` sent the words
12
+ * "post.author" as the author; `@structuredData(prodcut)` emitted a JSON-LD
13
+ * block holding nothing but `@context`. Each page looked fine and was wrong
14
+ * where it counts, in a link preview or a search result.
15
+ *
16
+ * So the directives share one way to find their call (balanced and quote-aware,
17
+ * so `'Smile :)'` does not end the call early), one way to tell a string literal
18
+ * from an expression, one evaluator that can say WHY a value is missing, and one
19
+ * way to report a failure: an inline comment where the tag would have been, plus
20
+ * a console warning naming the file.
21
+ */
22
+ import type { ErrorCode } from './errors/codes';
23
+ export interface DirectiveCall {
24
+ /** Index of the `@`. */
25
+ start: number;
26
+ /** Index one past the closing paren, or -1 when the call is never closed. */
27
+ end: number;
28
+ /** The text between the parens, as written. */
29
+ args: string;
30
+ /** The whole call, `@name(...)`, or its first 80 characters when unclosed. */
31
+ call: string;
32
+ }
33
+ /**
34
+ * Every `@name(...)` call in `template` matched by `pattern`, in order.
35
+ *
36
+ * `pattern` must be global and end at the opening paren. The closing paren is
37
+ * found by balance, skipping parens inside strings, so an argument may hold any
38
+ * expression: `@seo(build({ title: 'Smile :)' }))` is one call.
39
+ */
40
+ export declare function findDirectiveCalls(template: string, pattern: RegExp): DirectiveCall[];
41
+ /**
42
+ * Replace every call {@link findDirectiveCalls} finds.
43
+ *
44
+ * One left-to-right pass over the original text: a replacement is never
45
+ * rescanned, so markup that happens to contain the directive's name cannot
46
+ * loop. A call with no closing paren keeps its text, preceded by whatever
47
+ * `onUnterminated` returns, so the author sees both the report and the call.
48
+ */
49
+ export declare function replaceDirectiveCalls(template: string, pattern: RegExp, replace: (args: string, call: string) => string, onUnterminated: (call: string) => string): string;
50
+ /**
51
+ * Split an argument list at its top-level commas.
52
+ *
53
+ * Commas inside strings, template literals, brackets, braces and parens belong
54
+ * to the argument they sit in: `'a, b', fn(x, y)` is two arguments, not four.
55
+ * Arguments come back trimmed and exactly as written, quotes included, so the
56
+ * caller can still tell `'title'` (the word) from `title` (a variable).
57
+ */
58
+ export declare function splitTopLevelArgs(args: string): string[];
59
+ /**
60
+ * The value of `raw` when it is exactly one string literal, otherwise `null`.
61
+ *
62
+ * `'a' + b`, `'a' || 'b'` and a template literal with `${}` in it are
63
+ * expressions and return `null`. A literal is read here rather than handed to
64
+ * the evaluator because the evaluator's safety screen reads words, and a
65
+ * description is allowed to say "eval".
66
+ */
67
+ export declare function parseStringLiteral(raw: string): string | null;
68
+ export type EvaluatedArgument = {
69
+ ok: true;
70
+ value: unknown;
71
+ } | {
72
+ ok: false;
73
+ message: string;
74
+ code: ErrorCode;
75
+ };
76
+ /**
77
+ * Evaluate a directive argument against the template's context.
78
+ *
79
+ * The shared evaluator turns every failure into `undefined`, which is right for
80
+ * `{{ }}` and wrong here: a directive whose whole job is to produce a tag has
81
+ * to be able to say why it produced none. So the three ways an expression
82
+ * fails are told apart:
83
+ *
84
+ * - it is not allowed (the safety screen) or not JavaScript (a syntax error);
85
+ * - it names a variable the template does not have, which is almost always a
86
+ * value declared in a plain (client) `<script>`, or a typo;
87
+ * - it is an object or array literal that came back `undefined`, which only
88
+ * happens when something inside it threw.
89
+ *
90
+ * A defined variable that simply holds `null` or `undefined` is not a failure:
91
+ * `@seo(page.seo)` on a page without one is allowed to render nothing.
92
+ */
93
+ export declare function evaluateDirectiveArgument(expression: string, context: Record<string, unknown>): EvaluatedArgument;
94
+ /** How a value reads in an error message. */
95
+ export declare function describeValue(value: unknown): string;
96
+ /** A plain object: not null, not an array. */
97
+ export declare function isPlainRecord(value: unknown): value is Record<string, unknown>;
98
+ /** The call as it reads in a message: one line, and not the whole object. */
99
+ export declare function shortCall(call: string): string;
100
+ /**
101
+ * Report a directive that produced nothing: a console warning naming the file,
102
+ * and an inline comment where the tag would have been, for whoever reads the
103
+ * built HTML (which is how most of these were found).
104
+ */
105
+ export declare function reportDirectiveFailure(label: string, call: string, message: string, filePath: string, code?: ErrorCode): string;
106
+ /** Warn without replacing anything: the directive rendered, but not all of it. */
107
+ export declare function warnDirective(call: string, message: string, filePath: string): void;
@@ -0,0 +1,171 @@
1
+ import path from "node:path";
2
+ import { ErrorCodes, inlineError } from "./error-handling.js";
3
+ import { findMatchingDelimiter } from "./parser/tokenizer.js";
4
+ import { createSafeContext, createSafeFunction, freeIdentifiers, getExpressionSafetyRule } from "./safe-evaluator.js";
5
+ export function findDirectiveCalls(template, pattern) {
6
+ const calls = [];
7
+ pattern.lastIndex = 0;
8
+ let match;
9
+ while ((match = pattern.exec(template)) !== null) {
10
+ const start = match.index, openParen = start + match[0].length - 1, closeParen = findMatchingDelimiter(template, "(", ")", openParen);
11
+ if (closeParen === -1) {
12
+ calls.push({ start, end: -1, args: "", call: template.slice(start, Math.min(template.length, start + 80)) });
13
+ pattern.lastIndex = openParen + 1;
14
+ continue;
15
+ }
16
+ calls.push({
17
+ start,
18
+ end: closeParen + 1,
19
+ args: template.slice(openParen + 1, closeParen),
20
+ call: template.slice(start, closeParen + 1)
21
+ });
22
+ pattern.lastIndex = closeParen + 1;
23
+ }
24
+ return calls;
25
+ }
26
+ export function replaceDirectiveCalls(template, pattern, replace, onUnterminated) {
27
+ let output = "", cursor = 0;
28
+ for (const found of findDirectiveCalls(template, pattern)) {
29
+ output += template.slice(cursor, found.start);
30
+ if (found.end === -1) {
31
+ output += onUnterminated(found.call);
32
+ cursor = found.start;
33
+ continue;
34
+ }
35
+ output += replace(found.args, found.call);
36
+ cursor = found.end;
37
+ }
38
+ return output + template.slice(cursor);
39
+ }
40
+ export function splitTopLevelArgs(args) {
41
+ const parts = [];
42
+ let depth = 0, quote = null, current = "";
43
+ for (let i = 0;i < args.length; i++) {
44
+ const char = args[i];
45
+ if (quote) {
46
+ current += char;
47
+ if (char === "\\" && i + 1 < args.length) {
48
+ current += args[++i];
49
+ continue;
50
+ }
51
+ if (char === quote)
52
+ quote = null;
53
+ continue;
54
+ }
55
+ if (char === "'" || char === '"' || char === "`")
56
+ quote = char;
57
+ else if (char === "(" || char === "[" || char === "{")
58
+ depth++;
59
+ else if (char === ")" || char === "]" || char === "}")
60
+ depth--;
61
+ else if (char === "," && depth === 0) {
62
+ parts.push(current.trim());
63
+ current = "";
64
+ continue;
65
+ }
66
+ current += char;
67
+ }
68
+ if (current.trim())
69
+ parts.push(current.trim());
70
+ return parts;
71
+ }
72
+ export function parseStringLiteral(raw) {
73
+ const text = raw.trim(), quote = text[0];
74
+ if (quote !== "'" && quote !== '"' && quote !== "`" || text.length < 2 || text.at(-1) !== quote)
75
+ return null;
76
+ let value = "";
77
+ for (let i = 1;i < text.length - 1; i++) {
78
+ const char = text[i];
79
+ if (char === "\\" && i + 1 < text.length - 1) {
80
+ const next = text[++i];
81
+ value += next === "n" ? `
82
+ ` : next === "t" ? "\t" : next;
83
+ continue;
84
+ }
85
+ if (char === quote)
86
+ return null;
87
+ if (quote === "`" && char === "$" && text[i + 1] === "{")
88
+ return null;
89
+ value += char;
90
+ }
91
+ return value;
92
+ }
93
+ const NOT_VARIABLES = new Set([
94
+ "true",
95
+ "false",
96
+ "null",
97
+ "undefined",
98
+ "typeof",
99
+ "void",
100
+ "new",
101
+ "in",
102
+ "of",
103
+ "instanceof",
104
+ "this",
105
+ "delete",
106
+ "await",
107
+ "async",
108
+ "function",
109
+ "return",
110
+ "NaN",
111
+ "Infinity"
112
+ ]);
113
+ export function evaluateDirectiveArgument(expression, context) {
114
+ const source = expression.trim();
115
+ if (!source)
116
+ return { ok: !1, message: "the argument is empty", code: ErrorCodes.INVALID_DIRECTIVE_SYNTAX };
117
+ const rule = getExpressionSafetyRule(source);
118
+ if (rule)
119
+ return { ok: !1, message: `the expression is not allowed (${rule})`, code: ErrorCodes.UNSAFE_EXPRESSION };
120
+ const safeContext = createSafeContext(context);
121
+ let value;
122
+ try {
123
+ value = createSafeFunction(source, Object.keys(safeContext))(...Object.values(safeContext));
124
+ } catch (error) {
125
+ return { ok: !1, message: `the expression could not be evaluated: ${error instanceof Error ? error.message : String(error)}`, code: ErrorCodes.EVALUATION_ERROR };
126
+ }
127
+ if (value !== void 0 && value !== null)
128
+ return { ok: !0, value };
129
+ const missing = [...new Set(freeIdentifiers(source))].filter((name) => !NOT_VARIABLES.has(name) && !(name in safeContext) && !(name in globalThis));
130
+ if (missing.length > 0)
131
+ return {
132
+ ok: !1,
133
+ message: `${missing.map((name) => `"${name}"`).join(", ")} ${missing.length === 1 ? "is" : "are"} not defined here. Values a directive reads must come from <script server> or the render context; a plain <script> runs in the browser. To pass literal text, quote it.`,
134
+ code: ErrorCodes.UNDEFINED_VARIABLE
135
+ };
136
+ if (value === void 0 && (source.startsWith("{") || source.startsWith("[")))
137
+ return { ok: !1, message: "the expression threw while it was evaluated", code: ErrorCodes.EVALUATION_ERROR };
138
+ return { ok: !0, value };
139
+ }
140
+ export function describeValue(value) {
141
+ if (value === null)
142
+ return "null";
143
+ if (Array.isArray(value))
144
+ return "an array";
145
+ if (typeof value === "object")
146
+ return "an object";
147
+ if (typeof value === "string")
148
+ return `a string ("${value.length > 40 ? `${value.slice(0, 40)}...` : value}")`;
149
+ return `${/^[aeiou]/.test(typeof value) ? "an" : "a"} ${typeof value}`;
150
+ }
151
+ export function isPlainRecord(value) {
152
+ return typeof value === "object" && value !== null && !Array.isArray(value);
153
+ }
154
+ function displayPath(filePath) {
155
+ if (!filePath)
156
+ return "";
157
+ return path.isAbsolute(filePath) ? path.relative(process.cwd(), filePath) || filePath : filePath;
158
+ }
159
+ export function shortCall(call) {
160
+ const flat = call.replace(/\s+/g, " ").trim();
161
+ return flat.length > 80 ? `${flat.slice(0, 77)}...` : flat;
162
+ }
163
+ export function reportDirectiveFailure(label, call, message, filePath, code = ErrorCodes.EVALUATION_ERROR) {
164
+ const where = filePath ? ` in ${displayPath(filePath)}` : "";
165
+ console.warn(`[stx] ${shortCall(call)}${where}: ${message}`);
166
+ return inlineError(label, `${shortCall(call)}: ${message}`, code);
167
+ }
168
+ export function warnDirective(call, message, filePath) {
169
+ const where = filePath ? ` in ${displayPath(filePath)}` : "";
170
+ console.warn(`[stx] ${shortCall(call)}${where}: ${message}`);
171
+ }
@@ -162,7 +162,7 @@ export declare function escapeHtmlValue(unsafe: string): string;
162
162
  * did before and is still correct for a script that declares its own signals.
163
163
  */
164
164
  export declare function usesSignalsInScript(template: string, filePath?: string): boolean;
165
- export declare function interpolateScriptExpressions(scriptBody: string, context: Record<string, any>): string;
165
+ export declare function interpolateScriptExpressions(scriptBody: string, context: Record<string, any>, filePath?: string): string;
166
166
  /**
167
167
  * Interpolate expressions in a `<script>` tag's ATTRIBUTES.
168
168
  *
@@ -371,13 +371,25 @@ function expressionReadsServerBinding(expr, context) {
371
371
  return !0;
372
372
  return !1;
373
373
  }
374
- export function interpolateScriptExpressions(scriptBody, context) {
374
+ const reportedUnresolvedScriptExpressions = new Set;
375
+ function looksLikeServerValue(expression) {
376
+ return /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(expression.trim());
377
+ }
378
+ function unresolvedServerValue(expression, filePath) {
379
+ const key = `${filePath ?? "<unknown>"}\x00${expression}`;
380
+ if (process.env.STX_DEBUG || !reportedUnresolvedScriptExpressions.has(key)) {
381
+ reportedUnresolvedScriptExpressions.add(key);
382
+ console.warn(`[stx] {{ ${expression} }} in a <script> in ${filePath ?? "<unknown>"} has no server value, so it was emitted as undefined. A mustache left in JavaScript is a SyntaxError that takes the whole script down, so give the value a default in <script server>.`);
383
+ }
384
+ return "undefined";
385
+ }
386
+ export function interpolateScriptExpressions(scriptBody, context, filePath) {
375
387
  let output = scriptBody;
376
388
  output = output.replace(/\{!!([\s\S]*?)!!\}/g, (match, expr) => {
377
389
  try {
378
390
  const value = evaluateExpression(expr, context);
379
391
  if (value === void 0)
380
- return match;
392
+ return looksLikeServerValue(expr) ? unresolvedServerValue(expr.trim(), filePath) : match;
381
393
  return value === null ? "" : String(value);
382
394
  } catch {
383
395
  return match;
@@ -390,7 +402,7 @@ export function interpolateScriptExpressions(scriptBody, context) {
390
402
  try {
391
403
  const value = evaluateExpression(expr, context);
392
404
  if (value === void 0)
393
- return match;
405
+ return looksLikeServerValue(trimmed) ? unresolvedServerValue(trimmed, filePath) : match;
394
406
  return JSON.stringify(value).replace(/</g, "\\u003c");
395
407
  } catch {
396
408
  return match;
package/dist/head.d.ts CHANGED
@@ -1,32 +1,3 @@
1
- /**
2
- * STX Head Management
3
- *
4
- * Manage document head (title, meta, links, scripts) with a simple API.
5
- *
6
- * @example
7
- * ```typescript
8
- * import { useHead, useSeoMeta, definePageMeta } from 'stx'
9
- *
10
- * // Full control
11
- * useHead({
12
- * title: 'My Page',
13
- * meta: [{ name: 'description', content: '...' }]
14
- * })
15
- *
16
- * // SEO shorthand
17
- * useSeoMeta({
18
- * title: 'My Page',
19
- * description: 'Page description',
20
- * ogImage: '/og.png'
21
- * })
22
- *
23
- * // Page configuration
24
- * definePageMeta({
25
- * title: 'My Page',
26
- * layout: 'default'
27
- * })
28
- * ```
29
- */
30
1
  export interface MetaTag {
31
2
  name?: string;
32
3
  property?: string;
@@ -284,11 +255,17 @@ export declare function processTitleDirective(content: string, context: Record<s
284
255
  /**
285
256
  * Process @meta directive.
286
257
  *
258
+ * Stages each tag on the render's head. Reached only when the render has no
259
+ * custom directives, since the registered `metaDirective` (seo.ts) takes every
260
+ * `@meta` first; both resolve their arguments with `resolveMetaArguments`, so
261
+ * a quoted value is text and anything else is an expression.
262
+ *
287
263
  * @example
288
264
  * ```html
289
265
  * @meta('description', 'Page description')
290
266
  * @meta('og:image', ogImageUrl)
267
+ * @meta('author', post.author)
291
268
  * ```
292
269
  */
293
- export declare function processMetaDirective(content: string, context: Record<string, unknown>): string;
270
+ export declare function processMetaDirective(content: string, context: Record<string, unknown>, filePath?: string): string;
294
271
  export { usePageTitle as useTitle };
package/dist/head.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { replaceDirectiveCalls, reportDirectiveFailure, splitTopLevelArgs } from "./directive-arguments.js";
2
+ import { ErrorCodes } from "./error-handling.js";
3
+ import { resolveMetaArguments } from "./meta-arguments.js";
1
4
  let currentHead = {};
2
5
  const headStack = [];
3
6
  let currentPageMeta = {};
@@ -237,12 +240,19 @@ export function processTitleDirective(content, context) {
237
240
  return "";
238
241
  });
239
242
  }
240
- export function processMetaDirective(content, context) {
241
- return content.replace(/@meta\s*\(\s*(['"`])(.+?)\1\s*,\s*(?:(['"`])(.+?)\3|([^)]+))\s*\)/g, (_, _q1, name, q2, quotedValue, unquotedValue) => {
242
- const value = q2 ? quotedValue : unquotedValue, resolvedValue = q2 ? value : context[(value || "").trim()] ?? value, meta = name.includes(":") ? { property: name, content: resolvedValue } : { name, content: resolvedValue }, existing = context.__stx_runtime_head ?? {};
243
- context.__stx_runtime_head = mergeHeadConfigs(existing, { meta: [meta] });
243
+ export function processMetaDirective(content, context, filePath = "") {
244
+ if (!content.includes("@meta"))
245
+ return content;
246
+ return replaceDirectiveCalls(content, /(?<![\w$@])@meta\s*\(/g, (args, call) => {
247
+ const resolved = resolveMetaArguments(splitTopLevelArgs(args), context);
248
+ if (resolved.kind === "error")
249
+ return reportDirectiveFailure("Meta", call, resolved.message, filePath, resolved.code);
250
+ if (resolved.kind === "tag") {
251
+ const existing = context.__stx_runtime_head ?? {};
252
+ context.__stx_runtime_head = mergeHeadConfigs(existing, { meta: [resolved.tag] });
253
+ }
244
254
  return "";
245
- });
255
+ }, (call) => reportDirectiveFailure("Meta", call, "the call has no closing parenthesis", filePath, ErrorCodes.INVALID_DIRECTIVE_SYNTAX));
246
256
  }
247
257
 
248
258
  export {
package/dist/includes.js CHANGED
@@ -623,7 +623,7 @@ export async function processIncludes(template, context, filePath, options, depe
623
623
  const isServerScript = scriptAttrs.includes("server"), shouldTranspile = shouldTranspileTypeScript(scriptAttrs);
624
624
  if (!isServerScript && scriptContent.trim()) {
625
625
  const { interpolateScriptExpressions } = await import("./expressions.js");
626
- scriptContent = interpolateScriptExpressions(scriptContent, includeContext);
626
+ scriptContent = interpolateScriptExpressions(scriptContent, includeContext, includePath);
627
627
  }
628
628
  let vendorStyleTags = "";
629
629
  try {
@@ -684,7 +684,7 @@ export async function processIncludes(template, context, filePath, options, depe
684
684
  const { interpolateScriptExpressions } = await import("./expressions.js");
685
685
  workingContent = workingContent.replace(/__STX_DATA_BLOCK_(\d+)__/g, (_token, index) => {
686
686
  const block = dataBlocks[Number(index)];
687
- return `<script${block.attrs}>${interpolateScriptExpressions(block.body, includeContext)}</script>`;
687
+ return `<script${block.attrs}>${interpolateScriptExpressions(block.body, includeContext, includePath)}</script>`;
688
688
  });
689
689
  }
690
690
  if (signalScopeId) {
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Resolving `@meta(...)` arguments to a tag.
3
+ *
4
+ * `@meta` has three entry points -- the registered custom directive that stages
5
+ * the tag on the head (seo.ts `metaDirective`), the head pass for renders
6
+ * without custom directives (head.ts `processMetaDirective`), and the inline
7
+ * pass that partials use (seo.ts `processMetaDirectives`) -- and they used to
8
+ * read their arguments three different ways. A quoted value could be looked up
9
+ * in the context as if it were a variable name, and an unquoted one that was
10
+ * not a bare variable (`post.author`) was sent as literal text. One resolver
11
+ * now serves all three. It lives apart from both seo.ts and head.ts because
12
+ * seo.ts already imports head.ts.
13
+ */
14
+ import type { ErrorCode } from './errors/codes';
15
+ /**
16
+ * Namespaces whose tags are addressed by `property`, not `name`.
17
+ *
18
+ * Open Graph and its relatives are RDFa vocabularies, so the attribute is
19
+ * `property`; a `<meta name="og:title">` is ignored by every scraper that
20
+ * implements the spec. Twitter cards are the exception people expect to be a
21
+ * property and are not: the card spec says `name`.
22
+ */
23
+ export declare const PROPERTY_NAMESPACES: string[];
24
+ export type MetaTagAttrs = {
25
+ name: string;
26
+ content: string;
27
+ } | {
28
+ property: string;
29
+ content: string;
30
+ };
31
+ export type ResolvedMeta = {
32
+ kind: 'tag';
33
+ tag: MetaTagAttrs;
34
+ } | {
35
+ kind: 'empty';
36
+ } | {
37
+ kind: 'error';
38
+ message: string;
39
+ code: ErrorCode;
40
+ };
41
+ /**
42
+ * Resolve `@meta(...)` arguments, given as written (quotes kept), to a tag.
43
+ *
44
+ * - `@meta('author', 'Jane')`: literal text. A quoted value is never looked up
45
+ * in the context, so `@meta('description', 'title')` means the word.
46
+ * - `@meta('author', author)`, `@meta('og:title', post.title)`,
47
+ * `@meta('keywords', tags)`: the second argument is evaluated. An array joins
48
+ * with ", "; `null`, `undefined`, `false` and `''` render nothing.
49
+ * - `@meta('og:title')`: the one-argument form reads the context by the part
50
+ * after the colon (`title`), then `openGraph.title`.
51
+ */
52
+ export declare function resolveMetaArguments(args: string[], context: Record<string, unknown>): ResolvedMeta;
@@ -0,0 +1,55 @@
1
+ import { ErrorCodes } from "./error-handling.js";
2
+ import { describeValue, evaluateDirectiveArgument, isPlainRecord, parseStringLiteral } from "./directive-arguments.js";
3
+ export const PROPERTY_NAMESPACES = ["og:", "article:", "book:", "profile:", "fb:", "music:", "video:"];
4
+ const BARE_META_NAME = /^[a-z][\w-]*(?::[\w:-]+)?$/i;
5
+ export function resolveMetaArguments(args, context) {
6
+ if (args.length < 1)
7
+ return { kind: "error", message: "meta directive requires at least the meta name", code: ErrorCodes.INVALID_DIRECTIVE_SYNTAX };
8
+ if (args.length > 2)
9
+ return { kind: "error", message: `@meta takes a name and a value, got ${args.length} arguments`, code: ErrorCodes.INVALID_DIRECTIVE_SYNTAX };
10
+ const name = resolveMetaName(args[0], context);
11
+ if (typeof name !== "string")
12
+ return name;
13
+ let content;
14
+ if (args.length === 2) {
15
+ const value = resolveMetaValue(args[1], context);
16
+ if (typeof value !== "string")
17
+ return value;
18
+ content = value;
19
+ } else {
20
+ const segment = name.includes(":") ? name.slice(name.indexOf(":") + 1) : name, openGraph = context.openGraph, found = context[segment] ?? (isPlainRecord(openGraph) ? openGraph[segment] : void 0);
21
+ content = found === void 0 || found === null || typeof found === "object" ? "" : String(found);
22
+ }
23
+ if (!content)
24
+ return { kind: "empty" };
25
+ return { kind: "tag", tag: PROPERTY_NAMESPACES.some((prefix) => name.startsWith(prefix)) ? { property: name, content } : { name, content } };
26
+ }
27
+ function resolveMetaName(raw, context) {
28
+ const literal = parseStringLiteral(raw);
29
+ if (literal !== null)
30
+ return literal.trim() ? literal : { kind: "error", message: "the meta name is empty", code: ErrorCodes.INVALID_DIRECTIVE_SYNTAX };
31
+ if (BARE_META_NAME.test(raw.trim()))
32
+ return raw.trim();
33
+ const result = evaluateDirectiveArgument(raw, context);
34
+ if (!result.ok)
35
+ return { kind: "error", message: `meta name: ${result.message}`, code: result.code };
36
+ if (typeof result.value !== "string" || !result.value.trim())
37
+ return { kind: "error", message: `the meta name must be a non-empty string, got ${describeValue(result.value)}`, code: ErrorCodes.TYPE_ERROR };
38
+ return result.value;
39
+ }
40
+ function resolveMetaValue(raw, context) {
41
+ const literal = parseStringLiteral(raw);
42
+ if (literal !== null)
43
+ return literal;
44
+ const result = evaluateDirectiveArgument(raw, context);
45
+ if (!result.ok)
46
+ return { kind: "error", message: result.message, code: result.code };
47
+ const value = result.value;
48
+ if (value === void 0 || value === null || value === !1)
49
+ return "";
50
+ if (Array.isArray(value))
51
+ return value.filter((item) => item !== void 0 && item !== null && item !== "").map(String).join(", ");
52
+ if (typeof value === "object" && !(value instanceof Date))
53
+ return { kind: "error", message: `the content must be text, got ${describeValue(value)}`, code: ErrorCodes.TYPE_ERROR };
54
+ return String(value);
55
+ }
package/dist/process.js CHANGED
@@ -725,7 +725,7 @@ async function processOtherDirectives(template, context, filePath, options, depe
725
725
  context.__stx_head_raw = `${context.__stx_head_raw || ""}${headResult.headContent}
726
726
  `;
727
727
  output = processTitleDirective(output, context);
728
- output = processMetaDirective(output, context);
728
+ output = processMetaDirective(output, context, filePath);
729
729
  output = processRouteDirectives(output);
730
730
  output = processAuthDirectives(output, context);
731
731
  output = processCsrfDirectives(output);
package/dist/pwa.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
- import{Fk as wq,Gk as dq,Hk as Cq,Ik as mq,Jk as gq}from"./chunk-wvc7t11n.js";import{Kk as S,Lk as Tq,Mk as _q,Nk as z,Ok as D,Pk as P,Qk as kq,Rk as vq,Sk as w,Tk as Iq,Uk as d,Vk as Hq,Wk as Oq,Xk as Mq}from"./chunk-yzv702kk.js";import{ow as Aq,pw as bq,qw as Sq,rw as zq,sw as Dq,tw as Pq}from"./chunk-yr7camcd.js";import{xw as N}from"./chunk-p5d8sb50.js";import p from"fs";import E from"path";import C from"fs";import L from"path";var k=[72,96,128,144,152,192,384,512],v=[120,152,167,180],I=[16,32];async function H(G,B){let q={success:!0,generatedFiles:[],errors:[],warnings:[]},K=G.pwa?.icons;if(!K?.src)return q.errors.push("No source icon specified in pwa.icons.src"),q.success=!1,q;let Y=L.isAbsolute(K.src)?K.src:L.resolve(process.cwd(),K.src);if(!await Bun.file(Y).exists())return q.errors.push(`Source icon not found: ${Y}`),q.success=!1,q;let Q=L.resolve(B,K.outputDir||"pwa-icons");try{C.mkdirSync(Q,{recursive:!0})}catch{return q.errors.push(`Failed to create output directory: ${Q}`),q.success=!1,q}let X=K.sizes||k,j=K.generateWebP??!0,J=K.generateAppleIcons??!0;try{let y;try{y=await import("sharp")}catch{return q.errors.push(`sharp is not installed. Install it with: bun add sharp
3
- Icon generation requires the sharp package for image processing.`),q.success=!1,q}let R=await y.default(Y).metadata();if(R.width&&R.width<512)q.warnings.push(`Source icon is ${R.width}x${R.height}. Recommend using at least 512x512 for best quality.`);for(let $ of X){let x=L.join(Q,`icon-${$}x${$}.png`);if(await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x),j){let F=L.join(Q,`icon-${$}x${$}.webp`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).webp({quality:90}).toFile(F),q.generatedFiles.push(F)}}if(J){for(let x of v){let F=L.join(Q,`apple-touch-icon-${x}x${x}.png`);await y.default(Y).resize(x,x,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(F),q.generatedFiles.push(F)}let $=L.join(Q,"apple-touch-icon.png");await y.default(Y).resize(180,180,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile($),q.generatedFiles.push($)}for(let $ of I){let x=L.join(Q,`favicon-${$}x${$}.png`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x)}let U=512,T=L.join(Q,`icon-maskable-${U}x${U}.png`),Z=Math.floor(U*0.1),_=U-Z*2;await y.default(Y).resize(_,_,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).extend({top:Z,bottom:Z,left:Z,right:Z,background:{r:255,g:255,b:255,alpha:1}}).png().toFile(T),q.generatedFiles.push(T)}catch(y){let V=y instanceof Error?y.message:String(y);q.errors.push(`Icon generation failed: ${V}`),q.success=!1}return q}function n(G){let B=G.sizes||k,q=G.outputDir?`/${G.outputDir}`:"/pwa-icons",K=G.generateWebP??!0,Y=G.generateAppleIcons??!0,Q=[];for(let X of B)if(Q.push(`${q}/icon-${X}x${X}.png`),K)Q.push(`${q}/icon-${X}x${X}.webp`);if(Y){Q.push(`${q}/apple-touch-icon.png`);for(let X of v)Q.push(`${q}/apple-touch-icon-${X}x${X}.png`)}for(let X of I)Q.push(`${q}/favicon-${X}x${X}.png`);return Q.push(`${q}/icon-maskable-512x512.png`),Q}async function O(){try{return await import("sharp"),!0}catch{return!1}}function m(G,B,q,K){let Y=[],Q=K.join(" ");for(let X of G)if(Y.push({src:`${B}/icon-${X}x${X}.png`,sizes:`${X}x${X}`,type:"image/png",purpose:Q}),q)Y.push({src:`${B}/icon-${X}x${X}.webp`,sizes:`${X}x${X}`,type:"image/webp",purpose:Q});return Y}function g(G){return G.map((B)=>({name:B.name,short_name:B.shortName,description:B.description,url:B.url,icons:B.icons?.map((q)=>({src:q.src,sizes:q.sizes,type:q.type}))}))}function h(G){let B=G.pwa?.shareTarget;if(!B?.enabled)return;let q={action:B.action||"/share",method:B.method||"POST",enctype:B.enctype||"multipart/form-data",params:{title:B.params?.title||"title",text:B.params?.text||"text",url:B.params?.url||"url"}};if(B.acceptFiles&&B.acceptFiles.length>0)q.params.files=B.acceptFiles.map((K)=>({name:K.name,accept:K.accept}));return q}function c(G){let B=G.pwa?.fileHandlers;if(!B?.enabled||!B.accept)return;let q=[{action:B.action||"/",accept:B.accept,launch_type:B.launchType||"single-client"}];if(B.icons)q[0].icons=B.icons.map((K)=>({src:K.src,sizes:K.sizes,type:K.type}));return q}function l(G){let B=G.pwa?.protocolHandlers;if(!B||B.length===0)return;return B.map((q)=>({protocol:q.protocol,url:q.url}))}function u(G){let B=G.pwa;if(!B?.enabled||!B.manifest)return null;let{manifest:q,icons:K}=B,Y=K?.sizes||[72,96,128,144,152,192,384,512],Q=K?.outputDir?`/${K.outputDir}`:"/pwa-icons",X=K?.generateWebP??!0,j=K?.purpose||["any","maskable"],J={name:q.name,short_name:q.shortName||q.name,description:q.description,start_url:q.startUrl||"/",display:q.display||"standalone",display_override:q.displayOverride,orientation:q.orientation,theme_color:q.themeColor,background_color:q.backgroundColor,scope:q.scope||"/",lang:q.lang,dir:q.dir,id:q.id,categories:q.categories,icons:m(Y,Q,X,j)};if(q.shortcuts&&q.shortcuts.length>0)J.shortcuts=g(q.shortcuts);if(q.screenshots&&q.screenshots.length>0)J.screenshots=q.screenshots.map((U)=>({src:U.src,sizes:U.sizes,type:U.type,label:U.label,form_factor:U.platform}));let y=h(G);if(y)J.share_target=y;let V=c(G);if(V)J.file_handlers=V;let R=l(G);if(R)J.protocol_handlers=R;if(q.launchHandler)J.launch_handler={client_mode:q.launchHandler};if(q.handleLinks)J.handle_links=q.handleLinks;if(q.edgeSidePanel?.enabled)J.edge_side_panel={preferred_width:q.edgeSidePanel.preferredWidth};return J}function M(G){let B=u(G);if(!B)return"";let q=JSON.parse(JSON.stringify(B));return JSON.stringify(q,null,2)}function i(G,B){return{...B,...G,shortcuts:G.shortcuts||B.shortcuts,screenshots:G.screenshots||B.screenshots,categories:G.categories||B.categories}}function s(G){let B=[],q=G.pwa;if(!q?.enabled)return B;let K=q.manifest;if(!K)return B.push("PWA manifest configuration is missing"),B;if(!K.name)B.push("Manifest name is required");let Y=["fullscreen","standalone","minimal-ui","browser"];if(K.display&&!Y.includes(K.display))B.push(`Invalid display mode: ${K.display}. Must be one of: ${Y.join(", ")}`);let Q=["any","natural","landscape","landscape-primary","landscape-secondary","portrait","portrait-primary","portrait-secondary"];if(K.orientation&&!Q.includes(K.orientation))B.push(`Invalid orientation: ${K.orientation}`);let X=/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;if(K.themeColor&&!X.test(K.themeColor))B.push(`Invalid theme color: ${K.themeColor}. Must be a valid hex color.`);if(K.backgroundColor&&!X.test(K.backgroundColor))B.push(`Invalid background color: ${K.backgroundColor}. Must be a valid hex color.`);if(q.shareTarget?.enabled){if(!q.shareTarget.action)B.push("Share target action URL is required")}if(q.fileHandlers?.enabled){if(!q.fileHandlers.accept||Object.keys(q.fileHandlers.accept).length===0)B.push("File handlers must specify accepted MIME types")}if(q.protocolHandlers)for(let j of q.protocolHandlers){if(!j.protocol)B.push("Protocol handler missing protocol");if(!j.url||!j.url.includes("%s"))B.push(`Protocol handler URL must contain %s placeholder: ${j.protocol}`)}return B}import A from"path";async function b(G){let B=G.pwa?.offline;if(!B?.enabled)return"";if(B.page){let q=A.isAbsolute(B.page)?B.page:A.resolve(process.cwd(),B.page);if(await Bun.file(q).exists())try{let{processDirectives:K}=await import("./chunk-rwa10jm4.js"),Y=await Bun.file(q).text();return await K(Y,{},q,G,new Set)}catch(K){console.warn(`[PWA] Failed to process custom offline page: ${K}`)}else console.warn(`[PWA] Custom offline page not found: ${q}`)}return f(G)}function f(G){let B=G.pwa?.offline,q=G.pwa?.manifest,K=B?.fallbackTitle||"You are offline",Y=B?.fallbackMessage||"Please check your internet connection and try again.",Q=q?.themeColor||"#4f46e5",X=q?.shortName||q?.name||"App";return`<!DOCTYPE html>
2
+ import{Fk as wq,Gk as dq,Hk as Cq,Ik as mq,Jk as gq}from"./chunk-wvc7t11n.js";import{Kk as S,Lk as Tq,Mk as _q,Nk as z,Ok as D,Pk as P,Qk as kq,Rk as vq,Sk as w,Tk as Iq,Uk as d,Vk as Hq,Wk as Oq,Xk as Mq}from"./chunk-c30qnb6z.js";import{Ew as Aq,Fw as bq,Gw as Sq,Hw as zq,Iw as Dq,Jw as Pq}from"./chunk-9nrgjtah.js";import{Nw as N}from"./chunk-ejqpq0wc.js";import p from"fs";import E from"path";import C from"fs";import L from"path";var k=[72,96,128,144,152,192,384,512],v=[120,152,167,180],I=[16,32];async function H(G,B){let q={success:!0,generatedFiles:[],errors:[],warnings:[]},K=G.pwa?.icons;if(!K?.src)return q.errors.push("No source icon specified in pwa.icons.src"),q.success=!1,q;let Y=L.isAbsolute(K.src)?K.src:L.resolve(process.cwd(),K.src);if(!await Bun.file(Y).exists())return q.errors.push(`Source icon not found: ${Y}`),q.success=!1,q;let Q=L.resolve(B,K.outputDir||"pwa-icons");try{C.mkdirSync(Q,{recursive:!0})}catch{return q.errors.push(`Failed to create output directory: ${Q}`),q.success=!1,q}let X=K.sizes||k,j=K.generateWebP??!0,J=K.generateAppleIcons??!0;try{let y;try{y=await import("sharp")}catch{return q.errors.push(`sharp is not installed. Install it with: bun add sharp
3
+ Icon generation requires the sharp package for image processing.`),q.success=!1,q}let R=await y.default(Y).metadata();if(R.width&&R.width<512)q.warnings.push(`Source icon is ${R.width}x${R.height}. Recommend using at least 512x512 for best quality.`);for(let $ of X){let x=L.join(Q,`icon-${$}x${$}.png`);if(await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x),j){let F=L.join(Q,`icon-${$}x${$}.webp`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).webp({quality:90}).toFile(F),q.generatedFiles.push(F)}}if(J){for(let x of v){let F=L.join(Q,`apple-touch-icon-${x}x${x}.png`);await y.default(Y).resize(x,x,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile(F),q.generatedFiles.push(F)}let $=L.join(Q,"apple-touch-icon.png");await y.default(Y).resize(180,180,{fit:"contain",background:{r:255,g:255,b:255,alpha:1}}).png().toFile($),q.generatedFiles.push($)}for(let $ of I){let x=L.join(Q,`favicon-${$}x${$}.png`);await y.default(Y).resize($,$,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).png().toFile(x),q.generatedFiles.push(x)}let U=512,T=L.join(Q,`icon-maskable-${U}x${U}.png`),Z=Math.floor(U*0.1),_=U-Z*2;await y.default(Y).resize(_,_,{fit:"contain",background:{r:0,g:0,b:0,alpha:0}}).extend({top:Z,bottom:Z,left:Z,right:Z,background:{r:255,g:255,b:255,alpha:1}}).png().toFile(T),q.generatedFiles.push(T)}catch(y){let V=y instanceof Error?y.message:String(y);q.errors.push(`Icon generation failed: ${V}`),q.success=!1}return q}function n(G){let B=G.sizes||k,q=G.outputDir?`/${G.outputDir}`:"/pwa-icons",K=G.generateWebP??!0,Y=G.generateAppleIcons??!0,Q=[];for(let X of B)if(Q.push(`${q}/icon-${X}x${X}.png`),K)Q.push(`${q}/icon-${X}x${X}.webp`);if(Y){Q.push(`${q}/apple-touch-icon.png`);for(let X of v)Q.push(`${q}/apple-touch-icon-${X}x${X}.png`)}for(let X of I)Q.push(`${q}/favicon-${X}x${X}.png`);return Q.push(`${q}/icon-maskable-512x512.png`),Q}async function O(){try{return await import("sharp"),!0}catch{return!1}}function m(G,B,q,K){let Y=[],Q=K.join(" ");for(let X of G)if(Y.push({src:`${B}/icon-${X}x${X}.png`,sizes:`${X}x${X}`,type:"image/png",purpose:Q}),q)Y.push({src:`${B}/icon-${X}x${X}.webp`,sizes:`${X}x${X}`,type:"image/webp",purpose:Q});return Y}function g(G){return G.map((B)=>({name:B.name,short_name:B.shortName,description:B.description,url:B.url,icons:B.icons?.map((q)=>({src:q.src,sizes:q.sizes,type:q.type}))}))}function h(G){let B=G.pwa?.shareTarget;if(!B?.enabled)return;let q={action:B.action||"/share",method:B.method||"POST",enctype:B.enctype||"multipart/form-data",params:{title:B.params?.title||"title",text:B.params?.text||"text",url:B.params?.url||"url"}};if(B.acceptFiles&&B.acceptFiles.length>0)q.params.files=B.acceptFiles.map((K)=>({name:K.name,accept:K.accept}));return q}function c(G){let B=G.pwa?.fileHandlers;if(!B?.enabled||!B.accept)return;let q=[{action:B.action||"/",accept:B.accept,launch_type:B.launchType||"single-client"}];if(B.icons)q[0].icons=B.icons.map((K)=>({src:K.src,sizes:K.sizes,type:K.type}));return q}function l(G){let B=G.pwa?.protocolHandlers;if(!B||B.length===0)return;return B.map((q)=>({protocol:q.protocol,url:q.url}))}function u(G){let B=G.pwa;if(!B?.enabled||!B.manifest)return null;let{manifest:q,icons:K}=B,Y=K?.sizes||[72,96,128,144,152,192,384,512],Q=K?.outputDir?`/${K.outputDir}`:"/pwa-icons",X=K?.generateWebP??!0,j=K?.purpose||["any","maskable"],J={name:q.name,short_name:q.shortName||q.name,description:q.description,start_url:q.startUrl||"/",display:q.display||"standalone",display_override:q.displayOverride,orientation:q.orientation,theme_color:q.themeColor,background_color:q.backgroundColor,scope:q.scope||"/",lang:q.lang,dir:q.dir,id:q.id,categories:q.categories,icons:m(Y,Q,X,j)};if(q.shortcuts&&q.shortcuts.length>0)J.shortcuts=g(q.shortcuts);if(q.screenshots&&q.screenshots.length>0)J.screenshots=q.screenshots.map((U)=>({src:U.src,sizes:U.sizes,type:U.type,label:U.label,form_factor:U.platform}));let y=h(G);if(y)J.share_target=y;let V=c(G);if(V)J.file_handlers=V;let R=l(G);if(R)J.protocol_handlers=R;if(q.launchHandler)J.launch_handler={client_mode:q.launchHandler};if(q.handleLinks)J.handle_links=q.handleLinks;if(q.edgeSidePanel?.enabled)J.edge_side_panel={preferred_width:q.edgeSidePanel.preferredWidth};return J}function M(G){let B=u(G);if(!B)return"";let q=JSON.parse(JSON.stringify(B));return JSON.stringify(q,null,2)}function i(G,B){return{...B,...G,shortcuts:G.shortcuts||B.shortcuts,screenshots:G.screenshots||B.screenshots,categories:G.categories||B.categories}}function s(G){let B=[],q=G.pwa;if(!q?.enabled)return B;let K=q.manifest;if(!K)return B.push("PWA manifest configuration is missing"),B;if(!K.name)B.push("Manifest name is required");let Y=["fullscreen","standalone","minimal-ui","browser"];if(K.display&&!Y.includes(K.display))B.push(`Invalid display mode: ${K.display}. Must be one of: ${Y.join(", ")}`);let Q=["any","natural","landscape","landscape-primary","landscape-secondary","portrait","portrait-primary","portrait-secondary"];if(K.orientation&&!Q.includes(K.orientation))B.push(`Invalid orientation: ${K.orientation}`);let X=/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/;if(K.themeColor&&!X.test(K.themeColor))B.push(`Invalid theme color: ${K.themeColor}. Must be a valid hex color.`);if(K.backgroundColor&&!X.test(K.backgroundColor))B.push(`Invalid background color: ${K.backgroundColor}. Must be a valid hex color.`);if(q.shareTarget?.enabled){if(!q.shareTarget.action)B.push("Share target action URL is required")}if(q.fileHandlers?.enabled){if(!q.fileHandlers.accept||Object.keys(q.fileHandlers.accept).length===0)B.push("File handlers must specify accepted MIME types")}if(q.protocolHandlers)for(let j of q.protocolHandlers){if(!j.protocol)B.push("Protocol handler missing protocol");if(!j.url||!j.url.includes("%s"))B.push(`Protocol handler URL must contain %s placeholder: ${j.protocol}`)}return B}import A from"path";async function b(G){let B=G.pwa?.offline;if(!B?.enabled)return"";if(B.page){let q=A.isAbsolute(B.page)?B.page:A.resolve(process.cwd(),B.page);if(await Bun.file(q).exists())try{let{processDirectives:K}=await import("./chunk-j5qygdy9.js"),Y=await Bun.file(q).text();return await K(Y,{},q,G,new Set)}catch(K){console.warn(`[PWA] Failed to process custom offline page: ${K}`)}else console.warn(`[PWA] Custom offline page not found: ${q}`)}return f(G)}function f(G){let B=G.pwa?.offline,q=G.pwa?.manifest,K=B?.fallbackTitle||"You are offline",Y=B?.fallbackMessage||"Please check your internet connection and try again.",Q=q?.themeColor||"#4f46e5",X=q?.shortName||q?.name||"App";return`<!DOCTYPE html>
4
4
  <html lang="en">
5
5
  <head>
6
6
  <meta charset="UTF-8">
@@ -224,6 +224,28 @@ export declare const ALPINE_BRIDGE_X_ATTRS: readonly string[];
224
224
  export declare const COMPONENT_PASSTHROUGH_X_ATTRS: readonly string[];
225
225
  /** The `X_HANDLED` lookup the generated runtime embeds, as JS source. */
226
226
  export declare function runtimeHandledXAttrsLiteral(): string;
227
+ /**
228
+ * The colon names that route to their own binder rather than to the generic
229
+ * attribute binding: `:text` is a text binding, `:title` is an attribute.
230
+ *
231
+ * Shared for the same reason the `x-` table is. Anything that has to decide
232
+ * what an attribute *means* -- the runtime's dispatch, and the compile-time
233
+ * binding manifest that has to find every binding the runtime would -- needs
234
+ * this list, and two copies of it disagree eventually.
235
+ */
236
+ export declare const RUNTIME_DIRECTIVE_NAMES: readonly string[];
237
+ /** The `DIRECTIVE_NAMES` lookup the generated runtime embeds, as JS source. */
238
+ export declare function runtimeDirectiveNamesLiteral(): string;
239
+ /**
240
+ * Event names a bare `:name` or `@name` is read as a listener for.
241
+ *
242
+ * Prefix-matched, so `click` also covers `click.stop` and `click.prevent`.
243
+ */
244
+ export declare const RUNTIME_EVENT_NAMES: readonly string[];
245
+ /** The `EVENT_RE` the generated runtime embeds, as JS source. */
246
+ export declare function runtimeEventRegexLiteral(): string;
247
+ /** Does this name begin with an event the runtime binds as a listener? */
248
+ export declare function isRuntimeEventName(name: string): boolean;
227
249
  /**
228
250
  * Drop cached blanked documents.
229
251
  *