@habemus-papadum/aiui-source-processor 0.0.0-reserve.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +15 -1
- package/dist/index.d.ts +60 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +1 -0
- package/dist/source-locator.d.ts +180 -0
- package/package.json +40 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nehal Patel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
1
|
# @habemus-papadum/aiui-source-processor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The aiui source processor: the compile-time Babel pass that injects factory identity (name/loc/description for cell/control/action) and dev-only JSX source-location stamps, plus its Vite plugin. One transform, serve and build.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @habemus-papadum/aiui-source-processor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { greet } from "@habemus-papadum/aiui-source-processor";
|
|
15
|
+
|
|
16
|
+
greet("world"); // "Hello, world!"
|
|
17
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* index.ts — THE aiui Vite plugin (`@habemus-papadum/aiui-source-processor`),
|
|
3
|
+
* home of the whole build-time integration. Extracted into its own package
|
|
4
|
+
* (from `aiui-viz/vite`, itself moved from the dev overlay in the 2026-07-14
|
|
5
|
+
* restructure) so the source transform is a standalone, testable library. One
|
|
6
|
+
* plugin, two jobs — and deliberately nothing else:
|
|
7
|
+
*
|
|
8
|
+
* 1. **The source-locator compiler pass** (./source-locator). It applies to
|
|
9
|
+
* serve AND build: factory identity
|
|
10
|
+
* injection is load-bearing (durable cells need their `{name, loc}`
|
|
11
|
+
* identity in production), so a build that violates the pass's
|
|
12
|
+
* expectations FAILS in prod exactly as it would in dev. What is dev-only
|
|
13
|
+
* is the EMISSION of instrumentation: the `data-source-loc` DOM stamps
|
|
14
|
+
* default to `command === "serve"` (owner, 2026-07-14 — production
|
|
15
|
+
* bundles ship clean of machine paths; pass `stampJsx: true` to keep them
|
|
16
|
+
* deliberately).
|
|
17
|
+
* 2. **The dev-only `sourceRoot` seed**: a tiny HTML script setting
|
|
18
|
+
* `window.__AIUI__.sourceRoot` so the locator's relative stamps can be
|
|
19
|
+
* absolutized into the paths a prompt carries. Build-time knowledge,
|
|
20
|
+
* dev-only by the same rule as the stamps it serves.
|
|
21
|
+
*
|
|
22
|
+
* What this plugin deliberately does NOT do (the old overlay plugin's magic,
|
|
23
|
+
* retired): no channel-port injection, no page-side `/tools` dialing, no
|
|
24
|
+
* session bus, no overlay UI mounting. The `window.__AIUI__` global itself is
|
|
25
|
+
* the RUNTIME's job now (see ./aiui-global — it exists in production too),
|
|
26
|
+
* and channel connectivity arrives from OUTSIDE, via the intent client (the
|
|
27
|
+
* Chrome extension or the CDP tier), never from the app.
|
|
28
|
+
*/
|
|
29
|
+
import type { Plugin } from "vite";
|
|
30
|
+
import { type SourceLocatorViteOptions } from "./source-locator.ts";
|
|
31
|
+
export { cellFactory, defaultFactories, type FactorySpec, optionsFactory, type SourceLocatorOptions, type SourceLocatorViteOptions, sourceLocatorBabel, sourceLocatorVite, } from "./source-locator.ts";
|
|
32
|
+
export interface AiuiPluginOptions {
|
|
33
|
+
/**
|
|
34
|
+
* The locator pass's options (factories, stampJsx, include/exclude).
|
|
35
|
+
* `true`/omitted = defaults; `false` disables the pass entirely (rare —
|
|
36
|
+
* durable factory identity dies with it); `{ cellFactories: ["cell"] }` is
|
|
37
|
+
* back-compat sugar — names treated as cell-shaped factories.
|
|
38
|
+
*/
|
|
39
|
+
locator?: boolean | (SourceLocatorViteOptions & {
|
|
40
|
+
cellFactories?: string[];
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* The app's source root for absolutizing stamps (dev-only injection);
|
|
44
|
+
* defaults to the Vite root at config-resolve time.
|
|
45
|
+
*/
|
|
46
|
+
sourceRoot?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The aiui integration for an app's Vite config:
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* import aiui from "@habemus-papadum/aiui-source-processor";
|
|
53
|
+
* export default defineConfig({ plugins: [aiui(), solid()] });
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* Order matters: `aiui()` comes BEFORE the Solid plugin so the locator's
|
|
57
|
+
* `pre`-phase Babel pass sees the original JSX.
|
|
58
|
+
*/
|
|
59
|
+
export declare function aiui(options?: AiuiPluginOptions): Plugin[];
|
|
60
|
+
export default aiui;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
const P = "data-source-loc", E = "aiuiDevOverlay locator needs @babel/core — install it as a devDependency";
|
|
2
|
+
function x(t = "cell") {
|
|
3
|
+
return {
|
|
4
|
+
callee: t,
|
|
5
|
+
args: { min: 2, max: 3 },
|
|
6
|
+
optionsArg: 2,
|
|
7
|
+
inject: ["name", "loc", "description"],
|
|
8
|
+
namePolicy: "optional"
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function k(t) {
|
|
12
|
+
return {
|
|
13
|
+
callee: t,
|
|
14
|
+
args: { min: 1, max: 1 },
|
|
15
|
+
optionsArg: 0,
|
|
16
|
+
inject: ["name", "loc", "description"],
|
|
17
|
+
namePolicy: "required"
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function h() {
|
|
21
|
+
return [x(), k("control"), k("action")];
|
|
22
|
+
}
|
|
23
|
+
function $(t) {
|
|
24
|
+
return t.factories ? t.factories : t.cellFactories ? t.cellFactories.map((e) => x(e)) : h();
|
|
25
|
+
}
|
|
26
|
+
function I(t, e) {
|
|
27
|
+
if (t === "") return e;
|
|
28
|
+
if (e.startsWith(t)) return e.slice(t.length).replace(/^\//, "");
|
|
29
|
+
const n = t.replace(/\/+$/, "").split("/"), a = e.split("/");
|
|
30
|
+
let i = 0;
|
|
31
|
+
for (; i < n.length && i < a.length && n[i] === a[i]; ) i++;
|
|
32
|
+
return i === 0 ? e : [...Array(n.length - i).fill(".."), ...a.slice(i)].join("/");
|
|
33
|
+
}
|
|
34
|
+
const w = /^\s*(?:biome-ignore|eslint|@ts-|prettier-|@vitest-environment|<\/?aiui-scenery|@__PURE__|#__PURE__|v8 ignore|-{2,})/;
|
|
35
|
+
function _(t) {
|
|
36
|
+
var r, u, f;
|
|
37
|
+
if (!t || t.length === 0) return;
|
|
38
|
+
const e = t.filter((s) => !w.test(s.value));
|
|
39
|
+
if (e.length === 0) return;
|
|
40
|
+
const n = e[e.length - 1];
|
|
41
|
+
let a;
|
|
42
|
+
if (n.type === "CommentBlock")
|
|
43
|
+
a = n.value;
|
|
44
|
+
else {
|
|
45
|
+
const s = [n.value];
|
|
46
|
+
let m = (r = n.loc) == null ? void 0 : r.start.line;
|
|
47
|
+
for (let o = e.length - 2; o >= 0; o--) {
|
|
48
|
+
const d = e[o];
|
|
49
|
+
if (d.type !== "CommentLine" || m === void 0 || ((u = d.loc) == null ? void 0 : u.end.line) !== m - 1) break;
|
|
50
|
+
s.unshift(d.value), m = (f = d.loc) == null ? void 0 : f.start.line;
|
|
51
|
+
}
|
|
52
|
+
a = s.join(`
|
|
53
|
+
`);
|
|
54
|
+
}
|
|
55
|
+
const i = a.split(`
|
|
56
|
+
`).map((s) => s.replace(/^\s*\*+\s?/, "").trim()), l = i.findIndex((s) => s.startsWith("@")), c = (l === -1 ? i : i.slice(0, l)).join(" ").replace(/\s+/g, " ").trim();
|
|
57
|
+
return c.length > 0 ? c : void 0;
|
|
58
|
+
}
|
|
59
|
+
function A(t, e = {}) {
|
|
60
|
+
const n = t.types, a = e.root ?? "", i = e.locPrefix ?? "", l = e.stampJsx ?? !0, c = new Map($(e).map((r) => [r.callee, r]));
|
|
61
|
+
return {
|
|
62
|
+
name: "aiui-source-locator",
|
|
63
|
+
visitor: {
|
|
64
|
+
Program(r, u) {
|
|
65
|
+
const f = u.file.opts.filename ?? "", s = `${i}${I(a, f)}`, m = {
|
|
66
|
+
JSXOpeningElement(o) {
|
|
67
|
+
if (!l) return;
|
|
68
|
+
const d = o.node.name;
|
|
69
|
+
if (d.type !== "JSXIdentifier" || !/^[a-z]/.test(d.name) || !o.node.loc || o.node.attributes.some(
|
|
70
|
+
(g) => g.type === "JSXAttribute" && g.name.type === "JSXIdentifier" && g.name.name === P
|
|
71
|
+
)) return;
|
|
72
|
+
const { line: y, column: j } = o.node.loc.start;
|
|
73
|
+
o.node.attributes.push(
|
|
74
|
+
n.jsxAttribute(
|
|
75
|
+
n.jsxIdentifier(P),
|
|
76
|
+
n.stringLiteral(`${s}:${y}:${j + 1}`)
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
},
|
|
80
|
+
CallExpression(o) {
|
|
81
|
+
const d = o.node.callee;
|
|
82
|
+
if (d.type !== "Identifier") return;
|
|
83
|
+
const p = c.get(d.name);
|
|
84
|
+
if (!p) return;
|
|
85
|
+
const y = o.node.arguments;
|
|
86
|
+
y.length < p.args.min || y.length > p.args.max || o.node.loc && J(n, o, p, s);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
r.traverse(m);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function F(t) {
|
|
95
|
+
const e = /* @__PURE__ */ new Set();
|
|
96
|
+
for (const n of t.properties)
|
|
97
|
+
n.type === "ObjectProperty" && (n.key.type === "Identifier" ? e.add(n.key.name) : n.key.type === "StringLiteral" && e.add(n.key.value));
|
|
98
|
+
return e;
|
|
99
|
+
}
|
|
100
|
+
function R(t) {
|
|
101
|
+
for (const e of t.properties) {
|
|
102
|
+
if (e.type !== "ObjectProperty") continue;
|
|
103
|
+
if ((e.key.type === "Identifier" ? e.key.name : e.key.type === "StringLiteral" ? e.key.value : "") === "name") return e;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function S(t) {
|
|
107
|
+
var n, a, i, l, c;
|
|
108
|
+
const e = t.parent;
|
|
109
|
+
if (e.type === "VariableDeclarator" && e.id.type === "Identifier") {
|
|
110
|
+
const r = (n = t.parentPath) == null ? void 0 : n.parentPath, u = ((a = r == null ? void 0 : r.parentPath) == null ? void 0 : a.node.type) === "ExportNamedDeclaration" ? r.parentPath.node : void 0, f = ((i = e.leadingComments) != null && i.length ? e : void 0) ?? ((l = r == null ? void 0 : r.node.leadingComments) != null && l.length ? r.node : void 0) ?? u ?? (r == null ? void 0 : r.node);
|
|
111
|
+
return { name: e.id.name, commentNode: f };
|
|
112
|
+
}
|
|
113
|
+
if (e.type === "ObjectProperty" && e.key.type === "Identifier")
|
|
114
|
+
return { name: e.key.name, commentNode: e };
|
|
115
|
+
if (e.type === "AssignmentExpression" && e.left.type === "Identifier") {
|
|
116
|
+
const r = (c = t.parentPath) == null ? void 0 : c.node;
|
|
117
|
+
return { name: e.left.name, commentNode: r ?? e };
|
|
118
|
+
}
|
|
119
|
+
return e.type === "ExpressionStatement" ? { commentNode: e } : {};
|
|
120
|
+
}
|
|
121
|
+
function J(t, e, n, a) {
|
|
122
|
+
var m;
|
|
123
|
+
const i = e.node.arguments, l = i[n.optionsArg];
|
|
124
|
+
if (l && l.type !== "ObjectExpression") return;
|
|
125
|
+
const c = l, r = c ? F(c) : /* @__PURE__ */ new Set(), u = S(e);
|
|
126
|
+
let f;
|
|
127
|
+
if (n.inject.includes("name"))
|
|
128
|
+
if (c && r.has("name")) {
|
|
129
|
+
const o = R(c);
|
|
130
|
+
if (o && o.value.type !== "StringLiteral")
|
|
131
|
+
throw e.buildCodeFrameError(
|
|
132
|
+
`[aiui compiler] ${n.callee}() name must be a compile-time string literal — it is a durable key, a tool identity, and a grep target. Use a plain "…" string.`
|
|
133
|
+
);
|
|
134
|
+
} else if (u.name)
|
|
135
|
+
f = u.name;
|
|
136
|
+
else {
|
|
137
|
+
if (n.namePolicy === "required")
|
|
138
|
+
throw e.buildCodeFrameError(
|
|
139
|
+
`[aiui compiler] ${n.callee}(…) needs a name: assign it to a named binding (const kappa = ${n.callee}(…)) or pass { name: "…" }. The name is the durable key and the agent-tool identity, so it cannot be anonymous.`
|
|
140
|
+
);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const s = [];
|
|
144
|
+
if (f !== void 0 && !r.has("name") && s.push(t.objectProperty(t.identifier("name"), t.stringLiteral(f))), n.inject.includes("loc") && !r.has("loc") && e.node.loc) {
|
|
145
|
+
const o = `${a}:${e.node.loc.start.line}`;
|
|
146
|
+
s.push(t.objectProperty(t.identifier("loc"), t.stringLiteral(o)));
|
|
147
|
+
}
|
|
148
|
+
if (n.inject.includes("description") && !r.has("description")) {
|
|
149
|
+
const o = _((m = u.commentNode) == null ? void 0 : m.leadingComments);
|
|
150
|
+
o !== void 0 && s.push(t.objectProperty(t.identifier("description"), t.stringLiteral(o)));
|
|
151
|
+
}
|
|
152
|
+
s.length !== 0 && (c ? c.properties.push(...s) : n.optionsArg === i.length && i.push(t.objectExpression(s)));
|
|
153
|
+
}
|
|
154
|
+
function b(t, e) {
|
|
155
|
+
const n = [];
|
|
156
|
+
if (e && n.push("<[A-Za-z]"), t.length > 0) {
|
|
157
|
+
const a = t.map((i) => i.callee.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
158
|
+
n.push(`\\b(?:${a.join("|")})\\s*\\(`);
|
|
159
|
+
}
|
|
160
|
+
return n.length > 0 ? new RegExp(n.join("|")) : void 0;
|
|
161
|
+
}
|
|
162
|
+
function O(t = {}) {
|
|
163
|
+
let e = t.root ?? "", n = t.stampJsx ?? !0;
|
|
164
|
+
const a = $(t);
|
|
165
|
+
let i = b(a, n), l = b(a, !1);
|
|
166
|
+
const c = t.loadBabel ?? (() => import("@babel/core"));
|
|
167
|
+
let r, u;
|
|
168
|
+
const f = () => r ? Promise.resolve(r) : (u || (u = c().then(
|
|
169
|
+
(s) => (r = s, s),
|
|
170
|
+
() => {
|
|
171
|
+
throw u = void 0, new Error(E);
|
|
172
|
+
}
|
|
173
|
+
)), u);
|
|
174
|
+
return {
|
|
175
|
+
name: "aiui:source-locator",
|
|
176
|
+
enforce: "pre",
|
|
177
|
+
configResolved(s) {
|
|
178
|
+
t.root === void 0 && (e = s.root), t.stampJsx === void 0 && (n = s.command === "serve"), i = b(a, n), l = b(a, !1);
|
|
179
|
+
},
|
|
180
|
+
async buildStart() {
|
|
181
|
+
await f();
|
|
182
|
+
},
|
|
183
|
+
async transform(s, m) {
|
|
184
|
+
const o = m.replace(/\?.*$/, "");
|
|
185
|
+
if (!/\.[mc]?[tj]sx?$/.test(o) || o.includes("node_modules")) return null;
|
|
186
|
+
const d = e === "" || o.startsWith(e), p = d ? i : l;
|
|
187
|
+
if (!(p != null && p.test(s))) return null;
|
|
188
|
+
const { transformAsync: y } = await f(), g = /x$/.test(o) ? ["jsx", "typescript"] : ["typescript"], v = await y(s, {
|
|
189
|
+
filename: o,
|
|
190
|
+
parserOpts: { plugins: g },
|
|
191
|
+
plugins: [
|
|
192
|
+
[
|
|
193
|
+
A,
|
|
194
|
+
{
|
|
195
|
+
root: e,
|
|
196
|
+
factories: a,
|
|
197
|
+
stampJsx: n && d,
|
|
198
|
+
...t.locPrefix !== void 0 ? { locPrefix: t.locPrefix } : {}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
],
|
|
202
|
+
configFile: !1,
|
|
203
|
+
babelrc: !1,
|
|
204
|
+
sourceMaps: !0
|
|
205
|
+
});
|
|
206
|
+
return v != null && v.code ? { code: v.code, map: v.map } : null;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function C(t) {
|
|
211
|
+
let e = t;
|
|
212
|
+
return {
|
|
213
|
+
name: "aiui:source-root",
|
|
214
|
+
apply: "serve",
|
|
215
|
+
configResolved(n) {
|
|
216
|
+
e ?? (e = n.root);
|
|
217
|
+
},
|
|
218
|
+
transformIndexHtml() {
|
|
219
|
+
if (e !== void 0)
|
|
220
|
+
return [
|
|
221
|
+
{
|
|
222
|
+
tag: "script",
|
|
223
|
+
injectTo: "head-prepend",
|
|
224
|
+
children: `(window.__AIUI__ ??= { v: 1, frames: [] }).sourceRoot = ${JSON.stringify(e)};`
|
|
225
|
+
}
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function L(t = {}) {
|
|
231
|
+
const e = [];
|
|
232
|
+
if (t.locator !== !1) {
|
|
233
|
+
let n;
|
|
234
|
+
if (t.locator === void 0 || t.locator === !0)
|
|
235
|
+
n = { factories: h() };
|
|
236
|
+
else {
|
|
237
|
+
const { cellFactories: a, ...i } = t.locator;
|
|
238
|
+
n = {
|
|
239
|
+
...i,
|
|
240
|
+
factories: i.factories ?? (a !== void 0 ? a.map((l) => x(l)) : h())
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
e.push(O(n));
|
|
244
|
+
}
|
|
245
|
+
return e.push(C(t.sourceRoot)), e;
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
L as aiui,
|
|
249
|
+
x as cellFactory,
|
|
250
|
+
L as default,
|
|
251
|
+
h as defaultFactories,
|
|
252
|
+
k as optionsFactory,
|
|
253
|
+
A as sourceLocatorBabel,
|
|
254
|
+
O as sourceLocatorVite
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/source-locator.ts","../src/index.ts"],"sourcesContent":["/**\n * The aiui compiler — compile-time identity, description, and source-location\n * injection, absorbed into the dev overlay from the demo's\n * `babel-source-locator.mjs` and promoted from a one-off stamper to a\n * table-driven pass (docs/proposals/front_end_controls_guide_and_more.md §2a).\n * Node-side only (behind the `./vite` subpath): the browser bundle never\n * imports this.\n *\n * Two halves with DIFFERENT lifecycles:\n *\n * 1. **JSX stamping** (dev-only) — every *host* JSX element gets\n * `data-source-loc=\"src/ui/Controls.tsx:42:7\"` (path relative to the app\n * root, 1-based line:column). Paired with the overlay's injected\n * `window.__AIUI__.sourceRoot`, `sourceRoot + \"/\" + el.dataset.sourceLoc`\n * is an absolute, clickable `file:line:col`. Only host elements (lowercase\n * tags) are stamped — stamping a component would just pass a mystery prop.\n *\n * 2. **Factory call-site injection** (dev AND build — LOAD-BEARING) — a call\n * to a factory in the {@link FactorySpec} table gets identity injected\n * into its options object: `const catalog = cell(deps, compute)` becomes\n * `cell(deps, compute, { name: \"catalog\", loc: \"src/…:77\", description })`,\n * with the name inferred from where the value lands and the description\n * lifted from the leading doc comment. For `control()`/`action()` the\n * injected name is also the durable-persistence key and the agent-tool\n * identity, which is why this half runs in production builds too and why\n * the runtime fails loudly when it is missing.\n *\n * The principles this framework holds itself to (each one ratified, most paid\n * for — see the proposal §2a and docs/guide/frontend-hard-won.md):\n *\n * - **Compile time injects identity and location only.** No behavior may\n * depend on the transform beyond naming: with the plugin off, cells go\n * anonymous (legal) and controls/actions throw loudly at runtime for a\n * missing name (never silently anonymous — their names are keys).\n * - **Injection is idempotent.** Keys already present in an options object\n * are never clobbered; running the pass twice is a no-op.\n * - **Naming is syntactic.** The callee must literally be a configured\n * identifier — aliased or re-exported factories are invisible. A\n * deliberate 90% heuristic, documented rather than \"fixed\".\n * - **Explicit names must be compile-time string literals.** A dynamic name\n * defeats the durable key, the tool identity, and the agent's ability to\n * grep for it — rejected with a code-framed error, not a warning.\n * - **All work happens in `Program.enter` with an explicit traverse** (a\n * paid-for finding): this pass shares a babel run with babel-preset-solid,\n * whose compiler visits each outermost `JSXElement`, compiles the whole\n * subtree internally, and replaces it — a `JSXOpeningElement` visitor in\n * another plugin never fires for the children. `Program.enter` runs before\n * any replacement; an explicit `path.traverse` sees the intact tree.\n *\n * `@babel/core` is an OPTIONAL peer: it is `import type`-only here (erased at\n * build) and loaded lazily via dynamic import inside the Vite plugin, so it is\n * required only when a consumer opts into the locator.\n */\nimport type { types as BabelTypes, NodePath, PluginObj, PluginPass, Visitor } from \"@babel/core\";\nimport type { Plugin, Rollup } from \"vite\";\n\n/** @internal — the `@babel/core` module shape, loaded lazily. */\ntype BabelModule = typeof import(\"@babel/core\");\n\nconst ATTR = \"data-source-loc\";\n\n/** The friendly error when the optional `@babel/core` peer is not installed. */\nconst LOAD_ERROR = \"aiuiDevOverlay locator needs @babel/core — install it as a devDependency\";\n\n/** One factory the compiler injects identity into. */\nexport interface FactorySpec {\n /**\n * Callee identifier, matched syntactically — the call must literally read\n * `<callee>(…)`; aliases and re-exports are invisible (by design).\n */\n callee: string;\n /** Inclusive argument-count range for a call to qualify. */\n args: { min: number; max: number };\n /**\n * Index of the options object. When it can be ≥ the call's argument count\n * the argument is *appended* on demand (cell: index 2 of a 2-arg call);\n * when every qualifying call already has it (control/action: index 0) it is\n * only ever merged into. A present-but-non-object options argument (a\n * spread, a call) is left alone — the runtime's loud-failure guard is the\n * backstop for those.\n */\n optionsArg: number;\n /** Keys injected when absent. `name` behavior is governed by `namePolicy`. */\n inject: ReadonlyArray<\"name\" | \"loc\" | \"description\">;\n /**\n * What happens when a call has no explicit `name` and no inferrable\n * binding (not assigned to a `const`, an object property, or a plain\n * assignment):\n * - `\"optional\"` — skip injection entirely (anonymous cells are legal and\n * get no loc/description either, exactly the pre-table behavior);\n * - `\"required\"` — compile error with a code frame (a control/action name\n * is a durable key and a tool identity; the runtime would throw anyway,\n * so fail earlier and better).\n */\n namePolicy: \"optional\" | \"required\";\n}\n\n/** The `cell(deps, compute, opts?)` shape for a given callee name. */\nexport function cellFactory(callee = \"cell\"): FactorySpec {\n return {\n callee,\n args: { min: 2, max: 3 },\n optionsArg: 2,\n inject: [\"name\", \"loc\", \"description\"],\n namePolicy: \"optional\",\n };\n}\n\n/** The single-options-object shape (`control({…})`, `action({…})`). */\nexport function optionsFactory(callee: string): FactorySpec {\n return {\n callee,\n args: { min: 1, max: 1 },\n optionsArg: 0,\n inject: [\"name\", \"loc\", \"description\"],\n namePolicy: \"required\",\n };\n}\n\n/** The default table: `cell`, plus the control-surface factories. */\nexport function defaultFactories(): FactorySpec[] {\n return [cellFactory(), optionsFactory(\"control\"), optionsFactory(\"action\")];\n}\n\nexport interface SourceLocatorOptions {\n /**\n * Root for relativizing filenames. Files UNDER it stamp as root-relative\n * (\"src/model/store.ts:12\"); files OUTSIDE it (a workspace-linked package\n * the app consumes source-first) stamp relative-with-dotdots\n * (\"../../packages/spectra/src/store.ts:12\") — still resolvable against the\n * app root by the attribution consumers. Defaults to `\"\"` (absolute paths).\n */\n root?: string;\n /**\n * Prefix prepended to every stamped/injected path — for a LIBRARY running\n * this pass in its own build so its published dist carries identity: with\n * `root` = the package dir and `locPrefix: \"@you/spectra/\"`, locs read\n * \"@you/spectra/src/store.ts:12\" — package-qualified, meaningful in any\n * consumer. Apps normally leave this unset.\n */\n locPrefix?: string;\n /**\n * The factory table. Defaults to {@link defaultFactories}; pass `[]` to\n * disable call-site injection entirely (JSX stamping is independent).\n */\n factories?: FactorySpec[];\n /**\n * Back-compat sugar: names treated as cell-shaped factories. Ignored when\n * `factories` is given. `[]` disables call-site injection (the historical\n * \"keep JSX stamping only\" contract).\n */\n cellFactories?: string[];\n /**\n * Stamp `data-source-loc` on host JSX elements (default true). The Vite\n * plugin turns this off for production builds — instrumentation is dev-only;\n * identity injection is not.\n */\n stampJsx?: boolean;\n}\n\n/** Resolve the effective factory table from the options (back-compat aware). */\nfunction resolveFactories(options: SourceLocatorOptions): FactorySpec[] {\n if (options.factories) return options.factories;\n if (options.cellFactories) return options.cellFactories.map((name) => cellFactory(name));\n return defaultFactories();\n}\n\n/**\n * A file's stamped path: root-relative when under the root, dotdot-relative\n * when outside it (workspace-linked sources), absolute only with no root.\n * String-based on purpose (this module also runs under test tooling where\n * pulling in node:path is avoidable); paths here are Vite-normalized `/`.\n */\nfunction relativizeFile(root: string, file: string): string {\n if (root === \"\") return file;\n if (file.startsWith(root)) return file.slice(root.length).replace(/^\\//, \"\");\n const from = root.replace(/\\/+$/, \"\").split(\"/\");\n const to = file.split(\"/\");\n let common = 0;\n while (common < from.length && common < to.length && from[common] === to[common]) common++;\n if (common === 0) return file; // different volume/prefix — keep it absolute\n return [...Array<string>(from.length - common).fill(\"..\"), ...to.slice(common)].join(\"/\");\n}\n\n/**\n * Comments that are tooling directives or section banners, never descriptions.\n * Kept syntactic and conservative: when in doubt, a comment IS a candidate\n * description (an explicit `description` always wins anyway).\n */\nconst DIRECTIVE_COMMENT =\n /^\\s*(?:biome-ignore|eslint|@ts-|prettier-|@vitest-environment|<\\/?aiui-scenery|@__PURE__|#__PURE__|v8 ignore|-{2,})/;\n\n/**\n * A human description from a node's leading comments: the LAST non-directive\n * comment (closest to the declaration; a section banner further up loses to\n * the docblock), with `//` runs merged when the lines are contiguous. JSDoc\n * margins are stripped, tag sections (`@param …`) dropped, whitespace\n * collapsed — the same characters that render as the editor tooltip become the\n * registry description (the ratified JSDoc convention; `//` accepted too).\n */\nfunction descriptionFromComments(\n comments: ReadonlyArray<BabelTypes.Comment> | null | undefined,\n): string | undefined {\n if (!comments || comments.length === 0) return undefined;\n const usable = comments.filter((c) => !DIRECTIVE_COMMENT.test(c.value));\n if (usable.length === 0) return undefined;\n\n const last = usable[usable.length - 1];\n let raw: string;\n if (last.type === \"CommentBlock\") {\n raw = last.value;\n } else {\n // Merge the maximal run of contiguous trailing line comments (`// a` over\n // `// b` is one two-line description, not two candidates).\n const run: string[] = [last.value];\n let line = last.loc?.start.line;\n for (let i = usable.length - 2; i >= 0; i--) {\n const c = usable[i];\n if (c.type !== \"CommentLine\" || line === undefined || c.loc?.end.line !== line - 1) break;\n run.unshift(c.value);\n line = c.loc?.start.line;\n }\n raw = run.join(\"\\n\");\n }\n\n // Strip JSDoc margins first (`\\s` does not match the `*` gutter), then cut\n // at the first tag line — `@param`/`@remarks` sections are metadata, not\n // description.\n const lines = raw.split(\"\\n\").map((l) => l.replace(/^\\s*\\*+\\s?/, \"\").trim());\n const tagAt = lines.findIndex((l) => l.startsWith(\"@\"));\n const text = (tagAt === -1 ? lines : lines.slice(0, tagAt)).join(\" \").replace(/\\s+/g, \" \").trim();\n return text.length > 0 ? text : undefined;\n}\n\n/**\n * The babel plugin (both halves). Kept a plain function so it can be dropped\n * into any `@babel/core` `plugins` array — the standalone {@link sourceLocatorVite}\n * pass, or a consumer's existing babel pass at real-app scale.\n */\nexport function sourceLocatorBabel(\n babel: { types: typeof BabelTypes },\n options: SourceLocatorOptions = {},\n): PluginObj {\n const t = babel.types;\n const root = options.root ?? \"\";\n const locPrefix = options.locPrefix ?? \"\";\n const stampJsx = options.stampJsx ?? true;\n const factories = new Map(resolveFactories(options).map((f) => [f.callee, f]));\n\n return {\n name: \"aiui-source-locator\",\n visitor: {\n Program(programPath: NodePath<BabelTypes.Program>, state: PluginPass) {\n const file = state.file.opts.filename ?? \"\";\n const rel = `${locPrefix}${relativizeFile(root, file)}`;\n const visitor: Visitor = {\n JSXOpeningElement(path) {\n if (!stampJsx) return;\n const name = path.node.name;\n if (name.type !== \"JSXIdentifier\" || !/^[a-z]/.test(name.name)) return;\n if (!path.node.loc) return;\n const exists = path.node.attributes.some(\n (a) =>\n a.type === \"JSXAttribute\" &&\n a.name.type === \"JSXIdentifier\" &&\n a.name.name === ATTR,\n );\n if (exists) return;\n const { line, column } = path.node.loc.start;\n path.node.attributes.push(\n t.jsxAttribute(\n t.jsxIdentifier(ATTR),\n t.stringLiteral(`${rel}:${line}:${column + 1}`),\n ),\n );\n },\n\n CallExpression(path) {\n const callee = path.node.callee;\n if (callee.type !== \"Identifier\") return;\n const spec = factories.get(callee.name);\n if (!spec) return;\n const args = path.node.arguments;\n if (args.length < spec.args.min || args.length > spec.args.max) return;\n if (!path.node.loc) return;\n injectIdentity(t, path, spec, rel);\n },\n };\n programPath.traverse(visitor);\n },\n },\n };\n}\n\n/** Object-literal keys present on an options object (Identifier or string). */\nfunction presentKeys(opts: BabelTypes.ObjectExpression): Set<string> {\n const keys = new Set<string>();\n for (const p of opts.properties) {\n if (p.type !== \"ObjectProperty\") continue;\n if (p.key.type === \"Identifier\") keys.add(p.key.name);\n else if (p.key.type === \"StringLiteral\") keys.add(p.key.value);\n }\n return keys;\n}\n\n/** The explicit `name` property of an options object, if any. */\nfunction nameProperty(opts: BabelTypes.ObjectExpression): BabelTypes.ObjectProperty | undefined {\n for (const p of opts.properties) {\n if (p.type !== \"ObjectProperty\") continue;\n const key =\n p.key.type === \"Identifier\" ? p.key.name : p.key.type === \"StringLiteral\" ? p.key.value : \"\";\n if (key === \"name\") return p;\n }\n return undefined;\n}\n\n/**\n * Infer a factory value's name from where it lands, plus the node its doc\n * comment attaches to: `const kappa = control(…)` (and the `export` wrapping\n * it), `rose: cell(…)` in an object literal, `x = cell(…)` — the same three\n * positions the cell injection has always recognized.\n */\nfunction bindingOf(path: NodePath<BabelTypes.CallExpression>): {\n name?: string;\n commentNode?: BabelTypes.Node;\n} {\n const parent = path.parent;\n if (parent.type === \"VariableDeclarator\" && parent.id.type === \"Identifier\") {\n // Comments attach to the declarator, its declaration, or the export\n // statement wrapping it — closest first.\n const declaration = path.parentPath?.parentPath;\n const exported =\n declaration?.parentPath?.node.type === \"ExportNamedDeclaration\"\n ? declaration.parentPath.node\n : undefined;\n const commentNode =\n (parent.leadingComments?.length ? parent : undefined) ??\n (declaration?.node.leadingComments?.length ? declaration.node : undefined) ??\n exported ??\n declaration?.node;\n return { name: parent.id.name, commentNode };\n }\n if (parent.type === \"ObjectProperty\" && parent.key.type === \"Identifier\") {\n return { name: parent.key.name, commentNode: parent };\n }\n if (parent.type === \"AssignmentExpression\" && parent.left.type === \"Identifier\") {\n const statement = path.parentPath?.node;\n return { name: parent.left.name, commentNode: statement ?? parent };\n }\n // A bare statement (`action({ … });`): no name, but its comment still counts.\n if (parent.type === \"ExpressionStatement\") {\n return { commentNode: parent };\n }\n return {};\n}\n\n/** The injection half: names, locs, descriptions — per the spec's table row. */\nfunction injectIdentity(\n t: typeof BabelTypes,\n path: NodePath<BabelTypes.CallExpression>,\n spec: FactorySpec,\n rel: string,\n): void {\n const args = path.node.arguments;\n const existing = args[spec.optionsArg];\n\n // A present-but-non-literal options expression is left alone: we cannot see\n // inside `control(makeOpts())`. The runtime's missing-name guard is the\n // backstop; erroring here would reject legitimate dynamic composition.\n if (existing && existing.type !== \"ObjectExpression\") return;\n const opts = existing as BabelTypes.ObjectExpression | undefined;\n const keys = opts ? presentKeys(opts) : new Set<string>();\n\n // ---- name: verify an explicit one, or infer one --------------------------\n const binding = bindingOf(path);\n let inferredName: string | undefined;\n if (spec.inject.includes(\"name\")) {\n if (opts && keys.has(\"name\")) {\n const prop = nameProperty(opts);\n if (prop && prop.value.type !== \"StringLiteral\") {\n throw path.buildCodeFrameError(\n `[aiui compiler] ${spec.callee}() name must be a compile-time string literal — ` +\n `it is a durable key, a tool identity, and a grep target. Use a plain \"…\" string.`,\n );\n }\n } else if (binding.name) {\n inferredName = binding.name;\n } else if (spec.namePolicy === \"required\") {\n throw path.buildCodeFrameError(\n `[aiui compiler] ${spec.callee}(…) needs a name: assign it to a named binding ` +\n `(const kappa = ${spec.callee}(…)) or pass { name: \"…\" }. The name is the durable ` +\n `key and the agent-tool identity, so it cannot be anonymous.`,\n );\n } else {\n return; // anonymous cells stay anonymous — no loc/description either\n }\n }\n\n // ---- assemble the injected properties -------------------------------------\n const inject: BabelTypes.ObjectProperty[] = [];\n if (inferredName !== undefined && !keys.has(\"name\")) {\n inject.push(t.objectProperty(t.identifier(\"name\"), t.stringLiteral(inferredName)));\n }\n if (spec.inject.includes(\"loc\") && !keys.has(\"loc\") && path.node.loc) {\n const loc = `${rel}:${path.node.loc.start.line}`;\n inject.push(t.objectProperty(t.identifier(\"loc\"), t.stringLiteral(loc)));\n }\n if (spec.inject.includes(\"description\") && !keys.has(\"description\")) {\n const description = descriptionFromComments(binding.commentNode?.leadingComments);\n if (description !== undefined) {\n inject.push(t.objectProperty(t.identifier(\"description\"), t.stringLiteral(description)));\n }\n }\n if (inject.length === 0) return;\n\n if (opts) {\n opts.properties.push(...inject);\n } else if (spec.optionsArg === args.length) {\n args.push(t.objectExpression(inject));\n }\n // optionsArg beyond appendable position with no object: structurally\n // impossible given the arity gate above; nothing to do.\n}\n\n/** Options for the standalone Vite plugin. */\nexport interface SourceLocatorViteOptions extends SourceLocatorOptions {\n /**\n * @internal Test seam — override how `@babel/core` is loaded. Defaults to a\n * dynamic `import(\"@babel/core\")`.\n */\n loadBabel?: () => Promise<BabelModule>;\n}\n\n/** The content sniff: JSX open tag (when stamping) or a factory call. */\nfunction buildSniff(factories: FactorySpec[], stampJsx: boolean): RegExp | undefined {\n const parts: string[] = [];\n if (stampJsx) parts.push(\"<[A-Za-z]\");\n if (factories.length > 0) {\n const escaped = factories.map((f) => f.callee.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n parts.push(`\\\\b(?:${escaped.join(\"|\")})\\\\s*\\\\(`);\n }\n return parts.length > 0 ? new RegExp(parts.join(\"|\")) : undefined;\n}\n\n/**\n * The standalone Vite plugin — the recommended integration.\n *\n * Running our own babel pass (`enforce: \"pre\"`, before vite-plugin-solid)\n * instead of hooking vite-plugin-solid's `babel` option matters for two\n * reasons, both paid for: (1) vite-plugin-solid only transforms `.jsx/.tsx`, so\n * `cell()`/`control()` calls in plain `.ts` model files would never be\n * instrumented; (2) the decoupling means this works for ANY consumer, not just\n * Solid apps — the JSX stamping half applies to every JSX framework, and the\n * factory half to anything cell-shaped.\n *\n * Applies to **serve AND build**: factory identity injection is load-bearing\n * (a control's compiled-in name is its durable key and tool identity), so a\n * production build must run it too. JSX stamping remains dev-only by default\n * (`stampJsx` defaults to `command === \"serve\"`); pass `stampJsx: true` to\n * keep instrumentation in a production build deliberately.\n *\n * Scope of processing: `node_modules` is always skipped (published deps carry\n * their identity from their own build, or write explicit names). Files under\n * the app root get both halves. Files OUTSIDE the root but not in\n * node_modules — workspace-linked package sources consumed source-first — get\n * the factory-identity half only, with dotdot-relative locs\n * (\"../../packages/spectra/src/store.ts:12\"): a shared slice's controls are\n * named wherever the code lives, while JSX stamping stays app-scoped so\n * attribution keeps resolving to the app's own components. A cheap content\n * sniff skips files with neither JSX nor a factory call. `@babel/core` is\n * loaded lazily (dynamic import) and required only here — if it is missing,\n * `buildStart` fails fast with a clear install hint.\n */\nexport function sourceLocatorVite(options: SourceLocatorViteOptions = {}): Plugin {\n // Root defaults to the resolved Vite root (captured in configResolved); an\n // explicit option always wins. stampJsx defaults per-command (serve only).\n let root = options.root ?? \"\";\n let stampJsx = options.stampJsx ?? true;\n const factories = resolveFactories(options);\n let sniff = buildSniff(factories, stampJsx);\n // The out-of-root sniff: factory calls only (no JSX part) — see transform.\n let factorySniff = buildSniff(factories, false);\n\n const load = options.loadBabel ?? (() => import(\"@babel/core\"));\n let babel: BabelModule | undefined;\n let pending: Promise<BabelModule> | undefined;\n const ensureBabel = (): Promise<BabelModule> => {\n if (babel) return Promise.resolve(babel);\n if (!pending) {\n pending = load().then(\n (mod) => {\n babel = mod;\n return mod;\n },\n () => {\n pending = undefined; // allow a later transform to retry the load\n throw new Error(LOAD_ERROR);\n },\n );\n }\n return pending;\n };\n\n return {\n name: \"aiui:source-locator\",\n enforce: \"pre\",\n configResolved(config) {\n if (options.root === undefined) root = config.root;\n if (options.stampJsx === undefined) stampJsx = config.command === \"serve\";\n sniff = buildSniff(factories, stampJsx);\n factorySniff = buildSniff(factories, false);\n },\n async buildStart() {\n // Fail fast with a clear message if the optional peer is missing, rather\n // than mid-session on the first matching module.\n await ensureBabel();\n },\n async transform(code, id) {\n const file = id.replace(/\\?.*$/, \"\");\n if (!/\\.[mc]?[tj]sx?$/.test(file)) return null;\n if (file.includes(\"node_modules\")) return null;\n // Files OUTSIDE the app root — workspace-linked package sources the app\n // consumes source-first — get FACTORY IDENTITY only (a shared slice's\n // controls need their names wherever the code lives), never JSX stamps:\n // `data-source-loc` on library internals would win the shot locator's\n // innermost-containing resolution away from the app's own components.\n const inRoot = root === \"\" || file.startsWith(root);\n const effectiveSniff = inRoot ? sniff : factorySniff;\n if (!effectiveSniff?.test(code)) return null; // nothing to inject\n const { transformAsync } = await ensureBabel();\n // jsx parsing only for *.jsx/*.tsx: in plain .ts the jsx plugin makes\n // `<T>expr` type assertions ambiguous.\n const isJsxFile = /x$/.test(file);\n const plugins: (\"jsx\" | \"typescript\")[] = isJsxFile ? [\"jsx\", \"typescript\"] : [\"typescript\"];\n const result = await transformAsync(code, {\n filename: file,\n parserOpts: { plugins },\n plugins: [\n [\n sourceLocatorBabel,\n {\n root,\n factories,\n stampJsx: stampJsx && inRoot,\n ...(options.locPrefix !== undefined ? { locPrefix: options.locPrefix } : {}),\n } satisfies SourceLocatorOptions,\n ],\n ],\n configFile: false,\n babelrc: false,\n sourceMaps: true,\n });\n return result?.code ? { code: result.code, map: result.map as Rollup.SourceMapInput } : null;\n },\n };\n}\n","/**\n * index.ts — THE aiui Vite plugin (`@habemus-papadum/aiui-source-processor`),\n * home of the whole build-time integration. Extracted into its own package\n * (from `aiui-viz/vite`, itself moved from the dev overlay in the 2026-07-14\n * restructure) so the source transform is a standalone, testable library. One\n * plugin, two jobs — and deliberately nothing else:\n *\n * 1. **The source-locator compiler pass** (./source-locator). It applies to\n * serve AND build: factory identity\n * injection is load-bearing (durable cells need their `{name, loc}`\n * identity in production), so a build that violates the pass's\n * expectations FAILS in prod exactly as it would in dev. What is dev-only\n * is the EMISSION of instrumentation: the `data-source-loc` DOM stamps\n * default to `command === \"serve\"` (owner, 2026-07-14 — production\n * bundles ship clean of machine paths; pass `stampJsx: true` to keep them\n * deliberately).\n * 2. **The dev-only `sourceRoot` seed**: a tiny HTML script setting\n * `window.__AIUI__.sourceRoot` so the locator's relative stamps can be\n * absolutized into the paths a prompt carries. Build-time knowledge,\n * dev-only by the same rule as the stamps it serves.\n *\n * What this plugin deliberately does NOT do (the old overlay plugin's magic,\n * retired): no channel-port injection, no page-side `/tools` dialing, no\n * session bus, no overlay UI mounting. The `window.__AIUI__` global itself is\n * the RUNTIME's job now (see ./aiui-global — it exists in production too),\n * and channel connectivity arrives from OUTSIDE, via the intent client (the\n * Chrome extension or the CDP tier), never from the app.\n */\n\nimport type { Plugin } from \"vite\";\nimport {\n cellFactory,\n defaultFactories,\n type FactorySpec,\n type SourceLocatorViteOptions,\n sourceLocatorVite,\n} from \"./source-locator.ts\";\n\n// Configs import factory helpers from this one subpath.\nexport {\n cellFactory,\n defaultFactories,\n type FactorySpec,\n optionsFactory,\n type SourceLocatorOptions,\n type SourceLocatorViteOptions,\n sourceLocatorBabel,\n sourceLocatorVite,\n} from \"./source-locator.ts\";\n\nexport interface AiuiPluginOptions {\n /**\n * The locator pass's options (factories, stampJsx, include/exclude).\n * `true`/omitted = defaults; `false` disables the pass entirely (rare —\n * durable factory identity dies with it); `{ cellFactories: [\"cell\"] }` is\n * back-compat sugar — names treated as cell-shaped factories.\n */\n locator?: boolean | (SourceLocatorViteOptions & { cellFactories?: string[] });\n /**\n * The app's source root for absolutizing stamps (dev-only injection);\n * defaults to the Vite root at config-resolve time.\n */\n sourceRoot?: string;\n}\n\n/** The dev-only `sourceRoot` seed (see the module doc). */\nfunction sourceRootSeed(explicit: string | undefined): Plugin {\n let root: string | undefined = explicit;\n return {\n name: \"aiui:source-root\",\n apply: \"serve\",\n configResolved(config) {\n root ??= config.root;\n },\n transformIndexHtml() {\n if (root === undefined) {\n return;\n }\n return [\n {\n tag: \"script\",\n injectTo: \"head-prepend\" as const,\n children: `(window.__AIUI__ ??= { v: 1, frames: [] }).sourceRoot = ${JSON.stringify(root)};`,\n },\n ];\n },\n };\n}\n\n/**\n * The aiui integration for an app's Vite config:\n *\n * ```ts\n * import aiui from \"@habemus-papadum/aiui-source-processor\";\n * export default defineConfig({ plugins: [aiui(), solid()] });\n * ```\n *\n * Order matters: `aiui()` comes BEFORE the Solid plugin so the locator's\n * `pre`-phase Babel pass sees the original JSX.\n */\nexport function aiui(options: AiuiPluginOptions = {}): Plugin[] {\n const plugins: Plugin[] = [];\n if (options.locator !== false) {\n let locatorOptions: SourceLocatorViteOptions;\n if (options.locator === undefined || options.locator === true) {\n locatorOptions = { factories: defaultFactories() as FactorySpec[] };\n } else {\n const { cellFactories, ...rest } = options.locator;\n locatorOptions = {\n ...rest,\n factories:\n rest.factories ??\n (cellFactories !== undefined\n ? cellFactories.map((name) => cellFactory(name))\n : (defaultFactories() as FactorySpec[])),\n };\n }\n plugins.push(sourceLocatorVite(locatorOptions));\n }\n plugins.push(sourceRootSeed(options.sourceRoot));\n return plugins;\n}\n\nexport default aiui;\n"],"names":["ATTR","LOAD_ERROR","cellFactory","callee","optionsFactory","defaultFactories","resolveFactories","options","name","relativizeFile","root","file","from","to","common","DIRECTIVE_COMMENT","descriptionFromComments","comments","_a","_b","_c","usable","c","last","raw","run","line","i","lines","l","tagAt","text","sourceLocatorBabel","babel","t","locPrefix","stampJsx","factories","f","programPath","state","rel","visitor","path","a","column","spec","args","injectIdentity","presentKeys","opts","keys","p","nameProperty","bindingOf","_d","_e","parent","declaration","exported","commentNode","statement","existing","binding","inferredName","prop","inject","loc","description","buildSniff","parts","escaped","sourceLocatorVite","sniff","factorySniff","load","pending","ensureBabel","mod","config","code","id","inRoot","effectiveSniff","transformAsync","plugins","result","sourceRootSeed","explicit","aiui","locatorOptions","cellFactories","rest"],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The aiui compiler — compile-time identity, description, and source-location
|
|
3
|
+
* injection, absorbed into the dev overlay from the demo's
|
|
4
|
+
* `babel-source-locator.mjs` and promoted from a one-off stamper to a
|
|
5
|
+
* table-driven pass (docs/proposals/front_end_controls_guide_and_more.md §2a).
|
|
6
|
+
* Node-side only (behind the `./vite` subpath): the browser bundle never
|
|
7
|
+
* imports this.
|
|
8
|
+
*
|
|
9
|
+
* Two halves with DIFFERENT lifecycles:
|
|
10
|
+
*
|
|
11
|
+
* 1. **JSX stamping** (dev-only) — every *host* JSX element gets
|
|
12
|
+
* `data-source-loc="src/ui/Controls.tsx:42:7"` (path relative to the app
|
|
13
|
+
* root, 1-based line:column). Paired with the overlay's injected
|
|
14
|
+
* `window.__AIUI__.sourceRoot`, `sourceRoot + "/" + el.dataset.sourceLoc`
|
|
15
|
+
* is an absolute, clickable `file:line:col`. Only host elements (lowercase
|
|
16
|
+
* tags) are stamped — stamping a component would just pass a mystery prop.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Factory call-site injection** (dev AND build — LOAD-BEARING) — a call
|
|
19
|
+
* to a factory in the {@link FactorySpec} table gets identity injected
|
|
20
|
+
* into its options object: `const catalog = cell(deps, compute)` becomes
|
|
21
|
+
* `cell(deps, compute, { name: "catalog", loc: "src/…:77", description })`,
|
|
22
|
+
* with the name inferred from where the value lands and the description
|
|
23
|
+
* lifted from the leading doc comment. For `control()`/`action()` the
|
|
24
|
+
* injected name is also the durable-persistence key and the agent-tool
|
|
25
|
+
* identity, which is why this half runs in production builds too and why
|
|
26
|
+
* the runtime fails loudly when it is missing.
|
|
27
|
+
*
|
|
28
|
+
* The principles this framework holds itself to (each one ratified, most paid
|
|
29
|
+
* for — see the proposal §2a and docs/guide/frontend-hard-won.md):
|
|
30
|
+
*
|
|
31
|
+
* - **Compile time injects identity and location only.** No behavior may
|
|
32
|
+
* depend on the transform beyond naming: with the plugin off, cells go
|
|
33
|
+
* anonymous (legal) and controls/actions throw loudly at runtime for a
|
|
34
|
+
* missing name (never silently anonymous — their names are keys).
|
|
35
|
+
* - **Injection is idempotent.** Keys already present in an options object
|
|
36
|
+
* are never clobbered; running the pass twice is a no-op.
|
|
37
|
+
* - **Naming is syntactic.** The callee must literally be a configured
|
|
38
|
+
* identifier — aliased or re-exported factories are invisible. A
|
|
39
|
+
* deliberate 90% heuristic, documented rather than "fixed".
|
|
40
|
+
* - **Explicit names must be compile-time string literals.** A dynamic name
|
|
41
|
+
* defeats the durable key, the tool identity, and the agent's ability to
|
|
42
|
+
* grep for it — rejected with a code-framed error, not a warning.
|
|
43
|
+
* - **All work happens in `Program.enter` with an explicit traverse** (a
|
|
44
|
+
* paid-for finding): this pass shares a babel run with babel-preset-solid,
|
|
45
|
+
* whose compiler visits each outermost `JSXElement`, compiles the whole
|
|
46
|
+
* subtree internally, and replaces it — a `JSXOpeningElement` visitor in
|
|
47
|
+
* another plugin never fires for the children. `Program.enter` runs before
|
|
48
|
+
* any replacement; an explicit `path.traverse` sees the intact tree.
|
|
49
|
+
*
|
|
50
|
+
* `@babel/core` is an OPTIONAL peer: it is `import type`-only here (erased at
|
|
51
|
+
* build) and loaded lazily via dynamic import inside the Vite plugin, so it is
|
|
52
|
+
* required only when a consumer opts into the locator.
|
|
53
|
+
*/
|
|
54
|
+
import type { types as BabelTypes, PluginObj } from "@babel/core";
|
|
55
|
+
import type { Plugin } from "vite";
|
|
56
|
+
/** @internal — the `@babel/core` module shape, loaded lazily. */
|
|
57
|
+
type BabelModule = typeof import("@babel/core");
|
|
58
|
+
/** One factory the compiler injects identity into. */
|
|
59
|
+
export interface FactorySpec {
|
|
60
|
+
/**
|
|
61
|
+
* Callee identifier, matched syntactically — the call must literally read
|
|
62
|
+
* `<callee>(…)`; aliases and re-exports are invisible (by design).
|
|
63
|
+
*/
|
|
64
|
+
callee: string;
|
|
65
|
+
/** Inclusive argument-count range for a call to qualify. */
|
|
66
|
+
args: {
|
|
67
|
+
min: number;
|
|
68
|
+
max: number;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Index of the options object. When it can be ≥ the call's argument count
|
|
72
|
+
* the argument is *appended* on demand (cell: index 2 of a 2-arg call);
|
|
73
|
+
* when every qualifying call already has it (control/action: index 0) it is
|
|
74
|
+
* only ever merged into. A present-but-non-object options argument (a
|
|
75
|
+
* spread, a call) is left alone — the runtime's loud-failure guard is the
|
|
76
|
+
* backstop for those.
|
|
77
|
+
*/
|
|
78
|
+
optionsArg: number;
|
|
79
|
+
/** Keys injected when absent. `name` behavior is governed by `namePolicy`. */
|
|
80
|
+
inject: ReadonlyArray<"name" | "loc" | "description">;
|
|
81
|
+
/**
|
|
82
|
+
* What happens when a call has no explicit `name` and no inferrable
|
|
83
|
+
* binding (not assigned to a `const`, an object property, or a plain
|
|
84
|
+
* assignment):
|
|
85
|
+
* - `"optional"` — skip injection entirely (anonymous cells are legal and
|
|
86
|
+
* get no loc/description either, exactly the pre-table behavior);
|
|
87
|
+
* - `"required"` — compile error with a code frame (a control/action name
|
|
88
|
+
* is a durable key and a tool identity; the runtime would throw anyway,
|
|
89
|
+
* so fail earlier and better).
|
|
90
|
+
*/
|
|
91
|
+
namePolicy: "optional" | "required";
|
|
92
|
+
}
|
|
93
|
+
/** The `cell(deps, compute, opts?)` shape for a given callee name. */
|
|
94
|
+
export declare function cellFactory(callee?: string): FactorySpec;
|
|
95
|
+
/** The single-options-object shape (`control({…})`, `action({…})`). */
|
|
96
|
+
export declare function optionsFactory(callee: string): FactorySpec;
|
|
97
|
+
/** The default table: `cell`, plus the control-surface factories. */
|
|
98
|
+
export declare function defaultFactories(): FactorySpec[];
|
|
99
|
+
export interface SourceLocatorOptions {
|
|
100
|
+
/**
|
|
101
|
+
* Root for relativizing filenames. Files UNDER it stamp as root-relative
|
|
102
|
+
* ("src/model/store.ts:12"); files OUTSIDE it (a workspace-linked package
|
|
103
|
+
* the app consumes source-first) stamp relative-with-dotdots
|
|
104
|
+
* ("../../packages/spectra/src/store.ts:12") — still resolvable against the
|
|
105
|
+
* app root by the attribution consumers. Defaults to `""` (absolute paths).
|
|
106
|
+
*/
|
|
107
|
+
root?: string;
|
|
108
|
+
/**
|
|
109
|
+
* Prefix prepended to every stamped/injected path — for a LIBRARY running
|
|
110
|
+
* this pass in its own build so its published dist carries identity: with
|
|
111
|
+
* `root` = the package dir and `locPrefix: "@you/spectra/"`, locs read
|
|
112
|
+
* "@you/spectra/src/store.ts:12" — package-qualified, meaningful in any
|
|
113
|
+
* consumer. Apps normally leave this unset.
|
|
114
|
+
*/
|
|
115
|
+
locPrefix?: string;
|
|
116
|
+
/**
|
|
117
|
+
* The factory table. Defaults to {@link defaultFactories}; pass `[]` to
|
|
118
|
+
* disable call-site injection entirely (JSX stamping is independent).
|
|
119
|
+
*/
|
|
120
|
+
factories?: FactorySpec[];
|
|
121
|
+
/**
|
|
122
|
+
* Back-compat sugar: names treated as cell-shaped factories. Ignored when
|
|
123
|
+
* `factories` is given. `[]` disables call-site injection (the historical
|
|
124
|
+
* "keep JSX stamping only" contract).
|
|
125
|
+
*/
|
|
126
|
+
cellFactories?: string[];
|
|
127
|
+
/**
|
|
128
|
+
* Stamp `data-source-loc` on host JSX elements (default true). The Vite
|
|
129
|
+
* plugin turns this off for production builds — instrumentation is dev-only;
|
|
130
|
+
* identity injection is not.
|
|
131
|
+
*/
|
|
132
|
+
stampJsx?: boolean;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The babel plugin (both halves). Kept a plain function so it can be dropped
|
|
136
|
+
* into any `@babel/core` `plugins` array — the standalone {@link sourceLocatorVite}
|
|
137
|
+
* pass, or a consumer's existing babel pass at real-app scale.
|
|
138
|
+
*/
|
|
139
|
+
export declare function sourceLocatorBabel(babel: {
|
|
140
|
+
types: typeof BabelTypes;
|
|
141
|
+
}, options?: SourceLocatorOptions): PluginObj;
|
|
142
|
+
/** Options for the standalone Vite plugin. */
|
|
143
|
+
export interface SourceLocatorViteOptions extends SourceLocatorOptions {
|
|
144
|
+
/**
|
|
145
|
+
* @internal Test seam — override how `@babel/core` is loaded. Defaults to a
|
|
146
|
+
* dynamic `import("@babel/core")`.
|
|
147
|
+
*/
|
|
148
|
+
loadBabel?: () => Promise<BabelModule>;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The standalone Vite plugin — the recommended integration.
|
|
152
|
+
*
|
|
153
|
+
* Running our own babel pass (`enforce: "pre"`, before vite-plugin-solid)
|
|
154
|
+
* instead of hooking vite-plugin-solid's `babel` option matters for two
|
|
155
|
+
* reasons, both paid for: (1) vite-plugin-solid only transforms `.jsx/.tsx`, so
|
|
156
|
+
* `cell()`/`control()` calls in plain `.ts` model files would never be
|
|
157
|
+
* instrumented; (2) the decoupling means this works for ANY consumer, not just
|
|
158
|
+
* Solid apps — the JSX stamping half applies to every JSX framework, and the
|
|
159
|
+
* factory half to anything cell-shaped.
|
|
160
|
+
*
|
|
161
|
+
* Applies to **serve AND build**: factory identity injection is load-bearing
|
|
162
|
+
* (a control's compiled-in name is its durable key and tool identity), so a
|
|
163
|
+
* production build must run it too. JSX stamping remains dev-only by default
|
|
164
|
+
* (`stampJsx` defaults to `command === "serve"`); pass `stampJsx: true` to
|
|
165
|
+
* keep instrumentation in a production build deliberately.
|
|
166
|
+
*
|
|
167
|
+
* Scope of processing: `node_modules` is always skipped (published deps carry
|
|
168
|
+
* their identity from their own build, or write explicit names). Files under
|
|
169
|
+
* the app root get both halves. Files OUTSIDE the root but not in
|
|
170
|
+
* node_modules — workspace-linked package sources consumed source-first — get
|
|
171
|
+
* the factory-identity half only, with dotdot-relative locs
|
|
172
|
+
* ("../../packages/spectra/src/store.ts:12"): a shared slice's controls are
|
|
173
|
+
* named wherever the code lives, while JSX stamping stays app-scoped so
|
|
174
|
+
* attribution keeps resolving to the app's own components. A cheap content
|
|
175
|
+
* sniff skips files with neither JSX nor a factory call. `@babel/core` is
|
|
176
|
+
* loaded lazily (dynamic import) and required only here — if it is missing,
|
|
177
|
+
* `buildStart` fails fast with a clear install hint.
|
|
178
|
+
*/
|
|
179
|
+
export declare function sourceLocatorVite(options?: SourceLocatorViteOptions): Plugin;
|
|
180
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,14 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@habemus-papadum/aiui-source-processor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The aiui source processor: the compile-time Babel pass that injects factory identity (name/loc/description for cell/control/action) and dev-only JSX source-location stamps, plus its Vite plugin. One transform, serve and build.
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "The aiui source processor: the compile-time Babel pass that injects factory identity (name/loc/description for cell/control/action) and dev-only JSX source-location stamps, plus its Vite plugin. One transform, serve and build.",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/habemus-papadum/pdum_aiui.git",
|
|
9
10
|
"directory": "packages/aiui-source-processor"
|
|
10
11
|
},
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"main": "./dist/index.js",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
11
26
|
"publishConfig": {
|
|
12
27
|
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@babel/core": "^7.29.0",
|
|
31
|
+
"vite": ">=6"
|
|
32
|
+
},
|
|
33
|
+
"peerDependenciesMeta": {
|
|
34
|
+
"@babel/core": {
|
|
35
|
+
"optional": true
|
|
36
|
+
},
|
|
37
|
+
"vite": {
|
|
38
|
+
"optional": true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@babel/core": "^7.29.0",
|
|
43
|
+
"@types/babel__core": "^7.20.5",
|
|
44
|
+
"vite": "^6.4.1"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsc --emitDeclarationOnly -p tsconfig.json && vite build",
|
|
48
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
49
|
+
"test": "vitest run"
|
|
13
50
|
}
|
|
14
|
-
}
|
|
51
|
+
}
|