@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/detect.js ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Detection: decide which tag nodes are edit candidates.
3
+ *
4
+ * The guiding rule — proven necessary by real-world fixtures — is that the
5
+ * candidate set must equal *what the CaretCMS rewrite engine can actually
6
+ * round-trip*. The engine only swaps the text content of a pure text-leaf
7
+ * element (no child markup, unless `data-caret-rich`) and the `src` of an
8
+ * `<img>`. So we tag exactly those and nothing else; anything we can't faithfully
9
+ * edit is skipped (with a reason) rather than tagged into an inert binding.
10
+ */
11
+ import { isTagNode } from "./parse.js";
12
+ // Content tags we'll tag, with their confidence tier. Anything not listed is
13
+ // treated as non-content and skipped (whitelist, not blacklist).
14
+ //
15
+ // The key set MUST equal core's REWRITABLE_TEXT_TAGS (runtime/rewrite.ts):
16
+ // the rewrite engine only injects stored values into those tags, so tagging
17
+ // anything else mints a binding that saves through the editor but never
18
+ // renders to visitors. Held in lockstep by tests/unit/contracts-parity.test.ts.
19
+ export const CONFIDENCE = {
20
+ h1: "high", h2: "high", h3: "high", h4: "high", h5: "high", h6: "high",
21
+ p: "high", blockquote: "high",
22
+ li: "medium", a: "medium", button: "medium", figcaption: "medium",
23
+ summary: "medium", caption: "medium", dt: "medium", dd: "medium",
24
+ th: "medium", td: "medium", label: "medium", legend: "medium",
25
+ span: "low", strong: "low", em: "low", small: "low",
26
+ };
27
+ /** Can the rewrite engine swap this tag's text content? Gate for every tier
28
+ * that emits a text binding (tag pass via CONFIDENCE, bind tiers directly). */
29
+ export function isRewritableTextTag(tag) {
30
+ return tag.toLowerCase() in CONFIDENCE;
31
+ }
32
+ // Always requires the method call (`.map(`); also captures the immediate
33
+ // receiver identifier when it's a plain variable (group 1), so callers can tell
34
+ // whether the loop iterates a name a wrap tier already made editable. Receiver
35
+ // is optional in the capture so chained/expression receivers (`foo().map(`)
36
+ // still flag — they just carry no receiver to match against. Group 2 = method.
37
+ const ITERATOR_RE = /(?:([A-Za-z_$][\w$]*)\s*)?\.\s*(map|filter|forEach|flatMap|reduce)\s*\(/;
38
+ // Inline formatting tags the rich-text sanitizer keeps. MUST match
39
+ // RICH_ALLOWED_TAGS in core's runtime/rich-allowlist.ts (and the hand-mirrored
40
+ // static/cms/editor/sanitize.js) — anything outside this set is unwrapped on
41
+ // save, so promoting it would not round-trip. Held in lockstep by
42
+ // tests/unit/contracts-parity.test.ts.
43
+ export const RICH_INLINE_TAGS = new Set([
44
+ "b", "strong", "i", "em", "u", "s", "a", "br", "sub", "sup",
45
+ ]);
46
+ // Attributes the sanitizer keeps. Everything else (notably `class`) is stripped
47
+ // on save unless blessed via the runtime `allowedClasses` option — which the
48
+ // CLI can't see — so caretize stays conservative and treats any attribute here
49
+ // other than these as lossy. MUST match core's RICH_ALLOWED_ATTRS/SAFE_HREF_RE
50
+ // (same parity test).
51
+ export const RICH_SAFE_ATTRS = {
52
+ a: new Set(["href", "target", "rel"]),
53
+ };
54
+ export const SAFE_HREF_RE = /^(?:https?:|mailto:|tel:|\/)/i;
55
+ /** Are this element's own attributes within the sanitizer's keep set? */
56
+ function inlineAttrsSafe(node) {
57
+ const allowed = RICH_SAFE_ATTRS[node.name.toLowerCase()];
58
+ for (const a of node.attributes ?? []) {
59
+ if (!allowed || !allowed.has(a.name))
60
+ return false;
61
+ if (a.name === "href" && !SAFE_HREF_RE.test(a.value ?? ""))
62
+ return false;
63
+ }
64
+ return true;
65
+ }
66
+ /**
67
+ * Classify a mixed-content element's subtree for rich promotion. Walks every
68
+ * descendant: a non-inline tag / component / expression makes it "not-inline";
69
+ * an inline tag carrying a stripped attribute makes it "rich-unsafe-attrs";
70
+ * otherwise "rich-safe". `attrsClean` threads the downgrade through recursion.
71
+ */
72
+ function classifyRichShape(node) {
73
+ let attrsClean = true;
74
+ const visit = (n) => {
75
+ for (const child of n.children ?? []) {
76
+ if (child.type === "text")
77
+ continue;
78
+ if (child.type === "expression")
79
+ return "not-inline";
80
+ if (isTagNode(child)) {
81
+ if (child.type !== "element")
82
+ return "not-inline"; // component/fragment
83
+ if (!RICH_INLINE_TAGS.has(child.name.toLowerCase()))
84
+ return "not-inline";
85
+ if (!inlineAttrsSafe(child))
86
+ attrsClean = false;
87
+ const nested = visit(child);
88
+ if (nested === "not-inline")
89
+ return "not-inline";
90
+ }
91
+ // comments/doctype are inert — ignore
92
+ }
93
+ return null;
94
+ };
95
+ if (visit(node) === "not-inline")
96
+ return "not-inline";
97
+ return attrsClean ? "rich-safe" : "rich-unsafe-attrs";
98
+ }
99
+ function attr(node, name) {
100
+ return node.attributes.find((a) => a.name === name)?.value;
101
+ }
102
+ function hasCaretAttr(node) {
103
+ return node.attributes.some((a) => a.name === "data-caret" || a.name === "data-caret-scope");
104
+ }
105
+ /** Direct text content of an element (concatenated text-node children). */
106
+ function directText(node) {
107
+ const parts = [];
108
+ for (const child of node.children ?? []) {
109
+ if (child.type === "text")
110
+ parts.push(child.value ?? "");
111
+ }
112
+ return parts.join("");
113
+ }
114
+ /** Classify the children of a (non-image) element. */
115
+ function childShape(node) {
116
+ let sawText = false;
117
+ for (const child of node.children ?? []) {
118
+ if (child.type === "text") {
119
+ if ((child.value ?? "").trim() !== "")
120
+ sawText = true;
121
+ continue;
122
+ }
123
+ if (child.type === "expression")
124
+ return "has-expression";
125
+ if (isTagNode(child))
126
+ return "has-element";
127
+ // comments/doctype/etc. are inert — ignore
128
+ }
129
+ return sawText ? "pure-text" : "empty";
130
+ }
131
+ function nearestExpressionAncestor(ancestors) {
132
+ for (let i = ancestors.length - 1; i >= 0; i--) {
133
+ if (ancestors[i].type === "expression")
134
+ return ancestors[i];
135
+ }
136
+ return undefined;
137
+ }
138
+ /** Concatenated text of an expression node's direct text children (the JS). */
139
+ function expressionText(expr) {
140
+ const parts = [];
141
+ for (const child of expr.children ?? []) {
142
+ if (child.type === "text")
143
+ parts.push(child.value ?? "");
144
+ }
145
+ return parts.join("");
146
+ }
147
+ /**
148
+ * Walk a parsed root and classify every tag node.
149
+ *
150
+ * `walk` is injected (the caller passes `walkTags`) so this module stays a pure
151
+ * classifier with no parse dependency beyond the node shape.
152
+ */
153
+ export function detect(root, walk, options = {}) {
154
+ const candidates = [];
155
+ const skipped = [];
156
+ const flagsByOffset = new Map();
157
+ // Nodes promoted to data-caret-rich. Their descendants must NOT be tagged
158
+ // separately (the rich field owns the whole subtree); walkTags is preorder,
159
+ // so a host is recorded before its descendants are visited.
160
+ const richHosts = new Set();
161
+ walk(root, (node, ancestors) => {
162
+ const tag = node.name;
163
+ const startOffset = node.position?.start.offset ?? -1;
164
+ const skip = (reason) => {
165
+ skipped.push({ decision: "skip", node, tag, startOffset, reason });
166
+ };
167
+ if (hasCaretAttr(node))
168
+ return skip("already-tagged");
169
+ // Inside a node we already promoted to rich → the rich field owns it.
170
+ if (ancestors.some((a) => richHosts.has(a)))
171
+ return skip("inside-rich");
172
+ if (node.type !== "element")
173
+ return skip("component"); // component/custom-element/fragment
174
+ // Dynamic context: inside any {expression}. Record an iterator flag if the
175
+ // enclosing expression looks like a .map()/.filter() loop.
176
+ const expr = nearestExpressionAncestor(ancestors);
177
+ if (expr) {
178
+ const text = expressionText(expr);
179
+ const m = ITERATOR_RE.exec(text);
180
+ const exprOffset = expr.position?.start.offset ?? startOffset;
181
+ if (m && !flagsByOffset.has(exprOffset)) {
182
+ flagsByOffset.set(exprOffset, {
183
+ startOffset: exprOffset, method: m[2], receiver: m[1],
184
+ });
185
+ }
186
+ return skip(m ? "inside-iterator" : "inside-expression");
187
+ }
188
+ if (tag === "img") {
189
+ const src = attr(node, "src");
190
+ if (src === undefined || src.trim() === "")
191
+ return skip("empty");
192
+ candidates.push({
193
+ decision: "tag", kind: "image", node, tag, startOffset,
194
+ confidence: "high", text: src,
195
+ });
196
+ return;
197
+ }
198
+ if (!(tag in CONFIDENCE))
199
+ return skip("not-content");
200
+ const shape = childShape(node);
201
+ if (shape === "has-expression")
202
+ return skip("dynamic-content");
203
+ if (shape === "empty")
204
+ return skip("empty");
205
+ if (shape === "has-element") {
206
+ // Mixed content: a block/component child can't round-trip as a field, but
207
+ // a subtree of only sanitizer-safe inline markup can — as a rich field.
208
+ // Only promote genuine content hosts (block text / links), not the
209
+ // low-confidence generic inline containers (span/div/strong/em/…).
210
+ const richShape = classifyRichShape(node);
211
+ const promotable = CONFIDENCE[tag] !== "low";
212
+ if (richShape === "not-inline" || !promotable)
213
+ return skip("mixed-children");
214
+ if (richShape === "rich-unsafe-attrs")
215
+ return skip("rich-unsafe-attrs");
216
+ // rich-safe: promote only when opted in; otherwise report it as eligible.
217
+ if (!options.rich)
218
+ return skip("rich-eligible");
219
+ richHosts.add(node);
220
+ candidates.push({
221
+ decision: "tag", kind: "text", node, tag, startOffset,
222
+ confidence: "medium", text: directText(node).trim(), rich: true,
223
+ });
224
+ return;
225
+ }
226
+ candidates.push({
227
+ decision: "tag", kind: "text", node, tag, startOffset,
228
+ confidence: CONFIDENCE[tag], text: directText(node).trim(),
229
+ });
230
+ });
231
+ return {
232
+ candidates,
233
+ skipped,
234
+ flags: [...flagsByOffset.values()].sort((a, b) => a.startOffset - b.startOffset),
235
+ };
236
+ }
237
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Discovery: find the `.astro` files to consider, relative to the project root.
3
+ *
4
+ * Never descends into build/vendor/output dirs, and never touches symlinks that
5
+ * escape the project root (a safety invariant — caretize only edits files
6
+ * inside the project it was pointed at).
7
+ */
8
+ /**
9
+ * Walk `target` (a file or directory) under `rootDir`, returning project-root-
10
+ * relative POSIX-ish paths of `.astro` files, sorted for deterministic order.
11
+ */
12
+ export declare function discoverAstroFiles(rootDir: string, target?: string): string[];
13
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA2BH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CA8C7E"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Discovery: find the `.astro` files to consider, relative to the project root.
3
+ *
4
+ * Never descends into build/vendor/output dirs, and never touches symlinks that
5
+ * escape the project root (a safety invariant — caretize only edits files
6
+ * inside the project it was pointed at).
7
+ */
8
+ import { readdirSync, statSync, lstatSync, realpathSync } from "node:fs";
9
+ import { join, relative, resolve, sep } from "node:path";
10
+ const SKIP_DIRS = new Set([
11
+ "node_modules",
12
+ ".astro",
13
+ ".caret",
14
+ ".git",
15
+ "dist",
16
+ ".vercel",
17
+ ".netlify",
18
+ ".output",
19
+ ]);
20
+ function isSkippedFile(name) {
21
+ if (!name.endsWith(".astro"))
22
+ return true;
23
+ return /\.(test|spec)\.astro$/.test(name);
24
+ }
25
+ /** True if `child` resolves to a path inside `root` (no symlink escape). */
26
+ function isInside(root, child) {
27
+ const rel = relative(root, child);
28
+ return rel === "" || (!rel.startsWith("..") && !resolve(rel).startsWith(".."));
29
+ }
30
+ /**
31
+ * Walk `target` (a file or directory) under `rootDir`, returning project-root-
32
+ * relative POSIX-ish paths of `.astro` files, sorted for deterministic order.
33
+ */
34
+ export function discoverAstroFiles(rootDir, target) {
35
+ const root = resolve(rootDir);
36
+ const start = resolve(root, target ?? "src");
37
+ const found = [];
38
+ const visit = (abs) => {
39
+ let st;
40
+ try {
41
+ st = lstatSync(abs);
42
+ }
43
+ catch {
44
+ return;
45
+ }
46
+ // Reject symlinks that point outside the project root.
47
+ if (st.isSymbolicLink()) {
48
+ let realPath;
49
+ try {
50
+ realPath = realpathSync(abs);
51
+ }
52
+ catch {
53
+ return;
54
+ }
55
+ if (!isInside(root, realPath))
56
+ return;
57
+ st = statSync(abs);
58
+ }
59
+ if (st.isDirectory()) {
60
+ const base = abs.split(sep).pop() ?? "";
61
+ if (SKIP_DIRS.has(base))
62
+ return;
63
+ let entries;
64
+ try {
65
+ entries = readdirSync(abs);
66
+ }
67
+ catch {
68
+ return;
69
+ }
70
+ for (const name of entries.sort())
71
+ visit(join(abs, name));
72
+ return;
73
+ }
74
+ if (st.isFile()) {
75
+ const base = abs.split(sep).pop() ?? "";
76
+ if (isSkippedFile(base))
77
+ return;
78
+ found.push(relative(root, abs).split(sep).join("/"));
79
+ }
80
+ };
81
+ visit(start);
82
+ return found;
83
+ }
84
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Frontmatter parsing primitives shared by the wrap tiers (`wrap.ts`'s same-file
3
+ * loop detection and `props.ts`'s cross-file prop detection). Kept neutral so
4
+ * those two passes are siblings rather than one depending on the other.
5
+ */
6
+ /** Inner content range of the frontmatter fence, or null if there is none. */
7
+ export declare function frontmatterRange(source: string): {
8
+ start: number;
9
+ end: number;
10
+ } | null;
11
+ /** Names of frontmatter consts whose initializer is an array/object literal. */
12
+ export declare function literalConstNames(fmText: string): Set<string>;
13
+ export type ImportKind = "json" | "module";
14
+ /**
15
+ * Default-import bindings whose data could be wrapped with `editable()`, mapped
16
+ * to their specifier + kind. Only DEFAULT imports of a `.json` data file or a
17
+ * `.js`/`.ts` module are returned — named imports (`import { x }`), namespace
18
+ * imports (`import * as`), `import type`, and bare/package or extensionless
19
+ * specifiers are excluded, because their value shape can't be assumed safe to
20
+ * stega-encode. `import faqs from "./data/faqs.json"` → `faqs → {json}`.
21
+ */
22
+ export declare function importBindingNames(fmText: string): Map<string, {
23
+ specifier: string;
24
+ importKind: ImportKind;
25
+ }>;
26
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8EAA8E;AAC9E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAOtF;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAM7D;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE3C;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GACb,GAAG,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,UAAU,CAAA;CAAE,CAAC,CAa5D"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Frontmatter parsing primitives shared by the wrap tiers (`wrap.ts`'s same-file
3
+ * loop detection and `props.ts`'s cross-file prop detection). Kept neutral so
4
+ * those two passes are siblings rather than one depending on the other.
5
+ */
6
+ /** Inner content range of the frontmatter fence, or null if there is none. */
7
+ export function frontmatterRange(source) {
8
+ if (!source.startsWith("---"))
9
+ return null;
10
+ const firstNL = source.indexOf("\n");
11
+ if (firstNL < 0)
12
+ return null;
13
+ const close = source.indexOf("\n---", firstNL);
14
+ if (close < 0)
15
+ return null;
16
+ return { start: firstNL + 1, end: close };
17
+ }
18
+ /** Names of frontmatter consts whose initializer is an array/object literal. */
19
+ export function literalConstNames(fmText) {
20
+ const set = new Set();
21
+ const re = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([[{])/g;
22
+ let m;
23
+ while ((m = re.exec(fmText)))
24
+ set.add(m[1]);
25
+ return set;
26
+ }
27
+ /**
28
+ * Default-import bindings whose data could be wrapped with `editable()`, mapped
29
+ * to their specifier + kind. Only DEFAULT imports of a `.json` data file or a
30
+ * `.js`/`.ts` module are returned — named imports (`import { x }`), namespace
31
+ * imports (`import * as`), `import type`, and bare/package or extensionless
32
+ * specifiers are excluded, because their value shape can't be assumed safe to
33
+ * stega-encode. `import faqs from "./data/faqs.json"` → `faqs → {json}`.
34
+ */
35
+ export function importBindingNames(fmText) {
36
+ const out = new Map();
37
+ const re = /\bimport\s+(?!type\b)([A-Za-z_$][\w$]*)\s+from\s*['"]([^'"]+)['"]/g;
38
+ let m;
39
+ while ((m = re.exec(fmText))) {
40
+ const [, name, spec] = m;
41
+ let importKind;
42
+ if (/\.json$/.test(spec))
43
+ importKind = "json";
44
+ else if (/\.(?:js|ts|mjs|cjs|mts|cts)$/.test(spec))
45
+ importKind = "module";
46
+ else
47
+ continue; // bare/package or extensionless — shape unknown, leave alone
48
+ out.set(name, { specifier: spec, importKind });
49
+ }
50
+ return out;
51
+ }
52
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8EAA8E;AAC9E,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3B,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,MAAM,EAAE,GAAG,wDAAwD,CAAC;IACpE,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,OAAO,GAAG,CAAC;AACb,CAAC;AAID;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc;IAEd,MAAM,GAAG,GAAG,IAAI,GAAG,EAAyD,CAAC;IAC7E,MAAM,EAAE,GAAG,oEAAoE,CAAC;IAChF,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,UAAsB,CAAC;QAC3B,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,UAAU,GAAG,MAAM,CAAC;aACzC,IAAI,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,UAAU,GAAG,QAAQ,CAAC;;YACrE,SAAS,CAAC,6DAA6D;QAC5E,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared JS-identifier primitives used across the wrap/usage/resolve passes.
3
+ *
4
+ * Centralised so "what is an identifier" and "how do we match a reference to
5
+ * one" have a single definition — the matcher in particular is safety-relevant
6
+ * (a missed reference can let `editable()` encode into an attribute), so it must
7
+ * not drift between callers.
8
+ */
9
+ /** A valid JS identifier (ASCII letters/digits plus `_` and `$`). */
10
+ export declare const IDENT: RegExp;
11
+ export declare function isIdentifier(s: string): boolean;
12
+ /** Escape a string for safe literal use inside a `RegExp`. */
13
+ export declare function escapeRe(s: string): string;
14
+ /**
15
+ * A regex matching a whole-identifier reference to `name` in JS source.
16
+ *
17
+ * Uses `$`/`_`-aware lookaround boundaries rather than `\b`: `\b` sits between a
18
+ * word and a non-word char, so `\b$item\b` does NOT match `$item` (the `$` is a
19
+ * non-word char with no word char before it). Lookaround treats `$` and `_` as
20
+ * identifier characters, so `$item` and `item$` are bounded correctly while a
21
+ * substring like `item` inside `items` is still rejected.
22
+ */
23
+ export declare function identRefRe(name: string, flags?: string): RegExp;
24
+ /** True when `js` references any of the given identifiers as a whole word. */
25
+ export declare function referencesIdent(js: string, names: readonly string[]): boolean;
26
+ //# sourceMappingURL=identifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.d.ts","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qEAAqE;AACrE,eAAO,MAAM,KAAK,QAAuB,CAAC;AAE1C,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED,8DAA8D;AAC9D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,MAAM,CAE3D;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE7E"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Shared JS-identifier primitives used across the wrap/usage/resolve passes.
3
+ *
4
+ * Centralised so "what is an identifier" and "how do we match a reference to
5
+ * one" have a single definition — the matcher in particular is safety-relevant
6
+ * (a missed reference can let `editable()` encode into an attribute), so it must
7
+ * not drift between callers.
8
+ */
9
+ /** A valid JS identifier (ASCII letters/digits plus `_` and `$`). */
10
+ export const IDENT = /^[A-Za-z_$][\w$]*$/;
11
+ export function isIdentifier(s) {
12
+ return IDENT.test(s);
13
+ }
14
+ /** Escape a string for safe literal use inside a `RegExp`. */
15
+ export function escapeRe(s) {
16
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17
+ }
18
+ /**
19
+ * A regex matching a whole-identifier reference to `name` in JS source.
20
+ *
21
+ * Uses `$`/`_`-aware lookaround boundaries rather than `\b`: `\b` sits between a
22
+ * word and a non-word char, so `\b$item\b` does NOT match `$item` (the `$` is a
23
+ * non-word char with no word char before it). Lookaround treats `$` and `_` as
24
+ * identifier characters, so `$item` and `item$` are bounded correctly while a
25
+ * substring like `item` inside `items` is still rejected.
26
+ */
27
+ export function identRefRe(name, flags = "") {
28
+ return new RegExp(`(?<![\\w$])${escapeRe(name)}(?![\\w$])`, flags);
29
+ }
30
+ /** True when `js` references any of the given identifiers as a whole word. */
31
+ export function referencesIdent(js, names) {
32
+ return names.some((n) => identRefRe(n).test(js));
33
+ }
34
+ //# sourceMappingURL=identifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifiers.js","sourceRoot":"","sources":["../src/identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,KAAK,GAAG,oBAAoB,CAAC;AAE1C,MAAM,UAAU,YAAY,CAAC,CAAS;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,KAAK,GAAG,EAAE;IACjD,OAAO,IAAI,MAAM,CAAC,cAAc,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,KAAwB;IAClE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Tier-3 editable() wrapping for IMPORTED data — PURE INSERTION.
3
+ *
4
+ * A very common Astro pattern keeps list content in a data file and renders it
5
+ * with `.map()`:
6
+ *
7
+ * import faqs from "./data/faqs.json";
8
+ * …
9
+ * {faqs.map((f) => <details>{f.question}…</details>)}
10
+ *
11
+ * caretize's Tier-1 wrap only sees inline-literal consts (`const x = [...]`), so
12
+ * this content was previously left un-editable. Tier-3 makes it editable WITHOUT
13
+ * moving any code: it suffixes the import binding and rebinds the original name
14
+ * to an `editable()` wrap of it —
15
+ *
16
+ * import faqsRaw from "./data/faqs.json";
17
+ * const faqs = await editable("pages::home::faqs", faqsRaw);
18
+ *
19
+ * Every original character survives in order (the import binding only GAINS a
20
+ * suffix; a new const line is inserted), so the result is a provable pure
21
+ * insertion — the same subsequence safety contract as Tier-1. The `.map()`
22
+ * consumer is untouched and now reads the stega-encoded, click-to-edit value.
23
+ */
24
+ import { type AstroNode } from "./parse.js";
25
+ import { type ImportKind } from "./frontmatter.js";
26
+ import { type WrapResult, type WrapTarget } from "./wrap.js";
27
+ /** An import binding the wrapper could target, before safety is verified. */
28
+ export interface ImportWrapCandidate {
29
+ /** The original local binding, e.g. `faqs`. */
30
+ varName: string;
31
+ /** Binding key, e.g. `pages::home::faqs`. */
32
+ key: string;
33
+ /** The import specifier, e.g. `./data/faqs.json` (for display). */
34
+ specifier: string;
35
+ /** Whether the source is a `.json` data file or a `.js`/`.ts` module. */
36
+ importKind: ImportKind;
37
+ }
38
+ /**
39
+ * Rebind an imported `varName` to `await editable("<key>", <varName>Raw)` and
40
+ * ensure the import is present. Pure insertion: the import binding gains a
41
+ * suffix and a new const line is inserted; nothing is deleted.
42
+ */
43
+ export declare function wrapImport(source: string, varName: string, key: string): WrapResult;
44
+ /**
45
+ * Detect Tier-3 candidates: DEFAULT imports of a `.json`/module data file that
46
+ * are `.map()`/`.flatMap()`'d in the template. Key is derived from the file's
47
+ * scope (`collection::id`) plus the binding name as the field.
48
+ */
49
+ export declare function detectImportWrapCandidates(source: string, relPath: string): ImportWrapCandidate[];
50
+ /**
51
+ * Safety-checked Tier-3 detection: import-backed candidates minus any whose
52
+ * mapped fields flow into a native-element attribute (where `editable()`'s stega
53
+ * encoding would corrupt an href/src/class/…). Mirrors `detectWrapTargetsSafe`.
54
+ */
55
+ export declare function detectImportWrapTargetsSafe(source: string, relPath: string, root?: AstroNode): Promise<WrapTarget[]>;
56
+ //# sourceMappingURL=import-wrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-wrap.d.ts","sourceRoot":"","sources":["../src/import-wrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAA0B,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAErF,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,yEAAyE;IACzE,UAAU,EAAE,UAAU,CAAC;CACxB;AAWD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAuDnF;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,mBAAmB,EAAE,CAuCvB;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,SAAS,GACf,OAAO,CAAC,UAAU,EAAE,CAAC,CAOvB"}