isotone 1.1.3 → 1.2.1
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 +52 -5
- package/CHANGELOG.md +42 -0
- package/README.md +63 -14
- package/dist/{IterationPanel-phJRYtUO.d.ts → IterationPanel-Bq77d5NT.d.ts} +3 -1
- package/dist/{chunk-K5RAC2BT.js → chunk-2FY2JNPG.js} +3 -5
- package/dist/chunk-2FY2JNPG.js.map +1 -0
- package/dist/{chunk-RKZWO37H.js → chunk-BTJHQXF7.js} +85 -1
- 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-L6O4HYGP.js +1150 -0
- package/dist/chunk-L6O4HYGP.js.map +1 -0
- package/dist/{chunk-YTEUMO3O.js → chunk-LD7UWI7W.js} +17 -33
- package/dist/chunk-LD7UWI7W.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 +1015 -148
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +92 -2
- package/dist/core.js +10 -3
- package/dist/index.d.ts +13 -28
- package/dist/index.js +586 -257
- 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 +23 -19
- package/dist/vite-plugin.js.map +1 -1
- package/docs/blast.md +133 -0
- package/docs/integrations.md +75 -15
- package/docs/migration.md +19 -0
- package/docs/updates.md +110 -4
- package/docs/variants.md +65 -7
- package/package.json +2 -1
- package/dist/chunk-A4DX2V4C.js +0 -7
- package/dist/chunk-A4DX2V4C.js.map +0 -1
- package/dist/chunk-BRBXIGZP.js +0 -593
- package/dist/chunk-BRBXIGZP.js.map +0 -1
- package/dist/chunk-K5RAC2BT.js.map +0 -1
- package/dist/chunk-LXW6WFOC.js +0 -402
- package/dist/chunk-LXW6WFOC.js.map +0 -1
- package/dist/chunk-RKZWO37H.js.map +0 -1
- package/dist/chunk-RQ2YI5KS.js +0 -24
- package/dist/chunk-RQ2YI5KS.js.map +0 -1
- package/dist/chunk-YTEUMO3O.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/store.ts","../src/file-store.ts","../src/update-request.ts","../src/variants-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 { createHash, 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, NoteReply, VariantReference, VariantRemoval } from './core/index';\nimport { MAX_FEEDBACK_LENGTH } from './core/limits';\nimport { withFileLock, writeJson } from './file-store';\nimport { updateNoteText } from './core/update';\nimport { notesProjectRoot, updateRequestOf } from './update-request';\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, root: string) {\n const update = updateRequestOf(n.update, { root });\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 // An update note says what the server's request says, whatever the file holds.\n feedback: update ? updateNoteText(update) : 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 // Only the server writes an update request; a malformed one, or one whose\n // directory is outside the project, is not shown.\n ...(update ? { update } : {}),\n // Only the CLI writes a reply; a hand-edited, malformed one is not shown.\n ...(replyView(n) ? { reply: replyView(n) } : {}) };\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 root = notesProjectRoot(file);\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((note) => toItem(note, root));\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 // A body's `update` is never read: only the update endpoint writes one,\n // from the server's own check, and an update note cannot be edited.\n // Nor is `reply`: only the agent writes one, with `isotone --reply`.\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(String((element as { selector?: unknown }).selector ?? ''), MAX_SELECTOR),\n ...elementContext(element),\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?.update !== undefined) {\n return json(409, { error: 'An update note is written by Isotone and cannot be edited. Delete it to cancel the update.' });\n }\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\n/** Longest agent reply `isotone --reply` stores. */\nexport const MAX_REPLY_LENGTH = 2000;\n\n/** A stored reply, or null when it is missing or malformed. */\nexport function replyOf(value: unknown): NoteReply | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const v = value as Partial<NoteReply>;\n if (typeof v.text !== 'string' || !v.text.trim() || typeof v.at !== 'string' || !Number.isFinite(Date.parse(v.at))) return null;\n return { text: v.text.slice(0, MAX_REPLY_LENGTH), at: v.at, ...(typeof v.about === 'string' && v.about.length <= 64 ? { about: v.about } : {}) };\n}\n\n/**\n * What the agent was given for a note. A reviewer who edits a handed-out note\n * (to answer the agent's question, say) changes it, and the note is handed out\n * again.\n */\nexport function noteFingerprint(note: StoredNote): string {\n const update = updateRequestOf(note.update);\n const content = JSON.stringify([\n note.feedback, note.route,\n (note.elements ?? (note.element ? [note.element] : [])).map((element) => element.selector),\n note.variant ?? null, note.variantAction ?? null, update?.to ?? null,\n ]);\n return createHash('sha256').update(content).digest('base64url').slice(0, 16);\n}\n\n/**\n * A note's reply as readers see it. Once the note has changed since the agent\n * asked (the reviewer answers by editing it), the question is no longer\n * pending: it comes back marked `answered`.\n */\nexport function replyView(note: StoredNote): NoteReply | null {\n const reply = replyOf(note.reply);\n if (!reply) return null;\n return reply.about !== undefined && reply.about !== noteFingerprint(note) ? { ...reply, answered: true } : reply;\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 {\n tag: cap(String(v.tag ?? ''), 30),\n text: cap(String(v.text ?? ''), 120),\n selector: cap(v.selector, MAX_SELECTOR),\n ...elementContext(v),\n };\n}\n\n/** A path that climbs to the body in a deep tree runs long, and a cut one\n * points at some other element. */\nconst MAX_SELECTOR = 2000;\n\nfunction stringList(value: unknown, count: number, length: number): string[] {\n return Array.isArray(value)\n ? value.filter((item): item is string => typeof item === 'string' && item !== '').slice(0, count).map((item) => cap(item, length))\n : [];\n}\n\n/** The optional identifying fields, each bounded; anything malformed is dropped. */\nfunction elementContext(value: object): Partial<IterationElement> {\n const v = value as Record<string, unknown>;\n const out: Partial<IterationElement> = {};\n if (v.attributes && typeof v.attributes === 'object' && !Array.isArray(v.attributes)) {\n const entries = Object.entries(v.attributes)\n .filter((entry): entry is [string, string] => typeof entry[1] === 'string' && !UNSAFE_KEYS.has(entry[0]))\n .slice(0, 20)\n .map(([name, text]): [string, string] => [cap(name, 60), cap(text, 300)]);\n if (entries.length) out.attributes = Object.fromEntries(entries);\n }\n const component = stringList(v.component, 8, 80);\n if (component.length) out.component = component;\n const within = stringList(v.within, 6, 120);\n if (within.length) out.within = within;\n if (typeof v.source === 'string' && v.source) out.source = cap(v.source, 300);\n if (typeof v.near === 'string' && v.near) out.near = cap(v.near, 120);\n const rect = v.rect as Record<string, unknown> | null | undefined;\n if (rect && typeof rect === 'object' && [rect.x, rect.y, rect.w, rect.h].every((n) => typeof n === 'number' && Number.isFinite(n))) {\n out.rect = { x: rect.x, y: rect.y, w: rect.w, h: rect.h } as NonNullable<IterationElement['rect']>;\n }\n return out;\n}\n\nconst UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\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","/**\n * Version precedence and the validation of a stored update request. Kept free\n * of the package manifest and the network so the notes store can use it.\n */\nimport { existsSync, realpathSync, statSync } from 'node:fs';\nimport { dirname, isAbsolute, join, relative, sep } from 'node:path';\n\nimport type { UpdateRequest } from './core/update';\n\ninterface Version { parts: bigint[]; prerelease: string[] }\nexport function parseVersion(value: string): Version {\n if (value.length > 256) throw new Error('Invalid version.');\n const match = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([\\da-zA-Z-]+(?:\\.[\\da-zA-Z-]+)*))?(?:\\+([\\da-zA-Z-]+(?:\\.[\\da-zA-Z-]+)*))?$/.exec(value);\n if (!match) throw new Error('Invalid version.');\n const prerelease = match[4]?.split('.') ?? [];\n if (prerelease.some((part) => /^0\\d+$/.test(part))) throw new Error('Invalid version.');\n return { parts: match.slice(1, 4).map(BigInt), prerelease };\n}\n\n/** SemVer precedence, including numeric prereleases and ignored build metadata. */\nexport function compareVersions(a: string, b: string): number {\n const left = parseVersion(a), right = parseVersion(b);\n for (let i = 0; i < 3; i++) {\n if (left.parts[i] !== right.parts[i]) return left.parts[i] > right.parts[i] ? 1 : -1;\n }\n if (!left.prerelease.length || !right.prerelease.length) {\n return left.prerelease.length === right.prerelease.length ? 0 : left.prerelease.length ? -1 : 1;\n }\n for (let i = 0; i < Math.max(left.prerelease.length, right.prerelease.length); i++) {\n const x = left.prerelease[i], y = right.prerelease[i];\n if (x === y) continue;\n if (x === undefined || y === undefined) return x === undefined ? -1 : 1;\n const nx = /^\\d+$/.test(x), ny = /^\\d+$/.test(y);\n if (nx && ny) return BigInt(x) > BigInt(y) ? 1 : -1;\n if (nx !== ny) return nx ? -1 : 1;\n return x > y ? 1 : -1;\n }\n return 0;\n}\n\nfunction isVersion(value: unknown): value is string {\n if (typeof value !== 'string') return false;\n try { parseVersion(value); return true; } catch { return false; }\n}\n\nconst plainText = (value: unknown, max: number): value is string =>\n typeof value === 'string' && value !== '' && value.length <= max &&\n ![...value].some((char) => { const code = char.charCodeAt(0); return code < 32 || code === 127; });\n\ntype Manager = UpdateRequest['manager'];\nconst PACKAGE_NAME = 'isotone';\n/** MAJOR.MINOR.PATCH with an optional prerelease, and no build metadata. */\nconst STRICT_VERSION = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[\\da-zA-Z-]+(?:\\.[\\da-zA-Z-]+)*)?$/;\n\n/** A version an upgrade command may name: strict SemVer, nothing a shell could read as more. */\nexport function isStrictVersion(value: unknown): value is string {\n return typeof value === 'string' && value.length <= 256 && STRICT_VERSION.test(value) && isVersion(value);\n}\n\n/**\n * The upgrade command for Isotone, the one form `check-update` suggests: the\n * project's manager, an exact version, `-D` for a development dependency, and\n * the workspace-root flag where that manager needs one (pnpm `-w`, Yarn\n * Classic `-W`).\n */\nexport function upgradeCommand(manager: Manager, version: string, flags: { dev: boolean; workspace: boolean }): string {\n if (!isStrictVersion(version)) throw new Error('Invalid version.');\n const dev = flags.dev ? ' -D' : '';\n if (manager === 'npm') return `npm install${dev} --save-exact ${PACKAGE_NAME}@${version}`;\n const workspace = flags.workspace ? (manager === 'pnpm' ? ' -w' : ' -W') : '';\n return `${manager} add${dev}${workspace} --exact ${PACKAGE_NAME}@${version}`;\n}\n\n/** Every command an update note may carry for this manager and version. */\nexport function canonicalUpgradeCommands(manager: Manager, version: string): string[] {\n if (!isStrictVersion(version)) return [];\n const commands: string[] = [];\n for (const dev of [true, false]) {\n for (const workspace of manager === 'npm' ? [false] : [false, true]) commands.push(upgradeCommand(manager, version, { dev, workspace }));\n }\n return commands;\n}\n\n/**\n * The project a notes file belongs to: the nearest directory holding it that\n * is a Git checkout, or the file's own directory outside one. An update note\n * may only name a directory inside it.\n */\nexport function notesProjectRoot(file: string): string {\n const start = dirname(file);\n for (let directory = start; ; directory = dirname(directory)) {\n if (existsSync(join(directory, '.git'))) return directory;\n if (dirname(directory) === directory) return start;\n }\n}\n\n/** Whether `cwd` is an existing directory at or below `root`, after symlinks. */\nexport function isInside(cwd: string, root: string): boolean {\n try {\n const target = realpathSync(cwd);\n if (!statSync(target).isDirectory()) return false;\n const path = relative(realpathSync(root), target);\n return path === '' || (path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path));\n } catch { return false; }\n}\n\n/**\n * A stored update request, or null when any part of it is malformed. The\n * notes file is a plain JSON file in the project, so a hand edit is checked\n * before an agent is told to run the command in it: the command must be\n * exactly the one `check-update` would build for that manager and version,\n * and, given the project root, the directory must be inside it.\n */\nexport function updateRequestOf(value: unknown, context: { root?: string } = {}): UpdateRequest | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const v = value as Partial<UpdateRequest>;\n if (!isVersion(v.from) || !isStrictVersion(v.to)) return null;\n if (v.manager !== 'npm' && v.manager !== 'pnpm' && v.manager !== 'yarn') return null;\n if (!plainText(v.command, 500) || !plainText(v.cwd, 4096)) return null;\n if (!canonicalUpgradeCommands(v.manager, v.to).includes(v.command)) return null;\n if (!isAbsolute(v.cwd)) return null;\n if (context.root !== undefined && !isInside(v.cwd, context.root)) return null;\n if (v.changelog !== undefined && !(plainText(v.changelog, 500) && v.changelog.startsWith('https://'))) return null;\n return { from: v.from, to: v.to, command: v.command, cwd: v.cwd, manager: v.manager,\n ...(v.changelog !== undefined ? { changelog: v.changelog } : {}) };\n}\n\n/** Where a release's notes live: its tag's CHANGELOG, also shipped in the package. */\nexport function changelogUrl(version: string): string {\n parseVersion(version);\n return `https://github.com/aaroi/isotone/blob/v${version}/CHANGELOG.md`;\n}\n","/**\n * isotone — overlay variants store (node only).\n *\n * An overlay variant is a directory of alternative source files under\n * `.isotone/variants/<id>/`, mirrored at their repo-relative paths, plus\n * a `variant.json` manifest recording which git blob each override was made\n * from. That blob is the three-way merge base: when the real file moves on,\n * `syncVariant` diffs the override against what it started from and what\n * the file is now, the same way a branch rebases. Everything here is\n * synchronous and framework-free so the CLI and the Vite plugin (a\n * different owner) can both sit on top of it.\n */\nimport { execFileSync } from 'node:child_process';\nimport { createHash, randomUUID } from 'node:crypto';\nimport {\n existsSync,\n mkdirSync,\n lstatSync,\n readdirSync,\n readFileSync,\n realpathSync,\n rmSync,\n writeFileSync,\n} from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nimport type { IterationElement } from './core/index';\nimport {\n type ColorVariantAxis,\n type KnobAxis,\n OVERLAY_GROUP_ID,\n overlayGroups,\n type OverlayVariant,\n type OverlayVariantStatus,\n type RangeVariantAxis,\n type SelectVariantAxis,\n} from './core/variants';\nimport { ensureFileIgnored, type NotesRequest, type NotesResponse } from './store';\nimport { withFileLock, writeJson } from './file-store';\n\n/** Default location of the variants tree, relative to the project root. */\nexport const DEFAULT_VARIANTS_DIR = '.isotone/variants';\n\n/** Relative to root. Holds `{ \"active\": [\"<id>\", ...] }`, at most one id per\n * group. The Vite plugin keeps this out of git; nothing here assumes that. */\nexport const ACTIVE_FILE = '.isotone/active.json';\n\n/** Relative to root. A JSON array of {@link KnobAxis}; committed, since the\n * defaults in it are part of the design. Missing or malformed reads as none. */\nexport const KNOBS_FILE = '.isotone/knobs.json';\n\nexport interface VariantsStoreOptions {\n /** Project root: Vite's root or the CLI's cwd. Absolute. */\n root: string;\n /** Variants directory, relative to root or absolute. Default {@link DEFAULT_VARIANTS_DIR}. */\n dir?: string;\n}\n\ninterface ActiveFileShape {\n active: string[];\n}\n\n/** Agents in separate processes may edit the same manifest. Keep their\n * read-modify-write operations together, including promotion and removal. */\nfunction mutateVariants<T>(opts: VariantsStoreOptions, change: () => T): T {\n return withFileLock(join(variantsDir(opts), '.mutation'), change);\n}\n\n/* ── paths ────────────────────────────────────────────────────────────── */\n\nexport function variantsDir(opts: VariantsStoreOptions): string {\n const dir = opts.dir ?? DEFAULT_VARIANTS_DIR;\n return isAbsolute(dir) ? dir : resolve(opts.root, dir);\n}\n\nexport function variantDir(opts: VariantsStoreOptions, id: string): string {\n if (!isValidVariantId(id)) throw new Error(`Invalid variant id \"${id}\"`);\n return join(variantsDir(opts), id);\n}\n\nexport function isValidVariantId(id: string): boolean {\n return /^[a-z0-9][a-z0-9-]{0,63}$/.test(id);\n}\n\n/** POSIX-ify a path node gave us as platform-native (a no-op on POSIX,\n * where `sep` already is `/`; keeps manifest keys portable regardless). */\nfunction toPosixPath(p: string): string {\n return p.split(sep).join('/');\n}\n\n/** `relative()` puts you outside the base with a leading `..` (or, on a\n * different drive on Windows, an absolute path back out) — either way the\n * caller asked for something outside the tree we're allowed to touch. */\nfunction isEscaping(rel: string): boolean {\n return rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel);\n}\n\n/** Check existing ancestors too: a lexical child may be a symlink outside\n * the tree, including a not-yet-created file beneath a symlinked directory. */\nfunction staysInside(base: string, file: string): boolean {\n const canonical = (path: string): string => {\n let existing = resolve(path);\n while (dirname(existing) !== existing) {\n try { lstatSync(existing); break; } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;\n existing = dirname(existing);\n }\n }\n return resolve(realpathSync(existing), relative(existing, resolve(path)));\n };\n return !isEscaping(relative(canonical(base), canonical(file)));\n}\n\n/**\n * Resolve `file` (repo-relative, or absolute inside root) against `root`\n * and return both the absolute path and its POSIX repo-relative manifest\n * key. Throws for anything that escapes root, so every caller that takes a\n * user-supplied path gets the same guard.\n */\nfunction repoRelativeKey(root: string, file: string): { abs: string; key: string } {\n const abs = isAbsolute(file) ? resolve(file) : resolve(root, file);\n const rel = relative(root, abs);\n if (!rel || isEscaping(rel) || !isSafeManifestKey(toPosixPath(rel)) || !staysInside(root, abs)) {\n throw new Error(`\"${file}\" escapes the project root`);\n }\n return { abs, key: toPosixPath(rel) };\n}\n\n/* ── manifest read/write ──────────────────────────────────────────────── */\n\n/**\n * A manifest key is joined onto the variant dir and onto the project root,\n * so a key that climbs out of either (`../../x`, an absolute path, or a\n * backslash that Windows would read as a separator) would let a hand-edited\n * or checked-in variant.json reach files outside both trees. Such a manifest\n * is not a variant at all.\n */\nfunction isSafeManifestKey(key: string): boolean {\n if (!key || isAbsolute(key) || /[\\\\:\\0]/.test(key) || key === 'variant.json') return false;\n return !key.split('/').some((segment) => !segment || segment === '.' || segment === '..' || segment === '.git');\n}\n\nfunction isPlausibleVariant(v: unknown): v is OverlayVariant {\n if (!v || typeof v !== 'object' || Array.isArray(v)) return false;\n const o = v as Record<string, unknown>;\n return (\n typeof o.id === 'string' &&\n typeof o.label === 'string' &&\n typeof o.createdAt === 'string' &&\n (o.route === undefined || typeof o.route === 'string') &&\n (o.description === undefined || typeof o.description === 'string') &&\n (o.group === undefined || (typeof o.group === 'string' && OVERLAY_GROUP_ID.test(o.group))) &&\n (o.groupLabel === undefined || typeof o.groupLabel === 'string') &&\n (o.section === undefined || typeof o.section === 'string') &&\n !!o.files &&\n typeof o.files === 'object' &&\n !Array.isArray(o.files) &&\n Object.entries(o.files).every(([key, entry]) => isSafeManifestKey(key) && entry && typeof entry === 'object' &&\n ('base' in entry) && (entry.base === null || (typeof entry.base === 'string' && /^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(entry.base))) &&\n (!('added' in entry) || entry.added === true))\n );\n}\n\nexport function readVariant(opts: VariantsStoreOptions, id: string): OverlayVariant | null {\n // An id that is not a valid directory name (`..`, a path with a slash)\n // would resolve to somewhere outside the variants dir; there is no\n // variant there by definition.\n if (!isValidVariantId(id)) return null;\n const file = join(variantDir(opts, id), 'variant.json');\n if (!existsSync(file)) return null;\n try {\n const data = JSON.parse(readFileSync(file, 'utf8'));\n const dir = variantDir(opts, id);\n return isPlausibleVariant(data) && data.id === id && staysInside(variantsDir(opts), file) &&\n Object.keys(data.files).every((key) => staysInside(opts.root, join(opts.root, key)) && staysInside(dir, join(dir, key))) ? data : null;\n } catch {\n // Junk directory: unreadable or not JSON. listVariants skips it.\n return null;\n }\n}\n\nexport function writeVariant(opts: VariantsStoreOptions, v: OverlayVariant): void {\n if (!isPlausibleVariant(v)) throw new Error('Invalid variant manifest');\n const dir = variantDir(opts, v.id);\n if (!staysInside(variantsDir(opts), dir)) throw new Error('Variant directory escapes the variants tree');\n mkdirSync(dir, { recursive: true });\n writeJson(join(dir, 'variant.json'), v);\n}\n\n/** Whether a tracked file has drifted from what its override was derived\n * from. `base: null` (never hashed, or the manifest was hand-edited to\n * simulate an unreachable blob) counts as stale too: there's nothing to\n * compare against, so we can't claim it's unchanged. */\nfunction isStale(base: string | null, currentHash: string | null): boolean {\n return base === null || currentHash === null || currentHash !== base;\n}\n\n/** Every valid manifest, oldest first, without the per-file status that\n * {@link listVariants} hashes for. */\nexport function readVariants(opts: VariantsStoreOptions): OverlayVariant[] {\n const dir = variantsDir(opts);\n if (!existsSync(dir)) return [];\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => readVariant(opts, e.name))\n .filter((v): v is OverlayVariant => v !== null)\n .sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n}\n\nexport function listVariants(opts: VariantsStoreOptions): OverlayVariantStatus[] {\n const dir = variantsDir(opts);\n if (!existsSync(dir)) return [];\n const root = resolve(opts.root);\n const ids = readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n\n const statuses: OverlayVariantStatus[] = [];\n const hashes = new Map<string, string | null>();\n for (const id of ids) {\n const manifest = readVariant(opts, id);\n if (!manifest) continue; // no manifest, or invalid JSON: not a variant\n const stale: string[] = [];\n const missing: string[] = [];\n for (const [file, entry] of Object.entries(manifest.files)) {\n const overrideAbs = join(dir, id, file);\n if (!existsSync(overrideAbs)) {\n missing.push(file);\n continue;\n }\n // A file the variant adds is current for as long as the project still\n // lacks it; once a real one appears, the two need a person.\n if (entry.added) {\n if (existsSync(join(root, file))) stale.push(file);\n continue;\n }\n const hashKey = `${entry.base?.length}:${file}`;\n if (!hashes.has(hashKey)) hashes.set(hashKey, hashFile(join(root, file), entry.base));\n if (isStale(entry.base, hashes.get(hashKey) ?? null)) stale.push(file);\n }\n statuses.push({ ...manifest, stale, missing });\n }\n statuses.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n return statuses;\n}\n\nexport function createVariant(\n opts: VariantsStoreOptions,\n input: {\n id: string;\n label?: string;\n route?: string;\n element?: IterationElement | null;\n note?: string;\n description?: string;\n group?: string;\n groupLabel?: string;\n section?: string;\n },\n): OverlayVariant {\n return mutateVariants(opts, () => {\n if (!isValidVariantId(input.id)) {\n throw new Error(`Invalid variant id \"${input.id}\"`);\n }\n if (input.group !== undefined) assertGroupId(input.group);\n if (readVariant(opts, input.id)) {\n throw new Error(`Variant \"${input.id}\" already exists`);\n }\n const variant: OverlayVariant = {\n id: input.id,\n label: input.label || input.id,\n createdAt: new Date().toISOString(),\n files: {},\n ...(input.route !== undefined ? { route: input.route } : {}),\n ...(input.element !== undefined ? { element: input.element } : {}),\n ...(input.note !== undefined ? { note: input.note } : {}),\n ...(input.description !== undefined ? { description: input.description } : {}),\n ...(input.group !== undefined ? { group: input.group } : {}),\n ...(input.groupLabel !== undefined ? { groupLabel: input.groupLabel } : {}),\n ...(input.section !== undefined ? { section: input.section } : {}),\n };\n writeVariant(opts, variant);\n return variant;\n });\n}\n\nfunction assertGroupId(group: string): void {\n if (!OVERLAY_GROUP_ID.test(group)) {\n throw new Error(`Invalid group id \"${group}\" (lowercase letters, digits and hyphens)`);\n }\n}\n\n/** Put an existing variant into a group, so two variants made separately\n * become alternatives of one decision. `groupLabel` is left as it was\n * unless given. */\nexport function setVariantGroup(\n opts: VariantsStoreOptions,\n id: string,\n group: string,\n groupLabel?: string,\n): OverlayVariant {\n return mutateVariants(opts, () => {\n const manifest = readVariant(opts, id);\n if (!manifest) throw new Error(`No variant \"${id}\"`);\n assertGroupId(group);\n manifest.group = group;\n if (groupLabel !== undefined) manifest.groupLabel = groupLabel;\n writeVariant(opts, manifest);\n return manifest;\n });\n}\n\n/**\n * Put an existing variant's selector in a panel section, or take it out of\n * every section with `''`. `undefined` removes the field, so the group label's\n * prefix (`\"Header > Logo\"`) decides again.\n */\nexport function setVariantSection(\n opts: VariantsStoreOptions,\n id: string,\n section: string | undefined,\n): OverlayVariant {\n return mutateVariants(opts, () => {\n const manifest = readVariant(opts, id);\n if (!manifest) throw new Error(`No variant \"${id}\"`);\n if (section === undefined) delete manifest.section;\n else manifest.section = section.replace(/\\s+/g, ' ').trim();\n writeVariant(opts, manifest);\n return manifest;\n });\n}\n\n/**\n * Copy a real file into the variant. With `create`, the file is one the\n * project does not have yet: the variant gets an empty file at that path,\n * and the plugin resolves imports of it while the variant is loaded.\n */\nexport function addVariantFile(\n opts: VariantsStoreOptions,\n id: string,\n file: string,\n { create = false }: { create?: boolean } = {},\n): OverlayVariant {\n return mutateVariants(opts, () => {\n const manifest = readVariant(opts, id);\n if (!manifest) throw new Error(`No variant \"${id}\"`);\n const root = resolve(opts.root);\n const { abs: sourceAbs, key } = repoRelativeKey(root, file);\n // Idempotent: a file already tracked keeps its override and its base\n // untouched. Re-adding is a no-op, not a refresh (sync is how you refresh).\n if (Object.hasOwn(manifest.files, key)) return manifest;\n const destAbs = join(variantDir(opts, id), key);\n if (!staysInside(variantDir(opts, id), destAbs)) throw new Error('Override path escapes the variant directory');\n if (create) {\n if (existsSync(sourceAbs)) throw new Error(`\"${file}\" already exists; add it without --new`);\n mkdirSync(dirname(destAbs), { recursive: true });\n if (!existsSync(destAbs)) writeFileSync(destAbs, '');\n manifest.files = { ...manifest.files, [key]: { base: null, added: true } };\n writeVariant(opts, manifest);\n return manifest;\n }\n if (!existsSync(sourceAbs)) throw new Error(`\"${file}\" does not exist. Use --new for a file the variant adds.`);\n mkdirSync(dirname(destAbs), { recursive: true });\n writeFileSync(destAbs, readFileSync(sourceAbs));\n manifest.files = { ...manifest.files, [key]: { base: blobOf(sourceAbs) } };\n writeVariant(opts, manifest);\n return manifest;\n });\n}\n\nexport function removeVariantFile(\n opts: VariantsStoreOptions,\n id: string,\n file: string,\n): OverlayVariant {\n return mutateVariants(opts, () => {\n const manifest = readVariant(opts, id);\n if (!manifest) throw new Error(`No variant \"${id}\"`);\n const root = resolve(opts.root);\n const { key } = repoRelativeKey(root, file);\n if (Object.hasOwn(manifest.files, key)) {\n delete manifest.files[key];\n rmSync(join(variantDir(opts, id), key), { force: true });\n writeVariant(opts, manifest);\n }\n return manifest;\n });\n}\n\n/** The marker `git merge-file` leaves at the top of a conflicted hunk, with\n * the label `syncVariant` gives the override side. */\nconst CONFLICT_MARKER = '<<<<<<< variant';\n\n/**\n * Land a variant: copy every override over its real file, then delete the\n * variant. Refuses when an override would silently discard work: a file\n * whose real source moved on since the override was made (run `syncVariant`\n * first) or an override still carrying merge conflict markers. `force`\n * skips both checks for the case where the reviewer has decided the\n * override wins as-is.\n */\nexport function promoteVariant(\n opts: VariantsStoreOptions,\n id: string,\n { force = false }: { force?: boolean } = {},\n): { files: string[]; rebased: string[] } {\n return mutateVariants(opts, () => {\n // Checked before the manifest read so the rmSync below can never be\n // handed a directory the id escaped into.\n if (!isValidVariantId(id)) throw new Error(`Invalid variant id \"${id}\"`);\n const manifest = readVariant(opts, id);\n if (!manifest) throw new Error(`No variant \"${id}\"`);\n const active = readActive(opts);\n const root = resolve(opts.root);\n const vDir = variantDir(opts, id);\n const missing = Object.keys(manifest.files).filter((key) => !existsSync(join(vDir, key)));\n if (missing.length) throw new Error(`\"${id}\" has missing overrides: ${missing.join(', ')}. Restore or remove them before promoting.`);\n if (!force) {\n const status = listVariants(opts).find((v) => v.id === id);\n if (status?.stale.length) {\n // A null base is stale by definition, but the file did not necessarily\n // change: there was simply no git to record what it looked like.\n const reasons = status.stale.map((file) =>\n manifest.files[file]?.base === null\n ? `no base recorded for ${file} (not a git repo when the override was made)`\n : `${file} changed since the override was made`,\n );\n throw new Error(\n `\"${id}\" is stale (${reasons.join('; ')}). Run \\`variant sync ${id}\\` first, or promote with --force.`,\n );\n }\n const conflicted = Object.keys(manifest.files).filter((key) => {\n const overrideAbs = join(vDir, key);\n return existsSync(overrideAbs) && readFileSync(overrideAbs, 'utf8').includes(CONFLICT_MARKER);\n });\n if (conflicted.length) {\n throw new Error(\n `\"${id}\" has unresolved conflict markers in ${conflicted.join(', ')}. Resolve them, or promote with --force.`,\n );\n }\n }\n const siblings = overlayGroups(readVariants(opts)).find((g) => g.members.some((m) => m.id === id))\n ?.members.filter((m) => m.id !== id) ?? [];\n const files: string[] = [];\n const landed = new Map<string, { before: string | null; after: string | null }>();\n for (const key of Object.keys(manifest.files)) {\n const overrideAbs = join(vDir, key);\n const destAbs = join(root, key);\n const before = existsSync(destAbs) ? currentBlob(destAbs) : null;\n mkdirSync(dirname(destAbs), { recursive: true });\n writeFileSync(destAbs, readFileSync(overrideAbs));\n landed.set(key, { before, after: blobOf(destAbs) });\n files.push(key);\n }\n rmSync(vDir, { recursive: true, force: true });\n if (active.includes(id)) writeActive(opts, active.filter((a) => a !== id));\n return { files, rebased: rebaseSiblings(opts, siblings, landed) };\n });\n}\n\n/**\n * A sibling is another answer to the decision the promote just settled, so the\n * promoted file is what it now differs from: reading it as out of date would\n * only send someone to merge two alternatives into each other. Only a copy made\n * from the version the promote replaced moves. One that was already behind\n * stays stale, because the real file changed in ways nobody has looked at.\n */\nfunction rebaseSiblings(\n opts: VariantsStoreOptions,\n siblings: OverlayVariant[],\n landed: Map<string, { before: string | null; after: string | null }>,\n): string[] {\n const rebased: string[] = [];\n for (const sibling of siblings) {\n let moved = false;\n for (const [key, entry] of Object.entries(sibling.files)) {\n const change = landed.get(key);\n if (!change?.after || !(entry.added || (entry.base !== null && entry.base === change.before))) continue;\n sibling.files[key] = { base: change.after };\n moved = true;\n }\n if (!moved) continue;\n writeVariant(opts, sibling);\n rebased.push(sibling.id);\n }\n return rebased;\n}\n\nexport function dropVariant(opts: VariantsStoreOptions, id: string): void {\n mutateVariants(opts, () => {\n // `variant drop ..` would otherwise resolve to the variants dir's parent\n // and rmSync it, recursively.\n if (!isValidVariantId(id)) throw new Error(`Invalid variant id \"${id}\"`);\n const active = readActive(opts);\n rmSync(variantDir(opts, id), { recursive: true, force: true });\n if (active.includes(id)) writeActive(opts, active.filter((a) => a !== id));\n });\n}\n\n/* ── git blob hashing ─────────────────────────────────────────────────── */\n\n/** A failed lookup is an answer here (`null`), not something to print. */\nconst GIT_QUIET: ['ignore', 'pipe', 'ignore'] = ['ignore', 'pipe', 'ignore'];\n\n/**\n * `git hash-object` looks up the repository from its cwd, not from the\n * path it's hashing, so every call here runs with cwd set to the file's own\n * directory — always inside the right repo regardless of where the caller's\n * process happens to be.\n */\nexport function blobOf(absPath: string): string | null {\n try {\n return execFileSync('git', ['hash-object', '--no-filters', '-w', absPath], {\n cwd: dirname(absPath),\n encoding: 'utf8',\n stdio: GIT_QUIET,\n }).trim();\n } catch {\n return null;\n }\n}\n\nexport function currentBlob(absPath: string): string | null {\n try {\n return execFileSync('git', ['hash-object', '--no-filters', absPath], {\n cwd: dirname(absPath),\n encoding: 'utf8',\n stdio: GIT_QUIET,\n }).trim();\n } catch {\n return null;\n }\n}\n\n/** Git blob hashing is just a header and the file bytes. Existing bases tell\n * us the repository's hash format, avoiding a process per file on every poll. */\nfunction hashFile(file: string, base: string | null): string | null {\n if (base === null) return null;\n try {\n const contents = readFileSync(file);\n return createHash(base.length === 64 ? 'sha256' : 'sha1')\n .update(`blob ${contents.length}\\0`).update(contents).digest('hex');\n } catch { return null; }\n}\n\nfunction catFile(cwd: string, hash: string): string | null {\n try {\n return execFileSync('git', ['cat-file', '-p', hash], { cwd, encoding: 'utf8', stdio: GIT_QUIET });\n } catch {\n return null; // the blob isn't reachable (gc'd, or never written)\n }\n}\n\n/** Runs `git merge-file -p <override> <base> <current>`, capturing stdout\n * whether it exits clean (0) or with conflict markers. execFileSync throws\n * on a nonzero exit, but the thrown error still carries stdout and the exit\n * status, so both are read from there. `status` is null when git never\n * ran to completion (killed by a signal, or not found). */\nfunction mergeFile(overrideAbs: string, baseAbs: string, currentAbs: string): {\n status: number | null;\n stdout: string;\n} {\n try {\n const stdout = execFileSync(\n 'git',\n [\n 'merge-file',\n '-p',\n '-L',\n 'variant',\n '-L',\n 'base',\n '-L',\n 'default',\n overrideAbs,\n baseAbs,\n currentAbs,\n ],\n { encoding: 'utf8', stdio: GIT_QUIET },\n );\n return { status: 0, stdout };\n } catch (err) {\n const e = err as { status?: number | null; stdout?: string };\n return { status: e.status ?? null, stdout: e.stdout ?? '' };\n }\n}\n\n/** `git merge-file` exits with the number of conflicts it left, so 1..127\n * is a conflicted merge whose stdout still carries the whole file (with\n * markers). 128 and up, or no status at all, is git failing outright. */\nfunction isConflictStatus(status: number | null): boolean {\n return status !== null && status >= 1 && status <= 127;\n}\n\n/** The base a later sync merges from has to be in git's object store. A file\n * git does not track yet is in there only if this writes it. */\nfunction recordBase(absPath: string, hash: string): string {\n return blobOf(absPath) ?? hash;\n}\n\nexport function syncVariant(\n opts: VariantsStoreOptions,\n id: string,\n): { merged: string[]; conflicts: string[]; unchanged: string[] } {\n return mutateVariants(opts, () => {\n const manifest = readVariant(opts, id);\n if (!manifest) throw new Error(`No variant \"${id}\"`);\n const root = resolve(opts.root);\n const vDir = variantDir(opts, id);\n\n const merged: string[] = [];\n const conflicts: string[] = [];\n const unchanged: string[] = [];\n let dirty = false;\n\n for (const [file, entry] of Object.entries(manifest.files)) {\n const currentAbs = join(root, file);\n const overrideAbs = join(vDir, file);\n if (entry.added) {\n // Nothing to merge into; a real file that appeared meanwhile is a\n // conflict for a person to settle.\n (existsSync(currentAbs) ? conflicts : unchanged).push(file);\n continue;\n }\n const currentHash = currentBlob(currentAbs);\n\n if (!isStale(entry.base, currentHash)) {\n unchanged.push(file);\n continue;\n }\n\n // No base to diff against (never hashed, or the blob is unreachable), or\n // no current file to merge into: nothing to three-way merge, so the\n // override is left as-is and flagged for a person to sort out.\n const baseContent = entry.base !== null ? catFile(dirname(currentAbs), entry.base) : null;\n if (baseContent === null || currentHash === null) {\n conflicts.push(file);\n continue;\n }\n\n const baseTmp = join(tmpdir(), `isotone-base-${randomUUID()}`);\n writeFileSync(baseTmp, baseContent);\n try {\n const result = mergeFile(overrideAbs, baseTmp, currentAbs);\n if (result.status === 0) {\n writeFileSync(overrideAbs, result.stdout);\n entry.base = recordBase(currentAbs, currentHash);\n dirty = true;\n merged.push(file);\n } else if (isConflictStatus(result.status)) {\n // The markers go into the override for a person to resolve, and the\n // base advances anyway, the way git records a conflicted merge:\n // the current file is now what the override was reconciled against,\n // so resolving the markers is all that stands between it and\n // promote. Left behind, the base would make the resolved file read\n // as stale and a second sync would re-conflict the same hunks; the\n // marker check in promoteVariant is the guard that remains.\n writeFileSync(overrideAbs, result.stdout);\n entry.base = recordBase(currentAbs, currentHash);\n dirty = true;\n conflicts.push(file);\n } else {\n // Git failed outright; its empty stdout must not replace the\n // override, and the base stays where it was.\n conflicts.push(file);\n }\n } finally {\n rmSync(baseTmp, { force: true });\n }\n }\n\n if (dirty) writeVariant(opts, manifest);\n return { merged, conflicts, unchanged };\n });\n}\n\n/* ── active variants / vite load() lookup ─────────────────────────── */\n\n/**\n * The override that answers for `absSourcePath` under the active set, or\n * `null` when the real file stands. The first active variant whose manifest\n * names the file wins; `readActive` and the endpoint keep the set free of\n * two variants over one file, so in practice there is only ever one.\n */\nexport function resolveOverride(\n opts: VariantsStoreOptions,\n active: string[],\n absSourcePath: string,\n): string | null {\n if (active.length === 0) return null;\n const root = resolve(opts.root);\n const relToRoot = relative(root, resolve(absSourcePath));\n if (isEscaping(relToRoot)) return null;\n const key = toPosixPath(relToRoot);\n for (const id of active) {\n const manifest = readVariant(opts, id);\n if (!manifest || !Object.hasOwn(manifest.files, key)) continue;\n const overrideAbs = join(variantDir(opts, id), key);\n if (existsSync(overrideAbs)) return overrideAbs;\n }\n return null;\n}\n\nexport function overrideSourcePath(\n opts: VariantsStoreOptions,\n absOverridePath: string,\n): { id: string; file: string } | null {\n const rel = relative(variantsDir(opts), resolve(absOverridePath));\n if (!rel || isEscaping(rel)) return null;\n const [id, ...rest] = toPosixPath(rel).split('/');\n if (!id || rest.length === 0) return null;\n return { id, file: rest.join('/') };\n}\n\n/**\n * Two active variants over the same file, as `[first, second, file]`, or\n * `null` when the set is consistent. `load` can only serve one copy of a\n * file, so such a set is refused by the endpoint and trimmed by\n * {@link readActive}. Ids without a manifest are skipped; the callers\n * check existence themselves.\n */\nexport function activeConflict(\n opts: VariantsStoreOptions,\n ids: string[],\n): [string, string, string] | null {\n const owner = new Map<string, string>();\n for (const id of ids) {\n const manifest = readVariant(opts, id);\n for (const file of Object.keys(manifest?.files ?? {})) {\n const first = owner.get(file);\n if (first !== undefined && first !== id) return [first, id, file];\n owner.set(file, id);\n }\n }\n return null;\n}\n\n/**\n * Variants in another group that override one of this variant's files. Groups\n * are independent selectors, so picking both of these fails at activation.\n */\nexport function crossGroupOverlaps(opts: VariantsStoreOptions, id: string): { file: string; other: string }[] {\n const variants = readVariants(opts);\n const self = variants.find((v) => v.id === id);\n if (!self) return [];\n const siblings = overlayGroups(variants).find((g) => g.members.some((m) => m.id === id))?.members ?? [];\n return variants.flatMap((other) => other.id === id || siblings.includes(other) ? [] :\n Object.keys(other.files).filter((file) => Object.hasOwn(self.files, file)).map((file) => ({ file, other: other.id })));\n}\n\n/**\n * The active set, cleaned: ids whose variant is gone (dropped or promoted\n * since the file was written) are dropped, and of two variants over one\n * file the later one is dropped, so `load` never has to choose. The file's\n * older shape, one id or `null`, still reads.\n */\n/** The host's knobs, dropping entries that are not a usable axis. */\nexport function readKnobs(opts: VariantsStoreOptions): KnobAxis[] {\n const file = resolve(opts.root, KNOBS_FILE);\n if (!existsSync(file)) return [];\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(file, 'utf8'));\n } catch {\n return [];\n }\n if (!Array.isArray(parsed)) return [];\n const seen = new Set<string>();\n return parsed.filter((k): k is KnobAxis => {\n if (!k || typeof k !== 'object') return false;\n const a = k as Partial<KnobAxis>;\n if (typeof a.id !== 'string' || !isValidVariantId(a.id) || typeof a.label !== 'string') return false;\n if (a.route !== undefined && typeof a.route !== 'string' &&\n (!Array.isArray(a.route) || !a.route.every((route) => typeof route === 'string'))) return false;\n if (a.showWhen !== undefined && (!a.showWhen || typeof a.showWhen.axis !== 'string' ||\n (a.showWhen.equals !== undefined && typeof a.showWhen.equals !== 'string') ||\n (a.showWhen.not !== undefined && typeof a.showWhen.not !== 'string'))) return false;\n if (a.section !== undefined && typeof a.section !== 'string') return false;\n if (a.kind === 'range') {\n const { min, max, step, defaultValue } = a as Partial<RangeVariantAxis>;\n if (typeof min !== 'number' || typeof max !== 'number' || typeof step !== 'number' || typeof defaultValue !== 'number') return false;\n return [min, max, step, defaultValue, max - min].every(Number.isFinite) &&\n min < max && step > 0 && defaultValue >= min && defaultValue <= max &&\n (a.unit === undefined || typeof a.unit === 'string');\n }\n if (a.kind === 'color') {\n const c = a as Partial<ColorVariantAxis>;\n const hex = (value: unknown) => typeof value === 'string' && /^#(?:[a-f0-9]{3}|[a-f0-9]{6})$/i.test(value);\n return hex(c.defaultColor) && Array.isArray(c.swatches) && c.swatches.every((s) =>\n s && typeof s.id === 'string' && typeof s.label === 'string' && hex(s.value));\n }\n if (a.kind === 'select') {\n const s = a as Partial<SelectVariantAxis>;\n return Array.isArray(s.options) && s.options.length > 0 &&\n s.options.every((o) => o && typeof o.id === 'string' && typeof o.label === 'string') &&\n s.options.some((o) => o.id === s.defaultOption);\n }\n return false;\n }).filter((axis) => {\n if (seen.has(axis.id)) return false;\n seen.add(axis.id);\n return true;\n });\n}\n\nexport function readActive(opts: VariantsStoreOptions): string[] {\n const file = join(resolve(opts.root), ACTIVE_FILE);\n if (!existsSync(file)) return [];\n let raw: unknown;\n try {\n raw = (JSON.parse(readFileSync(file, 'utf8')) as { active?: unknown }).active;\n } catch {\n return [];\n }\n const ids = Array.isArray(raw) ? raw : typeof raw === 'string' ? [raw] : [];\n const kept: string[] = [];\n const seen = new Set<string>();\n const owner = new Set<string>();\n for (const id of ids) {\n if (typeof id !== 'string' || seen.has(id)) continue;\n seen.add(id);\n const manifest = readVariant(opts, id);\n if (!manifest) continue;\n const files = Object.keys(manifest.files);\n if (files.some((f) => owner.has(f))) continue;\n for (const f of files) owner.add(f);\n kept.push(id);\n }\n return kept;\n}\n\nexport function writeActive(opts: VariantsStoreOptions, ids: string[]): void {\n const file = join(resolve(opts.root), ACTIVE_FILE);\n mkdirSync(dirname(file), { recursive: true });\n const active = Array.from(new Set(ids));\n writeJson(file, { active } satisfies ActiveFileShape);\n // Which variants a machine is looking at is local state, never a commit;\n // the CLI writes this too, so the exclusion cannot live in the plugin alone.\n ensureFileIgnored(file);\n}\n\n/* ── the endpoint handler ─────────────────────────────────────────────── */\n\n/**\n * The transport-agnostic core of the variants endpoint, mirroring\n * {@link import('./store').notesResponse}. `req.url` is the path relative\n * to wherever the caller mounted `VARIANTS_ENDPOINT` (\"/\", \"/active\"), so\n * this function never needs to know the mount point.\n */\nexport function variantsResponse(opts: VariantsStoreOptions, req: NotesRequest): NotesResponse {\n const method = req.method ?? '';\n const path = (req.url ?? '/').split('?')[0] || '/';\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' && path === '/') {\n return json(200, { variants: listVariants(opts), active: readActive(opts), knobs: readKnobs(opts) });\n }\n\n if (method === 'POST' && path === '/active') {\n const body = req.body;\n if (body === null || typeof body !== 'object' || Array.isArray(body)) {\n return json(400, { error: 'body must be a JSON object' });\n }\n const raw = (body as { active?: unknown }).active;\n if (!Array.isArray(raw) || !raw.every((id) => typeof id === 'string')) {\n return json(400, { error: 'active must be an array of variant ids' });\n }\n const active = Array.from(new Set(raw as string[]));\n for (const id of active) {\n // Malformed, not merely unknown: a 404 would imply the shape was fine.\n if (!isValidVariantId(id)) return json(400, { error: `invalid variant id \"${id}\"` });\n }\n for (const id of active) {\n if (!readVariant(opts, id)) return json(404, { error: `unknown variant \"${id}\"` });\n }\n const conflict = activeConflict(opts, active);\n if (conflict) {\n const [a, b, file] = conflict;\n const groups = overlayGroups(readVariants(opts));\n const named = (id: string) => {\n const label = groups.find((group) => group.members.some((member) => member.id === id))?.label;\n return label ? `\"${id}\" (${label})` : `\"${id}\"`;\n };\n return json(409, {\n error: `${named(a)} and ${named(b)} both override ${file}; only one can be active`,\n conflict,\n });\n }\n writeActive(opts, active);\n return json(200, { active });\n }\n\n return json(405, { error: 'method not allowed' });\n}\n"],"mappings":"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","names":["randomUUID","existsSync","mkdirSync","readFileSync","realpathSync","statSync","writeFileSync","dirname","isAbsolute","join","relative","sep","dirname","existsSync","readFileSync","realpathSync","dirname","join","mkdirSync","writeFileSync","relative","sep","isAbsolute","randomUUID","statSync","execFileSync","createHash","randomUUID","existsSync","mkdirSync","readFileSync","realpathSync","rmSync","writeFileSync","dirname","isAbsolute","join","relative","resolve","sep","join","isAbsolute","resolve","sep","dirname","realpathSync","relative","existsSync","readFileSync","mkdirSync","writeFileSync","rmSync","execFileSync","createHash","randomUUID","parsed"]}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareVersions,
|
|
3
|
+
parseVersion,
|
|
4
|
+
upgradeCommand
|
|
5
|
+
} from "./chunk-L6O4HYGP.js";
|
|
6
|
+
|
|
1
7
|
// src/update.ts
|
|
2
8
|
import { existsSync, readFileSync } from "fs";
|
|
3
9
|
import { mkdir, open, readFile, rename, rm, stat, writeFile } from "fs/promises";
|
|
@@ -7,7 +13,7 @@ import { randomUUID } from "crypto";
|
|
|
7
13
|
|
|
8
14
|
// package.json
|
|
9
15
|
var name = "isotone";
|
|
10
|
-
var version = "1.1
|
|
16
|
+
var version = "1.2.1";
|
|
11
17
|
|
|
12
18
|
// src/update.ts
|
|
13
19
|
var UPDATE_ENDPOINT = "/api/iteration/update";
|
|
@@ -15,33 +21,6 @@ var REGISTRY_URL = `https://registry.npmjs.org/${name}/latest`;
|
|
|
15
21
|
var CHECK_TIMEOUT = 5e3;
|
|
16
22
|
var CACHE_TTL = 24 * 60 * 60 * 1e3;
|
|
17
23
|
var CACHE_FILE = join(tmpdir(), `isotone-update-${process.getuid?.() ?? "user"}-v1.json`);
|
|
18
|
-
function parseVersion(value) {
|
|
19
|
-
if (value.length > 256) throw new Error("Invalid version.");
|
|
20
|
-
const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*))?(?:\+([\da-zA-Z-]+(?:\.[\da-zA-Z-]+)*))?$/.exec(value);
|
|
21
|
-
if (!match) throw new Error("Invalid version.");
|
|
22
|
-
const prerelease = match[4]?.split(".") ?? [];
|
|
23
|
-
if (prerelease.some((part) => /^0\d+$/.test(part))) throw new Error("Invalid version.");
|
|
24
|
-
return { parts: match.slice(1, 4).map(BigInt), prerelease };
|
|
25
|
-
}
|
|
26
|
-
function compareVersions(a, b) {
|
|
27
|
-
const left = parseVersion(a), right = parseVersion(b);
|
|
28
|
-
for (let i = 0; i < 3; i++) {
|
|
29
|
-
if (left.parts[i] !== right.parts[i]) return left.parts[i] > right.parts[i] ? 1 : -1;
|
|
30
|
-
}
|
|
31
|
-
if (!left.prerelease.length || !right.prerelease.length) {
|
|
32
|
-
return left.prerelease.length === right.prerelease.length ? 0 : left.prerelease.length ? -1 : 1;
|
|
33
|
-
}
|
|
34
|
-
for (let i = 0; i < Math.max(left.prerelease.length, right.prerelease.length); i++) {
|
|
35
|
-
const x = left.prerelease[i], y = right.prerelease[i];
|
|
36
|
-
if (x === y) continue;
|
|
37
|
-
if (x === void 0 || y === void 0) return x === void 0 ? -1 : 1;
|
|
38
|
-
const nx = /^\d+$/.test(x), ny = /^\d+$/.test(y);
|
|
39
|
-
if (nx && ny) return BigInt(x) > BigInt(y) ? 1 : -1;
|
|
40
|
-
if (nx !== ny) return nx ? -1 : 1;
|
|
41
|
-
return x > y ? 1 : -1;
|
|
42
|
-
}
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
45
24
|
function manifest(directory) {
|
|
46
25
|
try {
|
|
47
26
|
const value = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
|
|
@@ -99,14 +78,19 @@ function upgradePlan(cwd, latest, override) {
|
|
|
99
78
|
const optional = project.json?.optionalDependencies;
|
|
100
79
|
const peers = project.json?.peerDependencies;
|
|
101
80
|
if (optional?.[name] || peers?.[name]) return { upgrade: null, message: "Review this optional/peer dependency manually with your package manager." };
|
|
102
|
-
const dev = production?.[name]
|
|
103
|
-
let workspace = manager === "pnpm" && existsSync(join(project.directory, "pnpm-workspace.yaml"))
|
|
81
|
+
const dev = !production?.[name];
|
|
82
|
+
let workspace = manager === "pnpm" && existsSync(join(project.directory, "pnpm-workspace.yaml"));
|
|
104
83
|
if (manager === "yarn" && project.json?.workspaces) {
|
|
105
84
|
const declared = project.json.packageManager;
|
|
106
85
|
if (typeof declared !== "string" || !/^yarn@\d+\./.test(declared)) return { upgrade: null, message: "Declare the Yarn version in packageManager before updating a workspace-root dependency; Yarn Classic and modern Yarn use different root flags." };
|
|
107
|
-
|
|
86
|
+
workspace = declared.startsWith("yarn@1.");
|
|
87
|
+
}
|
|
88
|
+
let command;
|
|
89
|
+
try {
|
|
90
|
+
command = upgradeCommand(manager, latest, { dev, workspace });
|
|
91
|
+
} catch {
|
|
92
|
+
return { upgrade: null, message: "This release has a version the upgrade command does not name. Upgrade with your project\u2019s package manager." };
|
|
108
93
|
}
|
|
109
|
-
const command = manager === "npm" ? `npm install${dev} --save-exact ${name}@${latest}` : `${manager} add${dev}${workspace} --exact ${name}@${latest}`;
|
|
110
94
|
return { upgrade: { manager, command, cwd: project.directory } };
|
|
111
95
|
}
|
|
112
96
|
async function fetchLatest(fetcher = fetch, signal) {
|
|
@@ -228,4 +212,4 @@ export {
|
|
|
228
212
|
checkUpdate,
|
|
229
213
|
startUpdateCheck
|
|
230
214
|
};
|
|
231
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-LD7UWI7W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/update.ts","../package.json"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { mkdir, open, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport type { UpdateInfo } from './core/update';\nimport { name as packageName, version as currentVersion } from '../package.json';\n\nexport const UPDATE_ENDPOINT = '/api/iteration/update';\nconst REGISTRY_URL = `https://registry.npmjs.org/${packageName}/latest`;\nconst CHECK_TIMEOUT = 5_000;\nconst CACHE_TTL = 24 * 60 * 60 * 1000;\nconst CACHE_FILE = join(tmpdir(), `isotone-update-${process.getuid?.() ?? 'user'}-v1.json`);\n\ntype Manager = 'npm' | 'pnpm' | 'yarn';\nexport type { UpdateInfo, UpdateRequest } from './core/update';\nexport { changelogUrl, compareVersions, updateRequestOf } from './update-request';\nimport { compareVersions, parseVersion, upgradeCommand } from './update-request';\n\nfunction manifest(directory: string): Record<string, unknown> | null {\n try {\n const value = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8'));\n return value && typeof value === 'object' && !Array.isArray(value) ? value : null;\n } catch { return null; }\n}\n\nfunction findProject(cwd: string): { directory: string; json: Record<string, unknown> | null } {\n let directory = resolve(cwd);\n while (true) {\n const json = manifest(directory);\n if (json) return { directory, json };\n if (dirname(directory) === directory || existsSync(join(directory, '.git'))) return { directory: resolve(cwd), json: null };\n directory = dirname(directory);\n }\n}\n\nfunction dependencyOwner(project: ReturnType<typeof findProject>): ReturnType<typeof findProject> {\n let directory = project.directory;\n while (true) {\n const json = manifest(directory);\n if (['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'].some((key) => {\n const dependencies = json?.[key];\n return dependencies && typeof dependencies === 'object' && Object.hasOwn(dependencies, packageName);\n })) return { directory, json };\n if (dirname(directory) === directory || existsSync(join(directory, '.git'))) return project;\n directory = dirname(directory);\n }\n}\n\nexport function upgradePlan(cwd: string, latest: string, override?: Manager): Pick<UpdateInfo, 'upgrade' | 'message'> {\n parseVersion(latest); // Only a validated version can enter a copied command.\n const project = dependencyOwner(findProject(cwd));\n if (project.json?.name === packageName) return { upgrade: null, message: 'This is the source checkout; update its source instead of adding itself as a dependency.' };\n let manager = override;\n let directory = project.directory;\n const locks = new Set<Manager>();\n while (!manager) {\n const declared = manifest(directory)?.packageManager;\n if (typeof declared === 'string') {\n const match = /^(npm|pnpm|yarn)@/.exec(declared);\n if (!match) return { upgrade: null, message: 'The declared package manager is not supported by this command. Upgrade with your project’s package manager.' };\n manager = match[1] as Manager;\n break;\n }\n if (['bun.lock', 'bun.lockb'].some((file) => existsSync(join(directory, file)))) return { upgrade: null, message: 'This project uses Bun. Upgrade with the project’s package manager.' };\n if (existsSync(join(directory, 'pnpm-lock.yaml'))) locks.add('pnpm');\n if (existsSync(join(directory, 'yarn.lock'))) locks.add('yarn');\n if (['package-lock.json', 'npm-shrinkwrap.json'].some((file) => existsSync(join(directory, file)))) locks.add('npm');\n if (locks.size || dirname(directory) === directory || existsSync(join(directory, '.git'))) break;\n directory = dirname(directory);\n }\n if (!manager && locks.size > 1) return { upgrade: null, message: 'Multiple package-manager lockfiles found. Pass --manager npm, pnpm or yarn.' };\n manager ??= [...locks][0] ?? 'npm';\n const production = project.json?.dependencies as Record<string, unknown> | undefined;\n const optional = project.json?.optionalDependencies as Record<string, unknown> | undefined;\n const peers = project.json?.peerDependencies as Record<string, unknown> | undefined;\n if (optional?.[packageName] || peers?.[packageName]) return { upgrade: null, message: 'Review this optional/peer dependency manually with your package manager.' };\n const dev = !production?.[packageName];\n let workspace = manager === 'pnpm' && existsSync(join(project.directory, 'pnpm-workspace.yaml'));\n if (manager === 'yarn' && project.json?.workspaces) {\n const declared = project.json.packageManager;\n if (typeof declared !== 'string' || !/^yarn@\\d+\\./.test(declared)) return { upgrade: null, message: 'Declare the Yarn version in packageManager before updating a workspace-root dependency; Yarn Classic and modern Yarn use different root flags.' };\n workspace = declared.startsWith('yarn@1.');\n }\n // One builder for the suggestion and for checking a stored update note.\n let command: string;\n try { command = upgradeCommand(manager, latest, { dev, workspace }); } catch {\n return { upgrade: null, message: 'This release has a version the upgrade command does not name. Upgrade with your project’s package manager.' };\n }\n return { upgrade: { manager, command, cwd: project.directory } };\n}\n\nexport async function fetchLatest(fetcher: typeof fetch = fetch, signal?: AbortSignal): Promise<string> {\n const controller = new AbortController();\n const abort = () => controller.abort();\n if (signal?.aborted) abort();\n else signal?.addEventListener('abort', abort, { once: true });\n const timer = setTimeout(abort, CHECK_TIMEOUT);\n try {\n if (controller.signal.aborted) throw new Error('Update check cancelled.');\n const response = await fetcher(REGISTRY_URL, { signal: controller.signal, redirect: 'error', headers: { accept: 'application/json' } });\n if (!response.ok) throw new Error(`Registry returned HTTP ${response.status}.`);\n if (!response.body) throw new Error('Registry returned an empty response.');\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let bytes = 0;\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n bytes += value.byteLength;\n if (bytes > 64 * 1024) {\n await reader.cancel();\n throw new Error('Registry response was too large.');\n }\n chunks.push(value);\n }\n } finally { reader.releaseLock(); }\n const data = JSON.parse(Buffer.concat(chunks).toString('utf8'));\n if (typeof data.version !== 'string') throw new Error('Registry returned no version.');\n parseVersion(data.version);\n return data.version;\n } finally { clearTimeout(timer); signal?.removeEventListener('abort', abort); }\n}\n\nexport function updateInfo(latest: string, cwd: string, manager?: Manager): UpdateInfo {\n const order = compareVersions(latest, currentVersion);\n return {\n currentVersion, latestVersion: latest,\n status: order > 0 ? 'available' : order < 0 ? 'ahead' : 'current',\n ...(order > 0 ? upgradePlan(cwd, latest, manager) : { upgrade: null }),\n };\n}\n\nexport async function checkUpdate(cwd: string, manager?: Manager, fetcher?: typeof fetch, signal?: AbortSignal): Promise<UpdateInfo> {\n try { return updateInfo(await fetchLatest(fetcher, signal), cwd, manager); }\n catch { return { currentVersion, latestVersion: null, status: 'unavailable', upgrade: null, message: 'Could not check npm for updates. The installed version remains usable; try again when online.' }; }\n}\n\ninterface CachedCheck { checkedAt: number; latestVersion: string | null }\nasync function readCache(file: string): Promise<CachedCheck | null> {\n try {\n if ((await stat(file)).size > 4096) return null;\n const data = JSON.parse(await readFile(file, 'utf8'));\n if (!Number.isFinite(data.checkedAt) || data.checkedAt > Date.now() || Date.now() - data.checkedAt >= CACHE_TTL) return null;\n if (data.latestVersion !== null) parseVersion(data.latestVersion);\n return data;\n } catch { return null; }\n}\n\nasync function saveCache(file: string, latestVersion: string | null): Promise<void> {\n const temporary = `${file}.${randomUUID()}.tmp`;\n try {\n await writeFile(temporary, JSON.stringify({ checkedAt: Date.now(), latestVersion }), { mode: 0o600, flag: 'wx' });\n await rename(temporary, file);\n } finally { await rm(temporary, { force: true }); }\n}\n\n/** One bounded request a day, including failed attempts, shared across dev servers. */\nexport function startUpdateCheck(cwd: string, enabled = true, options: { cacheFile?: string; fetcher?: typeof fetch; env?: NodeJS.ProcessEnv } = {}): { getSnapshot(): UpdateInfo; ready: Promise<void>; dispose(): void } {\n const controller = new AbortController();\n const env = options.env ?? process.env;\n const off = !enabled || env.ISOTONE_UPDATE_CHECK === '0' || (!!env.CI && env.CI !== 'false' && env.CI !== '0') || env.NODE_ENV === 'test' || !!env.VITEST;\n let info: UpdateInfo = { currentVersion, latestVersion: null, status: off ? 'disabled' : 'checking', upgrade: null };\n const ready = off ? Promise.resolve() : (async () => {\n const file = options.cacheFile ?? CACHE_FILE;\n let locked = false;\n try {\n const cache = await readCache(file);\n if (cache) { info = cache.latestVersion ? updateInfo(cache.latestVersion, cwd) : { ...info, status: 'unavailable' }; return; }\n await mkdir(dirname(file), { recursive: true });\n try { await (await open(`${file}.lock`, 'wx', 0o600)).close(); locked = true; }\n catch {\n // A concurrent server owns the request. Recover an abandoned lease next time.\n if (Date.now() - (await stat(`${file}.lock`)).mtimeMs > CHECK_TIMEOUT * 3) await rm(`${file}.lock`, { force: true });\n info = { ...info, status: 'unavailable' };\n return;\n }\n const refreshed = await readCache(file);\n if (refreshed) { info = refreshed.latestVersion ? updateInfo(refreshed.latestVersion, cwd) : { ...info, status: 'unavailable' }; return; }\n await saveCache(file, null);\n info = await checkUpdate(cwd, undefined, options.fetcher, controller.signal);\n if (info.latestVersion) await saveCache(file, info.latestVersion);\n } catch { info = { currentVersion, latestVersion: null, status: 'unavailable', upgrade: null }; }\n finally { if (locked) await rm(`${file}.lock`, { force: true }).catch(() => {}); }\n })();\n return { getSnapshot: () => info, ready, dispose: () => controller.abort() };\n}\n","{\n \"name\": \"isotone\",\n \"version\": \"1.2.1\",\n \"type\": \"module\",\n \"license\": \"MIT\",\n \"description\": \"Design variants and feedback for your running web app. Switch between variants your coding agent builds, pin elements, and give the agent the context to make the change.\",\n \"engines\": {\n \"node\": \">=20\"\n },\n \"exports\": {\n \"./react\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./core\": {\n \"types\": \"./dist/core.d.ts\",\n \"import\": \"./dist/core.js\"\n },\n \"./server\": {\n \"types\": \"./dist/server.d.ts\",\n \"import\": \"./dist/server.js\"\n },\n \"./serve\": {\n \"types\": \"./dist/serve.d.ts\",\n \"import\": \"./dist/serve.js\"\n },\n \"./vite\": {\n \"types\": \"./dist/vite-plugin.d.ts\",\n \"import\": \"./dist/vite-plugin.js\"\n },\n \"./cli\": {\n \"types\": \"./dist/cli.d.ts\",\n \"import\": \"./dist/cli.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"docs/assets/preview.png\",\n \"docs/integrations.md\",\n \"docs/migration.md\",\n \"docs/updates.md\",\n \"docs/blast.md\",\n \"docs/variants.md\",\n \"docs/third-party-notices.md\",\n \"AGENTS.md\",\n \"CHANGELOG.md\"\n ],\n \"bin\": {\n \"isotone\": \"./dist/cli.js\"\n },\n \"scripts\": {\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"biome lint .\",\n \"lint:fix\": \"biome lint --write .\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"build\": \"tsup\",\n \"check\": \"pnpm typecheck && pnpm lint && pnpm build && pnpm test\",\n \"prepublishOnly\": \"pnpm check\",\n \"dev\": \"vite --config examples/vite.config.ts\",\n \"build:demo\": \"vite build --config examples/vite.config.ts --mode hosted\",\n \"check:package\": \"node scripts/check-package.mjs\"\n },\n \"peerDependencies\": {\n \"react\": \"^18 || ^19\",\n \"react-dom\": \"^18 || ^19\",\n \"vite\": \"^5 || ^6 || ^7 || ^8\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"2.5.7\",\n \"@types/node\": \"24.13.3\",\n \"@types/react\": \"^19\",\n \"@types/react-dom\": \"^19\",\n \"jsdom\": \"^30.0.1\",\n \"react\": \"^19\",\n \"react-dom\": \"^19\",\n \"tsup\": \"^8\",\n \"typescript\": \"^5\",\n \"vite\": \"^7\",\n \"vitest\": \"^4.1.10\",\n \"lucide-react\": \"^1.55.0\"\n },\n \"keywords\": [\n \"feedback\",\n \"review\",\n \"dev-tool\",\n \"vite-plugin\",\n \"coding-agent\",\n \"annotations\",\n \"design-review\",\n \"variants\",\n \"iteration\"\n ],\n \"sideEffects\": false,\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/aaroi/isotone.git\"\n },\n \"homepage\": \"https://github.com/aaroi/isotone#readme\",\n \"bugs\": \"https://github.com/aaroi/isotone/issues\",\n \"peerDependenciesMeta\": {\n \"react\": {\n \"optional\": true\n },\n \"react-dom\": {\n \"optional\": true\n },\n \"vite\": {\n \"optional\": true\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// src/serve/tag.ts
|
|
2
|
+
function guardedScriptTag(src) {
|
|
3
|
+
return `<script>if(/^(localhost|127\\.0\\.0\\.1|\\[::1\\])$/.test(location.hostname))document.head.append(Object.assign(document.createElement('script'),{src:${JSON.stringify(src).replace(/"/g, "'")}}))</script>`;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
guardedScriptTag
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=chunk-MX4RQJFV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/serve/tag.ts"],"sourcesContent":["/**\n * The script tag for a page served by another dev server, guarded so it\n * requests nothing unless the page itself is on loopback. A plain tag that\n * reaches production HTML makes every visitor's browser try its own\n * localhost, which newer browsers turn into a permission prompt; this one\n * stays inert there.\n */\nexport function guardedScriptTag(src: string): string {\n return `<script>if(/^(localhost|127\\\\.0\\\\.0\\\\.1|\\\\[::1\\\\])$/.test(location.hostname))` +\n `document.head.append(Object.assign(document.createElement('script'),{src:${JSON.stringify(src).replace(/\"/g, \"'\")}}))</script>`;\n}\n"],"mappings":";AAOO,SAAS,iBAAiB,KAAqB;AACpD,SAAO,yJACuE,KAAK,UAAU,GAAG,EAAE,QAAQ,MAAM,GAAG,CAAC;AACtH;","names":[]}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
changelogUrl,
|
|
3
|
+
compareVersions,
|
|
4
|
+
notesProjectRoot,
|
|
5
|
+
readNotes,
|
|
6
|
+
updateRequestOf,
|
|
7
|
+
withFileLock,
|
|
8
|
+
writeNotes
|
|
9
|
+
} from "./chunk-L6O4HYGP.js";
|
|
10
|
+
import {
|
|
11
|
+
updateNoteText
|
|
12
|
+
} from "./chunk-JCTWHR7U.js";
|
|
13
|
+
|
|
14
|
+
// src/standalone/bundle.ts
|
|
15
|
+
import { existsSync, readFileSync } from "fs";
|
|
16
|
+
import { dirname, resolve } from "path";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
function readBundle() {
|
|
19
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
for (const candidate of [
|
|
21
|
+
// built: dist/serve.js sits beside the bundle
|
|
22
|
+
resolve(here, "isotone.global.js"),
|
|
23
|
+
resolve(here, "..", "dist", "isotone.global.js"),
|
|
24
|
+
// from source (tests): src/serve/ -> the repo's dist/
|
|
25
|
+
resolve(here, "..", "..", "dist", "isotone.global.js")
|
|
26
|
+
]) {
|
|
27
|
+
if (existsSync(candidate)) return readFileSync(candidate, "utf8");
|
|
28
|
+
}
|
|
29
|
+
throw new Error(
|
|
30
|
+
"isotone: the browser bundle is missing from dist/. Run `pnpm build`."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/update-note.ts
|
|
35
|
+
import { randomUUID } from "crypto";
|
|
36
|
+
import { mkdir, readFile, rename, rm, stat, writeFile } from "fs/promises";
|
|
37
|
+
import { tmpdir } from "os";
|
|
38
|
+
import { dirname as dirname2, join, resolve as resolve2 } from "path";
|
|
39
|
+
function updateRequestFrom(info, file) {
|
|
40
|
+
if (info.status !== "available" || !info.latestVersion || !info.upgrade) return null;
|
|
41
|
+
return updateRequestOf({
|
|
42
|
+
from: info.currentVersion,
|
|
43
|
+
to: info.latestVersion,
|
|
44
|
+
command: info.upgrade.command,
|
|
45
|
+
cwd: info.upgrade.cwd,
|
|
46
|
+
manager: info.upgrade.manager,
|
|
47
|
+
changelog: changelogUrl(info.latestVersion)
|
|
48
|
+
}, file === void 0 ? {} : { root: notesProjectRoot(file) });
|
|
49
|
+
}
|
|
50
|
+
var MAX_ROUTE = 2e3;
|
|
51
|
+
function requestUpdateNote(file, info, route = "/") {
|
|
52
|
+
const update = updateRequestFrom(info, file);
|
|
53
|
+
if (!update) return null;
|
|
54
|
+
const where = route.startsWith("/") && route.length <= MAX_ROUTE ? route : "/";
|
|
55
|
+
const root = notesProjectRoot(file);
|
|
56
|
+
return withFileLock(file, () => {
|
|
57
|
+
const notes = readNotes(file);
|
|
58
|
+
const open = notes.filter((note) => !note.done && updateRequestOf(note.update, { root })).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
59
|
+
const current = open[0];
|
|
60
|
+
if (!current) {
|
|
61
|
+
const id = randomUUID();
|
|
62
|
+
notes.push({
|
|
63
|
+
id,
|
|
64
|
+
route: where,
|
|
65
|
+
feedback: updateNoteText(update),
|
|
66
|
+
element: null,
|
|
67
|
+
done: false,
|
|
68
|
+
doneAt: null,
|
|
69
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
70
|
+
update
|
|
71
|
+
});
|
|
72
|
+
writeNotes(file, notes);
|
|
73
|
+
return { id, created: true };
|
|
74
|
+
}
|
|
75
|
+
const existing = updateRequestOf(current.update, { root });
|
|
76
|
+
let changed = open.length > 1;
|
|
77
|
+
if (compareVersions(existing.to, update.to) <= 0 && JSON.stringify(existing) !== JSON.stringify(update)) {
|
|
78
|
+
current.feedback = updateNoteText(update);
|
|
79
|
+
current.update = update;
|
|
80
|
+
delete current.workingAt;
|
|
81
|
+
changed = true;
|
|
82
|
+
}
|
|
83
|
+
if (changed) writeNotes(file, notes.filter((note) => note === current || !open.includes(note)));
|
|
84
|
+
return { id: current.id, created: false };
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function updateResponse(file, info, req) {
|
|
88
|
+
const json = (status, body) => ({
|
|
89
|
+
status,
|
|
90
|
+
headers: { "content-type": "application/json; charset=utf-8", "cache-control": "no-store" },
|
|
91
|
+
body
|
|
92
|
+
});
|
|
93
|
+
if (req.method === "GET") return json(200, info);
|
|
94
|
+
if (req.method !== "POST") return json(405, { error: "method not allowed" });
|
|
95
|
+
if (!file) return json(404, { error: "this server keeps no notes" });
|
|
96
|
+
const route = req.body?.route;
|
|
97
|
+
const result = requestUpdateNote(file, info, typeof route === "string" ? route : "/");
|
|
98
|
+
if (!result) return json(409, { error: "There is no update this server can request." });
|
|
99
|
+
return json(result.created ? 201 : 200, result);
|
|
100
|
+
}
|
|
101
|
+
var NOTED_FILE = join(tmpdir(), `isotone-update-notes-${process.getuid?.() ?? "user"}-v1.json`);
|
|
102
|
+
var NOTED_LIMIT = 100;
|
|
103
|
+
async function readNoted(file) {
|
|
104
|
+
try {
|
|
105
|
+
if ((await stat(file)).size > 64 * 1024) return {};
|
|
106
|
+
const data = JSON.parse(await readFile(file, "utf8"));
|
|
107
|
+
return data && typeof data === "object" && !Array.isArray(data) ? data : {};
|
|
108
|
+
} catch {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function autoUpdateNote(file, info, options = {}) {
|
|
113
|
+
const update = updateRequestFrom(info, file);
|
|
114
|
+
if (!update) return null;
|
|
115
|
+
const stateFile = options.stateFile ?? NOTED_FILE;
|
|
116
|
+
const key = resolve2(file);
|
|
117
|
+
const noted = await readNoted(stateFile);
|
|
118
|
+
const seen = noted[key];
|
|
119
|
+
try {
|
|
120
|
+
if (typeof seen === "string" && compareVersions(seen, update.to) >= 0) return null;
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
const result = requestUpdateNote(file, info, "/");
|
|
124
|
+
delete noted[key];
|
|
125
|
+
const entries = [...Object.entries(noted).filter(([, value]) => typeof value === "string"), [key, update.to]].slice(-NOTED_LIMIT);
|
|
126
|
+
const temporary = `${stateFile}.${randomUUID()}.tmp`;
|
|
127
|
+
try {
|
|
128
|
+
await mkdir(dirname2(stateFile), { recursive: true });
|
|
129
|
+
await writeFile(temporary, JSON.stringify(Object.fromEntries(entries)), { mode: 384, flag: "wx" });
|
|
130
|
+
await rename(temporary, stateFile);
|
|
131
|
+
} finally {
|
|
132
|
+
await rm(temporary, { force: true });
|
|
133
|
+
}
|
|
134
|
+
return result?.id ?? null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export {
|
|
138
|
+
readBundle,
|
|
139
|
+
updateResponse,
|
|
140
|
+
autoUpdateNote
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=chunk-YPDV3PX4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/standalone/bundle.ts","../src/update-note.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n/** The IIFE bundle, next to this module in `dist/`. */\nexport function readBundle(): string {\n const here = dirname(fileURLToPath(import.meta.url));\n for (const candidate of [\n // built: dist/serve.js sits beside the bundle\n resolve(here, 'isotone.global.js'),\n resolve(here, '..', 'dist', 'isotone.global.js'),\n // from source (tests): src/serve/ -> the repo's dist/\n resolve(here, '..', '..', 'dist', 'isotone.global.js'),\n ]) {\n if (existsSync(candidate)) return readFileSync(candidate, 'utf8');\n }\n throw new Error(\n 'isotone: the browser bundle is missing from dist/. Run `pnpm build`.',\n );\n}\n","/**\n * The update note: the user's request, through the feedback list, that their\n * coding agent upgrade Isotone. The local server builds it from its own update\n * check. A browser can ask for one but never supplies the versions, the\n * command or the directory, so page content cannot put a command in front of\n * the agent. At most one update note is open; a newer release updates it.\n */\nimport { randomUUID } from 'node:crypto';\nimport { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\n\nimport { updateNoteText, type UpdateInfo, type UpdateRequest } from './core/update';\nimport { withFileLock } from './file-store';\nimport { readNotes, writeNotes, type NotesResponse } from './store';\nimport { changelogUrl, compareVersions, notesProjectRoot, updateRequestOf } from './update-request';\n\nexport { updateNoteText } from './core/update';\n\n/**\n * The request a snapshot supports, or null when there is nothing to run. With\n * a notes file, its directory must also lie inside that file's project.\n */\nexport function updateRequestFrom(info: UpdateInfo, file?: string): UpdateRequest | null {\n if (info.status !== 'available' || !info.latestVersion || !info.upgrade) return null;\n return updateRequestOf({\n from: info.currentVersion, to: info.latestVersion,\n command: info.upgrade.command, cwd: info.upgrade.cwd, manager: info.upgrade.manager,\n changelog: changelogUrl(info.latestVersion),\n }, file === undefined ? {} : { root: notesProjectRoot(file) });\n}\n\nconst MAX_ROUTE = 2000;\n\n/**\n * Create the update note, or bring the open one up to date. Returns its id,\n * and whether it is new; null when the snapshot has no update to request.\n */\nexport function requestUpdateNote(file: string, info: UpdateInfo, route = '/'): { id: string; created: boolean } | null {\n const update = updateRequestFrom(info, file);\n if (!update) return null;\n const where = route.startsWith('/') && route.length <= MAX_ROUTE ? route : '/';\n const root = notesProjectRoot(file);\n return withFileLock(file, () => {\n const notes = readNotes(file);\n const open = notes\n .filter((note) => !note.done && updateRequestOf(note.update, { root }))\n .sort((a, b) => b.createdAt.localeCompare(a.createdAt));\n const current = open[0];\n if (!current) {\n const id = randomUUID();\n notes.push({\n id, route: where, feedback: updateNoteText(update), element: null,\n done: false, doneAt: null, createdAt: new Date().toISOString(), update,\n });\n writeNotes(file, notes);\n return { id, created: true };\n }\n const existing = updateRequestOf(current.update, { root }) as UpdateRequest;\n let changed = open.length > 1;\n // A newer release, or a new command for the same one, replaces the request.\n // An older snapshot (another server still on yesterday's check) leaves it.\n if (compareVersions(existing.to, update.to) <= 0 && JSON.stringify(existing) !== JSON.stringify(update)) {\n // The text is the server's, never the reviewer's, so it follows the request.\n current.feedback = updateNoteText(update);\n current.update = update;\n delete current.workingAt;\n changed = true;\n }\n if (changed) writeNotes(file, notes.filter((note) => note === current || !open.includes(note)));\n return { id: current.id, created: false };\n });\n}\n\n/**\n * The update endpoint: GET reads the server's cached check, POST asks for the\n * update note. Only `route` is read from the body, as context for the note.\n * `file` is null when this server keeps no notes.\n */\nexport function updateResponse(file: string | null, info: UpdateInfo, req: { method?: string; body?: unknown }): NotesResponse {\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 if (req.method === 'GET') return json(200, info);\n if (req.method !== 'POST') return json(405, { error: 'method not allowed' });\n if (!file) return json(404, { error: 'this server keeps no notes' });\n const route = (req.body as { route?: unknown } | undefined)?.route;\n const result = requestUpdateNote(file, info, typeof route === 'string' ? route : '/');\n if (!result) return json(409, { error: 'There is no update this server can request.' });\n return json(result.created ? 201 : 200, result);\n}\n\nconst NOTED_FILE = join(tmpdir(), `isotone-update-notes-${process.getuid?.() ?? 'user'}-v1.json`);\nconst NOTED_LIMIT = 100;\n\nasync function readNoted(file: string): Promise<Record<string, string>> {\n try {\n if ((await stat(file)).size > 64 * 1024) return {};\n const data = JSON.parse(await readFile(file, 'utf8'));\n return data && typeof data === 'object' && !Array.isArray(data) ? data : {};\n } catch { return {}; }\n}\n\n/**\n * `checkForUpdates: 'note'`: create the update note the first time this notes\n * file sees a release. A user who deletes it is not asked again for that\n * version; a later release asks once more. Remembered per user in the same\n * temporary directory as the daily check.\n */\nexport async function autoUpdateNote(file: string, info: UpdateInfo, options: { stateFile?: string } = {}): Promise<string | null> {\n const update = updateRequestFrom(info, file);\n if (!update) return null;\n const stateFile = options.stateFile ?? NOTED_FILE;\n const key = resolve(file);\n const noted = await readNoted(stateFile);\n const seen = noted[key];\n try { if (typeof seen === 'string' && compareVersions(seen, update.to) >= 0) return null; } catch { /* Malformed: ask again. */ }\n const result = requestUpdateNote(file, info, '/');\n delete noted[key];\n const entries = [...Object.entries(noted).filter(([, value]) => typeof value === 'string'), [key, update.to]].slice(-NOTED_LIMIT);\n const temporary = `${stateFile}.${randomUUID()}.tmp`;\n try {\n await mkdir(dirname(stateFile), { recursive: true });\n await writeFile(temporary, JSON.stringify(Object.fromEntries(entries)), { mode: 0o600, flag: 'wx' });\n await rename(temporary, stateFile);\n } finally { await rm(temporary, { force: true }); }\n return result?.id ?? null;\n}\n"],"mappings":"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","names":["dirname","resolve","resolve","dirname"]}
|