isotone 1.1.2 → 1.2.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/AGENTS.md +84 -6
- package/CHANGELOG.md +46 -0
- package/README.md +64 -14
- package/dist/{IterationPanel-phJRYtUO.d.ts → IterationPanel-Bq77d5NT.d.ts} +3 -1
- package/dist/{chunk-FKEKBZ3E.js → chunk-2FY2JNPG.js} +95 -9
- package/dist/chunk-2FY2JNPG.js.map +1 -0
- package/dist/chunk-BDTB3DFR.js +100 -0
- package/dist/chunk-BDTB3DFR.js.map +1 -0
- package/dist/{chunk-PUXQGUMQ.js → chunk-BTJHQXF7.js} +104 -5
- package/dist/chunk-BTJHQXF7.js.map +1 -0
- package/dist/chunk-JCTWHR7U.js +13 -0
- package/dist/chunk-JCTWHR7U.js.map +1 -0
- package/dist/{chunk-I7CZUPDO.js → chunk-KRS57POF.js} +17 -33
- package/dist/chunk-KRS57POF.js.map +1 -0
- package/dist/chunk-L6O4HYGP.js +1150 -0
- package/dist/chunk-L6O4HYGP.js.map +1 -0
- package/dist/chunk-MX4RQJFV.js +9 -0
- package/dist/chunk-MX4RQJFV.js.map +1 -0
- package/dist/chunk-YPDV3PX4.js +142 -0
- package/dist/chunk-YPDV3PX4.js.map +1 -0
- package/dist/cli.js +1079 -148
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +135 -10
- package/dist/core.js +12 -3
- package/dist/index.d.ts +13 -28
- package/dist/index.js +618 -281
- package/dist/index.js.map +1 -1
- package/dist/isotone.global.js +98 -42
- package/dist/serve.d.ts +5 -3
- package/dist/serve.js +60 -23
- package/dist/serve.js.map +1 -1
- package/dist/server.d.ts +7 -0
- package/dist/server.js +24 -11
- package/dist/server.js.map +1 -1
- package/dist/vite-plugin.d.ts +4 -3
- package/dist/vite-plugin.js +48 -128
- package/dist/vite-plugin.js.map +1 -1
- package/docs/blast.md +164 -0
- package/docs/integrations.md +75 -15
- package/docs/migration.md +19 -0
- package/docs/updates.md +110 -4
- package/docs/variants.md +102 -9
- package/package.json +2 -1
- package/dist/chunk-A4DX2V4C.js +0 -7
- package/dist/chunk-A4DX2V4C.js.map +0 -1
- package/dist/chunk-FKEKBZ3E.js.map +0 -1
- package/dist/chunk-I7CZUPDO.js.map +0 -1
- package/dist/chunk-PUXQGUMQ.js.map +0 -1
- package/dist/chunk-RQ2YI5KS.js +0 -24
- package/dist/chunk-RQ2YI5KS.js.map +0 -1
- package/dist/chunk-TGAU3WTF.js +0 -549
- package/dist/chunk-TGAU3WTF.js.map +0 -1
- package/dist/chunk-X6V2AKJL.js +0 -375
- package/dist/chunk-X6V2AKJL.js.map +0 -1
package/dist/chunk-X6V2AKJL.js
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
MAX_FEEDBACK_LENGTH
|
|
3
|
-
} from "./chunk-A4DX2V4C.js";
|
|
4
|
-
|
|
5
|
-
// src/store.ts
|
|
6
|
-
import { execFileSync } from "child_process";
|
|
7
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
8
|
-
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, realpathSync, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
9
|
-
import { basename, dirname as dirname2, isAbsolute, join, relative, resolve, sep } from "path";
|
|
10
|
-
|
|
11
|
-
// src/file-store.ts
|
|
12
|
-
import { randomUUID } from "crypto";
|
|
13
|
-
import { closeSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
14
|
-
import { dirname } from "path";
|
|
15
|
-
function writeJson(file, value) {
|
|
16
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
17
|
-
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
18
|
-
try {
|
|
19
|
-
writeFileSync(temporary, `${JSON.stringify(value, null, 2)}
|
|
20
|
-
`, { flag: "wx", mode: 384 });
|
|
21
|
-
renameSync(temporary, file);
|
|
22
|
-
} finally {
|
|
23
|
-
rmSync(temporary, { force: true });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function withFileLock(file, work) {
|
|
27
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
28
|
-
const lock = `${file}.lock`;
|
|
29
|
-
const deadline = Date.now() + 2e3;
|
|
30
|
-
while (true) {
|
|
31
|
-
try {
|
|
32
|
-
const fd = openSync(lock, "wx", 384);
|
|
33
|
-
try {
|
|
34
|
-
writeFileSync(fd, String(process.pid));
|
|
35
|
-
} finally {
|
|
36
|
-
closeSync(fd);
|
|
37
|
-
}
|
|
38
|
-
break;
|
|
39
|
-
} catch (error) {
|
|
40
|
-
if (error.code !== "EEXIST") throw error;
|
|
41
|
-
try {
|
|
42
|
-
const owner = Number(readFileSync(lock, "utf8"));
|
|
43
|
-
if (Number.isSafeInteger(owner) && owner > 0) {
|
|
44
|
-
try {
|
|
45
|
-
process.kill(owner, 0);
|
|
46
|
-
} catch (cause) {
|
|
47
|
-
if (cause.code === "ESRCH") {
|
|
48
|
-
rmSync(lock, { force: true });
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
} catch {
|
|
54
|
-
}
|
|
55
|
-
if (Date.now() >= deadline) throw new Error(`Local store is busy: ${file}. Retry after the other writer finishes.`);
|
|
56
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
return work();
|
|
61
|
-
} finally {
|
|
62
|
-
rmSync(lock, { force: true });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// src/store.ts
|
|
67
|
-
var PAYLOAD_MAX = 65536;
|
|
68
|
-
function readNotes(file) {
|
|
69
|
-
if (!existsSync(file)) return [];
|
|
70
|
-
try {
|
|
71
|
-
const raw = readFileSync2(file, "utf8").trim();
|
|
72
|
-
if (!raw) return [];
|
|
73
|
-
const data = JSON.parse(raw);
|
|
74
|
-
if (!Array.isArray(data) || !data.every((note) => note && typeof note === "object" && typeof note.id === "string" && typeof note.route === "string" && typeof note.feedback === "string" && typeof note.createdAt === "string")) {
|
|
75
|
-
throw new Error("expected an array of notes");
|
|
76
|
-
}
|
|
77
|
-
return data;
|
|
78
|
-
} catch (cause) {
|
|
79
|
-
throw new Error(`Could not read notes from ${file}; the file has been left untouched.`, { cause });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
function writeNotes(file, notes) {
|
|
83
|
-
writeJson(file, notes);
|
|
84
|
-
}
|
|
85
|
-
function updateNotes(file, update) {
|
|
86
|
-
withFileLock(file, () => writeNotes(file, update(readNotes(file))));
|
|
87
|
-
}
|
|
88
|
-
function ensureFileIgnored(file) {
|
|
89
|
-
try {
|
|
90
|
-
const dir = realpathSync(dirname2(resolve(file)));
|
|
91
|
-
const absolute = join(dir, basename(file));
|
|
92
|
-
const gitRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
93
|
-
cwd: dir,
|
|
94
|
-
encoding: "utf8",
|
|
95
|
-
// Outside a repo git writes "fatal: not a git repository" to stderr, and
|
|
96
|
-
// inheriting it prints that over the server's own startup banner. The
|
|
97
|
-
// throw is the signal we act on; the message is noise.
|
|
98
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
99
|
-
}).trim();
|
|
100
|
-
const pattern = excludePattern(gitRoot, absolute);
|
|
101
|
-
if (!pattern) return;
|
|
102
|
-
const infoExclude = excludeFile(dir);
|
|
103
|
-
const existing = existsSync(infoExclude) ? readFileSync2(infoExclude, "utf8") : "";
|
|
104
|
-
if (hasExcludePattern(existing, pattern)) return;
|
|
105
|
-
const marker = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
106
|
-
const heading = "# isotone (local dev feedback loop)";
|
|
107
|
-
const comment = existing.includes(heading) ? "" : `${heading}
|
|
108
|
-
`;
|
|
109
|
-
mkdirSync2(dirname2(infoExclude), { recursive: true });
|
|
110
|
-
writeFileSync2(infoExclude, `${existing}${marker}${comment}${pattern}
|
|
111
|
-
`);
|
|
112
|
-
} catch {
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
function ensureDirIgnored(dir) {
|
|
116
|
-
try {
|
|
117
|
-
const absolute = realpathSync(resolve(dir));
|
|
118
|
-
const gitRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
119
|
-
cwd: absolute,
|
|
120
|
-
encoding: "utf8",
|
|
121
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
122
|
-
}).trim();
|
|
123
|
-
const base = excludePattern(gitRoot, absolute);
|
|
124
|
-
if (!base) return;
|
|
125
|
-
const pattern = `${base}/`;
|
|
126
|
-
const infoExclude = excludeFile(absolute);
|
|
127
|
-
const existing = existsSync(infoExclude) ? readFileSync2(infoExclude, "utf8") : "";
|
|
128
|
-
if (hasExcludePattern(existing, pattern) || hasExcludePattern(existing, base)) return;
|
|
129
|
-
const marker = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
130
|
-
mkdirSync2(dirname2(infoExclude), { recursive: true });
|
|
131
|
-
writeFileSync2(
|
|
132
|
-
infoExclude,
|
|
133
|
-
`${existing}${marker}# isotone (local overlay variants)
|
|
134
|
-
${pattern}
|
|
135
|
-
`
|
|
136
|
-
);
|
|
137
|
-
} catch {
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
function excludeFile(cwd) {
|
|
141
|
-
const reported = execFileSync("git", ["rev-parse", "--git-path", "info/exclude"], {
|
|
142
|
-
cwd,
|
|
143
|
-
encoding: "utf8",
|
|
144
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
145
|
-
}).trim();
|
|
146
|
-
return resolve(cwd, reported);
|
|
147
|
-
}
|
|
148
|
-
function excludePattern(gitRoot, absolute) {
|
|
149
|
-
const rel = relative(gitRoot, absolute);
|
|
150
|
-
if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
return `/${rel.split(sep).join("/")}`;
|
|
154
|
-
}
|
|
155
|
-
function hasExcludePattern(exclude, pattern) {
|
|
156
|
-
const bare = pattern.replace(/^\//, "");
|
|
157
|
-
return exclude.split(/\r?\n/).some((line) => line.trim() === pattern || line.trim() === bare);
|
|
158
|
-
}
|
|
159
|
-
function toItem(n) {
|
|
160
|
-
const item = {
|
|
161
|
-
id: n.id,
|
|
162
|
-
route: n.route,
|
|
163
|
-
feedback: n.feedback,
|
|
164
|
-
element: n.element,
|
|
165
|
-
done: n.done,
|
|
166
|
-
createdAt: n.createdAt
|
|
167
|
-
};
|
|
168
|
-
if (n.workingAt && !n.done) item.workingAt = n.workingAt;
|
|
169
|
-
if (n.viewport !== void 0) item.viewport = n.viewport;
|
|
170
|
-
if (n.payload !== void 0) item.payload = n.payload;
|
|
171
|
-
return {
|
|
172
|
-
...item,
|
|
173
|
-
...n.elements !== void 0 ? { elements: n.elements } : {},
|
|
174
|
-
...n.variant !== void 0 ? { variant: n.variant } : {},
|
|
175
|
-
...n.variantAction !== void 0 ? { variantAction: n.variantAction } : {}
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
function notesResponse(file, req) {
|
|
179
|
-
if (req.method === "POST" || req.method === "DELETE") {
|
|
180
|
-
return withFileLock(file, () => handleNotesRequest(file, req));
|
|
181
|
-
}
|
|
182
|
-
return handleNotesRequest(file, req);
|
|
183
|
-
}
|
|
184
|
-
function handleNotesRequest(file, req) {
|
|
185
|
-
const method = req.method ?? "";
|
|
186
|
-
const query = Object.fromEntries(
|
|
187
|
-
new URL(req.url ?? "/", "http://localhost").searchParams
|
|
188
|
-
);
|
|
189
|
-
const json = (status, body) => ({
|
|
190
|
-
status,
|
|
191
|
-
headers: { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" },
|
|
192
|
-
body
|
|
193
|
-
});
|
|
194
|
-
if (method === "GET") {
|
|
195
|
-
const polled = query.rev !== void 0;
|
|
196
|
-
const rev = polled ? notesRevision(file) : "";
|
|
197
|
-
if (polled && rev === String(query.rev)) return json(200, { rev, unchanged: true });
|
|
198
|
-
const route = String(query.route ?? "");
|
|
199
|
-
const all = query.all !== void 0 && String(query.all) !== "" && String(query.all) !== "0";
|
|
200
|
-
const notes = polled ? readNotesAt(file, rev) : readNotes(file);
|
|
201
|
-
const rows = (all ? [...notes] : route ? notes.filter((n) => n.route === route) : notes.filter((n) => !n.done)).sort((a, b) => b.createdAt.localeCompare(a.createdAt)).map(toItem);
|
|
202
|
-
return json(200, polled ? { rev, notes: rows } : rows);
|
|
203
|
-
}
|
|
204
|
-
if (method === "DELETE") {
|
|
205
|
-
const id = String(query.id ?? "");
|
|
206
|
-
if (!id) return json(400, { error: "id is required" });
|
|
207
|
-
writeNotes(file, readNotes(file).filter((n) => n.id !== id));
|
|
208
|
-
return json(200, { ok: true });
|
|
209
|
-
}
|
|
210
|
-
if (method === "POST") {
|
|
211
|
-
const body = req.body ?? {};
|
|
212
|
-
const { id, route, feedback, element, payload, viewport } = body;
|
|
213
|
-
if (id !== void 0 && id !== null && (typeof id !== "string" || !id || id.length > 200)) {
|
|
214
|
-
return json(400, { error: "id must be a non-empty string of at most 200 characters" });
|
|
215
|
-
}
|
|
216
|
-
if (typeof route !== "string" || route === "") {
|
|
217
|
-
return json(400, { error: "route is required" });
|
|
218
|
-
}
|
|
219
|
-
const cleanFeedback = typeof feedback === "string" ? feedback.trim() : "";
|
|
220
|
-
if (cleanFeedback === "") {
|
|
221
|
-
return json(400, { error: "feedback is required" });
|
|
222
|
-
}
|
|
223
|
-
if (cleanFeedback.length > MAX_FEEDBACK_LENGTH) return json(400, { error: `feedback must be at most ${MAX_FEEDBACK_LENGTH} characters` });
|
|
224
|
-
if (payload !== void 0 && !withinPayloadCap(payload)) {
|
|
225
|
-
return json(400, {
|
|
226
|
-
error: `payload exceeds ${PAYLOAD_MAX} bytes or is not serializable`
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
const cleanViewport = viewportOf(viewport);
|
|
230
|
-
if (body.elements !== void 0 && (!Array.isArray(body.elements) || body.elements.length > 50)) {
|
|
231
|
-
return json(400, { error: "elements must be an array of at most 50 elements" });
|
|
232
|
-
}
|
|
233
|
-
const elements = body.elements === void 0 ? void 0 : body.elements.map(elementOf).filter((el) => el !== null);
|
|
234
|
-
const variant = body.variant === void 0 ? void 0 : variantOf(body.variant);
|
|
235
|
-
const variantAction = removalOf(body.variantAction);
|
|
236
|
-
if (body.variantAction !== void 0 && !variantAction) return json(400, { error: "Invalid variant action" });
|
|
237
|
-
let cleanElement = null;
|
|
238
|
-
if (element && typeof element === "object" && !Array.isArray(element)) {
|
|
239
|
-
cleanElement = {
|
|
240
|
-
tag: cap(String(element.tag ?? ""), 30),
|
|
241
|
-
text: cap(String(element.text ?? ""), 120),
|
|
242
|
-
selector: cap(
|
|
243
|
-
String(element.selector ?? ""),
|
|
244
|
-
300
|
|
245
|
-
)
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
const notes = readNotes(file);
|
|
249
|
-
const defaultsRequest = cleanFeedback.startsWith("[set-defaults]");
|
|
250
|
-
const previousDefaults = defaultsRequest && !notes.some((note) => note.id === id) ? notes.filter((n) => !n.done && n.route === route && n.feedback.startsWith("[set-defaults]")).sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0] : void 0;
|
|
251
|
-
const updateId = previousDefaults?.id ?? id;
|
|
252
|
-
if (typeof updateId === "string" && updateId !== "") {
|
|
253
|
-
const existing = notes.find((n) => n.id === updateId && n.route === route);
|
|
254
|
-
if (existing) {
|
|
255
|
-
if (previousDefaults) {
|
|
256
|
-
delete existing.elements;
|
|
257
|
-
delete existing.variant;
|
|
258
|
-
delete existing.variantAction;
|
|
259
|
-
delete existing.payload;
|
|
260
|
-
}
|
|
261
|
-
existing.feedback = cleanFeedback;
|
|
262
|
-
existing.element = cleanElement;
|
|
263
|
-
if (elements !== void 0) {
|
|
264
|
-
existing.elements = elements;
|
|
265
|
-
existing.element = elements[0] ?? null;
|
|
266
|
-
} else if (existing.elements) existing.element = existing.elements[0] ?? null;
|
|
267
|
-
if (variant !== void 0) existing.variant = variant;
|
|
268
|
-
if (variantAction) existing.variantAction = variantAction;
|
|
269
|
-
if (payload !== void 0) existing.payload = payload;
|
|
270
|
-
if (defaultsRequest && !existing.done) {
|
|
271
|
-
existing.createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
272
|
-
if (cleanViewport) existing.viewport = cleanViewport;
|
|
273
|
-
writeNotes(file, notes.filter((n) => n.id === existing.id || n.done || n.route !== route || !n.feedback.startsWith("[set-defaults]")));
|
|
274
|
-
} else writeNotes(file, notes);
|
|
275
|
-
return json(200, { id: existing.id });
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (typeof id === "string" && notes.some((note) => note.id === id)) {
|
|
279
|
-
return json(409, { error: "This note belongs to a different route" });
|
|
280
|
-
}
|
|
281
|
-
const newId = typeof id === "string" ? id : randomUUID2();
|
|
282
|
-
const createdAt = typeof body.createdAt === "string" && Number.isFinite(Date.parse(body.createdAt)) ? new Date(body.createdAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
|
|
283
|
-
notes.push({
|
|
284
|
-
id: newId,
|
|
285
|
-
route,
|
|
286
|
-
feedback: cleanFeedback,
|
|
287
|
-
element: cleanElement,
|
|
288
|
-
done: false,
|
|
289
|
-
doneAt: null,
|
|
290
|
-
createdAt,
|
|
291
|
-
...cleanViewport ? { viewport: cleanViewport } : {},
|
|
292
|
-
...payload !== void 0 ? { payload } : {},
|
|
293
|
-
...elements !== void 0 ? { elements, element: elements[0] ?? null } : {},
|
|
294
|
-
...variant !== void 0 ? { variant } : {},
|
|
295
|
-
...variantAction ? { variantAction } : {}
|
|
296
|
-
});
|
|
297
|
-
writeNotes(file, notes);
|
|
298
|
-
return json(201, { id: newId });
|
|
299
|
-
}
|
|
300
|
-
return json(405, { error: "method not allowed" });
|
|
301
|
-
}
|
|
302
|
-
var parsed = /* @__PURE__ */ new Map();
|
|
303
|
-
function readNotesAt(file, rev) {
|
|
304
|
-
const cached = parsed.get(file);
|
|
305
|
-
if (cached && cached.rev === rev && rev !== "none") return cached.notes;
|
|
306
|
-
const notes = readNotes(file);
|
|
307
|
-
parsed.set(file, { rev, notes });
|
|
308
|
-
return notes;
|
|
309
|
-
}
|
|
310
|
-
function notesRevision(file) {
|
|
311
|
-
try {
|
|
312
|
-
const stat = statSync(file, { bigint: true });
|
|
313
|
-
return `${stat.ino.toString(36)}.${stat.mtimeNs.toString(36)}.${stat.size.toString(36)}`;
|
|
314
|
-
} catch {
|
|
315
|
-
return "none";
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
function cap(v, max) {
|
|
319
|
-
return v.slice(0, max);
|
|
320
|
-
}
|
|
321
|
-
function elementOf(value) {
|
|
322
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
323
|
-
const v = value;
|
|
324
|
-
if (typeof v.selector !== "string" || !v.selector) return null;
|
|
325
|
-
return { tag: cap(String(v.tag ?? ""), 30), text: cap(String(v.text ?? ""), 120), selector: cap(v.selector, 300) };
|
|
326
|
-
}
|
|
327
|
-
function variantOf(value) {
|
|
328
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
329
|
-
const v = value;
|
|
330
|
-
if (typeof v.axisId !== "string" || typeof v.label !== "string") return null;
|
|
331
|
-
return {
|
|
332
|
-
axisId: cap(v.axisId, 200),
|
|
333
|
-
label: cap(v.label, 200),
|
|
334
|
-
...typeof v.optionId === "string" ? { optionId: cap(v.optionId, 200) } : {},
|
|
335
|
-
...typeof v.optionLabel === "string" ? { optionLabel: cap(v.optionLabel, 200) } : {},
|
|
336
|
-
...typeof v.value === "string" ? { value: cap(v.value, 200) } : {}
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
function viewportOf(v) {
|
|
340
|
-
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
341
|
-
const w = Math.round(Number(v.w));
|
|
342
|
-
const h = Math.round(Number(v.h));
|
|
343
|
-
if (!Number.isFinite(w) || !Number.isFinite(h)) return null;
|
|
344
|
-
if (w <= 0 || h <= 0 || w > 1e5 || h > 1e5) return null;
|
|
345
|
-
return { w, h };
|
|
346
|
-
}
|
|
347
|
-
function withinPayloadCap(payload) {
|
|
348
|
-
try {
|
|
349
|
-
return Buffer.byteLength(JSON.stringify(payload), "utf8") <= PAYLOAD_MAX;
|
|
350
|
-
} catch {
|
|
351
|
-
return false;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
var NOTES_ENDPOINT = "/api/iteration/notes";
|
|
355
|
-
function removalOf(value) {
|
|
356
|
-
if (!value || typeof value !== "object") return;
|
|
357
|
-
const v = value;
|
|
358
|
-
if (typeof v.axisId !== "string" || !v.axisId || v.axisId.length > 200 || !["remove-option", "remove-axis", "keep-default"].includes(v.action ?? "")) return;
|
|
359
|
-
if (v.action === "remove-option" && (typeof v.optionId !== "string" || !v.optionId || v.optionId.length > 200)) return;
|
|
360
|
-
return { axisId: v.axisId, action: v.action, ...typeof v.optionId === "string" ? { optionId: v.optionId.slice(0, 200) } : {} };
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
export {
|
|
364
|
-
writeJson,
|
|
365
|
-
withFileLock,
|
|
366
|
-
PAYLOAD_MAX,
|
|
367
|
-
readNotes,
|
|
368
|
-
writeNotes,
|
|
369
|
-
updateNotes,
|
|
370
|
-
ensureFileIgnored,
|
|
371
|
-
ensureDirIgnored,
|
|
372
|
-
notesResponse,
|
|
373
|
-
NOTES_ENDPOINT
|
|
374
|
-
};
|
|
375
|
-
//# sourceMappingURL=chunk-X6V2AKJL.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/store.ts","../src/file-store.ts"],"sourcesContent":["/**\n * File-backed store for iteration notes — shared by the Vite plugin (dev\n * server) and the CLI. Notes live in a gitignored JSON file on disk, no\n * database. The path comes from the plugin/CLI options.\n */\nimport { execFileSync } from 'node:child_process';\nimport { randomUUID } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from 'node:fs';\nimport { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nimport type { IterationElement, NoteContext, VariantReference, VariantRemoval } from './core/index';\nimport { MAX_FEEDBACK_LENGTH } from './core/limits';\nimport { withFileLock, writeJson } from './file-store';\n\nexport interface StoredNote extends NoteContext {\n id: string;\n route: string;\n feedback: string;\n element: IterationElement | null;\n done: boolean;\n doneAt: string | null;\n /** When an agent said it started on this note (`isotone --working <id>`).\n * Cleared when the note is closed or reopened. */\n workingAt?: string | null;\n createdAt: string;\n /** The window size the note was written at. Feedback about layout only\n * means something at the breakpoint it was seen on, so the agent reads\n * this next to the selector. Captured on create, kept on edit. */\n viewport?: { w: number; h: number };\n /** Host-specific context, stored and returned verbatim and never\n * interpreted here. A host with state that gives a note its meaning — the\n * knob settings a design note was written against, a feature-flag set, the\n * selected tenant — nests it under this key so the note survives a\n * round-trip through a store that knows nothing about it. Capped at\n * {@link PAYLOAD_MAX} serialized bytes. */\n payload?: unknown;\n}\n\n/** Serialized-size ceiling for a note's opaque `payload`, in bytes. Generous\n * next to normal review context and small\n * enough that a runaway host is rejected rather than bloating the file. */\nexport const PAYLOAD_MAX = 65_536;\n\nexport function readNotes(file: string): StoredNote[] {\n if (!existsSync(file)) return [];\n try {\n const raw = readFileSync(file, 'utf8').trim();\n if (!raw) return [];\n const data = JSON.parse(raw);\n if (!Array.isArray(data) || !data.every((note) => note && typeof note === 'object' &&\n typeof note.id === 'string' && typeof note.route === 'string' &&\n typeof note.feedback === 'string' && typeof note.createdAt === 'string')) {\n throw new Error('expected an array of notes');\n }\n return data as StoredNote[];\n } catch (cause) {\n throw new Error(`Could not read notes from ${file}; the file has been left untouched.`, { cause });\n }\n}\n\nexport function writeNotes(file: string, notes: StoredNote[]): void {\n // A `file` option can point into a directory that does not exist yet\n // (`notes/here.json`); the first note creates it.\n writeJson(file, notes);\n}\n\nexport function updateNotes(file: string, update: (notes: StoredNote[]) => StoredNote[]): void {\n withFileLock(file, () => writeNotes(file, update(readNotes(file))));\n}\n\n/**\n * Ensure the feedback file path is ignored by git via the repo-local\n * `.git/info/exclude` (never committed), so a consumer needs no `.gitignore`\n * edit. Idempotent and non-destructive; never fails the dev server.\n */\nexport function ensureFileIgnored(file: string): void {\n try {\n // Resolve through symlinks so the path is comparable with git's answer,\n // which is already canonical (macOS's /var → /private/var, for one).\n // The file itself may not exist yet, so canonicalize its directory.\n const dir = realpathSync(dirname(resolve(file)));\n const absolute = join(dir, basename(file));\n const gitRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {\n cwd: dir,\n encoding: 'utf8',\n // Outside a repo git writes \"fatal: not a git repository\" to stderr, and\n // inheriting it prints that over the server's own startup banner. The\n // throw is the signal we act on; the message is noise.\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n const pattern = excludePattern(gitRoot, absolute);\n if (!pattern) return;\n const infoExclude = excludeFile(dir);\n const existing = existsSync(infoExclude) ? readFileSync(infoExclude, 'utf8') : '';\n if (hasExcludePattern(existing, pattern)) return;\n const marker = existing && !existing.endsWith('\\n') ? '\\n' : '';\n // One comment header per exclude file, however many patterns land under it.\n const heading = '# isotone (local dev feedback loop)';\n const comment = existing.includes(heading) ? '' : `${heading}\\n`;\n mkdirSync(dirname(infoExclude), { recursive: true });\n writeFileSync(infoExclude, `${existing}${marker}${comment}${pattern}\\n`);\n } catch {\n // A stray dev-only feedback file is non-fatal if we can't ignore it.\n }\n}\n\n/**\n * Like {@link ensureFileIgnored}, for a directory: the pattern gets a trailing\n * slash so it matches the tree, not a file of the same name. Used for the\n * overlay variants directory when a host opts out of committing it.\n */\nexport function ensureDirIgnored(dir: string): void {\n try {\n const absolute = realpathSync(resolve(dir));\n const gitRoot = execFileSync('git', ['rev-parse', '--show-toplevel'], {\n cwd: absolute,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n const base = excludePattern(gitRoot, absolute);\n if (!base) return;\n const pattern = `${base}/`;\n const infoExclude = excludeFile(absolute);\n const existing = existsSync(infoExclude) ? readFileSync(infoExclude, 'utf8') : '';\n if (hasExcludePattern(existing, pattern) || hasExcludePattern(existing, base)) return;\n const marker = existing && !existing.endsWith('\\n') ? '\\n' : '';\n mkdirSync(dirname(infoExclude), { recursive: true });\n writeFileSync(\n infoExclude,\n `${existing}${marker}# isotone (local overlay variants)\\n${pattern}\\n`,\n );\n } catch {\n // Non-fatal, as above.\n }\n}\n\n/**\n * Where git reads the repo-local exclude file from, asked from `cwd`. Not\n * `<toplevel>/.git/info/exclude`: in a worktree `.git` is a file pointing at\n * the shared repository, and the exclude file lives in that common dir. Git\n * answers relative to the cwd when the file is under it, so resolve.\n */\nfunction excludeFile(cwd: string): string {\n const reported = execFileSync('git', ['rev-parse', '--git-path', 'info/exclude'], {\n cwd,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n return resolve(cwd, reported);\n}\n\n/**\n * The gitignore pattern that matches exactly `absolute` from `gitRoot`: its\n * path relative to the root with a leading slash, since a leading slash\n * anchors a pattern to the root of the repository. An absolute filesystem\n * path is read the same way and so matches nothing when the dev server runs\n * in a subdirectory of the checkout. Null when the file is outside the repo,\n * where no exclude entry could cover it.\n */\nexport function excludePattern(gitRoot: string, absolute: string): string | null {\n const rel = relative(gitRoot, absolute);\n if (!rel || rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {\n return null;\n }\n return `/${rel.split(sep).join('/')}`;\n}\n\n/** Whether `exclude` already carries `pattern`, with or without the anchor.\n * A hand-written bare `app/console/x.json` covers the file too (a pattern\n * with a slash inside it is root-relative either way), so it counts. */\nexport function hasExcludePattern(exclude: string, pattern: string): boolean {\n const bare = pattern.replace(/^\\//, '');\n return exclude\n .split(/\\r?\\n/)\n .some((line) => line.trim() === pattern || line.trim() === bare);\n}\n\nfunction toItem(n: StoredNote) {\n const item: {\n id: string;\n route: string;\n feedback: string;\n element: IterationElement | null;\n done: boolean;\n createdAt: string;\n workingAt?: string;\n viewport?: { w: number; h: number };\n payload?: unknown;\n } = {\n id: n.id,\n route: n.route,\n feedback: n.feedback,\n element: n.element,\n done: n.done,\n createdAt: n.createdAt,\n };\n // Absent stays absent: a host that never sends a payload should not start\n // seeing a null one appear in its notes.\n if (n.workingAt && !n.done) item.workingAt = n.workingAt;\n if (n.viewport !== undefined) item.viewport = n.viewport;\n if (n.payload !== undefined) item.payload = n.payload;\n return { ...item, ...(n.elements !== undefined ? { elements: n.elements } : {}),\n ...(n.variant !== undefined ? { variant: n.variant } : {}),\n ...(n.variantAction !== undefined ? { variantAction: n.variantAction } : {}) };\n}\n\n/** The transport-agnostic result of a notes request — any runtime (Vite\n * middleware, Next route handler, Bun serve, Vercel) can render it. */\nexport interface NotesResponse {\n status: number;\n /** Response headers object (frameworks set them on their own res). */\n headers: Record<string, string>;\n /** The JSON payload to send. */\n body: unknown;\n}\n\n/** A request the pure handler understands (framework-neutral). */\nexport interface NotesRequest {\n method?: string;\n url?: string;\n body?: unknown;\n}\n\n/**\n * The transport-agnostic core of the notes endpoint. Reads/writes the local\n * file and returns a `{ status, headers, body }` the caller renders. Works\n * under Vite middleware, Next route handler, Bun.serve, or Vercel.\n */\nexport function notesResponse(file: string, req: NotesRequest): NotesResponse {\n if (req.method === 'POST' || req.method === 'DELETE') {\n return withFileLock(file, () => handleNotesRequest(file, req));\n }\n return handleNotesRequest(file, req);\n}\n\nfunction handleNotesRequest(file: string, req: NotesRequest): NotesResponse {\n const method = req.method ?? '';\n const query = Object.fromEntries(\n new URL(req.url ?? '/', 'http://localhost').searchParams,\n );\n const json = (status: number, body: unknown): NotesResponse => ({\n status,\n headers: { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' },\n body,\n });\n\n if (method === 'GET') {\n // `?rev=<last>` is the panel's poll. The revision comes from the file's\n // metadata, so an unchanged store answers without reading or parsing the\n // file, however many notes it holds or how many tabs are polling. Servers\n // older than this ignore the parameter and send the plain array, which the\n // client treats as changed.\n const polled = query.rev !== undefined;\n const rev = polled ? notesRevision(file) : '';\n if (polled && rev === String(query.rev)) return json(200, { rev, unchanged: true });\n const route = String(query.route ?? '');\n const all = query.all !== undefined && String(query.all) !== '' && String(query.all) !== '0';\n const notes = polled ? readNotesAt(file, rev) : readNotes(file);\n // `?all=1` lists every note on every route, done ones included. `?route=`\n // scopes to a page; neither means the open backlog, which is what the\n // panel shows and polls.\n const rows = (\n all\n ? [...notes]\n : route\n ? notes.filter((n) => n.route === route)\n : notes.filter((n) => !n.done)\n )\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt))\n .map(toItem);\n return json(200, polled ? { rev, notes: rows } : rows);\n }\n\n if (method === 'DELETE') {\n const id = String(query.id ?? '');\n if (!id) return json(400, { error: 'id is required' });\n writeNotes(file, readNotes(file).filter((n) => n.id !== id));\n return json(200, { ok: true });\n }\n\n if (method === 'POST') {\n const body = (req.body ?? {}) as {\n id?: unknown;\n route?: unknown;\n feedback?: unknown;\n element?: unknown;\n payload?: unknown;\n viewport?: unknown;\n elements?: unknown;\n variant?: unknown;\n variantAction?: unknown;\n createdAt?: unknown;\n };\n const { id, route, feedback, element, payload, viewport } = body;\n if (id !== undefined && id !== null && (typeof id !== 'string' || !id || id.length > 200)) {\n return json(400, { error: 'id must be a non-empty string of at most 200 characters' });\n }\n if (typeof route !== 'string' || route === '') {\n return json(400, { error: 'route is required' });\n }\n const cleanFeedback = typeof feedback === 'string' ? feedback.trim() : '';\n if (cleanFeedback === '') {\n return json(400, { error: 'feedback is required' });\n }\n if (cleanFeedback.length > MAX_FEEDBACK_LENGTH) return json(400, { error: `feedback must be at most ${MAX_FEEDBACK_LENGTH} characters` });\n if (payload !== undefined && !withinPayloadCap(payload)) {\n return json(400, {\n error: `payload exceeds ${PAYLOAD_MAX} bytes or is not serializable`,\n });\n }\n const cleanViewport = viewportOf(viewport);\n if (body.elements !== undefined && (!Array.isArray(body.elements) || body.elements.length > 50)) {\n return json(400, { error: 'elements must be an array of at most 50 elements' });\n }\n const elements = body.elements === undefined ? undefined : (body.elements as unknown[]).map(elementOf).filter((el): el is IterationElement => el !== null);\n const variant = body.variant === undefined ? undefined : variantOf(body.variant);\n const variantAction = removalOf(body.variantAction);\n if (body.variantAction !== undefined && !variantAction) return json(400, { error: 'Invalid variant action' });\n let cleanElement: IterationElement | null = null;\n if (element && typeof element === 'object' && !Array.isArray(element)) {\n cleanElement = {\n tag: cap(String((element as { tag?: unknown }).tag ?? ''), 30),\n text: cap(String((element as { text?: unknown }).text ?? ''), 120),\n selector: cap(\n String((element as { selector?: unknown }).selector ?? ''),\n 300,\n ),\n };\n }\n const notes = readNotes(file);\n const defaultsRequest = cleanFeedback.startsWith('[set-defaults]');\n const previousDefaults = defaultsRequest && !notes.some((note) => note.id === id)\n ? notes.filter(n => !n.done && n.route === route && n.feedback.startsWith('[set-defaults]')).sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0]\n : undefined;\n const updateId = previousDefaults?.id ?? id;\n if (typeof updateId === 'string' && updateId !== '') {\n const existing = notes.find((n) => n.id === updateId && n.route === route);\n if (existing) {\n if (previousDefaults) {\n delete existing.elements;\n delete existing.variant;\n delete existing.variantAction;\n delete existing.payload;\n }\n existing.feedback = cleanFeedback;\n existing.element = cleanElement;\n if (elements !== undefined) {\n existing.elements = elements;\n existing.element = elements[0] ?? null;\n } else if (existing.elements) existing.element = existing.elements[0] ?? null;\n if (variant !== undefined) existing.variant = variant;\n if (variantAction) existing.variantAction = variantAction;\n // An omitted payload leaves the stored one alone, so a host that only\n // sends it on the first save does not lose it on the next autosave.\n if (payload !== undefined) existing.payload = payload;\n if (defaultsRequest && !existing.done) {\n existing.createdAt = new Date().toISOString();\n if (cleanViewport) existing.viewport = cleanViewport;\n writeNotes(file, notes.filter(n => n.id === existing.id || n.done || n.route !== route || !n.feedback.startsWith('[set-defaults]')));\n } else writeNotes(file, notes);\n return json(200, { id: existing.id });\n }\n }\n if (typeof id === 'string' && notes.some((note) => note.id === id)) {\n return json(409, { error: 'This note belongs to a different route' });\n }\n const newId = typeof id === 'string' ? id : randomUUID();\n const createdAt = typeof body.createdAt === 'string' && Number.isFinite(Date.parse(body.createdAt))\n ? new Date(body.createdAt).toISOString() : new Date().toISOString();\n notes.push({\n id: newId,\n route,\n feedback: cleanFeedback,\n element: cleanElement,\n done: false,\n doneAt: null,\n createdAt,\n ...(cleanViewport ? { viewport: cleanViewport } : {}),\n ...(payload !== undefined ? { payload } : {}),\n ...(elements !== undefined ? { elements, element: elements[0] ?? null } : {}),\n ...(variant !== undefined ? { variant } : {}),\n ...(variantAction ? { variantAction } : {}),\n });\n writeNotes(file, notes);\n return json(201, { id: newId });\n }\n\n return json(405, { error: 'method not allowed' });\n}\n\n// The last parse per file, keyed by revision: when the file changes, every\n// polling tab gets its answer from one read and one parse.\nconst parsed = new Map<string, { rev: string; notes: StoredNote[] }>();\nfunction readNotesAt(file: string, rev: string): StoredNote[] {\n const cached = parsed.get(file);\n if (cached && cached.rev === rev && rev !== 'none') return cached.notes;\n const notes = readNotes(file);\n parsed.set(file, { rev, notes });\n return notes;\n}\n\n/**\n * A cheap fingerprint of the notes file. Every store write replaces the file\n * by rename, which gives it a new inode; an in-place edit by hand changes the\n * nanosecond mtime or the size. A missing file has its own revision.\n */\nexport function notesRevision(file: string): string {\n try {\n const stat = statSync(file, { bigint: true });\n return `${stat.ino.toString(36)}.${stat.mtimeNs.toString(36)}.${stat.size.toString(36)}`;\n } catch {\n return 'none';\n }\n}\n\nfunction cap(v: string, max: number): string {\n return v.slice(0, max);\n}\n\nfunction elementOf(value: unknown): IterationElement | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const v = value as Partial<IterationElement>;\n if (typeof v.selector !== 'string' || !v.selector) return null;\n return { tag: cap(String(v.tag ?? ''), 30), text: cap(String(v.text ?? ''), 120), selector: cap(v.selector, 300) };\n}\n\nfunction variantOf(value: unknown): VariantReference | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const v = value as Partial<VariantReference>;\n if (typeof v.axisId !== 'string' || typeof v.label !== 'string') return null;\n return { axisId: cap(v.axisId, 200), label: cap(v.label, 200),\n ...(typeof v.optionId === 'string' ? { optionId: cap(v.optionId, 200) } : {}),\n ...(typeof v.optionLabel === 'string' ? { optionLabel: cap(v.optionLabel, 200) } : {}),\n ...(typeof v.value === 'string' ? { value: cap(v.value, 200) } : {}) };\n}\n\n/** A sane `{ w, h }` from an untrusted viewport value, else null. */\nfunction viewportOf(v: unknown): { w: number; h: number } | null {\n if (!v || typeof v !== 'object' || Array.isArray(v)) return null;\n const w = Math.round(Number((v as { w?: unknown }).w));\n const h = Math.round(Number((v as { h?: unknown }).h));\n if (!Number.isFinite(w) || !Number.isFinite(h)) return null;\n if (w <= 0 || h <= 0 || w > 100_000 || h > 100_000) return null;\n return { w, h };\n}\n\n/** Whether an opaque payload is serializable and inside {@link PAYLOAD_MAX}. */\nfunction withinPayloadCap(payload: unknown): boolean {\n try {\n return Buffer.byteLength(JSON.stringify(payload), 'utf8') <= PAYLOAD_MAX;\n } catch {\n return false; // circular or otherwise unserializable\n }\n}\n\n/** The endpoint path the client calls. */\nexport const NOTES_ENDPOINT = '/api/iteration/notes';\n\nfunction removalOf(value: unknown): VariantRemoval | undefined {\n if (!value || typeof value !== 'object') return;\n const v = value as Partial<VariantRemoval>;\n if (typeof v.axisId !== 'string' || !v.axisId || v.axisId.length > 200 || !['remove-option', 'remove-axis', 'keep-default'].includes(v.action ?? '')) return;\n if (v.action === 'remove-option' && (typeof v.optionId !== 'string' || !v.optionId || v.optionId.length > 200)) return;\n return { axisId: v.axisId, action: v.action as VariantRemoval['action'], ...(typeof v.optionId === 'string' ? { optionId: v.optionId.slice(0, 200) } : {}) };\n}\n","import { randomUUID } from 'node:crypto';\nimport { closeSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n/** Readers see either the old document or the complete replacement. */\nexport function writeJson(file: string, value: unknown): void {\n mkdirSync(dirname(file), { recursive: true });\n const temporary = `${file}.${randomUUID()}.tmp`;\n try {\n writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\\n`, { flag: 'wx', mode: 0o600 });\n renameSync(temporary, file);\n } finally {\n rmSync(temporary, { force: true });\n }\n}\n\n/** Serialize read-modify-write across the CLI and the dev server. A dead\n * process's lock is recoverable; a busy store fails without discarding data. */\nexport function withFileLock<T>(file: string, work: () => T): T {\n mkdirSync(dirname(file), { recursive: true });\n const lock = `${file}.lock`;\n const deadline = Date.now() + 2_000;\n while (true) {\n try {\n const fd = openSync(lock, 'wx', 0o600);\n try { writeFileSync(fd, String(process.pid)); } finally { closeSync(fd); }\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n try {\n const owner = Number(readFileSync(lock, 'utf8'));\n if (Number.isSafeInteger(owner) && owner > 0) {\n try { process.kill(owner, 0); } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === 'ESRCH') { rmSync(lock, { force: true }); continue; }\n }\n }\n } catch { /* The other writer may have just released its lock. */ }\n if (Date.now() >= deadline) throw new Error(`Local store is busy: ${file}. Retry after the other writer finishes.`);\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);\n }\n }\n try { return work(); } finally { rmSync(lock, { force: true }); }\n}\n"],"mappings":"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","names":["randomUUID","mkdirSync","readFileSync","writeFileSync","dirname","readFileSync","dirname","mkdirSync","writeFileSync","randomUUID"]}
|