@aaqiljamal/visual-editor-server 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fs/applyToFile.ts","../src/ast/className.ts","../src/fs/resolveSafe.ts","../src/fs/revertToFile.ts","../src/fs/applyCssProperty.ts","../src/css/cssModule.ts","../src/fs/applyStyledProperty.ts","../src/css/styledComponents.ts","../src/state/recentApplies.ts","../src/state/selection.ts","../src/state/auth.ts","../src/http/server.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport { createPatch } from \"diff\";\nimport {\n mutateClassName,\n mutateAttribute,\n} from \"../ast/className.ts\";\nimport type {\n MutateClassNameRefusalReason,\n MutateAttributeRefusalReason,\n} from \"../ast/className.ts\";\nimport { resolveWithinWorkspace } from \"./resolveSafe.ts\";\n\nexport type ApplyInput = {\n /** Path relative to the configured workspace root (the same root the Babel data-oid plugin used). */\n file: string;\n /** 1-based line number, as Babel reports in `loc.start.line`. */\n line: number;\n /** 0-based column number, as Babel reports in `loc.start.column`. */\n col: number;\n /** Attribute to mutate. Default \"className\" — token-swap semantics. Anything else uses whole-value swap (src, href, alt, …). */\n attribute?: string;\n /** Exact static class token to swap, e.g. \"p-4\". For non-className attributes, this is the whole expected current value (or null to skip conflict check). */\n before: string | null;\n /** Replacement, e.g. \"p-6\". For non-className attributes, this is the whole new value. */\n after: string;\n};\n\nexport type ApplyOutcome =\n | {\n ok: true;\n /** Unified diff between original and rewritten source. */\n diff: string;\n /** Absolute path that was written, for logging. */\n absolutePath: string;\n /** For non-className mutations called with before=null, the actual pre-mutation value. Undo uses this. */\n previousValue?: string;\n }\n | {\n ok: false;\n /** HTTP-shaped status (the http layer maps this 1:1). */\n status: 400 | 403 | 404 | 409 | 500;\n reason: ApplyRefusalReason;\n details: string;\n };\n\nexport type ApplyRefusalReason =\n | \"invalid-input\"\n | \"path-outside-workspace\"\n | \"file-not-found\"\n | \"read-failed\"\n | \"write-failed\"\n | MutateClassNameRefusalReason\n | MutateAttributeRefusalReason;\n\nexport type ApplyOptions = {\n workspaceRoot: string;\n /**\n * If `true`, mutate in memory and return the diff but do NOT write to disk.\n * /propose uses this; /apply doesn't.\n */\n dryRun: boolean;\n};\n\n/**\n * The single entry point for both /apply and /propose. Reads, mutates,\n * conflict-checks (the mutate step's `token-not-found` IS the conflict\n * signal — if the file moved out from under us, the `before` token won't\n * be there anymore), then either writes or skips the write.\n *\n * Returning `ApplyOutcome` instead of throwing keeps the HTTP layer\n * mechanical: every refusal carries its own status code and reason.\n */\nexport async function applyToFile(\n input: ApplyInput,\n options: ApplyOptions,\n): Promise<ApplyOutcome> {\n if (!isValidApplyInput(input)) {\n return {\n ok: false,\n status: 400,\n reason: \"invalid-input\",\n details:\n \"Body must be { file: string, line: number, col: number, before: string, after: string }\",\n };\n }\n\n const absolutePath = resolveWithinWorkspace(options.workspaceRoot, input.file);\n if (!absolutePath) {\n return {\n ok: false,\n status: 403,\n reason: \"path-outside-workspace\",\n details: `Refusing to touch path outside workspace root: ${input.file}`,\n };\n }\n\n let source: string;\n try {\n source = await fs.readFile(absolutePath, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n return {\n ok: false,\n status: 404,\n reason: \"file-not-found\",\n details: `File not found: ${input.file}`,\n };\n }\n return {\n ok: false,\n status: 500,\n reason: \"read-failed\",\n details: `Failed to read ${input.file}: ${e.message}`,\n };\n }\n\n const attribute = input.attribute ?? \"className\";\n const mutation =\n attribute === \"className\"\n ? mutateClassName({\n source,\n line: input.line,\n col: input.col,\n // mutateClassName requires before; reject if it's null\n before: input.before ?? \"\",\n after: input.after,\n })\n : mutateAttribute({\n source,\n line: input.line,\n col: input.col,\n attribute,\n before: input.before,\n after: input.after,\n });\n\n if (!mutation.ok) {\n // `token-not-found` is the conflict signal: the source no longer has\n // the `before` token at (line, col), most likely because the user\n // edited the file in their IDE between staging the change and applying\n // it. Map that to 409. Everything else is a \"you sent us a bad\n // request\" 400.\n const status = mutation.reason === \"token-not-found\" ? 409 : 400;\n return {\n ok: false,\n status,\n reason: mutation.reason,\n details: mutation.details,\n };\n }\n\n const diff = createPatch(input.file, source, mutation.output, \"before\", \"after\");\n\n if (!options.dryRun) {\n try {\n await fs.writeFile(absolutePath, mutation.output, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n return {\n ok: false,\n status: 500,\n reason: \"write-failed\",\n details: `Failed to write ${input.file}: ${e.message}`,\n };\n }\n }\n\n // mutateAttribute reports the actual previous value (useful when caller\n // passed before=null). mutateClassName always knows `before` so we just\n // echo input.before for it.\n const previousValue =\n \"previousValue\" in mutation && typeof mutation.previousValue === \"string\"\n ? mutation.previousValue\n : (input.before ?? undefined);\n return { ok: true, diff, absolutePath, previousValue };\n}\n\nfunction isValidApplyInput(x: unknown): x is ApplyInput {\n if (!x || typeof x !== \"object\") return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.file === \"string\" &&\n typeof o.line === \"number\" &&\n typeof o.col === \"number\" &&\n (typeof o.before === \"string\" || o.before === null) &&\n typeof o.after === \"string\" &&\n (o.attribute === undefined || typeof o.attribute === \"string\") &&\n Number.isInteger(o.line) &&\n Number.isInteger(o.col) &&\n o.line > 0 &&\n o.col >= 0\n );\n}\n","import * as recast from \"recast\";\n// `recast/parsers/babel-ts` parses JSX + TypeScript with location info,\n// and crucially keeps the formatting-preservation metadata recast needs\n// so that `recast.print(ast)` only reformats the parts we actually mutated.\nimport babelTsParser from \"recast/parsers/babel-ts.js\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Inputs are intentionally minimal:\n * - source: the full file contents (we don't read from disk here — that's\n * a layer up; this function stays pure)\n * - line/col: the JSXOpeningElement's loc.start, exactly as Babel reports\n * it (1-based line, 0-based column). The Babel data-oid plugin in\n * Spike A stamps this same pair into the DOM, so the producer and\n * consumer agree on the convention.\n * - before/after: the exact static class tokens (e.g. \"p-4\" → \"p-6\").\n * We do not parse arbitrary Tailwind expressions — that's the snap\n * engine's job; this function is just the deterministic swap.\n */\nexport type MutateClassNameInput = {\n source: string;\n line: number;\n col: number;\n before: string;\n after: string;\n};\n\n/**\n * The refusal reasons are structured so the consumer (overlay UI, MCP layer)\n * can branch on them: \"show user a 'this className is composed by cn()' badge\",\n * \"open the file at this location\", etc. The `details` field is the human\n * sentence; the `reason` is the machine code.\n *\n * v0.1 only accepts a static string literal at the located JSXOpeningElement.\n * Anything dynamic — cn/clsx/twMerge/cva, template literals, conditionals,\n * spreads, or anything we don't recognize — refuses with a clear reason.\n * This is Principle 1's \"determinism preconditions\" enforced at the writer.\n */\nexport type MutateClassNameRefusalReason =\n | \"dynamic-call-expression\" // legacy: kept so v0.1 consumers still see structured reasons\n | \"dynamic-template-literal\"\n | \"dynamic-conditional\"\n | \"dynamic-other\"\n | \"dynamic-uncertain-arg\" // v0.2: a non-string-literal arg in a known merger\n | \"dynamic-spread-arg\" // v0.2: ...spread arg in a known merger\n | \"dynamic-conflict\" // v0.2: tailwind-merge would drop our new token\n | \"unknown-merger\" // v0.2: call expression that isn't cn/clsx/twMerge/etc.\n | \"no-classname-attribute\"\n | \"no-jsx-at-location\"\n | \"token-not-found\";\n\n// Known classname-merger functions that combine multiple tokens at runtime.\n// We only enter the safety-analyzed mutation path for these — everything\n// else stays refused with `unknown-merger`.\nconst KNOWN_MERGERS = new Set([\n \"cn\",\n \"clsx\",\n \"classnames\",\n \"twMerge\",\n \"twJoin\",\n]);\n\nexport type MutateClassNameResult =\n | { ok: true; output: string }\n | {\n ok: false;\n reason: MutateClassNameRefusalReason;\n details: string;\n };\n\nexport function mutateClassName(\n input: MutateClassNameInput,\n): MutateClassNameResult {\n const { source, line, col, before, after } = input;\n\n let ast: ReturnType<typeof recast.parse>;\n try {\n ast = recast.parse(source, { parser: babelTsParser });\n } catch (err) {\n return {\n ok: false,\n reason: \"dynamic-other\",\n details: `parse error: ${(err as Error).message}`,\n };\n }\n\n // Holder object so TypeScript's control-flow analysis can narrow on\n // `.value` later — TS doesn't see through the visitor closure's\n // assignment to a plain `let`.\n const out: { value: MutateClassNameResult | null; visited: boolean } = {\n value: null,\n visited: false,\n };\n\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n const loc = node.loc;\n if (!loc) {\n this.traverse(path);\n return undefined;\n }\n if (loc.start.line !== line || loc.start.column !== col) {\n this.traverse(path);\n return undefined;\n }\n\n out.visited = true;\n out.value = mutateOnNode(node, before, after);\n // We've found and processed our target. Don't descend further.\n return false;\n },\n });\n\n if (!out.visited) {\n return {\n ok: false,\n reason: \"no-jsx-at-location\",\n details: `No JSXOpeningElement found at ${line}:${col}`,\n };\n }\n\n if (!out.value) {\n return {\n ok: false,\n reason: \"dynamic-other\",\n details: \"Internal: visitor matched but produced no result\",\n };\n }\n\n if (out.value.ok) {\n // recast.print preserves formatting for every node we didn't touch.\n return { ok: true, output: recast.print(ast).code };\n }\n return out.value;\n}\n\ntype JsxAttributeLike = {\n type: \"JSXAttribute\" | \"JSXSpreadAttribute\";\n name?: { type: string; name?: string };\n value?: JsxAttrValue;\n};\n\ntype JsxAttrValue =\n | { type: \"StringLiteral\"; value: string; extra?: unknown }\n | { type: \"Literal\"; value: string; extra?: unknown }\n | { type: \"JSXExpressionContainer\"; expression: JsxExpr }\n | { type: string };\n\ntype JsxExpr =\n | { type: \"StringLiteral\"; value: string; extra?: unknown }\n | { type: \"Literal\"; value: string; extra?: unknown }\n | { type: \"TemplateLiteral\" }\n | {\n type: \"CallExpression\";\n callee:\n | { type: \"Identifier\"; name: string }\n | { type: \"MemberExpression\"; property?: { name?: string } };\n }\n | { type: \"ConditionalExpression\" }\n | { type: string };\n\nfunction mutateOnNode(\n node: unknown,\n before: string,\n after: string,\n): MutateClassNameResult {\n const open = node as { attributes: JsxAttributeLike[] };\n\n const attrs = open.attributes ?? [];\n const classNameAttr = attrs.find(\n (a) =>\n a.type === \"JSXAttribute\" &&\n a.name?.type === \"JSXIdentifier\" &&\n a.name?.name === \"className\",\n );\n\n if (!classNameAttr) {\n return {\n ok: false,\n reason: \"no-classname-attribute\",\n details:\n \"JSX element has no className attribute (possibly composed via {...spread} or passed as a prop)\",\n };\n }\n\n const value = classNameAttr.value;\n if (!value) {\n return {\n ok: false,\n reason: \"no-classname-attribute\",\n details: \"className attribute has no value\",\n };\n }\n\n // className=\"p-4 ...\" — plain StringLiteral\n if (value.type === \"StringLiteral\" || value.type === \"Literal\") {\n const lit = value as { value: string; extra?: unknown };\n return swapAndReturn(lit, before, after);\n }\n\n // className={...}\n if (value.type === \"JSXExpressionContainer\") {\n const ec = value as { expression: JsxExpr };\n return mutateOnExpression(ec.expression, before, after);\n }\n\n return {\n ok: false,\n reason: \"dynamic-other\",\n details: `className uses unsupported value node type: ${value.type}`,\n };\n}\n\nfunction mutateOnExpression(\n expr: JsxExpr,\n before: string,\n after: string,\n): MutateClassNameResult {\n if (expr.type === \"StringLiteral\" || expr.type === \"Literal\") {\n const lit = expr as { value: string; extra?: unknown };\n return swapAndReturn(lit, before, after);\n }\n if (expr.type === \"TemplateLiteral\") {\n return {\n ok: false,\n reason: \"dynamic-template-literal\",\n details:\n \"className uses a template literal; v0.1 only mutates static string literals at the source location\",\n };\n }\n if (expr.type === \"CallExpression\") {\n return mutateOnCallExpression(expr as unknown as CallExprLike, before, after);\n }\n if (expr.type === \"ConditionalExpression\") {\n return {\n ok: false,\n reason: \"dynamic-conditional\",\n details:\n \"className uses a conditional (ternary) expression; v0.1 only mutates static string literals\",\n };\n }\n return {\n ok: false,\n reason: \"dynamic-other\",\n details: `className uses unsupported expression type: ${expr.type}`,\n };\n}\n\nfunction swapAndReturn(\n lit: { value: string; extra?: unknown },\n before: string,\n after: string,\n): MutateClassNameResult {\n const swapped = swapToken(lit.value, before, after);\n if (swapped === null) {\n return {\n ok: false,\n reason: \"token-not-found\",\n details: `Token \"${before}\" not found in className value \"${lit.value}\"`,\n };\n }\n lit.value = swapped;\n // Recast preserves the original raw source via `extra.raw`. If we mutate\n // `.value` without clearing `extra`, recast will reprint the *original*\n // string literal unchanged. Clear the `extra` so the new value is used.\n if (lit.extra) lit.extra = undefined;\n return { ok: true, output: \"\" };\n}\n\n// ---------------------------------------------------------------------------\n// CallExpression handling (v0.2 — B1)\n// ---------------------------------------------------------------------------\n\ntype CallExprLike = {\n type: \"CallExpression\";\n callee:\n | { type: \"Identifier\"; name: string }\n | {\n type: \"MemberExpression\";\n property?: { name?: string };\n };\n arguments: Array<{\n type: string;\n value?: string;\n extra?: unknown;\n }>;\n};\n\n/**\n * v0.2 entry into a known classname-merger call: cn(...), clsx(...),\n * twMerge(...), classnames(...), twJoin(...). The first cut of the safety\n * analysis is conservative:\n *\n * 1. Find the static StringLiteral arg containing `before`. If none, refuse.\n * 2. If ANY other arg is non-StringLiteral (conditional, identifier, nested\n * call, spread, object), refuse with a per-shape reason. We don't try\n * to reason about runtime branches.\n * 3. Build the would-be concatenated className with `before` swapped for\n * `after`. Run it through tailwind-merge. If `after`'s tokens don't\n * all survive the merge, another arg silently overrides our mutation\n * — refuse with `dynamic-conflict`.\n * 4. Otherwise mutate the StringLiteral and let recast.print emit it.\n */\nfunction mutateOnCallExpression(\n call: CallExprLike,\n before: string,\n after: string,\n): MutateClassNameResult {\n const calleeName = getCalleeName(call.callee);\n if (!calleeName || !KNOWN_MERGERS.has(calleeName)) {\n return {\n ok: false,\n reason: \"unknown-merger\",\n details: `className uses ${calleeName ?? \"(call)\"}(...). v0.2 only mutates inside known classname mergers: ${[...KNOWN_MERGERS].join(\", \")}.`,\n };\n }\n\n const args = call.arguments ?? [];\n\n let targetIdx = -1;\n for (let i = 0; i < args.length; i++) {\n const a = args[i];\n if (\n a &&\n (a.type === \"StringLiteral\" || a.type === \"Literal\") &&\n typeof a.value === \"string\" &&\n tokensFromValue(a.value).includes(before)\n ) {\n targetIdx = i;\n break;\n }\n }\n if (targetIdx === -1) {\n return {\n ok: false,\n reason: \"token-not-found\",\n details: `Token \"${before}\" not present in any static string argument of ${calleeName}(...)`,\n };\n }\n\n // Reject anything non-static in the OTHER args. The whole point of B1\n // is to mutate ONLY when we can prove the result by static analysis.\n for (let i = 0; i < args.length; i++) {\n if (i === targetIdx) continue;\n const a = args[i];\n if (!a) continue;\n if (a.type === \"StringLiteral\" || a.type === \"Literal\") continue;\n if (a.type === \"SpreadElement\") {\n return {\n ok: false,\n reason: \"dynamic-spread-arg\",\n details: `Argument ${i} of ${calleeName}() is a spread. v0.2 cannot analyze spread contents — refuse to mutate.`,\n };\n }\n return {\n ok: false,\n reason: \"dynamic-uncertain-arg\",\n details: `Argument ${i} of ${calleeName}() is ${a.type}. v0.2 refuses to mutate when any other argument is non-static.`,\n };\n }\n\n // Build the post-mutation concatenated className and check tailwind-merge.\n const targetArg = args[targetIdx] as { value: string; extra?: unknown };\n const newTargetValue = swapToken(targetArg.value, before, after);\n if (newTargetValue === null) {\n return {\n ok: false,\n reason: \"token-not-found\",\n details: `Internal: target arg matched but swap couldn't find \"${before}\"`,\n };\n }\n\n const concatenated = args\n .map((a, i) =>\n i === targetIdx\n ? newTargetValue\n : ((a as { value?: string }).value ?? \"\"),\n )\n .join(\" \")\n .trim();\n\n const mergedTokens = twMerge(concatenated).split(/\\s+/).filter(Boolean);\n const afterTokens = after.split(/\\s+/).filter(Boolean);\n for (const tok of afterTokens) {\n if (!mergedTokens.includes(tok)) {\n return {\n ok: false,\n reason: \"dynamic-conflict\",\n details: `Mutation would be silently overridden: tailwind-merge resolves \"${concatenated}\" to \"${mergedTokens.join(\" \")}\", which doesn't include \"${tok}\". A later argument wins.`,\n };\n }\n }\n\n // Safe. Apply.\n targetArg.value = newTargetValue;\n if (targetArg.extra) targetArg.extra = undefined;\n return { ok: true, output: \"\" };\n}\n\n// ---------------------------------------------------------------------------\n// Generic attribute mutation (v0.2 — B8)\n//\n// For attributes whose value is a simple string literal (src, href, alt,\n// title, …), the mutation is a whole-value swap, not a token swap. This\n// function does NOT handle className — that path stays in mutateClassName\n// because of its tokenized semantics + safety analysis.\n// ---------------------------------------------------------------------------\n\nexport type MutateAttributeInput = {\n source: string;\n line: number;\n col: number;\n attribute: string;\n /** If null, no conflict check — current value is overwritten. */\n before: string | null;\n after: string;\n};\n\nexport type MutateAttributeRefusalReason =\n | \"no-jsx-at-location\"\n | \"no-such-attribute\"\n | \"dynamic-value\"\n | \"token-not-found\"\n | \"parse-error\";\n\nexport type MutateAttributeResult =\n | { ok: true; output: string; previousValue: string }\n | {\n ok: false;\n reason: MutateAttributeRefusalReason;\n details: string;\n };\n\nexport function mutateAttribute(\n input: MutateAttributeInput,\n): MutateAttributeResult {\n const { source, line, col, attribute, before, after } = input;\n\n let ast: ReturnType<typeof recast.parse>;\n try {\n ast = recast.parse(source, { parser: babelTsParser });\n } catch (err) {\n return {\n ok: false,\n reason: \"parse-error\",\n details: (err as Error).message,\n };\n }\n\n const out: {\n value: MutateAttributeResult | null;\n visited: boolean;\n } = { value: null, visited: false };\n\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n const loc = node.loc;\n if (!loc) {\n this.traverse(path);\n return undefined;\n }\n if (loc.start.line !== line || loc.start.column !== col) {\n this.traverse(path);\n return undefined;\n }\n out.visited = true;\n\n const attrs = (node.attributes ?? []) as JsxAttributeLike[];\n const targetAttr = attrs.find(\n (a) =>\n a.type === \"JSXAttribute\" &&\n a.name?.type === \"JSXIdentifier\" &&\n a.name?.name === attribute,\n );\n if (!targetAttr) {\n out.value = {\n ok: false,\n reason: \"no-such-attribute\",\n details: `JSX element has no \\`${attribute}\\` attribute`,\n };\n return false;\n }\n const v = targetAttr.value;\n if (!v) {\n out.value = {\n ok: false,\n reason: \"no-such-attribute\",\n details: `Attribute \\`${attribute}\\` has no value`,\n };\n return false;\n }\n\n // Locate the StringLiteral we're going to swap. Two shapes:\n // attr=\"value\" → JSXAttribute.value is StringLiteral\n // attr={\"value\"} → JSXExpressionContainer wrapping StringLiteral\n let lit: { value: string; extra?: unknown } | null = null;\n if (v.type === \"StringLiteral\" || v.type === \"Literal\") {\n lit = v as { value: string; extra?: unknown };\n } else if (v.type === \"JSXExpressionContainer\") {\n const expr = (v as { expression: { type: string; value?: string; extra?: unknown } })\n .expression;\n if (expr.type === \"StringLiteral\" || expr.type === \"Literal\") {\n lit = expr as { value: string; extra?: unknown };\n }\n }\n\n if (!lit) {\n out.value = {\n ok: false,\n reason: \"dynamic-value\",\n details: `Attribute \\`${attribute}\\` value is ${(v as { type: string }).type}, not a static string literal`,\n };\n return false;\n }\n\n if (before !== null && lit.value !== before) {\n out.value = {\n ok: false,\n reason: \"token-not-found\",\n details: `Current value \"${lit.value}\" doesn't match expected \"${before}\" — file may have changed externally`,\n };\n return false;\n }\n\n const previousValue = lit.value;\n lit.value = after;\n if (lit.extra) lit.extra = undefined;\n out.value = {\n ok: true,\n output: \"\",\n previousValue,\n };\n return false;\n },\n });\n\n if (!out.visited) {\n return {\n ok: false,\n reason: \"no-jsx-at-location\",\n details: `No JSXOpeningElement found at ${line}:${col}`,\n };\n }\n if (!out.value) {\n return {\n ok: false,\n reason: \"no-jsx-at-location\",\n details: \"Visitor matched location but produced no result\",\n };\n }\n if (out.value.ok) {\n return {\n ok: true,\n output: recast.print(ast).code,\n previousValue: out.value.previousValue,\n };\n }\n return out.value;\n}\n\nfunction getCalleeName(\n callee: CallExprLike[\"callee\"] | unknown,\n): string | null {\n if (!callee || typeof callee !== \"object\") return null;\n const c = callee as {\n type: string;\n name?: string;\n property?: { name?: string };\n };\n if (c.type === \"Identifier\") return c.name ?? null;\n if (c.type === \"MemberExpression\") return c.property?.name ?? null;\n return null;\n}\n\nfunction tokensFromValue(value: string): string[] {\n return value.split(/\\s+/).filter(Boolean);\n}\n\n// Swap a single occurrence of `before` with `after` in a whitespace-separated\n// className value, preserving the original whitespace between tokens. Returns\n// `null` if the `before` token isn't present.\nfunction swapToken(\n value: string,\n before: string,\n after: string,\n): string | null {\n const parts = value.split(/(\\s+)/);\n let found = false;\n const out = parts.map((p) => {\n if (!found && p === before) {\n found = true;\n return after;\n }\n return p;\n });\n if (!found) return null;\n return out.join(\"\");\n}\n","import * as path from \"node:path\";\n\n/**\n * Resolve a user-supplied relative path against a workspace root, refusing\n * any path that escapes the root (via `..`, absolute paths, or symlink\n * trickery the caller might pass in).\n *\n * Returns the absolute path on success, or `null` if the resolved path\n * would land outside the workspace. The server uses this on every file\n * I/O so a malicious or buggy client cannot write `/etc/passwd` by\n * passing `file: \"../../../../etc/passwd\"`.\n */\nexport function resolveWithinWorkspace(\n workspaceRoot: string,\n relativePath: string,\n): string | null {\n if (typeof relativePath !== \"string\" || relativePath.length === 0) {\n return null;\n }\n\n const root = path.resolve(workspaceRoot);\n const candidate = path.resolve(root, relativePath);\n\n // Containment check: candidate must equal root or start with root + sep.\n if (candidate !== root && !candidate.startsWith(root + path.sep)) {\n return null;\n }\n return candidate;\n}\n","import { applyToFile } from \"./applyToFile.ts\";\nimport type { ApplyOutcome, ApplyOptions } from \"./applyToFile.ts\";\nimport type { RecentApplies } from \"../state/recentApplies.ts\";\n\nexport type RevertInput = {\n /** Optional — when omitted, the most-recent apply is reverted. */\n file?: string;\n line?: number;\n col?: number;\n};\n\nexport type RevertOutcome =\n | ApplyOutcome // delegates to applyToFile when an entry was found\n | {\n ok: false;\n status: 404;\n reason: \"no-recent-apply\";\n details: string;\n }\n | {\n ok: false;\n status: 400;\n reason: \"invalid-input\";\n details: string;\n };\n\n/**\n * Revert a recent apply by swapping `before` and `after` and re-running\n * the deterministic mutation through `applyToFile`. The conflict path\n * (file edited externally between Apply and Revert) is the same as\n * /apply — returns 409 with `token-not-found` if the \"after\" token no\n * longer sits at line:col.\n *\n * On success, removes the entry from the buffer so re-reverting is not\n * an infinite loop. The user can re-apply by re-driving the gesture.\n */\nexport async function revertToFile(\n input: RevertInput,\n options: ApplyOptions,\n recent: RecentApplies,\n): Promise<RevertOutcome> {\n // Validate the partial-key shape: either all three of file/line/col, or\n // none (revert most-recent).\n const partial =\n input.file !== undefined ||\n input.line !== undefined ||\n input.col !== undefined;\n const complete =\n typeof input.file === \"string\" &&\n typeof input.line === \"number\" &&\n typeof input.col === \"number\" &&\n Number.isInteger(input.line) &&\n Number.isInteger(input.col);\n if (partial && !complete) {\n return {\n ok: false,\n status: 400,\n reason: \"invalid-input\",\n details:\n \"Either pass all of {file, line, col} or omit them to revert the most-recent apply.\",\n };\n }\n\n const key =\n complete &&\n typeof input.file === \"string\" &&\n typeof input.line === \"number\" &&\n typeof input.col === \"number\"\n ? { file: input.file, line: input.line, col: input.col }\n : undefined;\n const entry = recent.find(key);\n if (!entry) {\n return {\n ok: false,\n status: 404,\n reason: \"no-recent-apply\",\n details: key\n ? `No recent apply found at ${key.file}:${key.line}:${key.col}`\n : \"No recent applies to revert\",\n };\n }\n\n const outcome = await applyToFile(\n {\n file: entry.file,\n line: entry.line,\n col: entry.col,\n before: entry.after, // swap\n after: entry.before,\n },\n options,\n );\n\n if (outcome.ok) {\n // Don't leave the entry in the buffer — that'd make repeated reverts\n // loop A→B, B→A, A→B forever.\n recent.remove(entry);\n }\n\n return outcome;\n}\n","import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { createPatch } from \"diff\";\nimport { detectCssModule, mutateCssProperty } from \"../css/cssModule.ts\";\nimport { resolveWithinWorkspace } from \"./resolveSafe.ts\";\n\nexport type ApplyCssPropertyInput = {\n /** JSX file containing the JSXOpeningElement we resolve from. Workspace-relative. */\n file: string;\n /** 1-based line of the JSXOpeningElement. */\n line: number;\n /** 0-based column of the JSXOpeningElement. */\n col: number;\n /** CSS property to set, e.g. \"padding\". */\n property: string;\n /** New value, e.g. \"1.5rem\". */\n value: string;\n};\n\nexport type ApplyCssPropertyRefusalReason =\n | \"invalid-input\"\n | \"path-outside-workspace\"\n | \"jsx-file-not-found\"\n | \"css-file-not-found\"\n | \"read-failed\"\n | \"write-failed\"\n | \"no-jsx-at-location\"\n | \"no-classname-attribute\"\n | \"dynamic-classname\"\n | \"not-a-css-module\"\n | \"unresolved-import\"\n | \"parse-error\"\n | \"css-parse-error\"\n | \"selector-not-found\"\n | \"composes-chain\"\n | \"invalid-property\";\n\nexport type ApplyCssPropertyOutcome =\n | {\n ok: true;\n diff: string;\n cssAbsolutePath: string;\n selector: string;\n previousValue: string | null;\n }\n | {\n ok: false;\n status: 400 | 403 | 404 | 409 | 500;\n reason: ApplyCssPropertyRefusalReason;\n details: string;\n };\n\nexport async function applyCssProperty(\n input: ApplyCssPropertyInput,\n options: { workspaceRoot: string; dryRun: boolean },\n): Promise<ApplyCssPropertyOutcome> {\n if (!isValid(input)) {\n return {\n ok: false,\n status: 400,\n reason: \"invalid-input\",\n details:\n \"Body must be { file: string, line: number, col: number, property: string, value: string }\",\n };\n }\n\n const jsxAbs = resolveWithinWorkspace(options.workspaceRoot, input.file);\n if (!jsxAbs) {\n return {\n ok: false,\n status: 403,\n reason: \"path-outside-workspace\",\n details: `JSX file outside workspace: ${input.file}`,\n };\n }\n\n let jsxSource: string;\n try {\n jsxSource = await fs.readFile(jsxAbs, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n return {\n ok: false,\n status: 404,\n reason: \"jsx-file-not-found\",\n details: `JSX file not found: ${input.file}`,\n };\n }\n return {\n ok: false,\n status: 500,\n reason: \"read-failed\",\n details: `Read JSX failed: ${e.message}`,\n };\n }\n\n const detect = detectCssModule(jsxSource, input.line, input.col);\n if (!detect.ok) {\n return {\n ok: false,\n status: 400,\n reason: detect.reason,\n details: detect.details,\n };\n }\n\n // Resolve the CSS file path relative to the JSX file's dir, then\n // re-check workspace containment.\n const cssAbsolutePath = path.resolve(\n path.dirname(jsxAbs),\n detect.ref.cssFile,\n );\n const cssRel = path.relative(options.workspaceRoot, cssAbsolutePath);\n if (cssRel.startsWith(\"..\") || path.isAbsolute(cssRel)) {\n return {\n ok: false,\n status: 403,\n reason: \"path-outside-workspace\",\n details: `Resolved CSS file outside workspace: ${cssAbsolutePath}`,\n };\n }\n\n let cssSource: string;\n try {\n cssSource = await fs.readFile(cssAbsolutePath, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n return {\n ok: false,\n status: 404,\n reason: \"css-file-not-found\",\n details: `CSS file not found: ${cssAbsolutePath}`,\n };\n }\n return {\n ok: false,\n status: 500,\n reason: \"read-failed\",\n details: `Read CSS failed: ${e.message}`,\n };\n }\n\n const mutation = mutateCssProperty(\n cssSource,\n detect.ref.selector,\n input.property,\n input.value,\n );\n if (!mutation.ok) {\n return {\n ok: false,\n status: 400,\n reason: mutation.reason,\n details: mutation.details,\n };\n }\n\n const diff = createPatch(cssRel, cssSource, mutation.output, \"before\", \"after\");\n\n if (!options.dryRun) {\n try {\n await fs.writeFile(cssAbsolutePath, mutation.output, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n return {\n ok: false,\n status: 500,\n reason: \"write-failed\",\n details: `Write CSS failed: ${e.message}`,\n };\n }\n }\n\n return {\n ok: true,\n diff,\n cssAbsolutePath,\n selector: detect.ref.selector,\n previousValue: mutation.previousValue,\n };\n}\n\nfunction isValid(x: unknown): x is ApplyCssPropertyInput {\n if (!x || typeof x !== \"object\") return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.file === \"string\" &&\n typeof o.line === \"number\" &&\n typeof o.col === \"number\" &&\n typeof o.property === \"string\" &&\n typeof o.value === \"string\" &&\n Number.isInteger(o.line) &&\n Number.isInteger(o.col) &&\n o.line > 0 &&\n o.col >= 0\n );\n}\n","import * as recast from \"recast\";\nimport babelTsParser from \"recast/parsers/babel-ts.js\";\nimport postcss from \"postcss\";\n\n/**\n * v0.2 B2a — CSS Modules write-back.\n *\n * Two layers:\n * 1. Given a JSX file + (line, col) pointing at a JSXOpeningElement,\n * detect whether its className is `{styles.foo}` and resolve the\n * `.module.css` file + the class selector (`.foo`).\n * 2. Given a CSS file + a selector + a property + a value, parse with\n * postcss, update or insert that declaration on the matching rule,\n * and stringify back. Refuses on `composes:` chains because they\n * reach into other rules and our change could leak.\n *\n * Out of scope (v0.3): tracking which JSX files import which CSS modules\n * (cross-file refactor), CSS variables, nested rules, media queries that\n * override the property.\n */\n\nexport type CssModuleRef = {\n /** The .module.css path the className resolved to. Absolute or relative? See `resolved`. */\n cssFile: string;\n /** The class selector inside that file, e.g. `.foo`. */\n selector: string;\n};\n\nexport type DetectCssModuleResult =\n | { ok: true; ref: CssModuleRef }\n | {\n ok: false;\n reason:\n | \"no-jsx-at-location\"\n | \"no-classname-attribute\"\n | \"not-a-css-module\"\n | \"dynamic-classname\"\n | \"unresolved-import\"\n | \"parse-error\";\n details: string;\n };\n\n/**\n * Detect whether the className at (line, col) in `jsxSource` uses a CSS\n * Modules pattern: `<div className={styles.foo}>` where `styles` is a\n * default import from a `.module.css` file. Returns the resolved CSS\n * file path (kept relative to the JSX file — the caller resolves to\n * absolute via the workspace root) and the class selector.\n */\nexport function detectCssModule(\n jsxSource: string,\n line: number,\n col: number,\n): DetectCssModuleResult {\n let ast: ReturnType<typeof recast.parse>;\n try {\n ast = recast.parse(jsxSource, { parser: babelTsParser });\n } catch (err) {\n return {\n ok: false,\n reason: \"parse-error\",\n details: (err as Error).message,\n };\n }\n\n // Step 1: find the JSXOpeningElement at line:col and inspect its className.\n let found: {\n objectName: string | null;\n propertyName: string | null;\n visited: boolean;\n } = { objectName: null, propertyName: null, visited: false };\n\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n const loc = node.loc;\n if (!loc) {\n this.traverse(path);\n return undefined;\n }\n if (loc.start.line !== line || loc.start.column !== col) {\n this.traverse(path);\n return undefined;\n }\n found.visited = true;\n const attrs = (node.attributes ?? []) as Array<{\n type: string;\n name?: { type: string; name?: string };\n value?: {\n type: string;\n expression?: {\n type: string;\n object?: { type: string; name?: string };\n property?: { type: string; name?: string };\n computed?: boolean;\n };\n };\n }>;\n const classNameAttr = attrs.find(\n (a) =>\n a.type === \"JSXAttribute\" &&\n a.name?.type === \"JSXIdentifier\" &&\n a.name?.name === \"className\",\n );\n if (!classNameAttr) return false;\n const v = classNameAttr.value;\n if (!v || v.type !== \"JSXExpressionContainer\") return false;\n const expr = v.expression;\n if (!expr) return false;\n // Accept ONLY `styles.foo` form. cn(styles.foo)/styles[k]/string is\n // handled elsewhere or refused.\n if (\n expr.type === \"MemberExpression\" &&\n expr.computed === false &&\n expr.object?.type === \"Identifier\" &&\n expr.property?.type === \"Identifier\"\n ) {\n found.objectName = expr.object.name ?? null;\n found.propertyName = expr.property.name ?? null;\n }\n return false;\n },\n });\n\n if (!found.visited) {\n return {\n ok: false,\n reason: \"no-jsx-at-location\",\n details: `No JSXOpeningElement found at ${line}:${col}`,\n };\n }\n if (!found.objectName) {\n return {\n ok: false,\n reason: \"dynamic-classname\",\n details:\n \"className isn't a `{identifier.property}` member expression. B2 only handles direct `{styles.foo}` access.\",\n };\n }\n\n // Step 2: find the default import for `objectName` and verify it points\n // to a .module.css file. Holder object so TS's CFA can narrow on `.value`.\n const importHolder: { value: string | null } = { value: null };\n recast.visit(ast, {\n visitImportDeclaration(path) {\n const node = path.node as {\n source: { value: string };\n specifiers: Array<{\n type: string;\n local?: { name?: string };\n }>;\n };\n const defaultSpec = node.specifiers.find(\n (s) =>\n s.type === \"ImportDefaultSpecifier\" &&\n s.local?.name === found.objectName,\n );\n if (defaultSpec) {\n importHolder.value = node.source.value;\n return false;\n }\n this.traverse(path);\n return undefined;\n },\n });\n\n if (!importHolder.value) {\n return {\n ok: false,\n reason: \"unresolved-import\",\n details: `No default import found for \\`${found.objectName}\\``,\n };\n }\n const importSource = importHolder.value;\n if (!importSource.endsWith(\".module.css\")) {\n return {\n ok: false,\n reason: \"not-a-css-module\",\n details: `Import \\`${importSource}\\` is not a .module.css file`,\n };\n }\n\n return {\n ok: true,\n ref: {\n cssFile: importSource,\n selector: `.${found.propertyName}`,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// CSS mutation (postcss)\n// ---------------------------------------------------------------------------\n\nexport type MutateCssPropertyResult =\n | { ok: true; output: string; previousValue: string | null }\n | {\n ok: false;\n reason:\n | \"css-parse-error\"\n | \"selector-not-found\"\n | \"composes-chain\"\n | \"invalid-property\";\n details: string;\n };\n\n/**\n * Update or insert a property declaration on the rule matching `selector`\n * in `cssSource`. Returns the rewritten CSS (and the previous value if\n * the property already existed, for undo bookkeeping).\n *\n * Refuses if the rule has a `composes:` declaration — composes chains\n * reach into other rules and a property change here could leak elsewhere.\n * That's the v0.3 work; for v0.2 we surface the refusal cleanly.\n */\nexport function mutateCssProperty(\n cssSource: string,\n selector: string,\n property: string,\n value: string,\n): MutateCssPropertyResult {\n if (!/^[a-zA-Z-]+$/.test(property)) {\n return {\n ok: false,\n reason: \"invalid-property\",\n details: `Property name \"${property}\" must be alphabetic + hyphens only`,\n };\n }\n let root: postcss.Root;\n try {\n root = postcss.parse(cssSource);\n } catch (err) {\n return {\n ok: false,\n reason: \"css-parse-error\",\n details: (err as Error).message,\n };\n }\n\n let targetRule: postcss.Rule | null = null;\n root.walkRules((rule) => {\n if (rule.selector === selector) {\n targetRule = rule;\n return false; // stop walking\n }\n });\n\n if (!targetRule) {\n return {\n ok: false,\n reason: \"selector-not-found\",\n details: `No rule matching selector \\`${selector}\\` in CSS source`,\n };\n }\n\n // Refuse on composes chains.\n let hasComposes = false;\n (targetRule as postcss.Rule).walkDecls(\"composes\", () => {\n hasComposes = true;\n });\n if (hasComposes) {\n return {\n ok: false,\n reason: \"composes-chain\",\n details: `Rule \\`${selector}\\` uses \\`composes:\\`. v0.2 refuses to mutate composes chains because the property change could leak through.`,\n };\n }\n\n // Find an existing declaration of `property` on this rule (not in nested\n // at-rules); if present, update its value, otherwise append.\n let previousValue: string | null = null;\n let updated = false;\n (targetRule as postcss.Rule).walkDecls(property, (decl) => {\n // Skip declarations nested inside at-rules (media queries etc).\n if (decl.parent !== targetRule) return;\n if (!updated) {\n previousValue = decl.value;\n decl.value = value;\n updated = true;\n }\n });\n\n if (!updated) {\n (targetRule as postcss.Rule).append({ prop: property, value });\n }\n\n return {\n ok: true,\n output: root.toString(),\n previousValue,\n };\n}\n","import * as fs from \"node:fs/promises\";\nimport { createPatch } from \"diff\";\nimport {\n detectStyledComponent,\n mutateStyledProperty,\n} from \"../css/styledComponents.ts\";\nimport { resolveWithinWorkspace } from \"./resolveSafe.ts\";\n\nexport type ApplyStyledPropertyInput = {\n /** Workspace-relative JSX file containing both the JSX element AND the styled definition. */\n file: string;\n line: number;\n col: number;\n property: string;\n value: string;\n};\n\nexport type ApplyStyledPropertyRefusalReason =\n | \"invalid-input\"\n | \"path-outside-workspace\"\n | \"file-not-found\"\n | \"read-failed\"\n | \"write-failed\"\n | \"no-jsx-at-location\"\n | \"not-a-styled-component\"\n | \"styled-with-interpolation\"\n | \"styled-extension-not-supported\"\n | \"styled-attrs-not-supported\"\n | \"cross-file-styled-not-supported\"\n | \"component-not-found\"\n | \"parse-error\"\n | \"css-parse-error\"\n | \"invalid-property\";\n\nexport type ApplyStyledPropertyOutcome =\n | {\n ok: true;\n diff: string;\n componentName: string;\n previousValue: string | null;\n }\n | {\n ok: false;\n status: 400 | 403 | 404 | 500;\n reason: ApplyStyledPropertyRefusalReason;\n details: string;\n };\n\nexport async function applyStyledProperty(\n input: ApplyStyledPropertyInput,\n options: { workspaceRoot: string; dryRun: boolean },\n): Promise<ApplyStyledPropertyOutcome> {\n if (!isValid(input)) {\n return {\n ok: false,\n status: 400,\n reason: \"invalid-input\",\n details:\n \"Body must be { file: string, line: number, col: number, property: string, value: string }\",\n };\n }\n\n const absPath = resolveWithinWorkspace(options.workspaceRoot, input.file);\n if (!absPath) {\n return {\n ok: false,\n status: 403,\n reason: \"path-outside-workspace\",\n details: `File outside workspace: ${input.file}`,\n };\n }\n\n let source: string;\n try {\n source = await fs.readFile(absPath, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n if (e.code === \"ENOENT\") {\n return {\n ok: false,\n status: 404,\n reason: \"file-not-found\",\n details: `File not found: ${input.file}`,\n };\n }\n return {\n ok: false,\n status: 500,\n reason: \"read-failed\",\n details: `Read failed: ${e.message}`,\n };\n }\n\n const detect = detectStyledComponent(source, input.line, input.col);\n if (!detect.ok) {\n return {\n ok: false,\n status: 400,\n reason: detect.reason,\n details: detect.details,\n };\n }\n\n const mutation = mutateStyledProperty(\n source,\n detect.ref.componentName,\n input.property,\n input.value,\n );\n if (!mutation.ok) {\n return {\n ok: false,\n status: 400,\n reason: mutation.reason,\n details: mutation.details,\n };\n }\n\n const diff = createPatch(input.file, source, mutation.output, \"before\", \"after\");\n\n if (!options.dryRun) {\n try {\n await fs.writeFile(absPath, mutation.output, \"utf8\");\n } catch (err) {\n const e = err as NodeJS.ErrnoException;\n return {\n ok: false,\n status: 500,\n reason: \"write-failed\",\n details: `Write failed: ${e.message}`,\n };\n }\n }\n\n return {\n ok: true,\n diff,\n componentName: detect.ref.componentName,\n previousValue: mutation.previousValue,\n };\n}\n\nfunction isValid(x: unknown): x is ApplyStyledPropertyInput {\n if (!x || typeof x !== \"object\") return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.file === \"string\" &&\n typeof o.line === \"number\" &&\n typeof o.col === \"number\" &&\n typeof o.property === \"string\" &&\n typeof o.value === \"string\" &&\n Number.isInteger(o.line) &&\n Number.isInteger(o.col) &&\n o.line > 0 &&\n o.col >= 0\n );\n}\n","import * as recast from \"recast\";\nimport babelTsParser from \"recast/parsers/babel-ts.js\";\nimport postcss from \"postcss\";\n\n/**\n * v0.2 B3a — styled-components write-back.\n *\n * Two layers (mirrors the CSS Modules pipeline from B2):\n * 1. Given a JSX file + (line, col) pointing at a `<Button>`-style element,\n * detect whether `Button` is defined IN THE SAME FILE as a\n * styled.tagname`…` tagged template. Return the variable name and\n * the tagged-template's location.\n * 2. Mutate a CSS property inside the template's static text via postcss,\n * preserving every other declaration verbatim. Refuses on:\n * - any `${…}` interpolation present\n * - styled().attrs(...) / .withConfig(...) chains\n * - extension form `styled(BaseComponent)`\n * - cross-file imports (component declared elsewhere)\n *\n * Out of scope (v0.3): interpolation-aware mutation, attrs/withConfig,\n * twin.macro / tailwind-styled-components / tw-shorthand.\n */\n\nexport type StyledRef = {\n /** Variable name, e.g. \"Button\". */\n componentName: string;\n /** HTML tag name from `styled.X`, e.g. \"button\". */\n htmlTag: string;\n};\n\nexport type DetectStyledResult =\n | { ok: true; ref: StyledRef }\n | {\n ok: false;\n reason:\n | \"no-jsx-at-location\"\n | \"not-a-styled-component\"\n | \"styled-with-interpolation\"\n | \"styled-extension-not-supported\"\n | \"styled-attrs-not-supported\"\n | \"cross-file-styled-not-supported\"\n | \"parse-error\";\n details: string;\n };\n\nexport function detectStyledComponent(\n jsxSource: string,\n line: number,\n col: number,\n): DetectStyledResult {\n let ast: ReturnType<typeof recast.parse>;\n try {\n ast = recast.parse(jsxSource, { parser: babelTsParser });\n } catch (err) {\n return {\n ok: false,\n reason: \"parse-error\",\n details: (err as Error).message,\n };\n }\n\n // Step 1: find the JSXOpeningElement at line:col and extract its tag name.\n const jsxFound: { tagName: string | null; visited: boolean } = {\n tagName: null,\n visited: false,\n };\n recast.visit(ast, {\n visitJSXOpeningElement(path) {\n const node = path.node;\n const loc = node.loc;\n if (!loc) {\n this.traverse(path);\n return undefined;\n }\n if (loc.start.line !== line || loc.start.column !== col) {\n this.traverse(path);\n return undefined;\n }\n jsxFound.visited = true;\n const name = node.name as { type: string; name?: string };\n if (name.type === \"JSXIdentifier\" && name.name) {\n jsxFound.tagName = name.name;\n }\n return false;\n },\n });\n\n if (!jsxFound.visited) {\n return {\n ok: false,\n reason: \"no-jsx-at-location\",\n details: `No JSXOpeningElement found at ${line}:${col}`,\n };\n }\n if (!jsxFound.tagName || /^[a-z]/.test(jsxFound.tagName)) {\n // Lowercase tag = native HTML element, not a styled component.\n return {\n ok: false,\n reason: \"not-a-styled-component\",\n details: `JSX tag is not a component identifier (got \"${jsxFound.tagName ?? \"<none>\"}\")`,\n };\n }\n\n // Step 2: walk top-level declarations looking for\n // `const <tagName> = styled.<htmlTag>\\`...\\``\n // with NO interpolations and NO attrs/withConfig.\n type RefusalReason =\n | \"no-jsx-at-location\"\n | \"not-a-styled-component\"\n | \"styled-with-interpolation\"\n | \"styled-extension-not-supported\"\n | \"styled-attrs-not-supported\"\n | \"cross-file-styled-not-supported\"\n | \"parse-error\";\n\n let foundRef: StyledRef | null = null;\n let refusedReason: RefusalReason | null = null;\n let refusedDetails = \"\";\n\n type DeclLike = {\n type: string;\n declarations?: Array<{\n type: string;\n id?: { type: string; name?: string };\n init?: unknown;\n }>;\n };\n const program = (ast as { program: { body: DeclLike[] } }).program;\n for (const stmt of program.body) {\n if (stmt.type !== \"VariableDeclaration\") continue;\n for (const decl of stmt.declarations ?? []) {\n if (decl.type !== \"VariableDeclarator\") continue;\n if (decl.id?.type !== \"Identifier\") continue;\n if (decl.id.name !== jsxFound.tagName) continue;\n const init = decl.init as\n | {\n type: string;\n tag?: {\n type: string;\n object?: { type: string; name?: string };\n property?: { type: string; name?: string };\n computed?: boolean;\n callee?: unknown;\n };\n quasi?: {\n type: string;\n expressions?: unknown[];\n };\n }\n | undefined;\n if (!init) continue;\n if (init.type !== \"TaggedTemplateExpression\") {\n refusedReason = \"not-a-styled-component\";\n refusedDetails = `\\`${jsxFound.tagName}\\` is declared but isn't a tagged template`;\n continue;\n }\n const tag = init.tag;\n if (!tag) continue;\n // Accept ONLY `styled.tagname` — refuse extensions and attrs chains.\n if (\n tag.type === \"MemberExpression\" &&\n tag.computed === false &&\n tag.object?.type === \"Identifier\" &&\n tag.object.name === \"styled\" &&\n tag.property?.type === \"Identifier\"\n ) {\n // The quasi must have NO interpolations.\n if ((init.quasi?.expressions?.length ?? 0) > 0) {\n refusedReason = \"styled-with-interpolation\";\n refusedDetails = `\\`${jsxFound.tagName}\\` template has \\${…} interpolations — v0.2 only mutates fully-static templates`;\n continue;\n }\n foundRef = {\n componentName: jsxFound.tagName,\n htmlTag: tag.property.name ?? \"div\",\n };\n break;\n }\n // Common refusal shapes:\n if (tag.type === \"CallExpression\") {\n const callee = tag as unknown as {\n callee?: { type: string; name?: string; property?: { name?: string } };\n };\n const c = callee.callee;\n if (c?.type === \"Identifier\" && c.name === \"styled\") {\n refusedReason = \"styled-extension-not-supported\";\n refusedDetails = `\\`styled(...)\\` extension form — v0.2 only handles \\`styled.tag\\``;\n } else if (c?.type === \"MemberExpression\" && c.property?.name === \"attrs\") {\n refusedReason = \"styled-attrs-not-supported\";\n refusedDetails = `\\`.attrs(...)\\` chain not supported in v0.2`;\n }\n }\n }\n if (foundRef) break;\n }\n\n if (foundRef) return { ok: true, ref: foundRef };\n if (refusedReason) {\n return {\n ok: false,\n reason: refusedReason,\n details: refusedDetails,\n };\n }\n return {\n ok: false,\n reason: \"cross-file-styled-not-supported\",\n details: `\\`${jsxFound.tagName}\\` isn't declared in this file. v0.2 only resolves same-file styled definitions.`,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Mutation\n// ---------------------------------------------------------------------------\n\nexport type MutateStyledResult =\n | { ok: true; output: string; previousValue: string | null }\n | {\n ok: false;\n reason:\n | \"component-not-found\"\n | \"styled-with-interpolation\"\n | \"css-parse-error\"\n | \"invalid-property\"\n | \"parse-error\";\n details: string;\n };\n\n/**\n * Find `const componentName = styled.X\\`...\\`` in `jsxSource`, parse the\n * template's static text as CSS, update or insert a property, write back\n * to the template. Returns the rewritten source.\n *\n * Refuses if the template has ANY interpolations (we can't safely reason\n * about which static segment owns a property when interpolations are\n * present — v0.3 work).\n */\nexport function mutateStyledProperty(\n jsxSource: string,\n componentName: string,\n property: string,\n value: string,\n): MutateStyledResult {\n if (!/^[a-zA-Z-]+$/.test(property)) {\n return {\n ok: false,\n reason: \"invalid-property\",\n details: `Property name \"${property}\" must be alphabetic + hyphens only`,\n };\n }\n\n let ast: ReturnType<typeof recast.parse>;\n try {\n ast = recast.parse(jsxSource, { parser: babelTsParser });\n } catch (err) {\n return {\n ok: false,\n reason: \"parse-error\",\n details: (err as Error).message,\n };\n }\n\n type MutateRefusalReason =\n | \"component-not-found\"\n | \"styled-with-interpolation\"\n | \"css-parse-error\"\n | \"invalid-property\"\n | \"parse-error\";\n\n const out: {\n found: boolean;\n interpolated: boolean;\n previousValue: string | null;\n error: { reason: MutateRefusalReason; details: string } | null;\n } = {\n found: false,\n interpolated: false,\n previousValue: null,\n error: null,\n };\n\n type Quasi = {\n type: \"TemplateElement\";\n value: { cooked?: string; raw: string };\n };\n type TemplateLiteralNode = {\n type: \"TemplateLiteral\";\n quasis: Quasi[];\n expressions: unknown[];\n };\n\n recast.visit(ast, {\n visitVariableDeclarator(path) {\n const node = path.node as {\n id?: { type: string; name?: string };\n init?: {\n type: string;\n tag?: {\n type: string;\n object?: { type: string; name?: string };\n property?: { type: string; name?: string };\n computed?: boolean;\n };\n quasi?: TemplateLiteralNode;\n };\n };\n if (out.found || out.error) return false;\n if (node.id?.type !== \"Identifier\" || node.id.name !== componentName) {\n this.traverse(path);\n return undefined;\n }\n const init = node.init;\n if (!init || init.type !== \"TaggedTemplateExpression\") return false;\n const tag = init.tag;\n if (\n !tag ||\n tag.type !== \"MemberExpression\" ||\n tag.object?.type !== \"Identifier\" ||\n tag.object.name !== \"styled\"\n ) {\n return false;\n }\n const quasi = init.quasi;\n if (!quasi) return false;\n if ((quasi.expressions?.length ?? 0) > 0) {\n out.interpolated = true;\n out.error = {\n reason: \"styled-with-interpolation\",\n details: `\\`${componentName}\\` template has interpolations — v0.2 only mutates fully-static templates`,\n };\n return false;\n }\n const onlyQuasi = quasi.quasis[0];\n if (!onlyQuasi) return false;\n\n const css = onlyQuasi.value.cooked ?? onlyQuasi.value.raw ?? \"\";\n // postcss parses CSS without selectors — wrap in a synthetic rule so\n // we can use the same walkDecls machinery as B2's mutateCssProperty.\n const wrapped = `:root {${css}}`;\n let root: postcss.Root;\n try {\n root = postcss.parse(wrapped);\n } catch (err) {\n out.error = {\n reason: \"css-parse-error\",\n details: (err as Error).message,\n };\n return false;\n }\n const rule = root.first as postcss.Rule | undefined;\n if (!rule) {\n out.error = {\n reason: \"css-parse-error\",\n details: \"Internal: synthetic wrap produced no rule\",\n };\n return false;\n }\n\n let updated = false;\n rule.walkDecls(property, (decl) => {\n if (decl.parent !== rule) return; // skip nested at-rules\n if (!updated) {\n out.previousValue = decl.value;\n decl.value = value;\n updated = true;\n }\n });\n if (!updated) {\n rule.append({ prop: property, value });\n }\n\n // Re-extract the inside-of-rule text.\n const rebuilt = rule.toString();\n // postcss serializes as `:root {\\n …\\n}` (or :root{ … }). Strip the\n // outer selector + braces, restore the inner declarations as our\n // new quasi value.\n const innerMatch = rebuilt.match(/^[^{]*\\{([\\s\\S]*)\\}\\s*$/);\n const inner = innerMatch && innerMatch[1] !== undefined ? innerMatch[1] : rebuilt;\n onlyQuasi.value.cooked = inner;\n onlyQuasi.value.raw = inner;\n out.found = true;\n return false;\n },\n });\n\n if (out.error) {\n return { ok: false, ...out.error };\n }\n if (!out.found) {\n return {\n ok: false,\n reason: \"component-not-found\",\n details: `No \\`const ${componentName} = styled.X\\`…\\`\\` definition found`,\n };\n }\n return {\n ok: true,\n output: recast.print(ast).code,\n previousValue: out.previousValue,\n };\n}\n","import * as fs from \"node:fs/promises\";\n\n/**\n * Small in-memory deque of recently applied mutations, sized to keep the\n * footprint negligible. Used by the 4th MCP tool (`revert_change`) and\n * the overlay's \"undo last apply\" affordance.\n *\n * v0.2: persists to `<workspace>/.visual-editor/history.json` so the undo\n * stack survives server restarts. Persistence is fire-and-forget on every\n * mutation — `persistNow()` returns a Promise that tests can await for\n * deterministic checks.\n */\nexport type Apply = {\n file: string;\n line: number;\n col: number;\n before: string;\n after: string;\n appliedAt: number; // Date.now()\n};\n\nexport class RecentApplies {\n private buffer: Apply[] = [];\n private filePath: string | null = null;\n\n constructor(private readonly maxSize = 50) {}\n\n /**\n * Wire up disk persistence. If the file exists, its contents become the\n * initial buffer. If it doesn't, we remember the path for future writes.\n */\n async load(filePath: string): Promise<void> {\n this.filePath = filePath;\n try {\n const raw = await fs.readFile(filePath, \"utf8\");\n const parsed = JSON.parse(raw) as { applies?: Apply[] };\n if (Array.isArray(parsed.applies)) {\n // Take the last N to enforce the bound even if the file grew externally.\n this.buffer = parsed.applies\n .filter(isApply)\n .slice(-this.maxSize);\n }\n } catch {\n // File missing or unreadable — start empty. The path is set so the\n // next push will create the file.\n }\n }\n\n push(apply: Apply): void {\n this.buffer.push(apply);\n if (this.buffer.length > this.maxSize) this.buffer.shift();\n void this.persistNow();\n }\n\n /**\n * Find the most-recent entry matching `key`. If no key is provided, returns\n * the most-recent entry overall (single-step undo). Returns `null` when no\n * matching entry is in the buffer.\n */\n find(key?: { file: string; line: number; col: number }): Apply | null {\n if (this.buffer.length === 0) return null;\n if (!key) return this.buffer[this.buffer.length - 1] ?? null;\n for (let i = this.buffer.length - 1; i >= 0; i--) {\n const a = this.buffer[i];\n if (\n a &&\n a.file === key.file &&\n a.line === key.line &&\n a.col === key.col\n ) {\n return a;\n }\n }\n return null;\n }\n\n remove(apply: Apply): void {\n const idx = this.buffer.lastIndexOf(apply);\n if (idx !== -1) this.buffer.splice(idx, 1);\n void this.persistNow();\n }\n\n list(): readonly Apply[] {\n return this.buffer;\n }\n\n clear(): void {\n this.buffer = [];\n void this.persistNow();\n }\n\n get size(): number {\n return this.buffer.length;\n }\n\n /**\n * Write the buffer to disk. No-op when no path has been configured.\n * Safe to `void`-call for fire-and-forget; tests can `await` it for\n * determinism.\n */\n async persistNow(): Promise<void> {\n if (!this.filePath) return;\n const json = JSON.stringify(\n { applies: this.buffer, savedAt: Date.now() },\n null,\n 2,\n );\n try {\n await fs.writeFile(this.filePath, json, \"utf8\");\n } catch {\n // Disk full or perms — surface via dedicated health endpoint later.\n // For now, undo just won't survive a restart.\n }\n }\n}\n\nfunction isApply(x: unknown): x is Apply {\n if (!x || typeof x !== \"object\") return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.file === \"string\" &&\n typeof o.line === \"number\" &&\n typeof o.col === \"number\" &&\n typeof o.before === \"string\" &&\n typeof o.after === \"string\" &&\n typeof o.appliedAt === \"number\"\n );\n}\n","/**\n * Single in-memory \"what is the user currently looking at\" record, set by\n * the overlay on element acquire and read by the MCP `get_selected_element`\n * tool. Intentionally null when no selection is active — the MCP tool\n * returns \"no selection\" rather than stale state.\n */\nexport type Selection = {\n /** Workspace-relative file path from the element's data-oid. */\n file: string;\n /** 1-based line number (Babel convention). */\n line: number;\n /** 0-based column (Babel convention). */\n col: number;\n /** The full data-oid string for traceability. */\n oid: string;\n /** Whole className string (overlay should split if it wants tokens). */\n className: string;\n /** DOM tag name like \"div\", \"button\". */\n tagName: string;\n /** Best-guess component name (file basename or Fiber name). */\n componentName: string | null;\n /** How many DOM elements share this data-oid right now (Principle 11). */\n instanceCount: number;\n};\n\nexport class CurrentSelection {\n private current: Selection | null = null;\n set(s: Selection | null): void {\n this.current = s;\n }\n get(): Selection | null {\n return this.current;\n }\n clear(): void {\n this.current = null;\n }\n}\n","import { randomBytes } from \"node:crypto\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\n\nconst SESSION_DIRNAME = \".visual-editor\";\nconst SESSION_FILENAME = \"session.json\";\n\n/**\n * Per-session token. Mounted on startup, kept in memory, and persisted to\n * `<workspace>/.visual-editor/session.json` so the MCP stdio server (a\n * sibling process spawned by Claude Code) can read it without us having to\n * pipe it through a separate transport.\n *\n * Threat model: a random page running on the same dev machine (e.g. an ad\n * iframe, a stale tab on another local port) could POST to the loopback\n * server and clobber files. Requiring an Authorization header that only\n * the overlay (and the MCP server, via the file) knows blocks that vector.\n *\n * Open trade-off in v0.1: GET /token returns the token without auth so\n * the overlay can bootstrap. A drive-by page could fetch it the same way.\n * Production hardening would either (a) inject the token into the page at\n * dev-build time, or (b) pin the Origin/Referer header. Both require\n * dev-server integration that's out of scope here.\n */\nexport class SessionToken {\n private token: string | null = null;\n private filePath: string | null = null;\n\n async load(workspaceRoot: string): Promise<string> {\n const dir = path.join(workspaceRoot, SESSION_DIRNAME);\n this.filePath = path.join(dir, SESSION_FILENAME);\n\n // Reuse the existing session if the file is there. Refreshing the token\n // on every restart would invalidate the MCP server's cached value.\n try {\n const existing = JSON.parse(\n await fs.readFile(this.filePath, \"utf8\"),\n ) as { token?: unknown };\n if (typeof existing.token === \"string\" && existing.token.length >= 16) {\n this.token = existing.token;\n return this.token;\n }\n } catch {\n /* file missing or unreadable — fall through to mint a new one */\n }\n\n await fs.mkdir(dir, { recursive: true });\n this.token = randomBytes(24).toString(\"hex\");\n await fs.writeFile(\n this.filePath,\n JSON.stringify({ token: this.token, createdAt: Date.now() }, null, 2),\n { encoding: \"utf8\", mode: 0o600 },\n );\n return this.token;\n }\n\n /** Construct the token in-process without touching disk. Used by tests. */\n setInMemory(token: string): void {\n this.token = token;\n }\n\n get(): string {\n if (!this.token) {\n throw new Error(\"SessionToken not loaded — call load() first\");\n }\n return this.token;\n }\n\n /**\n * Constant-time compare against the bearer string the client sent. Returns\n * `true` when the lengths match AND every byte is equal. Plain `===` would\n * leak length information through timing.\n */\n matches(received: string | null): boolean {\n if (!this.token || !received) return false;\n if (received.length !== this.token.length) return false;\n let diff = 0;\n for (let i = 0; i < received.length; i++) {\n diff |= received.charCodeAt(i) ^ this.token.charCodeAt(i);\n }\n return diff === 0;\n }\n}\n\nexport function parseBearer(header: string | undefined): string | null {\n if (!header) return null;\n const m = /^Bearer\\s+(.+)$/i.exec(header);\n return m ? m[1]!.trim() : null;\n}\n","import * as http from \"node:http\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { applyToFile } from \"../fs/applyToFile.ts\";\nimport type { ApplyInput } from \"../fs/applyToFile.ts\";\nimport { revertToFile } from \"../fs/revertToFile.ts\";\nimport type { RevertInput } from \"../fs/revertToFile.ts\";\nimport { applyCssProperty } from \"../fs/applyCssProperty.ts\";\nimport type { ApplyCssPropertyInput } from \"../fs/applyCssProperty.ts\";\nimport { applyStyledProperty } from \"../fs/applyStyledProperty.ts\";\nimport type { ApplyStyledPropertyInput } from \"../fs/applyStyledProperty.ts\";\nimport { RecentApplies } from \"../state/recentApplies.ts\";\nimport { CurrentSelection } from \"../state/selection.ts\";\nimport type { Selection } from \"../state/selection.ts\";\nimport { SessionToken, parseBearer } from \"../state/auth.ts\";\n\nexport type ServerOptions = {\n /** Absolute path inside which all /apply and /propose writes are constrained. */\n workspaceRoot: string;\n /** Optional pre-built buffer (tests inject their own). */\n recentApplies?: RecentApplies;\n /** Optional pre-built selection state (tests inject their own). */\n currentSelection?: CurrentSelection;\n /** Optional pre-loaded session token (tests inject their own). */\n sessionToken?: SessionToken;\n /**\n * Pinned allowed origins. Empty means any origin (compat with v0.1).\n * Production: pass the dev URL, e.g. `[\"http://localhost:3000\"]`.\n */\n allowedOrigins?: readonly string[];\n};\n\nexport type ServerContext = {\n options: ServerOptions;\n recentApplies: RecentApplies;\n currentSelection: CurrentSelection;\n sessionToken: SessionToken;\n};\n\n/**\n * Build (but do not start) the local HTTP server. Callers do `.listen(port)`.\n * Tests pass `port: 0` to get a random port; the CLI binds 7790.\n */\nexport function createServer(options: ServerOptions): http.Server {\n const ctx: ServerContext = {\n options,\n recentApplies: options.recentApplies ?? new RecentApplies(),\n currentSelection: options.currentSelection ?? new CurrentSelection(),\n sessionToken: options.sessionToken ?? new SessionToken(),\n };\n return http.createServer((req, res) => {\n void handle(req, res, ctx).catch((err) => {\n writeJson(res, 500, {\n ok: false,\n reason: \"internal-error\",\n details: (err as Error).message,\n });\n });\n });\n}\n\nasync function handle(\n req: IncomingMessage,\n res: ServerResponse,\n ctx: ServerContext,\n): Promise<void> {\n // CORS — accept any localhost origin so the overlay (running on whatever\n // port `next dev` chose) can call us. v0.1 will tighten this to the\n // active dev URL once we have the per-session token in place.\n const origin = req.headers.origin;\n const allowed = ctx.options.allowedOrigins ?? [];\n const originAllowed = allowed.length === 0 || (origin && allowed.includes(origin));\n\n // CORS — when an allowlist is configured, only echo the actual origin if\n // it matches. With no allowlist (v0.1 behavior), echo `*` for the spike.\n if (allowed.length === 0) {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n } else if (originAllowed && origin) {\n res.setHeader(\"Access-Control-Allow-Origin\", origin);\n res.setHeader(\"Vary\", \"Origin\");\n }\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, DELETE, OPTIONS\");\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization\");\n if (req.method === \"OPTIONS\") {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const url = req.url ?? \"/\";\n\n // /health is always open — it's a sanity ping with no surface to abuse.\n if (req.method === \"GET\" && url === \"/health\") {\n writeJson(res, 200, { ok: true });\n return;\n }\n\n // Origin check applies to /token too. The bootstrap is the most-attacked\n // endpoint — refusing wrong-origin requests is what closes the hole the\n // spike left open.\n if (!originAllowed) {\n writeJson(res, 403, {\n ok: false,\n reason: \"origin-not-allowed\",\n details: `Origin ${origin ?? \"(missing)\"} is not in the allowlist`,\n });\n return;\n }\n\n if (req.method === \"GET\" && url === \"/token\") {\n writeJson(res, 200, { token: ctx.sessionToken.get() });\n return;\n }\n\n // Everything below requires the bearer token.\n const bearer = parseBearer(req.headers.authorization);\n if (!ctx.sessionToken.matches(bearer)) {\n writeJson(res, 401, {\n ok: false,\n reason: \"unauthorized\",\n details:\n \"Include `Authorization: Bearer <token>` (fetch the token from GET /token).\",\n });\n return;\n }\n\n if (req.method === \"GET\" && url === \"/recent\") {\n // Diagnostic / introspection — the overlay can show a history list later.\n writeJson(res, 200, { ok: true, applies: ctx.recentApplies.list() });\n return;\n }\n\n if (req.method === \"GET\" && url === \"/assets\") {\n await dispatchAssets(res, ctx);\n return;\n }\n\n if (req.method === \"POST\" && url === \"/apply\") {\n await dispatchMutation(req, res, ctx, false);\n return;\n }\n\n if (req.method === \"POST\" && url === \"/propose\") {\n await dispatchMutation(req, res, ctx, true);\n return;\n }\n\n if (req.method === \"POST\" && url === \"/revert\") {\n await dispatchRevert(req, res, ctx);\n return;\n }\n\n if (req.method === \"POST\" && url === \"/apply-css-prop\") {\n await dispatchCssProperty(req, res, ctx);\n return;\n }\n\n if (req.method === \"POST\" && url === \"/apply-styled-prop\") {\n await dispatchStyledProperty(req, res, ctx);\n return;\n }\n\n if (req.method === \"GET\" && url === \"/selection\") {\n writeJson(res, 200, {\n ok: true,\n selection: ctx.currentSelection.get(),\n });\n return;\n }\n\n if (req.method === \"POST\" && url === \"/selection\") {\n await dispatchSelection(req, res, ctx);\n return;\n }\n\n if (req.method === \"DELETE\" && url === \"/selection\") {\n ctx.currentSelection.clear();\n writeJson(res, 200, { ok: true });\n return;\n }\n\n writeJson(res, 404, {\n ok: false,\n reason: \"not-found\",\n details: `No route for ${req.method} ${url}`,\n });\n}\n\nasync function dispatchMutation(\n req: IncomingMessage,\n res: ServerResponse,\n ctx: ServerContext,\n dryRun: boolean,\n): Promise<void> {\n let body: unknown;\n try {\n body = await readJsonBody(req);\n } catch (err) {\n writeJson(res, 400, {\n ok: false,\n reason: \"invalid-json\",\n details: (err as Error).message,\n });\n return;\n }\n\n const outcome = await applyToFile(body as ApplyInput, {\n workspaceRoot: ctx.options.workspaceRoot,\n dryRun,\n });\n\n if (outcome.ok) {\n // Record only real applies — /propose is dryRun and shouldn't show up\n // in the revert history.\n if (!dryRun) {\n const input = body as ApplyInput;\n // Persist the actual previous value so undo can swap back even\n // when the client originally sent before=null (asset picker UX).\n const beforeForBuffer = outcome.previousValue ?? input.before ?? \"\";\n ctx.recentApplies.push({\n file: input.file,\n line: input.line,\n col: input.col,\n before: beforeForBuffer,\n after: input.after,\n appliedAt: Date.now(),\n });\n }\n writeJson(res, 200, { ok: true, diff: outcome.diff });\n return;\n }\n\n writeJson(res, outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n}\n\nasync function dispatchRevert(\n req: IncomingMessage,\n res: ServerResponse,\n ctx: ServerContext,\n): Promise<void> {\n let body: unknown;\n try {\n body = await readJsonBody(req);\n } catch (err) {\n writeJson(res, 400, {\n ok: false,\n reason: \"invalid-json\",\n details: (err as Error).message,\n });\n return;\n }\n\n const outcome = await revertToFile(\n body as RevertInput,\n { workspaceRoot: ctx.options.workspaceRoot, dryRun: false },\n ctx.recentApplies,\n );\n\n if (outcome.ok) {\n writeJson(res, 200, { ok: true, diff: outcome.diff });\n return;\n }\n\n writeJson(res, outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n}\n\nasync function dispatchCssProperty(\n req: IncomingMessage,\n res: ServerResponse,\n ctx: ServerContext,\n): Promise<void> {\n let body: unknown;\n try {\n body = await readJsonBody(req);\n } catch (err) {\n writeJson(res, 400, {\n ok: false,\n reason: \"invalid-json\",\n details: (err as Error).message,\n });\n return;\n }\n const outcome = await applyCssProperty(body as ApplyCssPropertyInput, {\n workspaceRoot: ctx.options.workspaceRoot,\n dryRun: false,\n });\n if (outcome.ok) {\n writeJson(res, 200, {\n ok: true,\n diff: outcome.diff,\n selector: outcome.selector,\n previousValue: outcome.previousValue,\n });\n return;\n }\n writeJson(res, outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n}\n\nasync function dispatchStyledProperty(\n req: IncomingMessage,\n res: ServerResponse,\n ctx: ServerContext,\n): Promise<void> {\n let body: unknown;\n try {\n body = await readJsonBody(req);\n } catch (err) {\n writeJson(res, 400, {\n ok: false,\n reason: \"invalid-json\",\n details: (err as Error).message,\n });\n return;\n }\n const outcome = await applyStyledProperty(body as ApplyStyledPropertyInput, {\n workspaceRoot: ctx.options.workspaceRoot,\n dryRun: false,\n });\n if (outcome.ok) {\n writeJson(res, 200, {\n ok: true,\n diff: outcome.diff,\n componentName: outcome.componentName,\n previousValue: outcome.previousValue,\n });\n return;\n }\n writeJson(res, outcome.status, {\n ok: false,\n reason: outcome.reason,\n details: outcome.details,\n });\n}\n\nasync function dispatchAssets(\n res: ServerResponse,\n ctx: ServerContext,\n): Promise<void> {\n const fs = await import(\"node:fs/promises\");\n const path = await import(\"node:path\");\n const IMAGE_EXTS = new Set([\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".svg\",\n \".webp\",\n \".avif\",\n ]);\n const publicDir = path.join(ctx.options.workspaceRoot, \"public\");\n\n async function walk(dir: string, rel: string): Promise<string[]> {\n let entries: import(\"node:fs\").Dirent[] = [];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n const out: string[] = [];\n for (const e of entries) {\n const child = path.join(dir, e.name);\n const childRel = rel ? `${rel}/${e.name}` : e.name;\n if (e.isDirectory()) {\n const sub = await walk(child, childRel);\n out.push(...sub);\n } else if (\n e.isFile() &&\n IMAGE_EXTS.has(path.extname(e.name).toLowerCase())\n ) {\n // public/foo.png is referenced as \"/foo.png\" in src attributes.\n out.push(`/${childRel}`);\n }\n }\n return out;\n }\n\n try {\n const assets = await walk(publicDir, \"\");\n assets.sort();\n writeJson(res, 200, { ok: true, assets });\n } catch (err) {\n writeJson(res, 500, {\n ok: false,\n reason: \"assets-list-failed\",\n details: (err as Error).message,\n });\n }\n}\n\nasync function dispatchSelection(\n req: IncomingMessage,\n res: ServerResponse,\n ctx: ServerContext,\n): Promise<void> {\n let body: unknown;\n try {\n body = await readJsonBody(req);\n } catch (err) {\n writeJson(res, 400, {\n ok: false,\n reason: \"invalid-json\",\n details: (err as Error).message,\n });\n return;\n }\n if (!isValidSelection(body)) {\n writeJson(res, 400, {\n ok: false,\n reason: \"invalid-input\",\n details:\n \"Body must be { file, line, col, oid, className, tagName, componentName?, instanceCount }\",\n });\n return;\n }\n ctx.currentSelection.set(body);\n writeJson(res, 200, { ok: true });\n}\n\nfunction isValidSelection(x: unknown): x is Selection {\n if (!x || typeof x !== \"object\") return false;\n const o = x as Record<string, unknown>;\n return (\n typeof o.file === \"string\" &&\n typeof o.line === \"number\" &&\n typeof o.col === \"number\" &&\n typeof o.oid === \"string\" &&\n typeof o.className === \"string\" &&\n typeof o.tagName === \"string\" &&\n (o.componentName === null || typeof o.componentName === \"string\") &&\n typeof o.instanceCount === \"number\"\n );\n}\n\nasync function readJsonBody(req: IncomingMessage): Promise<unknown> {\n const chunks: Buffer[] = [];\n for await (const chunk of req as AsyncIterable<Buffer>) {\n chunks.push(chunk);\n // Guard: refuse pathologically large bodies. JSX className mutations\n // are small payloads — anything over 1 MB is suspicious.\n if (chunks.reduce((n, c) => n + c.length, 0) > 1024 * 1024) {\n throw new Error(\"Request body exceeds 1 MB\");\n }\n }\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (raw.length === 0) return {};\n return JSON.parse(raw);\n}\n\nfunction writeJson(res: ServerResponse, status: number, payload: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(payload));\n}\n"],"mappings":"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","names":["path","fs","path","createPatch","recast","babelTsParser","path","createPatch","fs","createPatch","recast","babelTsParser","postcss","path","isValid","createPatch","fs","fs","path","createServer","fs","path"]}
package/dist/cli.js ADDED
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ RecentApplies,
4
+ SessionToken,
5
+ createServer
6
+ } from "./chunk-QEI2RGE2.js";
7
+
8
+ // src/cli.ts
9
+ import * as path from "path";
10
+ var DEFAULT_PORT = 7790;
11
+ var args = process.argv.slice(2);
12
+ var port = DEFAULT_PORT;
13
+ var workspaceRoot = process.cwd();
14
+ var allowedOrigins = [];
15
+ for (let i = 0; i < args.length; i++) {
16
+ const arg = args[i];
17
+ if (arg === "--port" || arg === "-p") {
18
+ const v = args[++i];
19
+ if (!v) throw new Error("--port requires a value");
20
+ port = Number(v);
21
+ if (!Number.isInteger(port) || port < 0) {
22
+ throw new Error(`Invalid --port: ${v}`);
23
+ }
24
+ } else if (arg === "--workspace" || arg === "-w") {
25
+ const v = args[++i];
26
+ if (!v) throw new Error("--workspace requires a value");
27
+ workspaceRoot = path.resolve(v);
28
+ } else if (arg === "--allow-origin") {
29
+ const v = args[++i];
30
+ if (!v) throw new Error("--allow-origin requires a value");
31
+ allowedOrigins.push(v);
32
+ } else if (arg === "--help" || arg === "-h") {
33
+ process.stdout.write(
34
+ [
35
+ "visual-editor server \u2014 local HTTP server that wraps deterministic",
36
+ "className AST mutations behind /apply and /propose endpoints.",
37
+ "",
38
+ "Usage:",
39
+ " visual-editor-server [--port 7790] [--workspace .]",
40
+ " [--allow-origin http://localhost:3000 ...]",
41
+ "",
42
+ "Endpoints:",
43
+ " GET /health \u2014 sanity ping",
44
+ " POST /apply \u2014 write the mutated source to disk",
45
+ " POST /propose \u2014 return the would-be source + diff, no write",
46
+ "",
47
+ "Request body for /apply and /propose:",
48
+ ' { "file": "app/page.tsx", "line": 6, "col": 6,',
49
+ ' "before": "p-4", "after": "p-6" }',
50
+ ""
51
+ ].join("\n")
52
+ );
53
+ process.exit(0);
54
+ }
55
+ }
56
+ var sessionToken = new SessionToken();
57
+ var token = await sessionToken.load(workspaceRoot);
58
+ var recentApplies = new RecentApplies();
59
+ await recentApplies.load(path.join(workspaceRoot, ".visual-editor", "history.json"));
60
+ var server = createServer({
61
+ workspaceRoot,
62
+ sessionToken,
63
+ recentApplies,
64
+ allowedOrigins
65
+ });
66
+ server.listen(port, "127.0.0.1", () => {
67
+ const addr = server.address();
68
+ const actualPort = typeof addr === "object" && addr ? addr.port : port;
69
+ const originSummary = allowedOrigins.length === 0 ? "any (configure --allow-origin to restrict)" : allowedOrigins.join(", ");
70
+ process.stdout.write(
71
+ `visual-editor server listening on http://127.0.0.1:${actualPort}
72
+ workspace: ${workspaceRoot}
73
+ allowed origins: ${originSummary}
74
+ session token: ${token.slice(0, 8)}\u2026 (written to ${path.join(workspaceRoot, ".visual-editor", "session.json")})
75
+ `
76
+ );
77
+ });
78
+ var shutdown = () => {
79
+ server.close(() => process.exit(0));
80
+ };
81
+ process.on("SIGINT", shutdown);
82
+ process.on("SIGTERM", shutdown);
83
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as path from \"node:path\";\nimport { createServer } from \"./http/server.ts\";\nimport { SessionToken } from \"./state/auth.ts\";\nimport { RecentApplies } from \"./state/recentApplies.ts\";\n\nconst DEFAULT_PORT = 7790;\n\nconst args = process.argv.slice(2);\nlet port = DEFAULT_PORT;\nlet workspaceRoot = process.cwd();\nconst allowedOrigins: string[] = [];\n\nfor (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--port\" || arg === \"-p\") {\n const v = args[++i];\n if (!v) throw new Error(\"--port requires a value\");\n port = Number(v);\n if (!Number.isInteger(port) || port < 0) {\n throw new Error(`Invalid --port: ${v}`);\n }\n } else if (arg === \"--workspace\" || arg === \"-w\") {\n const v = args[++i];\n if (!v) throw new Error(\"--workspace requires a value\");\n workspaceRoot = path.resolve(v);\n } else if (arg === \"--allow-origin\") {\n const v = args[++i];\n if (!v) throw new Error(\"--allow-origin requires a value\");\n allowedOrigins.push(v);\n } else if (arg === \"--help\" || arg === \"-h\") {\n process.stdout.write(\n [\n \"visual-editor server — local HTTP server that wraps deterministic\",\n \"className AST mutations behind /apply and /propose endpoints.\",\n \"\",\n \"Usage:\",\n \" visual-editor-server [--port 7790] [--workspace .]\",\n \" [--allow-origin http://localhost:3000 ...]\",\n \"\",\n \"Endpoints:\",\n \" GET /health — sanity ping\",\n \" POST /apply — write the mutated source to disk\",\n \" POST /propose — return the would-be source + diff, no write\",\n \"\",\n \"Request body for /apply and /propose:\",\n ' { \"file\": \"app/page.tsx\", \"line\": 6, \"col\": 6,',\n ' \"before\": \"p-4\", \"after\": \"p-6\" }',\n \"\",\n ].join(\"\\n\"),\n );\n process.exit(0);\n }\n}\n\nconst sessionToken = new SessionToken();\nconst token = await sessionToken.load(workspaceRoot);\n\nconst recentApplies = new RecentApplies();\nawait recentApplies.load(path.join(workspaceRoot, \".visual-editor\", \"history.json\"));\n\nconst server = createServer({\n workspaceRoot,\n sessionToken,\n recentApplies,\n allowedOrigins,\n});\nserver.listen(port, \"127.0.0.1\", () => {\n const addr = server.address();\n const actualPort = typeof addr === \"object\" && addr ? addr.port : port;\n const originSummary =\n allowedOrigins.length === 0\n ? \"any (configure --allow-origin to restrict)\"\n : allowedOrigins.join(\", \");\n process.stdout.write(\n `visual-editor server listening on http://127.0.0.1:${actualPort}\\n` +\n ` workspace: ${workspaceRoot}\\n` +\n ` allowed origins: ${originSummary}\\n` +\n ` session token: ${token.slice(0, 8)}… (written to ${path.join(workspaceRoot, \".visual-editor\", \"session.json\")})\\n`,\n );\n});\n\nconst shutdown = () => {\n server.close(() => process.exit(0));\n};\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n"],"mappings":";;;;;;;;AACA,YAAY,UAAU;AAKtB,IAAM,eAAe;AAErB,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAI,OAAO;AACX,IAAI,gBAAgB,QAAQ,IAAI;AAChC,IAAM,iBAA2B,CAAC;AAElC,SAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAM,MAAM,KAAK,CAAC;AAClB,MAAI,QAAQ,YAAY,QAAQ,MAAM;AACpC,UAAM,IAAI,KAAK,EAAE,CAAC;AAClB,QAAI,CAAC,EAAG,OAAM,IAAI,MAAM,yBAAyB;AACjD,WAAO,OAAO,CAAC;AACf,QAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,GAAG;AACvC,YAAM,IAAI,MAAM,mBAAmB,CAAC,EAAE;AAAA,IACxC;AAAA,EACF,WAAW,QAAQ,iBAAiB,QAAQ,MAAM;AAChD,UAAM,IAAI,KAAK,EAAE,CAAC;AAClB,QAAI,CAAC,EAAG,OAAM,IAAI,MAAM,8BAA8B;AACtD,oBAAqB,aAAQ,CAAC;AAAA,EAChC,WAAW,QAAQ,kBAAkB;AACnC,UAAM,IAAI,KAAK,EAAE,CAAC;AAClB,QAAI,CAAC,EAAG,OAAM,IAAI,MAAM,iCAAiC;AACzD,mBAAe,KAAK,CAAC;AAAA,EACvB,WAAW,QAAQ,YAAY,QAAQ,MAAM;AAC3C,YAAQ,OAAO;AAAA,MACb;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,IAAI;AAAA,IACb;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,IAAM,eAAe,IAAI,aAAa;AACtC,IAAM,QAAQ,MAAM,aAAa,KAAK,aAAa;AAEnD,IAAM,gBAAgB,IAAI,cAAc;AACxC,MAAM,cAAc,KAAU,UAAK,eAAe,kBAAkB,cAAc,CAAC;AAEnF,IAAM,SAAS,aAAa;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AACD,OAAO,OAAO,MAAM,aAAa,MAAM;AACrC,QAAM,OAAO,OAAO,QAAQ;AAC5B,QAAM,aAAa,OAAO,SAAS,YAAY,OAAO,KAAK,OAAO;AAClE,QAAM,gBACJ,eAAe,WAAW,IACtB,+CACA,eAAe,KAAK,IAAI;AAC9B,UAAQ,OAAO;AAAA,IACb,sDAAsD,UAAU;AAAA,eAC9C,aAAa;AAAA,qBACP,aAAa;AAAA,mBACf,MAAM,MAAM,GAAG,CAAC,CAAC,sBAAsB,UAAK,eAAe,kBAAkB,cAAc,CAAC;AAAA;AAAA,EACpH;AACF,CAAC;AAED,IAAM,WAAW,MAAM;AACrB,SAAO,MAAM,MAAM,QAAQ,KAAK,CAAC,CAAC;AACpC;AACA,QAAQ,GAAG,UAAU,QAAQ;AAC7B,QAAQ,GAAG,WAAW,QAAQ;","names":[]}