@caretcms/caretize 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +70 -0
  2. package/dist/backup.d.ts +30 -0
  3. package/dist/backup.d.ts.map +1 -0
  4. package/dist/backup.js +89 -0
  5. package/dist/backup.js.map +1 -0
  6. package/dist/bind-collection.d.ts +56 -0
  7. package/dist/bind-collection.d.ts.map +1 -0
  8. package/dist/bind-collection.js +140 -0
  9. package/dist/bind-collection.js.map +1 -0
  10. package/dist/bind-route.d.ts +40 -0
  11. package/dist/bind-route.d.ts.map +1 -0
  12. package/dist/bind-route.js +150 -0
  13. package/dist/bind-route.js.map +1 -0
  14. package/dist/cli-args.d.ts +30 -0
  15. package/dist/cli-args.d.ts.map +1 -0
  16. package/dist/cli-args.js +118 -0
  17. package/dist/cli-args.js.map +1 -0
  18. package/dist/cli.d.ts +11 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +356 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/detect.d.ts +76 -0
  23. package/dist/detect.d.ts.map +1 -0
  24. package/dist/detect.js +237 -0
  25. package/dist/detect.js.map +1 -0
  26. package/dist/discover.d.ts +13 -0
  27. package/dist/discover.d.ts.map +1 -0
  28. package/dist/discover.js +84 -0
  29. package/dist/discover.js.map +1 -0
  30. package/dist/frontmatter.d.ts +26 -0
  31. package/dist/frontmatter.d.ts.map +1 -0
  32. package/dist/frontmatter.js +52 -0
  33. package/dist/frontmatter.js.map +1 -0
  34. package/dist/identifiers.d.ts +26 -0
  35. package/dist/identifiers.d.ts.map +1 -0
  36. package/dist/identifiers.js +34 -0
  37. package/dist/identifiers.js.map +1 -0
  38. package/dist/import-wrap.d.ts +56 -0
  39. package/dist/import-wrap.d.ts.map +1 -0
  40. package/dist/import-wrap.js +149 -0
  41. package/dist/import-wrap.js.map +1 -0
  42. package/dist/index.d.ts +18 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +18 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/name.d.ts +49 -0
  47. package/dist/name.d.ts.map +1 -0
  48. package/dist/name.js +164 -0
  49. package/dist/name.js.map +1 -0
  50. package/dist/output.d.ts +30 -0
  51. package/dist/output.d.ts.map +1 -0
  52. package/dist/output.js +110 -0
  53. package/dist/output.js.map +1 -0
  54. package/dist/parse.d.ts +86 -0
  55. package/dist/parse.d.ts.map +1 -0
  56. package/dist/parse.js +92 -0
  57. package/dist/parse.js.map +1 -0
  58. package/dist/plan.d.ts +46 -0
  59. package/dist/plan.d.ts.map +1 -0
  60. package/dist/plan.js +76 -0
  61. package/dist/plan.js.map +1 -0
  62. package/dist/preflight.d.ts +19 -0
  63. package/dist/preflight.d.ts.map +1 -0
  64. package/dist/preflight.js +95 -0
  65. package/dist/preflight.js.map +1 -0
  66. package/dist/prop-hoist.d.ts +67 -0
  67. package/dist/prop-hoist.d.ts.map +1 -0
  68. package/dist/prop-hoist.js +232 -0
  69. package/dist/prop-hoist.js.map +1 -0
  70. package/dist/props.d.ts +38 -0
  71. package/dist/props.d.ts.map +1 -0
  72. package/dist/props.js +116 -0
  73. package/dist/props.js.map +1 -0
  74. package/dist/report.d.ts +42 -0
  75. package/dist/report.d.ts.map +1 -0
  76. package/dist/report.js +35 -0
  77. package/dist/report.js.map +1 -0
  78. package/dist/resolve.d.ts +15 -0
  79. package/dist/resolve.d.ts.map +1 -0
  80. package/dist/resolve.js +35 -0
  81. package/dist/resolve.js.map +1 -0
  82. package/dist/run.d.ts +52 -0
  83. package/dist/run.d.ts.map +1 -0
  84. package/dist/run.js +213 -0
  85. package/dist/run.js.map +1 -0
  86. package/dist/splice.d.ts +43 -0
  87. package/dist/splice.d.ts.map +1 -0
  88. package/dist/splice.js +90 -0
  89. package/dist/splice.js.map +1 -0
  90. package/dist/usage.d.ts +90 -0
  91. package/dist/usage.d.ts.map +1 -0
  92. package/dist/usage.js +249 -0
  93. package/dist/usage.js.map +1 -0
  94. package/dist/wrap.d.ts +72 -0
  95. package/dist/wrap.d.ts.map +1 -0
  96. package/dist/wrap.js +170 -0
  97. package/dist/wrap.js.map +1 -0
  98. package/dist/write.d.ts +28 -0
  99. package/dist/write.d.ts.map +1 -0
  100. package/dist/write.js +37 -0
  101. package/dist/write.js.map +1 -0
  102. package/package.json +54 -0
package/dist/run.js ADDED
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Run orchestration. The atomicity model is "validate everything in memory,
3
+ * then write": every file's tagged output is computed and verified (re-parses +
4
+ * is pure-insertion-reversible) BEFORE a single byte hits disk. If any file
5
+ * fails verification, the run aborts having written nothing. During the write
6
+ * phase, backups are taken per file; if a write throws, everything written so
7
+ * far is restored from those backups.
8
+ */
9
+ import { readFileSync, writeFileSync } from "node:fs";
10
+ import { resolve } from "node:path";
11
+ import { parseAstro } from "./parse.js";
12
+ import { applyTags } from "./write.js";
13
+ import { wrapConst } from "./wrap.js";
14
+ import { wrapImport } from "./import-wrap.js";
15
+ import { hoistPropLiterals, verifyHoistResult } from "./prop-hoist.js";
16
+ import { writeBackup, restoreBackups } from "./backup.js";
17
+ /** Apply the right editable() wrap for a target's origin. */
18
+ function applyWrap(source, t) {
19
+ return t.origin === "import"
20
+ ? wrapImport(source, t.varName, t.key)
21
+ : wrapConst(source, t.varName, t.key);
22
+ }
23
+ /** Compute and verify a single file's tagged output. No disk access. */
24
+ export async function prepareFile(relPath, source, items) {
25
+ const base = {
26
+ relPath, source, output: source, inserted: [], tagCount: 0, ok: true,
27
+ };
28
+ if (items.length === 0)
29
+ return base;
30
+ const { output, inserted, failures } = applyTags(source, items);
31
+ if (failures.length > 0) {
32
+ return { ...base, ok: false, reason: `could not place ${failures.length} tag(s)` };
33
+ }
34
+ // Gate 1: the result must still be valid Astro.
35
+ try {
36
+ await parseAstro(output);
37
+ }
38
+ catch (err) {
39
+ return { ...base, ok: false, reason: `output failed to re-parse: ${err.message}` };
40
+ }
41
+ // Gate 2: pure-insertion — stripping the inserted attrs restores the original.
42
+ let restored = output;
43
+ for (const attr of inserted) {
44
+ const idx = restored.indexOf(attr);
45
+ if (idx < 0)
46
+ return { ...base, ok: false, reason: "inserted attribute not found on verify" };
47
+ restored = restored.slice(0, idx) + restored.slice(idx + attr.length);
48
+ }
49
+ if (restored !== source) {
50
+ return { ...base, ok: false, reason: "output is not a pure insertion of the original" };
51
+ }
52
+ return { relPath, source, output, inserted, tagCount: items.length, ok: true };
53
+ }
54
+ /** True when every character of `needle` appears in `haystack` in order. */
55
+ function isSubsequence(needle, haystack) {
56
+ let i = 0;
57
+ for (let j = 0; j < haystack.length && i < needle.length; j++) {
58
+ if (haystack[j] === needle[i])
59
+ i++;
60
+ }
61
+ return i === needle.length;
62
+ }
63
+ /**
64
+ * Compute and verify a single file's editable()-wrapped output (Tier-1). Same
65
+ * atomicity contract as prepareFile, so the result feeds commitRun unchanged.
66
+ *
67
+ * Gate 2 here is a SUBSEQUENCE check rather than strip-by-string: the inserted
68
+ * `await editable(…, ` / `)` spans contain characters (`)`, commas) that also
69
+ * occur elsewhere, so verifying "original is a subsequence of output" is the
70
+ * robust way to prove pure insertion (no original char deleted or reordered).
71
+ */
72
+ export async function prepareWrapFile(relPath, source, targets) {
73
+ const base = {
74
+ relPath, source, output: source, inserted: [], tagCount: 0, ok: true,
75
+ };
76
+ if (targets.length === 0)
77
+ return base;
78
+ let output = source;
79
+ const inserted = [];
80
+ for (const t of targets) {
81
+ const result = applyWrap(output, t);
82
+ if (!result.ok)
83
+ return { ...base, ok: false, reason: `${t.varName}: ${result.reason}` };
84
+ if (result.alreadyWrapped)
85
+ continue;
86
+ inserted.push(`editable(${JSON.stringify(t.key)}) around ${t.varName}`);
87
+ output = result.output;
88
+ }
89
+ if (inserted.length === 0)
90
+ return base; // nothing applicable (already wrapped)
91
+ // Gate 1: the result must still be valid Astro.
92
+ try {
93
+ await parseAstro(output);
94
+ }
95
+ catch (err) {
96
+ return { ...base, ok: false, reason: `output failed to re-parse: ${err.message}` };
97
+ }
98
+ // Gate 2: pure insertion — every original character survives, in order.
99
+ if (!isSubsequence(source, output)) {
100
+ return { ...base, ok: false, reason: "output is not a pure insertion of the original" };
101
+ }
102
+ return { relPath, source, output, inserted, tagCount: inserted.length, ok: true };
103
+ }
104
+ /**
105
+ * Compute and verify a file's output with BOTH passes: attribute tags and
106
+ * editable() wraps. Tags are applied first (offset-based, in the template), then
107
+ * wraps (content-scan, in the frontmatter) — so the frontmatter insertions never
108
+ * shift the template tag offsets. Same atomicity gates; one PreparedFile out.
109
+ */
110
+ export async function prepareFileFull(relPath, source, items, targets, hoistTargets = []) {
111
+ const base = {
112
+ relPath, source, output: source, inserted: [], tagCount: 0, ok: true,
113
+ };
114
+ if (items.length === 0 && targets.length === 0 && hoistTargets.length === 0)
115
+ return base;
116
+ let output = source;
117
+ const inserted = [];
118
+ // Pass 1: attribute tags (template).
119
+ if (items.length > 0) {
120
+ const tagged = applyTags(source, items);
121
+ if (tagged.failures.length > 0) {
122
+ return { ...base, ok: false, reason: `could not place ${tagged.failures.length} tag(s)` };
123
+ }
124
+ output = tagged.output;
125
+ inserted.push(...tagged.inserted);
126
+ }
127
+ // Pass 2: editable() wraps (frontmatter) — wrapConst for consts, wrapImport
128
+ // for default data imports. Both are pure insertion.
129
+ let wrapCount = 0;
130
+ for (const t of targets) {
131
+ const wrapped = applyWrap(output, t);
132
+ if (!wrapped.ok)
133
+ return { ...base, ok: false, reason: `${t.varName}: ${wrapped.reason}` };
134
+ if (wrapped.alreadyWrapped)
135
+ continue;
136
+ inserted.push(`editable(${JSON.stringify(t.key)}) around ${t.varName}`);
137
+ output = wrapped.output;
138
+ wrapCount++;
139
+ }
140
+ if (output === source && hoistTargets.length === 0)
141
+ return base; // nothing changed
142
+ // Passes 1+2 are pure insertion: verify the running output is a subsequence of
143
+ // the original BEFORE the (deletion-bearing) hoist pass runs.
144
+ const afterWraps = output;
145
+ if (!isSubsequence(source, afterWraps)) {
146
+ return { ...base, ok: false, reason: "output is not a pure insertion of the original" };
147
+ }
148
+ // Pass 3 (LAST): prop-hoist rewrite. Self-locating, so it runs after the
149
+ // offset-based tag pass. Verified by an inverse gate against `afterWraps`.
150
+ let hoistCount = 0;
151
+ if (hoistTargets.length > 0) {
152
+ const h = hoistPropLiterals(afterWraps, hoistTargets);
153
+ if (!h.ok)
154
+ return { ...base, ok: false, reason: `hoist: ${h.reason}` };
155
+ if (h.propsRewritten > 0) {
156
+ if (!verifyHoistResult(afterWraps, h.output, hoistTargets, h.addedImport)) {
157
+ return { ...base, ok: false, reason: "hoist not reversible to pre-hoist source" };
158
+ }
159
+ output = h.output;
160
+ hoistCount = h.propsRewritten;
161
+ inserted.push(...h.constsDeclared.map((c) => `editable() hoist → const ${c}`));
162
+ }
163
+ }
164
+ if (output === source)
165
+ return base; // nothing actually changed
166
+ // Gate 1: still valid Astro.
167
+ try {
168
+ await parseAstro(output);
169
+ }
170
+ catch (err) {
171
+ return { ...base, ok: false, reason: `output failed to re-parse: ${err.message}` };
172
+ }
173
+ return {
174
+ relPath, source, output, inserted,
175
+ tagCount: items.length + wrapCount + hoistCount, ok: true,
176
+ };
177
+ }
178
+ /**
179
+ * Write verified files to disk with backups. Throws (after rolling back) if any
180
+ * prepared file failed verification or any write fails.
181
+ */
182
+ export function commitRun(rootDir, prepared, stamp) {
183
+ const toWrite = prepared.filter((p) => p.tagCount > 0);
184
+ const bad = toWrite.find((p) => !p.ok);
185
+ if (bad) {
186
+ throw new Error(`refusing to write: ${bad.relPath} failed verification (${bad.reason})`);
187
+ }
188
+ const written = [];
189
+ const backups = [];
190
+ const thisRun = [];
191
+ try {
192
+ for (const file of toWrite) {
193
+ const backupAbs = writeBackup(rootDir, file.relPath, stamp);
194
+ backups.push(backupAbs);
195
+ thisRun.push({ relPath: file.relPath, backupAbs });
196
+ writeFileSync(resolve(rootDir, file.relPath), file.output, "utf8");
197
+ written.push(file.relPath);
198
+ }
199
+ }
200
+ catch (err) {
201
+ // Roll back exactly the files THIS run backed up — never "the latest
202
+ // stamp", which could be a previous run's backups if we died before
203
+ // writing our first one.
204
+ restoreBackups(rootDir, thisRun);
205
+ throw new Error(`write failed, rolled back: ${err.message}`);
206
+ }
207
+ return { written, backups };
208
+ }
209
+ /** Convenience: read a file from disk for preparation. */
210
+ export function readSource(rootDir, relPath) {
211
+ return readFileSync(resolve(rootDir, relPath), "utf8");
212
+ }
213
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The byte-correct splice core for caretize.
3
+ *
4
+ * Why this file exists at all: `@astrojs/compiler` reports node positions as
5
+ * **0-based UTF-8 byte offsets** (see `Point.offset` in its AST types), but
6
+ * JavaScript string indexing counts UTF-16 code units. The two diverge the
7
+ * moment a file contains a multibyte character (em-dash, curly quote,
8
+ * box-drawing glyph, emoji) before an element. Splicing at a byte offset into a
9
+ * JS string therefore lands in the wrong place and silently corrupts the tag.
10
+ *
11
+ * The rule, proven by spike against the example corpus: do ALL offset
12
+ * arithmetic and splicing on a UTF-8 `Buffer`, never on a string. Everything in
13
+ * this module takes and returns `Buffer`s for that reason.
14
+ *
15
+ * We use the compiler AST for DETECTION only. Writing is pure insertion of an
16
+ * attribute just before the `>` of an opening tag — never AST reprinting — so
17
+ * the user's formatting, comments, and whitespace are preserved byte-for-byte.
18
+ */
19
+ export interface OpenTagEnd {
20
+ /** Byte offset at which to insert an attribute (just before `>` or `/>`). */
21
+ insertAt: number;
22
+ /** Byte offset of the closing `>` itself. */
23
+ gtOffset: number;
24
+ /** Whether the tag is self-closing (`<img ... />`). */
25
+ selfClosing: boolean;
26
+ }
27
+ /**
28
+ * Given the byte offset of an opening tag's `<`, find where its opening tag
29
+ * ends. Scans forward to the `>` that is not inside a quoted attribute value or
30
+ * a `{...}` expression (Astro attributes can hold JS expressions and template
31
+ * literals that legitimately contain `>`), so we don't stop early.
32
+ *
33
+ * Returns null if `startOffset` isn't a `<` or no closing `>` is found.
34
+ */
35
+ export declare function findOpenTagEnd(buf: Buffer, startOffset: number): OpenTagEnd | null;
36
+ /**
37
+ * Insert `attribute` (e.g. ` data-caret="pages::home::headline"`, including its
38
+ * leading space) at the given byte offset, returning a new buffer. Pure
39
+ * insertion: removing the inserted bytes yields the original exactly, which is
40
+ * the reversibility invariant the writer relies on for backups/undo.
41
+ */
42
+ export declare function spliceAttribute(buf: Buffer, insertAt: number, attribute: string): Buffer;
43
+ //# sourceMappingURL=splice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splice.d.ts","sourceRoot":"","sources":["../src/splice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAaH,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,GAClB,UAAU,GAAG,IAAI,CA0CnB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,CAOR"}
package/dist/splice.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The byte-correct splice core for caretize.
3
+ *
4
+ * Why this file exists at all: `@astrojs/compiler` reports node positions as
5
+ * **0-based UTF-8 byte offsets** (see `Point.offset` in its AST types), but
6
+ * JavaScript string indexing counts UTF-16 code units. The two diverge the
7
+ * moment a file contains a multibyte character (em-dash, curly quote,
8
+ * box-drawing glyph, emoji) before an element. Splicing at a byte offset into a
9
+ * JS string therefore lands in the wrong place and silently corrupts the tag.
10
+ *
11
+ * The rule, proven by spike against the example corpus: do ALL offset
12
+ * arithmetic and splicing on a UTF-8 `Buffer`, never on a string. Everything in
13
+ * this module takes and returns `Buffer`s for that reason.
14
+ *
15
+ * We use the compiler AST for DETECTION only. Writing is pure insertion of an
16
+ * attribute just before the `>` of an opening tag — never AST reprinting — so
17
+ * the user's formatting, comments, and whitespace are preserved byte-for-byte.
18
+ */
19
+ // ASCII byte constants (all the structural characters we scan for are ASCII,
20
+ // so single-byte comparisons on the UTF-8 buffer are safe).
21
+ const LT = 0x3c; // <
22
+ const GT = 0x3e; // >
23
+ const SLASH = 0x2f; // /
24
+ const LBRACE = 0x7b; // {
25
+ const RBRACE = 0x7d; // }
26
+ const DQUOTE = 0x22; // "
27
+ const SQUOTE = 0x27; // '
28
+ const BACKTICK = 0x60; // `
29
+ /**
30
+ * Given the byte offset of an opening tag's `<`, find where its opening tag
31
+ * ends. Scans forward to the `>` that is not inside a quoted attribute value or
32
+ * a `{...}` expression (Astro attributes can hold JS expressions and template
33
+ * literals that legitimately contain `>`), so we don't stop early.
34
+ *
35
+ * Returns null if `startOffset` isn't a `<` or no closing `>` is found.
36
+ */
37
+ export function findOpenTagEnd(buf, startOffset) {
38
+ let i = startOffset;
39
+ if (buf[i] !== LT)
40
+ return null;
41
+ i++;
42
+ let braceDepth = 0;
43
+ while (i < buf.length) {
44
+ const b = buf[i];
45
+ // Skip quoted attribute values (only when not inside an expression).
46
+ if (braceDepth === 0 && (b === DQUOTE || b === SQUOTE || b === BACKTICK)) {
47
+ const quote = b;
48
+ i++;
49
+ while (i < buf.length && buf[i] !== quote)
50
+ i++;
51
+ i++; // step past the closing quote
52
+ continue;
53
+ }
54
+ if (b === LBRACE) {
55
+ braceDepth++;
56
+ i++;
57
+ continue;
58
+ }
59
+ if (b === RBRACE) {
60
+ braceDepth = Math.max(0, braceDepth - 1);
61
+ i++;
62
+ continue;
63
+ }
64
+ if (braceDepth === 0 && b === GT) {
65
+ const selfClosing = buf[i - 1] === SLASH;
66
+ return {
67
+ insertAt: selfClosing ? i - 1 : i,
68
+ gtOffset: i,
69
+ selfClosing,
70
+ };
71
+ }
72
+ i++;
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Insert `attribute` (e.g. ` data-caret="pages::home::headline"`, including its
78
+ * leading space) at the given byte offset, returning a new buffer. Pure
79
+ * insertion: removing the inserted bytes yields the original exactly, which is
80
+ * the reversibility invariant the writer relies on for backups/undo.
81
+ */
82
+ export function spliceAttribute(buf, insertAt, attribute) {
83
+ const attrBuf = Buffer.from(attribute, "utf8");
84
+ return Buffer.concat([
85
+ buf.subarray(0, insertAt),
86
+ attrBuf,
87
+ buf.subarray(insertAt),
88
+ ]);
89
+ }
90
+ //# sourceMappingURL=splice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splice.js","sourceRoot":"","sources":["../src/splice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,6EAA6E;AAC7E,4DAA4D;AAC5D,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI;AACrB,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI;AACrB,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,IAAI;AACxB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI;AACzB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI;AACzB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI;AACzB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI;AACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,IAAI;AAW3B;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,GAAW,EACX,WAAmB;IAEnB,IAAI,CAAC,GAAG,WAAW,CAAC;IACpB,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC/B,CAAC,EAAE,CAAC;IAEJ,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAEjB,qEAAqE;QACrE,IAAI,UAAU,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;YACzE,MAAM,KAAK,GAAG,CAAC,CAAC;YAChB,CAAC,EAAE,CAAC;YACJ,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK;gBAAE,CAAC,EAAE,CAAC;YAC/C,CAAC,EAAE,CAAC,CAAC,8BAA8B;YACnC,SAAS;QACX,CAAC;QAED,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC;YACjB,UAAU,EAAE,CAAC;YACb,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC;YACjB,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;YACzC,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QAED,IAAI,UAAU,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC;YACzC,OAAO;gBACL,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjC,QAAQ,EAAE,CAAC;gBACX,WAAW;aACZ,CAAC;QACJ,CAAC;QAED,CAAC,EAAE,CAAC;IACN,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAW,EACX,QAAgB,EAChB,SAAiB;IAEjB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC;QACzB,OAAO;QACP,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;KACvB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Usage safety classifier — the single safety primitive both wrap tiers depend on.
3
+ *
4
+ * `editable()` stega-encodes EVERY string leaf of the value it wraps (see
5
+ * `@caretcms/core`'s encodeLeaves). An encoded string is invisible and harmless
6
+ * when it renders as visible TEXT, but it CORRUPTS anything it lands in as an
7
+ * attribute — a broken href/src/class/id/datetime. So a literal const is only
8
+ * safe to wrap when every field that flows out of it reaches the DOM as text and
9
+ * never as a native-element attribute.
10
+ *
11
+ * This module answers exactly that, from the `@astrojs/compiler` AST:
12
+ * - native element (`type:"element"`) + a DYNAMIC attribute that references a
13
+ * field of the value → UNSAFE.
14
+ * - native element text position (`{item.title}`) → safe.
15
+ * - component (`type:"component"`) attribute that references a field → a prop
16
+ * hand-off; reported so a cross-file pass can verify the child (Tier-2),
17
+ * and — depending on `componentHandoffUnsafe` — either tolerated (Tier-1,
18
+ * preserving prior behavior) or treated as unprovable/UNSAFE (Tier-2 child).
19
+ *
20
+ * The bias is conservative throughout: anything we cannot positively prove is
21
+ * text-only is reported UNSAFE. False negatives (a safe const left unwrapped)
22
+ * are acceptable; a false positive (encoding into an attribute) is not. In line
23
+ * with that, the loop-variable scan over-approximates — within an expression
24
+ * that mentions the const, EVERY `.map()`/`.flatMap()` binding is treated as
25
+ * carrying a field of it, so nested and chained loops can never smuggle a field
26
+ * into an attribute unnoticed.
27
+ */
28
+ import type { AstroNode } from "./parse.js";
29
+ export interface Handoff {
30
+ /** Component the value (or a field of it) is passed to. */
31
+ component: string;
32
+ /** Prop name it is passed as. */
33
+ prop: string;
34
+ }
35
+ export interface UsageVerdict {
36
+ safe: boolean;
37
+ /** Populated when `safe` is false. */
38
+ reason?: string;
39
+ /** Component prop hand-offs of this const, for an optional cross-file check. */
40
+ handoffs: Handoff[];
41
+ }
42
+ export interface ClassifyOptions {
43
+ /**
44
+ * When true, a field flowing into a component prop counts as UNSAFE (we can't
45
+ * prove how the component renders it). Tier-2's child analysis sets this so a
46
+ * re-pass to a grandchild is conservatively skipped. Tier-1 leaves it false to
47
+ * preserve its established "component prop ≈ text" behavior.
48
+ */
49
+ componentHandoffUnsafe?: boolean;
50
+ /**
51
+ * When true, the `set:html` / `set:text` directives count as TEXT sinks, not
52
+ * native attributes. They render the value as element CONTENT, where stega
53
+ * survives harmlessly — so a field reaching them is safe to encode. Off by
54
+ * default (the wrap tiers stay conservative); the prop-hoist child check sets
55
+ * it so a `<p set:html={prop} />` rich field can be hoisted.
56
+ */
57
+ htmlDirectivesSafe?: boolean;
58
+ }
59
+ /**
60
+ * Loop-binding identifiers introduced by `.map()`/`.flatMap()` callbacks in a JS
61
+ * fragment. With `receiver`, only loops over that exact variable are read; with
62
+ * no receiver, EVERY map/flatMap is read (the conservative scan used during
63
+ * classification, so chained/nested loops can't hide a field).
64
+ */
65
+ export declare function mapParamIdents(js: string, receiver?: string): string[];
66
+ /**
67
+ * Classify how a literal const `varName` is consumed in a parsed template.
68
+ *
69
+ * For every tag node, the field-carrying identifiers in scope are `varName`
70
+ * itself plus the binding variables of any ancestor expression that mentions
71
+ * `varName` and loops. A dynamic attribute referencing one of those names is the
72
+ * encoded value escaping into a sink: a native element → UNSAFE; a component → a
73
+ * recorded prop hand-off (and UNSAFE too under `componentHandoffUnsafe`). Text
74
+ * positions (`{item.x}` as an element's child) are never attributes, so they
75
+ * never trip this — exactly the leaves `editable()` can safely encode.
76
+ */
77
+ export declare function classifyConstUsage(root: AstroNode, varName: string, opts?: ClassifyOptions): UsageVerdict;
78
+ /**
79
+ * Does the template render any of `names` in a BODY-reachable position — a text
80
+ * expression (`{title}`) or a `set:html`/`set:text` content sink — that is NOT
81
+ * inside the document `<head>`?
82
+ *
83
+ * `classifyConstUsage` proves a field never lands in a corrupting attribute, but
84
+ * a field that renders ONLY into `<head>` (a layout's `<title>{title}</title>`)
85
+ * is text-safe yet has no click-to-edit target. The prop-hoist tier requires a
86
+ * positive body sink so it doesn't mint `editable()` bindings the editor can't
87
+ * surface (and, incidentally, won't hoist a prop the child never renders at all).
88
+ */
89
+ export declare function rendersOutsideHead(root: AstroNode, names: string[]): boolean;
90
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../src/usage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,YAAY,CAAC;AAGrD,MAAM,WAAW,OAAO;IACtB,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AA8ED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAatE;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,eAAoB,GACzB,YAAY,CAqCd;AAYD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CA4B5E"}