@volter/live 0.5.212 → 0.5.214

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/BUNDLED_NOTICES +4 -4
  2. package/README.md +4 -1
  3. package/dist/asset-library.d.ts +98 -0
  4. package/dist/asset-library.js +7 -0
  5. package/dist/asset-library.js.map +7 -0
  6. package/dist/chunk-B2HUWFTT.js +25 -0
  7. package/dist/chunk-B2HUWFTT.js.map +7 -0
  8. package/dist/{chunk-7SZ3C4CC.js → chunk-IZJOOON2.js} +45 -8
  9. package/dist/chunk-IZJOOON2.js.map +7 -0
  10. package/dist/{chunk-Y5W77HYT.js → chunk-JDOIPALX.js} +38 -3
  11. package/dist/chunk-JDOIPALX.js.map +7 -0
  12. package/dist/{chunk-YPAOTPIF.js → chunk-JFAHP5CC.js} +19 -3
  13. package/dist/chunk-JFAHP5CC.js.map +7 -0
  14. package/dist/{chunk-SDIWRX5X.js → chunk-PC56TDHV.js} +5 -5
  15. package/dist/chunk-PC56TDHV.js.map +7 -0
  16. package/dist/{chunk-USXOIBVX.js → chunk-WIAH4XZE.js} +5 -4
  17. package/dist/chunk-WIAH4XZE.js.map +7 -0
  18. package/dist/client.d.ts +10 -4
  19. package/dist/client.js +2 -1
  20. package/dist/discovery.js +2 -2
  21. package/dist/editor.d.ts +11 -2
  22. package/dist/index.d.ts +2 -1
  23. package/dist/index.js +18 -51
  24. package/dist/index.js.map +3 -3
  25. package/dist/product-locator.d.ts +2 -2
  26. package/dist/product-locator.js +2 -2
  27. package/dist/registry-format.d.ts +31 -3
  28. package/dist/registry-format.js +13 -3
  29. package/dist/session.d.ts +4 -15
  30. package/dist/volter-manifest.js +1 -1
  31. package/dist/workbench-locator.js +2 -2
  32. package/package.json +7 -3
  33. package/src/asset-library.ts +104 -0
  34. package/src/client.ts +48 -7
  35. package/src/discovery.ts +2 -2
  36. package/src/editor.ts +11 -3
  37. package/src/index.ts +2 -1
  38. package/src/product-locator.ts +2 -2
  39. package/src/registry-format.ts +68 -5
  40. package/src/session.ts +6 -86
  41. package/dist/chunk-7SZ3C4CC.js.map +0 -7
  42. package/dist/chunk-SDIWRX5X.js.map +0 -7
  43. package/dist/chunk-USXOIBVX.js.map +0 -7
  44. package/dist/chunk-Y5W77HYT.js.map +0 -7
  45. package/dist/chunk-YPAOTPIF.js.map +0 -7
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/http-transport.ts", "../../sdk/src/commands.ts", "../src/client.ts"],
4
- "sourcesContent": ["/** Node session clients need explicit header deadlines for long modeling calls.\n * Some runtimes supply an undici-compatible Agent without destroy(); callers\n * close explicitly when that lifecycle method exists. */\nimport { Agent, fetch as nodeFetch, type Dispatcher } from 'undici';\n\nexport function createDispatcher(timeoutMs: number): Dispatcher {\n return new Agent({ headersTimeout: timeoutMs, bodyTimeout: timeoutMs });\n}\n\nexport async function dispatchFetch(\n url: string,\n init?: RequestInit,\n dispatcher?: Dispatcher,\n): Promise<Response> {\n if (!dispatcher) return fetch(url, init);\n return await nodeFetch(url, {\n ...(init as object), dispatcher,\n }) as unknown as Response;\n}\n", "/**\n * A PACKAGE'S VERBS ARE CODE-OSS COMMANDS (`docs/adr/plugin-apis.md`): the verbs the editor's\n * `eval` command, `volter <verb>` and every agent send through the editor's ONE relay\n * (`POST /__editor/command`). A type the host's own table has is answered by the host; any other\n * type `T` runs as the command `volter.T`, which a package's page half registers in its\n * `activate` and its `package.json` declares:\n *\n * `contributes.commands`\n * { \"command\": \"volter.list-instances\", \"title\": \"\u2026\",\n * \"volter\": { \"timeoutMs\": 60000, \"derivedRefresh\": \"if-content-changed\" } }\n *\n * page entry\n * export function activate(context: PluginContext) {\n * registerVerbCommands(context, { 'list-instances': (command) => \u2026 });\n * }\n *\n * The declaration is the verb's budget and its status derivation, read where the relay waits\n * (the session's server reads the manifests) and where the page derives (the page's packages):\n * `timeoutMs` is how long the relay waits for the WORK (5 s when omitted), and `derivedRefresh`\n * whether a completed command owes a tree-scale status derivation (`'always'` when omitted).\n * A view's verbs are commands too, `volter.<view>.<verb>` (`registerViewCommands`).\n */\nimport type { PluginContext } from './plugins';\n\nexport interface EditorCommandMessage {\n readonly type: string;\n readonly _requestId?: string;\n readonly [key: string]: unknown;\n}\n\n/** The relay's answer shape (`command-table.ts`'s `CommandResult`): `ok`, an\n * `error` sentence on failure, and `data` for verbs that answer with a\n * payload \u2014 structured markers (`{ code: \u2026 }`) the SDK matches on, never\n * message prose. */\nexport interface EditorCommandResult {\n readonly ok: boolean;\n readonly error?: string;\n readonly data?: Record<string, unknown>;\n}\n\n/** Whether a completed command owes a status derivation: none, only when\n * the tree's content changed, or always. */\nexport type CommandDerivedRefresh = 'none' | 'if-content-changed' | 'always';\n\n/** A verb's handler: the relay's message in, the answer the host's own verbs give out. */\nexport type VerbHandler = (command: EditorCommandMessage) => EditorCommandResult | Promise<EditorCommandResult>;\n\n/** The Code-OSS command a relay type runs as: `<namespace>-start` \u2192 `volter.<namespace>-start`. */\nexport function verbCommandId(type: string): string {\n return `volter.${type}`;\n}\n\n/** A verb's refusal, carrying its answer's `data` (structured markers such as `{ code }`) to the relay. */\nexport class VerbError extends Error {\n override readonly name = 'VerbError';\n constructor(message: string, readonly data?: Record<string, unknown>) {\n super(message);\n }\n}\n\n/**\n * Register a table of verbs as Code-OSS commands, each released with the page half. A verb's\n * command answers its `data`, and throws its refusal as a {@link VerbError}, as a command does.\n */\nexport function registerVerbCommands(\n context: Pick<PluginContext, 'commands' | 'subscriptions'>,\n verbs: Readonly<Record<string, VerbHandler>>,\n): void {\n for (const [type, handle] of Object.entries(verbs)) {\n context.subscriptions.push(context.commands.registerCommand(verbCommandId(type), async (args) => {\n const answer = await handle({ ...(args as Record<string, unknown> | undefined), type });\n if (!answer.ok) throw new VerbError(answer.error ?? `${type} failed`, answer.data);\n return answer.data ?? {};\n }));\n }\n}\n\nexport interface ViewVerb {\n /** The verb, bare: `state`, `view-all`, `zoom`; its command is `volter.<view>.<verb>`. */\n readonly id: string;\n /** Run it. `args` is the command's own argument object, unvalidated: a view validates its own\n * arguments and REFUSES BY NAME, because what an argument means is the view's. */\n readonly run: (args?: Record<string, unknown>) => unknown;\n}\n\n/** Register a view's verbs as the commands `volter.<view>.<verb>`. */\nexport function registerViewCommands(\n context: Pick<PluginContext, 'commands' | 'subscriptions'>,\n view: string,\n verbs: readonly ViewVerb[],\n): void {\n for (const verb of verbs) {\n context.subscriptions.push(context.commands.registerCommand(`volter.${view}.${verb.id}`, (args) =>\n verb.run(args as Record<string, unknown> | undefined)));\n }\n}\n\n/** A job's state as its status verb answers it: running, done, or failed with why. */\nexport interface CommandJob {\n readonly state: 'rendering' | 'done' | 'failed';\n readonly error?: string;\n}\n\n/** How long a status verb waits for a running job to end: inside the relay's default deadline for a command. */\nconst JOB_STATUS_WAIT_MS = 3_000;\n\n/**\n * THE JOBS A START VERB BEGINS AND A STATUS VERB FOLLOWS, keyed (a movie render: started, answering at once, then read\n * until it ends). Work longer than any command's deadline runs this way, so each command answers inside the relay's\n * default deadline whatever the work's length, and a page's own control reads the same state.\n */\nexport function commandJobs<Job extends CommandJob>(): {\n /** Begin `work` under `key`, answering `started` at once. `work` reports each new state; a throw ends it failed. */\n start(key: string, started: Job, work: (report: (job: Job) => void) => Promise<void>): Job;\n /** The job under `key`, once it has ended or a status wait has passed. */\n status(key: string): Promise<Job>;\n /** The job under `key` as it stands, if one has started. */\n get(key: string): Job | null;\n /** Whether any job is running. */\n running(): boolean;\n subscribe(listener: () => void): () => void;\n version(): number;\n} {\n const jobs = new Map<string, Job>();\n const ends = new Map<string, Promise<void>>();\n const listeners = new Set<() => void>();\n let version = 0;\n const set = (key: string, job: Job) => {\n jobs.set(key, job);\n version++;\n for (const listener of listeners) listener();\n };\n return {\n start(key, started, work) {\n set(key, started);\n let last = started;\n const report = (job: Job) => { last = job; set(key, job); };\n ends.set(key, work(report).catch((thrown: unknown) => {\n report({ ...last, state: 'failed', error: thrown instanceof Error ? thrown.message : String(thrown) });\n }).finally(() => ends.delete(key)));\n return started;\n },\n async status(key) {\n const job = jobs.get(key);\n if (!job) throw new Error(`Nothing has started under ${key}.`);\n if (job.state === 'rendering') await Promise.race([ends.get(key), new Promise((done) => setTimeout(done, JOB_STATUS_WAIT_MS))]);\n return jobs.get(key)!;\n },\n get: (key) => jobs.get(key) ?? null,\n running: () => [...jobs.values()].some((job) => job.state === 'rendering'),\n subscribe(listener) {\n listeners.add(listener);\n return () => { listeners.delete(listener); };\n },\n version: () => version,\n };\n}\n", "import type { Dispatcher } from 'undici';\nimport { createDispatcher, dispatchFetch } from './http-transport.js';\nimport { verbCommandId } from '@volter/sdk/commands';\nimport type { DocumentProbeResult, DocumentProbeStep } from '@volter/sdk/document-probe';\nimport type {\n ActiveDocumentCapture,\n AssetCompareCapture,\n AssetCompareOptions,\n AssetKind,\n AssetPreviewCapture,\n AssetPreviewOptions,\n AssetPreviewShotSetDefinition,\n AssetPreviewSource,\n CaptureDimensions,\n DocumentCameraPose,\n DocumentLookOutcome,\n StageFrameCostReading,\n DocumentTableProjection,\n EditorChromeCapture,\n EditorChromeCaptureOptions,\n EditorState,\n EditorView,\n EditorWorkspaceName,\n HelperVisibility,\n InspectedFieldWrite,\n InspectedHierarchy,\n InspectedInspection,\n LabeledShotSetCapture,\n PresentedEditorView,\n ProjectInfo,\n RecentProject,\n ShadingMode,\n StructureOp,\n StructureOpOptions,\n StructureOpResult,\n TransformMode,\n TransformSpace,\n Vec3Value,\n ViewPreset,\n ViewportCapture,\n} from '@volter/sdk';\n\n// The editor's default origin, spelled out because this package deliberately\n// does not depend on `@volter/project`. `DEFAULT_EDITOR_PORT` in\n// `packages/project/src/manifest/editor-port.ts` is the owner of the number and\n// of the never-`localhost` rule; keep this in step with it.\nconst DEFAULT_URL = 'http://127.0.0.1:20173';\n\n/**\n * A refused editor command, carrying the relay's own STRUCTURED failure code\n * (`/__editor/command`'s `{ ok: false, error, code }`) alongside the prose, so\n * a caller that reacts to a specific refusal matches the code, never the\n * sentence.\n */\nexport class EditorCommandError extends Error {\n readonly code: string | undefined;\n constructor(message: string, code?: string | undefined) {\n super(message);\n this.name = 'EditorCommandError';\n this.code = code;\n }\n}\n\n/**\n * The client's own ceiling on ONE relayed command.\n *\n * A backstop for a LOST server, not a per-command budget: the server already\n * owns per-type budgets (`server/server-utils.ts`'s `relayCommandTimeoutMs`)\n * and its timer must always be the one that fires, because its message names\n * the tab and the remedy while this one can only say \"no answer\". So this is\n * deliberately ONE number, comfortably above the longest server budget\n * (120s, `play`/`capture-story-variants`) rather than a mirror of that table \u2014\n * a second copy of it would drift silently, and the drift would show up as\n * this timer winning a race it must always lose.\n *\n * Without it a `fetch` with no `AbortSignal` waits on the OS: a dev server\n * that stops answering mid-command holds the CLI open indefinitely, with no\n * output and nothing to read.\n */\nconst COMMAND_DEADLINE_MS = 150_000;\n/** undici's default `headersTimeout`; a command deadline beyond it needs its own dispatcher (see `command`). */\nconst UNDICI_DEFAULT_HEADERS_TIMEOUT_MS = 300_000;\n\n/** Deadline for console-ledger reads and acknowledgements. */\nconst CONSOLE_DRAIN_TIMEOUT_MS = 1_500;\n\n/**\n * Node's `fetch` collapses EVERY network-layer failure into one two-word\n * `TypeError: fetch failed`. The real reason \u2014 `ECONNREFUSED`, `ECONNRESET`,\n * `EPIPE`, a DNS miss \u2014 lives only on `error.cause` (sometimes two links down,\n * or inside an `AggregateError`), and nothing prints it unless something walks\n * the chain, so a bare \"fetch failed\" cannot tell a dead editor from a\n * momentary one. This walks the chain so the message can say which.\n */\nfunction describeFetchFailure(error: unknown): { code: string; detail: string } {\n const messages: string[] = [];\n let current: unknown = error;\n for (let depth = 0; depth < 8; depth++) {\n if (!(current instanceof Error)) break;\n if (current.message) messages.push(current.message);\n const code = (current as { code?: unknown }).code;\n if (typeof code === 'string' && code !== '') {\n return { code, detail: messages.join(' <- ') };\n }\n const aggregate = (current as { errors?: unknown }).errors;\n if (Array.isArray(aggregate) && aggregate.length > 0) {\n const inner = describeFetchFailure(aggregate[0]);\n return { code: inner.code, detail: [...messages, inner.detail].join(' <- ') };\n }\n current = (current as { cause?: unknown }).cause;\n }\n return { code: 'UNKNOWN', detail: messages.join(' <- ') || String(error) };\n}\n\n/**\n * Connection failures eligible for one retry when a command opts in. An editor\n * response is not a transport failure and does not use this retry path.\n */\nconst RETRYABLE_TRANSPORT_CODES = new Set([\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EPIPE',\n 'ETIMEDOUT',\n 'EHOSTUNREACH',\n 'UND_ERR_SOCKET',\n 'UND_ERR_CONNECT_TIMEOUT',\n]);\n\n/** Gap before the one automatic retry \u2014 long enough for a dev-server restart's\n * listen socket to come back, short enough to stay invisible. */\nconst TRANSPORT_RETRY_DELAY_MS = 400;\n\n/** What one undo/redo step reports back \u2014 `moved` is false when there was\n * nothing left in that direction, which is an answer, not an error. */\nexport interface HistoryStep {\n readonly moved: boolean;\n readonly canUndo: boolean;\n readonly canRedo: boolean;\n readonly undoLabel: string | null;\n readonly redoLabel: string | null;\n}\n\n/** What `open()` acknowledges: the workspace document the scene-table entry\n * resolved to, and the title its tab now carries \u2014 the project's own word for\n * that composition, not a filename. */\nexport interface OpenedDocument {\n readonly documentId: string;\n readonly title: string;\n}\n\nexport class EditorClient {\n private readonly baseUrl: string;\n private readonly fetchOverride: typeof fetch | undefined;\n private readonly transport:\n | ((body: Record<string, unknown>) => Promise<Record<string, unknown>>)\n | null;\n\n constructor(opts?: {\n url?: string;\n /** An authenticated transport for the same HTTP routes (e.g. a hosted tab). */\n fetch?: typeof fetch;\n /**\n * An IN-PAGE command channel, for a client that lives inside the editor\n * page itself and has no editor server to reach. Given, every command goes through it instead of\n * `POST /__editor/command`, and answers in the route's own body shape\n * (`{ ok: true, ...data }` / `{ ok: false, error, code? }`).\n */\n transport?: (body: Record<string, unknown>) => Promise<Record<string, unknown>>;\n }) {\n if (opts !== undefined && (typeof opts !== 'object' || opts === null || Array.isArray(opts))) {\n throw new TypeError(\n 'EditorClient options must be an object. Use new EditorClient({ url: \"http://127.0.0.1:20173\" }), not new EditorClient(\"...\").',\n );\n }\n const unknownOptions = Object.keys(opts ?? {}).filter(\n (key) => key !== 'url' && key !== 'transport' && key !== 'fetch',\n );\n if (unknownOptions.length > 0) {\n throw new Error(\n `EditorClient: unknown option${unknownOptions.length === 1 ? '' : 's'} ${unknownOptions.map((key) => `\"${key}\"`).join(', ')}. Use { url: \"http://127.0.0.1:<port>\" } to target an editor.`,\n );\n }\n this.baseUrl = (opts?.url ?? DEFAULT_URL).replace(/\\/$/, '');\n this.fetchOverride = opts?.fetch;\n this.transport = opts?.transport ?? null;\n }\n\n /**\n * `retryTransport` opts a command into ONE automatic retry after a transport\n * failure (see {@link RETRYABLE_TRANSPORT_CODES}). It is deliberately\n * OPT-IN and off by default: a socket that died after the request was written\n * cannot prove the editor did not already run the command, so a blanket retry\n * would risk playing/stopping/writing twice. Read-only relays \u2014 the captures \u2014\n * have no such hazard and turn it on.\n */\n private async command<T extends object = Record<string, never>>(\n body: Record<string, unknown>,\n options?: { retryTransport?: boolean; deadlineMs?: number },\n ): Promise<T> {\n const type = String(body['type'] ?? 'command');\n const deadlineMs = options?.deadlineMs ?? COMMAND_DEADLINE_MS;\n if (this.transport) {\n const answered = (await this.transport(body)) as {\n ok: boolean;\n error?: string;\n code?: string;\n } & T;\n if (!answered.ok) {\n throw new EditorCommandError(\n answered.error ?? `Editor command \"${type}\" failed`,\n answered.code,\n );\n }\n return answered;\n }\n const url = `${this.baseUrl}/__editor/command`;\n const request = JSON.stringify(body);\n let res: Response | undefined;\n let retried = false;\n let commandDispatcher: Dispatcher | undefined;\n for (;;) {\n try {\n const init: RequestInit = {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: request,\n signal: AbortSignal.timeout(deadlineMs),\n };\n // The route answers when the command completes. Undici's default header\n // timeout is 300s, independent of the abort signal; longer command budgets\n // use an explicit dispatcher through undici's fetch to extend that timeout.\n commandDispatcher = deadlineMs > UNDICI_DEFAULT_HEADERS_TIMEOUT_MS\n ? createDispatcher(deadlineMs + 30_000)\n : undefined;\n res = this.fetchOverride ? await this.fetchOverride(url, init) : await dispatchFetch(url, init, commandDispatcher);\n break;\n } catch (error) {\n await commandDispatcher?.destroy();\n commandDispatcher = undefined;\n // Only the deadline is reshaped into a \"no answer\" verdict; everything\n // else is a TRANSPORT failure, and Node hides its reason behind a bare\n // `fetch failed` (see `describeFetchFailure`).\n if ((error as { name?: string } | null)?.name === 'TimeoutError') {\n throw new EditorCommandError(\n `The editor at ${this.baseUrl} never answered \"${type}\" ` +\n `within ${Math.round(deadlineMs / 1000)}s \u2014 past every server-side budget, so ` +\n 'the server itself is not answering. Check the terminal that started this editor session.',\n );\n }\n const { code, detail } = describeFetchFailure(error);\n if (options?.retryTransport === true && !retried && RETRYABLE_TRANSPORT_CODES.has(code)) {\n retried = true;\n await new Promise((resolve) => setTimeout(resolve, TRANSPORT_RETRY_DELAY_MS));\n continue;\n }\n throw new EditorCommandError(\n `POST ${url} (\"${type}\") never reached the editor: ${code}${detail ? ` (${detail})` : ''}.` +\n (retried\n ? ` Retried once after ${TRANSPORT_RETRY_DELAY_MS}ms; it failed the same way.`\n : '') +\n (options?.retryTransport === true\n ? ''\n : ' Not retried automatically: this command can change editor state, and a socket that' +\n ' died after the request was written cannot prove the editor did not already run it.') +\n ' A transport failure means the port stopped answering, not that the editor refused \u2014' +\n ' the dev server restarts on any watched source edit, and it shuts itself down after an' +\n ' idle window. Check the terminal that started this editor session and confirm the port' +\n ' this client resolved.',\n code,\n );\n }\n }\n let data: { ok: boolean; error?: string; code?: string } & T;\n try {\n data = (await this.readJson(res)) as typeof data;\n } finally {\n await commandDispatcher?.destroy();\n }\n if (!data.ok) {\n throw new EditorCommandError(\n data.error ?? `Editor command failed: ${res.status}`,\n data.code,\n );\n }\n return data;\n }\n\n /** Refuse shutdown if a document cannot be saved. Does not stop the server. */\n async prepareClose(): Promise<void> {\n await this.command({ type: 'session-prepare-close' });\n }\n\n // --- Selection ---\n\n async select(id: string | null): Promise<void> {\n await this.command({ type: 'select', id });\n }\n\n async selectMultiple(ids: string[]): Promise<void> {\n await this.command({ type: 'select-multiple', ids });\n }\n\n async selectAll(): Promise<void> {\n await this.command({ type: 'select-all' });\n }\n\n // --- Viewport ---\n\n async focusEntity(id: string): Promise<void> {\n await this.command({ type: 'focus-entity', id });\n }\n\n async focusSelection(): Promise<void> {\n await this.command({ type: 'focus-selection' });\n }\n\n /**\n * Frame the EDIT viewport camera on one entity \u2014 the strict sibling of\n * {@link focusEntity}. Same framing; an id the scene does not know is a\n * refusal naming the id (`EditorCommandError`, code `ENTITY_NOT_FOUND`)\n * rather than `focusEntity`'s silent no-op, so a caller that frames an\n * entity before capturing it cannot photograph the wrong thing.\n */\n async frameEntity(id: string): Promise<void> {\n await this.command({ type: 'frame-entity', id });\n }\n\n async viewPreset(preset: ViewPreset): Promise<void> {\n await this.command({ type: 'view-preset', preset });\n }\n\n /**\n * LOOK AROUND THE OPEN MODEL, visibly. Swings the active asset\n * document's camera \u2014 the one on the human's screen \u2014 by `azimuth`/\n * `elevation` radians, animated over `duration` seconds, and resolves when\n * the move ends. A human drag during the move cancels it where it stands\n * (`cancelledBy: 'human'`); the promise still resolves.\n */\n async orbitDocument(options: {\n azimuth?: number;\n elevation?: number;\n duration?: number;\n }): Promise<DocumentLookOutcome> {\n return this.command<DocumentLookOutcome>({ type: 'document-orbit', ...options });\n }\n\n /** What a frame of a 3D document's stage costs, uncapped (`document-frame-cost`). */\n async frameCostDocument(options?: { readonly frames?: number; readonly stage?: string; readonly quality?: 'full' | 'navigation' }): Promise<StageFrameCostReading> {\n return this.command<StageFrameCostReading>({ type: 'document-frame-cost', ...options });\n }\n\n /** A slow full revolution around the open document's subject, at a constant rate. */\n async turntableDocument(options?: {\n seconds?: number;\n revolutions?: number;\n }): Promise<DocumentLookOutcome> {\n return this.command<DocumentLookOutcome>({ type: 'document-turntable', ...options });\n }\n\n /**\n * Frame the open document's subject (its selection if it has one). `fit`\n * scales the fitted distance: 1 is the toolbar Frame button's tight fit.\n */\n async frameDocument(fit?: number): Promise<DocumentCameraPose> {\n return this.command<DocumentCameraPose>({\n type: 'document-frame',\n ...(fit === undefined ? {} : { fit }),\n });\n }\n\n async setCamera(position: Vec3Value, target: Vec3Value, fov?: number): Promise<void> {\n await this.command({\n type: 'set-camera',\n position,\n target,\n ...(fov === undefined ? {} : { fov }),\n });\n }\n\n async captureViewport(size?: number): Promise<ViewportCapture> {\n const data = await this.command<ViewportCapture>({\n type: 'capture-viewport',\n ...(size === undefined ? {} : { size }),\n });\n return { base64: data.base64, mimeType: data.mimeType };\n }\n\n /**\n * Capture an isolated, deterministic four-view preview through the editor's\n * native Asset Lab. The SDK delegates rendering to the editor; it never\n * loads, clones, or interprets 3D assets itself.\n */\n async captureAssetPreview(\n source: AssetPreviewSource,\n options: AssetPreviewOptions = {},\n ): Promise<AssetPreviewCapture> {\n const data = await this.command<AssetPreviewCapture>(\n {\n type: 'capture-asset-preview',\n ...source,\n ...options,\n },\n // Photographing changes nothing, so a momentary transport failure is\n // retried rather than costing the caller its capture.\n { retryTransport: true },\n );\n return {\n width: data.width,\n height: data.height,\n orientation: data.orientation,\n views: data.views,\n contactSheet: data.contactSheet,\n };\n }\n\n /**\n * A caller-defined labeled shot set (`live`'s `assetPreviewShots`): the\n * DEFINITION travels with the command (see `AssetPreviewShotSetDefinition`),\n * and the editor's generic\n * capture engine renders it \u2014 see the 3D integration's\n * `captureShotSetAssetPreview`. Throws (via `command`'s `{ok:false}`\n * unwrap) with a clear message naming the missing joint(s) when the asset\n * lacks a bone the definition requires.\n */\n async captureShotSetPreview(\n source: AssetPreviewSource,\n definition: AssetPreviewShotSetDefinition,\n options: AssetPreviewOptions = {},\n ): Promise<LabeledShotSetCapture> {\n const data = await this.command<LabeledShotSetCapture>(\n {\n type: 'capture-asset-preview',\n ...source,\n ...options,\n shotSet: definition,\n },\n { retryTransport: true },\n );\n return {\n width: data.width,\n height: data.height,\n shots: data.shots,\n warnings: data.warnings,\n contactSheet: data.contactSheet,\n };\n }\n\n /**\n * Score the asset against a reference GLB (the editor's `screenshot\n * <model.glb> --compare <ref.glb>` command): matched orthographic front + side silhouettes\n * (equal-height bounding-box framing, both yaw-normalized to face the\n * camera), per-view IoU numbers, and overlay evidence images. The\n * reference GLB's raw bytes travel base64 in the command; the editor\n * renders and scores \u2014 the SDK never interprets 3D assets itself.\n */\n async captureAssetComparePreview(\n source: AssetPreviewSource,\n refGlbBase64: string,\n options: AssetCompareOptions = {},\n ): Promise<AssetCompareCapture> {\n const { refForward, ...dimensions } = options;\n const data = await this.command<AssetCompareCapture>({\n type: 'capture-asset-preview',\n ...source,\n ...dimensions,\n compare: { glbBase64: refGlbBase64, ...(refForward ? { forward: refForward } : {}) },\n });\n return { width: data.width, height: data.height, views: data.views };\n }\n\n // --- Panels ---\n\n /** Focus a static workspace panel by the key the editor's panel registry\n * holds; an unknown key refuses naming the keys it does hold. */\n async showPanel(panel: string): Promise<void> {\n await this.command({ type: 'show-panel', panel });\n }\n\n /** Show several instances of the running project split-screen \u2014 multiplayer\n * authoring. Pass a total `count` (default \"Player N\" labels) or an array of\n * `names` (its length is the count; index 0 is the primary). Requires a live\n * play session. */\n async setInstanceCount(countOrNames: number | string[]): Promise<void> {\n await this.command(\n Array.isArray(countOrNames)\n ? { type: 'set-instance-count', names: countOrNames }\n : { type: 'set-instance-count', count: countOrNames },\n );\n }\n\n async openAsset(path: string, kind: AssetKind): Promise<void> {\n await this.command({ type: 'open-asset-tab', path, kind });\n }\n\n /** SELECT a project asset \u2014 the other half of the browser's\n * selection-vs-open contract (single click selects and fills the\n * Inspector; double click opens a document). */\n async selectAsset(path: string): Promise<void> {\n await this.command({ type: 'select-asset', path });\n }\n\n async closeAsset(key: string): Promise<void> {\n await this.command({ type: 'close-asset-tab', key });\n }\n\n async toggleCommandPalette(): Promise<void> {\n await this.command({ type: 'toggle-command-palette' });\n }\n\n async toggleConsole(): Promise<void> {\n await this.command({ type: 'toggle-console' });\n }\n\n /** Switch the editor's NAMED WORKSPACE \u2014 the task-named layout memory\n * (`design`/`animate`/`look`, or one a package contributes). Resolves once the\n * dock has finished rebuilding, so a following capture photographs the\n * arrangement that was asked for. */\n async setWorkspace(workspace: EditorWorkspaceName): Promise<void> {\n await this.command({ type: 'set-workspace', workspace });\n }\n\n /** Choose a STYLE (`volter.style`) \u2014 its material, icon set and type, and the colour theme it\n * was drawn with (`classic`/`glass`/`maya`/`substance`, or one a package the project or\n * product composes carries). */\n async setStyle(style: string): Promise<void> {\n await this.command({ type: 'set-style', style });\n }\n\n /** A view's presentation, answered resolved. With a LAYER the person's choice is recorded\n * first. */\n async viewportPresentation(\n documentId: string,\n layer?: import('@volter/sdk/viewport-presentation').PresentationLayer,\n ): Promise<{\n presentation: import('@volter/sdk/viewport-presentation').ViewportPresentation;\n binding: { stageKind: string; documentLayer: import('@volter/sdk/viewport-presentation').PresentationLayer | null } | null;\n bound: { viewId: string; stageKind: string }[];\n lastDraw: import('@volter/sdk/viewport-presentation').ViewDrawReport | null;\n presets: string[];\n /** The environment images registered (`./environment-images`), for `lighting.preview.environment.image`. */\n environmentImages: string[];\n }> {\n return this.command({\n type: 'viewport-presentation',\n documentId,\n ...(layer ? { layer } : {}),\n });\n }\n\n async showBuild(): Promise<void> {\n await this.command({ type: 'show-build' });\n }\n\n /** Atomically present a durable editor view and return its shareable URL. */\n async present(view: EditorView): Promise<PresentedEditorView> {\n const presented = await this.command<PresentedEditorView>({ type: 'present-view', view });\n return { view: presented.view, url: presented.url, warnings: presented.warnings };\n }\n\n\n async inspect(): Promise<InspectedInspection> {\n const data = await this.command<{ subject: InspectedInspection }>({ type: 'inspect' });\n return data.subject;\n }\n\n /** Run one verb exposed by the active Inspector subject, by its id. */\n async runInspectionAction(actionId: string): Promise<InspectedInspection> {\n const data = await this.command<{ subject: InspectedInspection }>({\n type: 'run-inspection-action',\n actionId,\n });\n return data.subject;\n }\n\n /**\n * Run ONE command by id \u2014 the door to everything the command palette lists.\n *\n * Under the Code-OSS frame this is the workbench's own `ICommandService`, so\n * any command id works: a view's `volter.<view>.<verb>`, an editor action's\n * `volter.action.<id>`, or one of VS Code's own. A call made before the\n * command service lands waits for it.\n *\n * The result is whatever the command answered \u2014 a view verb's state, or\n * `null` for a command that returns nothing.\n */\n async runCommand(commandId: string, args?: unknown): Promise<unknown> {\n const data = await this.command<{ result: unknown }>({\n type: 'run-command',\n commandId,\n ...(args === undefined ? {} : { args }),\n });\n return data.result;\n }\n\n /**\n * One STRUCTURE op on the authored tree \u2014 the hierarchy context menu's own\n * verbs, on the same helpers, for a caller with no pointer to right-click\n * with. `id`/`ids` default to the current selection.\n */\n async structureOp(op: StructureOp, options: StructureOpOptions = {}): Promise<StructureOpResult> {\n return this.command<StructureOpResult>({ type: 'structure-op', op, ...options });\n }\n\n /** \"Extract Component\u2026\" \u2014 the hierarchy row's action, as a command. Answers\n * the action's own sentence, which NAMES the files it created. */\n async extractComponent(options: { id?: string; name?: string } = {}): Promise<{ hint: string }> {\n return this.command<{ hint: string }>({ type: 'extract-component', ...options });\n }\n\n /** \"Fork Component\u2026\" \u2014 extract's twin: one new file, one callsite retargeted. */\n async forkComponent(options: { id?: string } = {}): Promise<{ hint: string }> {\n return this.command<{ hint: string }>({ type: 'fork-component', ...options });\n }\n\n /**\n * The HIERARCHY PANEL's actual rendered row tree, as data.\n *\n * The panel projects component marks, document promotion, child caps,\n * collapse state, search and selection scope onto the adapter tree.\n *\n * Deliberately NOT `status().entities`, which walks the raw adapter tree and\n * therefore answers a different question: a panel defect is invisible in it.\n *\n * Each row reports the projected child count and whether the panel draws a disclosure control.\n *\n * Rejects, naming the panel, when no hierarchy panel is mounted: an empty\n * tree would be a fabricated answer about a surface nobody is being shown.\n */\n async hierarchy(): Promise<InspectedHierarchy> {\n const data = await this.command<{ hierarchy: InspectedHierarchy }>({ type: 'hierarchy' });\n return data.hierarchy;\n }\n\n /** Run the Hierarchy panel's own Expand All action. */\n async expandHierarchyAll(): Promise<void> {\n await this.command<Record<string, never>>({ type: 'expand-hierarchy-all' });\n }\n\n /** Run the Hierarchy panel's own Collapse All action \u2014 Expand All's other\n * half, and the only way back to the tree's rest state through the product\n * (see `HierarchyPanelSnapshot.collapseAll`). */\n async collapseHierarchyAll(): Promise<void> {\n await this.command<Record<string, never>>({ type: 'collapse-hierarchy-all' });\n }\n\n /**\n * Write one editable path through the active Inspector's own IO.\n *\n * The answer carries `write` as well as the subject, because an ack alone\n * cannot be believed: a write with no persistence route open succeeds and\n * changes no byte, and `write.persisted` is how the caller tells the two\n * apart without diffing the tree (`InspectedWriteDestination` in `types.ts`).\n */\n async setInspectionField(path: string, value: unknown): Promise<InspectedFieldWrite> {\n const data = await this.command<InspectedFieldWrite>({\n type: 'set-inspection-field',\n path,\n value,\n });\n return { subject: data.subject, write: data.write };\n }\n\n /**\n * REMOVE one editable path's authored override \u2014 the other half of the write\n * door, and the only one that can express byte-ABSENCE.\n *\n * {@link setInspectionField} writes a VALUE, so reverting a property an\n * authoring gesture ADDED puts the default back EXPLICITLY and leaves the\n * source one attribute heavier than it started. This drops the property, so\n * whatever governs it in its absence takes over \u2014 the same `io.remove` the\n * Inspector's revert arrow calls, the same persistence pipe, the same awaited\n * `{ destination, persisted }` ack.\n *\n * Rejects with `code: 'REMOVAL_UNAVAILABLE'` when the field does not declare\n * itself removable or the lane implements no removal door. That refusal is a\n * MISSING SEAM, not a failed removal, and it is coded rather than phrased\n * precisely so a caller can grade the two differently.\n */\n async removeInspectionField(path: string): Promise<InspectedFieldWrite> {\n const data = await this.command<InspectedFieldWrite>({\n type: 'remove-inspection-field',\n path,\n });\n return { subject: data.subject, write: data.write };\n }\n\n /**\n * OPEN one piece of the adapter's SCENE TABLE by id \u2014 a scene, a prefab, or\n * a story state, because the table makes them siblings (they differ only in\n * instance site). The ids are exactly what `getState().adapter.scenes.entries`\n * reports, so the table is both the menu and the address space. It opens\n * the entry's document in Play as in Edit.\n *\n * Rejects with a coded reason rather than prose: `SCENE_NOT_FOUND` (and it\n * names the ids that DO exist), `SCENE_NOT_OPENABLE` carrying the reason an\n * entry owns no document (the adapter's own, when it declared one),\n * `SCENE_TABLE_UNAVAILABLE` before the adapter has loaded, and\n * `SCENE_DOCUMENT_NOT_MOUNTED` when the host has no document for a piece the\n * table says is openable \u2014 a host gap, not a table statement.\n */\n async open(id: string): Promise<OpenedDocument> {\n return this.command<OpenedDocument>({ type: 'open', id });\n }\n\n /**\n * Undo / redo one project transaction \u2014 the same queue the keyboard shortcut\n * drives. `moved` is false when there was nothing left in that direction.\n */\n async undo(): Promise<HistoryStep> {\n return this.command<HistoryStep>({ type: 'undo' });\n }\n\n async redo(): Promise<HistoryStep> {\n return this.command<HistoryStep>({ type: 'redo' });\n }\n\n /** Read the editor's actual current durable projection. */\n async currentView(): Promise<EditorView> {\n const data = await this.command<{ view: EditorView }>({ type: 'current-view' });\n return data.view;\n }\n\n /** Photograph the editor PAGE itself \u2014 every panel as the person sees it, at\n * `scale` output pixels per CSS pixel (default `devicePixelRatio`), which is\n * what a 1 px border or a glyph edge is judged through. */\n async captureEditorChrome(options?: EditorChromeCaptureOptions): Promise<EditorChromeCapture> {\n return this.command<EditorChromeCapture>({\n type: 'capture-editor-chrome',\n ...(options?.scale === undefined ? {} : { scale: options.scale }),\n ...(options?.region === undefined ? {} : { region: options.region }),\n ...(options?.name === undefined ? {} : { name: options.name }),\n });\n }\n\n /** Capture the active document's render, excluding editor chrome. A number\n * requests a square; `{width, height}` requests that shape, bounded by\n * {@link CaptureDimensions}. With a view, present and capture it in one request\n * so discovery cannot retarget the capture between client calls. */\n async captureActiveDocument(\n size?: CaptureDimensions,\n view?: EditorView,\n ): Promise<ActiveDocumentCapture> {\n return this.command<ActiveDocumentCapture>({\n type: 'capture-active-document',\n ...(view ? { view } : {}),\n ...(typeof size === 'number' ? { size } : {}),\n ...(typeof size === 'object' && size !== null\n ? { width: size.width, height: size.height }\n : {}),\n });\n }\n\n /**\n * Read or drive the ACTIVE center document's own DOM \u2014 the scoped\n * editor-chrome door, and the read/gesture half of the same subject\n * {@link captureActiveDocument} photographs. NOT play-mode gated, and NOT\n * page automation: a target outside the active document's container is\n * refused by name. Design and scope contract:\n * `packages/sdk/src/editor-document-probe.ts`.\n */\n async documentProbe(step: DocumentProbeStep): Promise<DocumentProbeResult> {\n return this.command<DocumentProbeResult>({ type: 'document-probe', step });\n }\n\n /**\n * A verb a package contributes: the Code-OSS command `volter.<type>` its page half registers,\n * waited on as long as its declaration says (`contributes.commands`' `volter.timeoutMs`, which\n * the relay reads from the same manifest), so a package's long verb (a modeling chunk, minutes\n * under Wasm) has its budget in one place. A verb no package declares is refused by name\n * before anything is sent.\n */\n async contributedVerb<T extends object = Record<string, unknown>>(\n type: string,\n fields: Record<string, unknown> = {},\n ): Promise<T> {\n const id = verbCommandId(type);\n const res = await this.httpFetch(`${this.baseUrl}/__editor/commands/${encodeURIComponent(id)}`);\n const declared = (await this.readJson(res)) as { timeoutMs: number; error?: string };\n if (!res.ok) throw new Error(declared.error ?? `No package declares the command \"${id}\" (${res.status}).`);\n // Past the relay's own budget, so the relay's answer (its result or its timeout) is what arrives.\n return this.command<T>({ type, ...fields }, { deadlineMs: declared.timeoutMs + 30_000 });\n }\n\n /**\n * Run a wire-carried step against the ACTIVE document's published context\n * (`packages/editor-core/src/document-context-registry.ts`) \u2014 the REPL door over\n * an open document. `src` is JavaScript source evaluated in the page; references to the caller's\n * closures are unavailable.\n */\n async documentScript<T = unknown>(src: string): Promise<T> {\n const outcome = await this.command<{ result: T }>({ type: 'document-script', src });\n return outcome.result;\n }\n\n // --- Display (set semantics) ---\n\n async setGrid(enabled: boolean): Promise<void> {\n await this.command({ type: 'set-grid', enabled });\n }\n\n async setHelpers(enabled: boolean): Promise<void> {\n await this.command({ type: 'set-helpers', enabled });\n }\n\n async setStats(enabled: boolean): Promise<void> {\n await this.command({ type: 'set-stats', enabled });\n }\n\n async setShadingMode(mode: ShadingMode): Promise<void> {\n await this.command({ type: 'set-shading-mode', mode });\n }\n\n async setHelperType(helperType: keyof HelperVisibility, enabled: boolean): Promise<void> {\n await this.command({ type: 'set-helper-type', helperType, enabled });\n }\n\n // --- Transform tools (set semantics) ---\n\n async setTransformMode(mode: TransformMode): Promise<void> {\n await this.command({ type: 'set-transform-mode', mode });\n }\n\n async setTransformSpace(space: TransformSpace): Promise<void> {\n await this.command({ type: 'set-transform-space', space });\n }\n\n async setSnap(enabled: boolean): Promise<void> {\n await this.command({ type: 'set-snap', enabled });\n }\n\n // --- Project management ---\n\n /** A new project from one of the running product's templates or examples (its `/__editor/templates`), or its default\n * where neither is named. */\n async createProject(\n name: string,\n location: string,\n from: { readonly template?: string; readonly example?: string } = {},\n ): Promise<ProjectInfo> {\n const res = await this.httpFetch(`${this.baseUrl}/__editor/create-project`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name, location, ...from }),\n });\n const data = (await this.readJson(res)) as {\n ok?: boolean;\n error?: string;\n path?: string;\n config?: ProjectInfo['config'];\n };\n if (!res.ok) {\n throw new Error(data.error ?? `Create project failed: ${res.status}`);\n }\n return { path: data.path as string, config: data.config as ProjectInfo['config'] };\n }\n\n async openProject(path: string): Promise<void> {\n const res = await this.httpFetch(`${this.baseUrl}/__editor/open-project`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ path }),\n });\n const body = (await this.readJson(res)) as { error?: string };\n if (!res.ok) {\n throw new Error(body.error ?? `Open project failed: ${res.status}`);\n }\n }\n\n async getProject(): Promise<ProjectInfo | null> {\n const res = await this.httpFetch(`${this.baseUrl}/__editor/project`);\n const data = (await this.readJson(res)) as { project: ProjectInfo | null; error?: string };\n if (!res.ok) throw new Error(data.error ?? `Failed to get project: ${res.status}`);\n return data.project;\n }\n\n async listRecentProjects(): Promise<RecentProject[]> {\n const res = await this.httpFetch(`${this.baseUrl}/__editor/recent-projects`);\n const data = (await this.readJson(res)) as { projects: RecentProject[]; error?: string };\n if (!res.ok) throw new Error(data.error ?? `Failed to list projects: ${res.status}`);\n return data.projects;\n }\n\n // --- State ---\n\n /**\n * The document table the host resolved \u2014 every scene, prefab, page, model,\n * shot, take \u2026 the project's finders produced (`getState().adapter.scenes`\n * is the same projection). A command, so it answers wherever the control\n * channel reaches, not only where `/__editor/state` is served.\n */\n async documentTable(): Promise<DocumentTableProjection> {\n return this.command<DocumentTableProjection>({ type: 'document-table' });\n }\n\n\n async reloadPage(): Promise<void> {\n const timeoutMs = 60_000;\n const epochs = async () => {\n const { tabs } = await this.getState().catch(() => ({ tabs: [] }));\n return new Map(tabs.map((tab) => [tab.tabId8, tab] as const));\n };\n const pause = () => new Promise((settle) => setTimeout(settle, 250));\n const before = await epochs();\n const deadline = Date.now() + timeoutMs;\n await this.command({ type: 'page-reload' });\n let reloaded: string[] = [];\n while (reloaded.length === 0) {\n if (Date.now() > deadline) throw new Error('The reload was ordered and no tab of this session reported a new page load.');\n await pause();\n const now = await epochs();\n reloaded = [...now].filter(([tabId, tab]) => tab.epochCount > (before.get(tabId)?.epochCount ?? 0)).map(([tabId]) => tabId);\n }\n for (;;) {\n if (Date.now() > deadline) throw new Error('A new page loaded, but its command listener did not become ready.');\n const now = await epochs();\n if (reloaded.some((tabId) => {\n const listener = now.get(tabId)?.commandListener;\n return typeof listener !== 'string' || listener === 'ready';\n })) break;\n await pause();\n }\n for (;;) {\n try {\n await this.command({ type: 'current-view' });\n return;\n } catch (error) {\n if (Date.now() > deadline) throw error;\n await pause();\n }\n }\n }\n\n async getState(): Promise<EditorState> {\n const res = await this.httpFetch(`${this.baseUrl}/__editor/state`);\n const state = (await this.readJson(res)) as EditorState;\n if (!res.ok) throw new Error(`Failed to get editor state: ${res.status} ${res.statusText}`);\n return state;\n }\n\n /**\n * The complete unresolved console set the session is holding right now.\n *\n * The console endpoint returns named conditions; command envelopes carry\n * only their counts. The request has a bounded header deadline.\n */\n async getUnresolvedConsole(opts?: { all?: boolean }): Promise<unknown> {\n const res = await this.httpFetch(\n `${this.baseUrl}/__editor/console${opts?.all === true ? '?all=1' : ''}`,\n { signal: AbortSignal.timeout(CONSOLE_DRAIN_TIMEOUT_MS) },\n );\n // Status first: a 404's empty body would otherwise die inside readJson\n // with a parse error that hides the one fact the caller classifies on\n // (does this server SERVE the console route at all?).\n if (!res.ok) {\n throw new Error(`Failed to read unresolved console: ${res.status}`);\n }\n return await this.readJson(res);\n }\n\n /** Acknowledge one named console condition. */\n async acknowledgeConsole(input: {\n readonly id: string;\n readonly reason: string;\n readonly by: string;\n }): Promise<unknown> {\n const res = await this.httpFetch(`${this.baseUrl}/__editor/console/ack`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(input),\n signal: AbortSignal.timeout(CONSOLE_DRAIN_TIMEOUT_MS * 4),\n });\n const body = await this.readJson(res);\n return body;\n }\n\n /**\n * `fetch` for this client's plain routes, with the ONE thing Node's `fetch`\n * will not do: name why it failed.\n *\n * `readJson` below already owns \"the server answered the wrong thing\"; this\n * owns \"nothing answered at all\", naming the real code Node buries on\n * `.cause`. No retry\n * here \u2014 these routes create projects, run tools and acknowledge console\n * conditions, so repeating one is the caller's decision. The relayed\n * `command` path above has its own opt-in retry for the read-only captures.\n */\n private async httpFetch(\n url: string,\n init?: RequestInit,\n ): Promise<Response> {\n try {\n return this.fetchOverride ? await this.fetchOverride(url, init) : await dispatchFetch(url, init);\n } catch (error) {\n if ((error as { name?: string } | null)?.name === 'TimeoutError') throw error;\n const { code, detail } = describeFetchFailure(error);\n throw new EditorCommandError(\n `${init?.method ?? 'GET'} ${url} never reached the editor: ${code}` +\n `${detail ? ` (${detail})` : ''}. Nothing answered on that port \u2014 check the terminal ` +\n 'that started this editor session and confirm the port this client resolved.',\n code,\n );\n }\n }\n\n /** Require a JSON response from the editor server. */\n private async readJson(res: Response): Promise<unknown> {\n // The client's JSON response routes funnel through here, so this is\n // where \"did the editor server answer?\" is asked \u2014 the same question, and\n // the same JSON-content-type rule, that\n // `packages/sdk/src/editor-server-response.ts` owns on the browser side.\n // It is asked again rather than imported because THIS package is published\n // and depends on neither `@volter/project` nor the editor bundle (see\n // `DEFAULT_URL` above for that policy).\n //\n // Not theoretical here: `baseUrl` is whatever `--url`/`VOLTER_EDITOR_URL`\n // says, so the CLI can be pointed at a static host, which answers\n // `200 text/html` for a route nothing serves, and `res.json()` would die as\n // `Unexpected token '<'`, naming neither the URL nor the cause.\n const contentType = res.headers.get('content-type') ?? '';\n if (!contentType.includes('application/json')) {\n throw new Error(\n `The editor at ${this.baseUrl} answered with its page fallback ` +\n `(${contentType || 'no content-type'}) rather than JSON, so no editor server handled ` +\n 'the request. Check that this URL is a running editor session.',\n );\n }\n return await res.json();\n }\n\n /**\n * Whether an editor browser tab is connected to the server *right now*.\n * Unlike {@link getState}, this reflects live SSE connections, not cached\n * state \u2014 use it to check whether commands will actually reach an editor.\n */\n async isConnected(): Promise<boolean> {\n const state = await this.getState();\n return state.connected === true;\n }\n\n async waitForState(\n predicate: (s: EditorState) => boolean,\n timeoutMs = 10_000,\n ): Promise<EditorState> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n const state = await this.getState();\n if (predicate(state)) return state;\n await new Promise((r) => setTimeout(r, 100));\n }\n throw new Error(`waitForState timed out after ${timeoutMs}ms`);\n }\n}\n"],
5
- "mappings": "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",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/product-locator.ts"],
4
- "sourcesContent": ["\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { installedPackageDir } from './installed-package';\nimport { declaredContributions, type ProductDeclaration, readVolterManifest } from './volter-manifest';\n\n\nexport const PRODUCT_DECLARATION_KEY = 'contributes.volter.product';\n\n/** Light or dark \u2014 the two the workbench's own `ColorScheme` has plain names\n * for, and the only two a product needs to state (a high-contrast scheme is a\n * person's accessibility choice, never a product's declaration). */\nexport type ProductColorTheme = 'dark' | 'light';\n\n\nexport interface ProductWorkbench {\n /** The GitHub Release tag on `volter-ai/code-oss`. */\n readonly release: string;\n /** The sha256 of that release's tarball asset, hex. */\n readonly tarballSha256: string;\n}\n\n/** A product package, as everything outside it needs to know it. */\nexport interface ProductIdentity {\n\n readonly name: string;\n /** Its version, reported beside the workbench by the editor's `status` command. */\n readonly version: string;\n\n readonly command: string;\n /** The name a person sees \u2014 `contributes.volter.product.displayName`. */\n readonly displayName: string;\n /**\n * `contributes.volter.product.upgrade: true` \u2014 its command has `upgrade [version]`\n * (the product CLI's own), so a pinned-engine refusal\n * may tell a person to run it. Optional: a product that says nothing has none.\n */\n readonly upgrade?: boolean;\n /**\n * `contributes.volter.product.adapterRequired: true` \u2014 a project this product opens must have its own\n * adapter module (`editor/volter.adapter.ts`), because the product's layout and document\n * finders are declared there and the native default shows none of them. Such a product refuses\n * a project without one. Optional: a product that says nothing opens it on the native default.\n */\n readonly adapterRequired?: boolean;\n /** Its package root, absolute. */\n readonly dir: string;\n /**\n * Its ONE source entry, package-relative and POSIX (`./src/index.ts`). That\n * entry IS the product's frame entry: it composes and re-exports `mountVolter`,\n * which is the name the fork's contribution reads off the served module.\n */\n readonly entry: string;\n /**\n * The packages this product mounts, in order \u2014 `contributes.volter.product.composes`. The ONE statement of\n * the composition: the build serves it to the entry as `volter:composition`, and the session\n * reads it here for the catalog and the serving doors.\n */\n readonly composes: readonly string[];\n\n readonly colorTheme: ProductColorTheme;\n /** WHICH WORKBENCH THIS PRODUCT IS \u2014 see {@link ProductWorkbench}. */\n readonly workbench: ProductWorkbench | null;\n /**\n * The ONE LINE that installs this product and starts a project in it \u2014\n * `contributes.volter.product.install` (`npx <name> create my-project`). A limited view quotes\n * it wherever it cannot do what the local editor does (its agent answers with it, and every\n * route it does not carry names it), so it is a declaration read without running the product.\n * An undeclared one is `npx <name> create my-project`, which is what every product's `create`\n * verb accepts.\n */\n readonly install: string;\n}\n\n/** What a project without a product is told to add: the declaration this module reads, naming no package. */\nexport const PRODUCT_DECLARATION_HINT =\n `a product package as a dev dependency: one whose package.json declares \\`${PRODUCT_DECLARATION_KEY}\\``;\n\nconst PRODUCT_COLOR_THEMES: readonly ProductColorTheme[] = ['dark', 'light'];\n\n/**\n * Read one product package's declaration, or `null` when the manifest is not a\n * product's. Throws when it declares `contributes.volter.product` and the declaration is\n * unusable \u2014 a half-written product must be named, never skipped.\n */\nexport function readProductManifest(packageDir: string): ProductIdentity | null {\n const manifestPath = join(packageDir, 'package.json');\n const manifest = readVolterManifest(manifestPath);\n const declared = manifest?.product;\n if (manifest === null || declared === undefined) return null;\n const entry = declared.entry;\n if (typeof entry !== 'string' || !entry.startsWith('./'))\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its \"entry\" is ` +\n `${JSON.stringify(entry)}. It must be a ./-relative module \u2014 the product's ONE source ` +\n 'entry, which is also its frame entry (e.g. \"./src/index.ts\").',\n );\n const resolved = join(packageDir, entry);\n if (!existsSync(resolved))\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY}.entry ${JSON.stringify(entry)}, ` +\n `which does not exist (${resolved}).`,\n );\n const colorTheme = declared.colorTheme;\n // REQUIRED, like `entry`, and for the same reason: a product that does not\n // say which way round it paints gets the workbench's web default, which is\n // LIGHT, and its cover is white until its real theme loads. There is no\n // defensible default to invent here \u2014 a dark product and a light product are\n // both ordinary \u2014 so an unstated one is named rather than guessed.\n if (typeof colorTheme !== 'string' || !PRODUCT_COLOR_THEMES.includes(colorTheme as never))\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its \"colorTheme\" is ` +\n `${JSON.stringify(colorTheme)}. It must be ${PRODUCT_COLOR_THEMES.map((one) =>\n JSON.stringify(one),\n ).join(' or ')} \u2014 which way round this product paints, read before any of its code ` +\n \"runs so the workbench's first frame is already the product's own background.\",\n );\n // REQUIRED, for the same reason as `entry`: the kit names verbs in every\n // message it writes, and there is no command to name but the product's own.\n const composes = productComposes(manifestPath, declared);\n const bin = manifest.bin;\n const commands = bin !== null && typeof bin === 'object' ? Object.keys(bin) : [];\n if (commands.length !== 1)\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its \"bin\" names ` +\n `${commands.length === 0 ? 'no command' : `${commands.length} commands`}. A product has ` +\n 'exactly one command \u2014 the one every session message tells a person to run.',\n );\n const displayName = declared.displayName;\n if (typeof displayName !== 'string' || displayName.trim() === '')\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its \"displayName\" is ` +\n `${JSON.stringify(displayName)}. It must be the name a person sees (e.g. \"Volter Editor\").`,\n );\n const name = manifest.name ?? packageDir;\n const install = declared.install;\n if (install !== undefined && (typeof install !== 'string' || install.trim() === ''))\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its \"install\" is ` +\n `${JSON.stringify(install)}. It is the one line that installs this product and starts a ` +\n `project (e.g. \"npx ${name} create my-project\"), or absent.`,\n );\n return {\n name,\n version: manifest.version ?? 'unknown',\n command: commands[0]!,\n displayName,\n upgrade: declared.upgrade === true,\n adapterRequired: declared.adapterRequired === true,\n dir: packageDir,\n entry,\n composes,\n colorTheme: colorTheme as ProductColorTheme,\n // A private source product may use an explicitly selected matching checkout.\n // Public packages must always carry a downloadable, checksummed workbench.\n workbench: manifest.private && declared.workbench === undefined\n ? null : readProductWorkbench(manifestPath, declared.workbench),\n install: typeof install === 'string' ? install.trim() : `npx ${name} create my-project`,\n };\n}\n\n/** A product declaration's `composes`, checked: the packages it mounts, by name, in order. The\n * build's composition module and the session read it through this one check. */\nexport function productComposes(manifestPath: string, declared: ProductDeclaration): string[] {\n const composes = declared.composes;\n if (!Array.isArray(composes) || composes.some((name) => typeof name !== 'string' || name === ''))\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its \"composes\" is ` +\n `${JSON.stringify(composes)}. It must list the packages this product mounts, by name ` +\n '(e.g. [\"<package>\"]); each is a dependency of the product package.',\n );\n return composes as string[];\n}\n\n/** The sha256 a release pins its tarball with, as `BUILD.json` writes it. */\nconst SHA256_HEX = /^[0-9a-f]{64}$/;\n\n/**\n * REQUIRED, like `entry` and `colorTheme`, and for the sharpest reason of all\n * three of them: a product that does not say which workbench it is cannot be INSTALLED\n * \u2014 `npx @volter/<product> create <name>` on a machine with nothing else on it\n * has no other way to find the bytes it runs in, and the person is left writing\n * a path to a directory they would have to build first. There is no default to\n * invent (the tag names a fork commit nobody can guess), so an unstated one is\n * named rather than guessed.\n */\nfunction readProductWorkbench(manifestPath: string, declared: unknown): ProductWorkbench {\n const example =\n ' \"contributes\": { \"volter\": { \"product\": { \"workbench\": { \"release\": \"<product>-<fork sha 12>-<platform>\", ' +\n '\"tarballSha256\": \"<64 hex>\" } } } }\\n' +\n '`node scripts/workbench/build-release.mjs --publish --out <release dir>` cuts the release ' +\n 'and prints exactly that block.';\n if (typeof declared !== 'object' || declared === null || Array.isArray(declared))\n throw new Error(\n `${manifestPath} declares ${PRODUCT_DECLARATION_KEY} but its \"workbench\" is ` +\n `${JSON.stringify(declared)}. A product IS the Code-OSS workbench it was compiled into, ` +\n `so it names the published release those bytes are:\\n${example}`,\n );\n const platforms = declared as Record<string, unknown>;\n if (!('release' in platforms) && !('tarballSha256' in platforms)) {\n const machine = `${process.platform}-${process.arch}`;\n const declaredHere = platforms[machine];\n if (declaredHere === undefined)\n throw new Error(\n `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench declares releases for ` +\n `${Object.keys(platforms).join(', ') || 'no platform'} and this machine is ${machine}. A ` +\n \"Code-OSS server package carries its own platform's node binary and native modules, so \" +\n `there is nothing this product can run in here until a ${machine} release is cut:\\n` +\n ` node scripts/workbench/build-release.mjs --product <id> --platform ${machine} --checkout <fork dir> --publish`,\n );\n return readPlatformWorkbench(manifestPath, declaredHere, example);\n }\n return readPlatformWorkbench(manifestPath, declared, example);\n}\n\nfunction readPlatformWorkbench(manifestPath: string, declared: unknown, example: string): ProductWorkbench {\n if (typeof declared !== 'object' || declared === null || Array.isArray(declared))\n throw new Error(\n `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench names ${JSON.stringify(declared)} ` +\n `for this platform, which is not a release:\\n${example}`,\n );\n const record = declared as Record<string, unknown>;\n const extra = Object.keys(record).filter((key) => key !== 'release' && key !== 'tarballSha256');\n if (extra.length > 0)\n throw new Error(\n `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench carries ` +\n `${extra.map((key) => `\"${key}\"`).join(', ')}, which this declaration has no meaning for. ` +\n `It names the release and the pin, and nothing else:\\n${example}`,\n );\n const release = record['release'];\n if (typeof release !== 'string' || release.trim() === '')\n throw new Error(\n `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench names no \"release\" \u2014 the GitHub ` +\n `Release tag its workbench was published under:\\n${example}`,\n );\n const tarballSha256 = record['tarballSha256'];\n if (typeof tarballSha256 !== 'string' || !SHA256_HEX.test(tarballSha256))\n throw new Error(\n `${manifestPath}'s ${PRODUCT_DECLARATION_KEY}.workbench has tarballSha256 ` +\n `${JSON.stringify(tarballSha256)}, which is not 64 hex characters. It is the PIN \u2014 the ` +\n 'hash the downloaded tarball is checked against before a byte of it is extracted, so it ' +\n `is never approximate:\\n${example}`,\n );\n return { release, tarballSha256 };\n}\n\n/**\n * A product's SHORT id \u2014 `@<scope>/<name>` \u2192 `<name>`.\n *\n * It is the product's own `product({ id })`, the directory its workbench half\n * lives in (`packages/<id>/workbench`), the `--product` flag the overlay and the\n * release build take, and what a release records in `BUILD.json`. One derivation\n * rather than four literals: the package name is the only place the product is\n * NAMED, and everything else is that name's last segment.\n */\nexport function workbenchProductId(packageName: string): string {\n return packageName.split('/').pop() ?? packageName;\n}\n\n/** A product's entry as a path relative to `fromDir`, POSIX \u2014 the spelling a\n * Vite manifest key and a dev-server URL both use. */\nexport function productEntryPath(product: ProductIdentity, fromDir: string): string {\n const absolute = join(product.dir, product.entry);\n const prefix = fromDir.endsWith('/') ? fromDir : `${fromDir}/`;\n const relative = absolute.startsWith(prefix) ? absolute.slice(prefix.length) : absolute;\n return relative.split('\\\\').join('/');\n}\n\n/** Every `@volter/*` this project declares, in either section, sorted. */\nfunction declaredVolterPackages(projectRoot: string): string[] {\n const manifest = readVolterManifest(join(projectRoot, 'package.json'));\n return (manifest?.dependencyNames ?? []).filter((name) => name.startsWith('@volter/')).sort();\n}\n\n/**\n * The packages `product` mounts that are installed and contribute: its `contributes.volter.product.composes`,\n * each resolved from the product's own directory and kept when its manifest declares\n * a `contributes.volter` object (its serving half, `contributes.volter.serving`, may be all a package contributes).\n */\nexport function productComposedPackages(product: ProductIdentity): string[] {\n return product.composes.filter((name) => {\n const dir = installedPackageDir(product.dir, name);\n if (dir === null) return false;\n const manifest = readVolterManifest(join(dir, 'package.json'));\n return declaredContributions(manifest) !== null;\n });\n}\n\n/** The products this project declares AND resolves, in declaration order. */\nexport function declaredProducts(projectRoot: string): ProductIdentity[] {\n const found: ProductIdentity[] = [];\n for (const name of declaredVolterPackages(projectRoot)) {\n const dir = installedPackageDir(projectRoot, name);\n if (dir === null) continue;\n const product = readProductManifest(dir);\n if (product !== null) found.push(product);\n }\n return found;\n}\n\n/**\n * THE product this project opens in, or a throw naming what is wrong.\n *\n * The refusals are the whole point: a project with none and a project with two\n * are both states a person can be in, and neither has an answer this code may\n * invent.\n */\nexport function resolveProductForProject(projectRoot: string): ProductIdentity {\n const products = declaredProducts(projectRoot);\n if (products.length === 1) return products[0] as ProductIdentity;\n if (products.length === 0)\n throw new Error(\n `${projectRoot} declares no editor product, so there is no editor to open it in.\\n` +\n 'The installed product supplies the editor,\\n' +\n 'and which one runs is what the project installed, never a flag.\\n' +\n `For THIS folder, install ${PRODUCT_DECLARATION_HINT},\\n` +\n \"then run that product's `edit` command again. For a NEW project, the product's own `create` verb makes it.\",\n );\n throw new Error(\n `${projectRoot} declares ${products.length} editor products \u2014 ` +\n `${products.map((product) => product.name).join(' and ')}.\\n` +\n 'One page runs one product, and nothing here may choose between them. Remove the one you\\n' +\n 'are not building with:\\n' +\n products.map((product) => ` npm uninstall ${product.name}`).join('\\n'),\n );\n}\n"],
5
- "mappings": ";;;;;;;;;AAEA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAKd,IAAM,0BAA0B;AAoEhC,IAAM,2BACX,4EAA4E,uBAAuB;AAErG,IAAM,uBAAqD,CAAC,QAAQ,OAAO;AAOpE,SAAS,oBAAoB,YAA4C;AAC9E,QAAM,eAAe,KAAK,YAAY,cAAc;AACpD,QAAM,WAAW,mBAAmB,YAAY;AAChD,QAAM,WAAW,UAAU;AAC3B,MAAI,aAAa,QAAQ,aAAa,OAAW,QAAO;AACxD,QAAM,QAAQ,SAAS;AACvB,MAAI,OAAO,UAAU,YAAY,CAAC,MAAM,WAAW,IAAI;AACrD,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,uBAC9C,KAAK,UAAU,KAAK,CAAC;AAAA,IAE5B;AACF,QAAM,WAAW,KAAK,YAAY,KAAK;AACvC,MAAI,CAAC,WAAW,QAAQ;AACtB,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,UAAU,KAAK,UAAU,KAAK,CAAC,2BACvD,QAAQ;AAAA,IACrC;AACF,QAAM,aAAa,SAAS;AAM5B,MAAI,OAAO,eAAe,YAAY,CAAC,qBAAqB,SAAS,UAAmB;AACtF,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,4BAC9C,KAAK,UAAU,UAAU,CAAC,gBAAgB,qBAAqB;AAAA,QAAI,CAAC,QACrE,KAAK,UAAU,GAAG;AAAA,MACpB,EAAE,KAAK,MAAM,CAAC;AAAA,IAElB;AAGF,QAAM,WAAW,gBAAgB,cAAc,QAAQ;AACvD,QAAM,MAAM,SAAS;AACrB,QAAM,WAAW,QAAQ,QAAQ,OAAO,QAAQ,WAAW,OAAO,KAAK,GAAG,IAAI,CAAC;AAC/E,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,wBAC9C,SAAS,WAAW,IAAI,eAAe,GAAG,SAAS,MAAM,WAAW;AAAA,IAE3E;AACF,QAAM,cAAc,SAAS;AAC7B,MAAI,OAAO,gBAAgB,YAAY,YAAY,KAAK,MAAM;AAC5D,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,6BAC9C,KAAK,UAAU,WAAW,CAAC;AAAA,IAClC;AACF,QAAM,OAAO,SAAS,QAAQ;AAC9B,QAAM,UAAU,SAAS;AACzB,MAAI,YAAY,WAAc,OAAO,YAAY,YAAY,QAAQ,KAAK,MAAM;AAC9E,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,yBAC9C,KAAK,UAAU,OAAO,CAAC,mFACJ,IAAI;AAAA,IAC9B;AACF,SAAO;AAAA,IACL;AAAA,IACA,SAAS,SAAS,WAAW;AAAA,IAC7B,SAAS,SAAS,CAAC;AAAA,IACnB;AAAA,IACA,SAAS,SAAS,YAAY;AAAA,IAC9B,iBAAiB,SAAS,oBAAoB;AAAA,IAC9C,KAAK;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA,IAGA,WAAW,SAAS,WAAW,SAAS,cAAc,SAClD,OAAO,qBAAqB,cAAc,SAAS,SAAS;AAAA,IAChE,SAAS,OAAO,YAAY,WAAW,QAAQ,KAAK,IAAI,OAAO,IAAI;AAAA,EACrE;AACF;AAIO,SAAS,gBAAgB,cAAsB,UAAwC;AAC5F,QAAM,WAAW,SAAS;AAC1B,MAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,KAAK,CAAC,SAAS,OAAO,SAAS,YAAY,SAAS,EAAE;AAC7F,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,0BAC9C,KAAK,UAAU,QAAQ,CAAC;AAAA,IAE/B;AACF,SAAO;AACT;AAGA,IAAM,aAAa;AAWnB,SAAS,qBAAqB,cAAsB,UAAqC;AACvF,QAAM,UACJ;AAIF,MAAI,OAAO,aAAa,YAAY,aAAa,QAAQ,MAAM,QAAQ,QAAQ;AAC7E,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,aAAa,uBAAuB,2BAC9C,KAAK,UAAU,QAAQ,CAAC;AAAA,EAC4B,OAAO;AAAA,IAClE;AACF,QAAM,YAAY;AAClB,MAAI,EAAE,aAAa,cAAc,EAAE,mBAAmB,YAAY;AAChE,UAAM,UAAU,GAAG,QAAQ,QAAQ,IAAI,QAAQ,IAAI;AACnD,UAAM,eAAe,UAAU,OAAO;AACtC,QAAI,iBAAiB;AACnB,YAAM,IAAI;AAAA,QACR,GAAG,YAAY,MAAM,uBAAuB,oCACvC,OAAO,KAAK,SAAS,EAAE,KAAK,IAAI,KAAK,aAAa,wBAAwB,OAAO,mJAE3B,OAAO;AAAA,uEACQ,OAAO;AAAA,MACnF;AACF,WAAO,sBAAsB,cAAc,cAAc,OAAO;AAAA,EAClE;AACA,SAAO,sBAAsB,cAAc,UAAU,OAAO;AAC9D;AAEA,SAAS,sBAAsB,cAAsB,UAAmB,SAAmC;AACzG,MAAI,OAAO,aAAa,YAAY,aAAa,QAAQ,MAAM,QAAQ,QAAQ;AAC7E,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,MAAM,uBAAuB,oBAAoB,KAAK,UAAU,QAAQ,CAAC;AAAA,EACvC,OAAO;AAAA,IAC1D;AACF,QAAM,SAAS;AACf,QAAM,QAAQ,OAAO,KAAK,MAAM,EAAE,OAAO,CAAC,QAAQ,QAAQ,aAAa,QAAQ,eAAe;AAC9F,MAAI,MAAM,SAAS;AACjB,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,MAAM,uBAAuB,sBACvC,MAAM,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG,EAAE,KAAK,IAAI,CAAC;AAAA,EACY,OAAO;AAAA,IACnE;AACF,QAAM,UAAU,OAAO,SAAS;AAChC,MAAI,OAAO,YAAY,YAAY,QAAQ,KAAK,MAAM;AACpD,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,MAAM,uBAAuB;AAAA,EACS,OAAO;AAAA,IAC9D;AACF,QAAM,gBAAgB,OAAO,eAAe;AAC5C,MAAI,OAAO,kBAAkB,YAAY,CAAC,WAAW,KAAK,aAAa;AACrE,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,MAAM,uBAAuB,gCACvC,KAAK,UAAU,aAAa,CAAC;AAAA,EAEN,OAAO;AAAA,IACrC;AACF,SAAO,EAAE,SAAS,cAAc;AAClC;AAWO,SAAS,mBAAmB,aAA6B;AAC9D,SAAO,YAAY,MAAM,GAAG,EAAE,IAAI,KAAK;AACzC;AAIO,SAAS,iBAAiB,SAA0B,SAAyB;AAClF,QAAM,WAAW,KAAK,QAAQ,KAAK,QAAQ,KAAK;AAChD,QAAM,SAAS,QAAQ,SAAS,GAAG,IAAI,UAAU,GAAG,OAAO;AAC3D,QAAM,WAAW,SAAS,WAAW,MAAM,IAAI,SAAS,MAAM,OAAO,MAAM,IAAI;AAC/E,SAAO,SAAS,MAAM,IAAI,EAAE,KAAK,GAAG;AACtC;AAGA,SAAS,uBAAuB,aAA+B;AAC7D,QAAM,WAAW,mBAAmB,KAAK,aAAa,cAAc,CAAC;AACrE,UAAQ,UAAU,mBAAmB,CAAC,GAAG,OAAO,CAAC,SAAS,KAAK,WAAW,UAAU,CAAC,EAAE,KAAK;AAC9F;AAOO,SAAS,wBAAwB,SAAoC;AAC1E,SAAO,QAAQ,SAAS,OAAO,CAAC,SAAS;AACvC,UAAM,MAAM,oBAAoB,QAAQ,KAAK,IAAI;AACjD,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,WAAW,mBAAmB,KAAK,KAAK,cAAc,CAAC;AAC7D,WAAO,sBAAsB,QAAQ,MAAM;AAAA,EAC7C,CAAC;AACH;AAGO,SAAS,iBAAiB,aAAwC;AACvE,QAAM,QAA2B,CAAC;AAClC,aAAW,QAAQ,uBAAuB,WAAW,GAAG;AACtD,UAAM,MAAM,oBAAoB,aAAa,IAAI;AACjD,QAAI,QAAQ,KAAM;AAClB,UAAM,UAAU,oBAAoB,GAAG;AACvC,QAAI,YAAY,KAAM,OAAM,KAAK,OAAO;AAAA,EAC1C;AACA,SAAO;AACT;AASO,SAAS,yBAAyB,aAAsC;AAC7E,QAAM,WAAW,iBAAiB,WAAW;AAC7C,MAAI,SAAS,WAAW,EAAG,QAAO,SAAS,CAAC;AAC5C,MAAI,SAAS,WAAW;AACtB,UAAM,IAAI;AAAA,MACR,GAAG,WAAW;AAAA;AAAA;AAAA,2BAGgB,wBAAwB;AAAA;AAAA,IAExD;AACF,QAAM,IAAI;AAAA,IACR,GAAG,WAAW,aAAa,SAAS,MAAM,2BACrC,SAAS,IAAI,CAAC,YAAY,QAAQ,IAAI,EAAE,KAAK,OAAO,CAAC;AAAA;AAAA;AAAA,IAGxD,SAAS,IAAI,CAAC,YAAY,mBAAmB,QAAQ,IAAI,EAAE,EAAE,KAAK,IAAI;AAAA,EAC1E;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/discovery.ts"],
4
- "sourcesContent": ["/** Registered-session discovery; never selects another project's session. */\nimport { readLiveRegisteredSessions, servedProjectAnswer } from './registry-format.js';\nexport { servedProjectAnswer } from './registry-format.js';\n\nexport const EDITOR_SESSION_DISCOVERY_TIMEOUT_MS = 2000;\n\nexport const EDITOR_PROBE_TIMEOUT_MS = 1500;\n\nexport class EditorTimeoutError extends Error {\n constructor(label: string, ms: number) {\n super(`${label} timed out after ${ms}ms`);\n this.name = 'EditorTimeoutError';\n }\n}\n\nexport function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n return new Promise<T>((resolvePromise, reject) => {\n const timer = setTimeout(() => reject(new EditorTimeoutError(label, ms)), ms);\n promise.then(\n (v) => {\n clearTimeout(timer);\n resolvePromise(v);\n },\n (err) => {\n clearTimeout(timer);\n reject(err instanceof Error ? err : new Error(String(err)));\n },\n );\n });\n}\n\nexport interface EditorSessionInfo {\n /** The port this session's dev server is bound to. */\n port: number;\n /** Canonical project root path currently open, or null when none. */\n project: string | null;\n /** Dev-server process id, when known from the local session registry (null for an unregistered server the caller only probed by port). */\n pid: number | null;\n /** Exact explicitly targeted editor origin/base URL, including protocol and host. */\n url?: string;\n /**\n * Why this server cannot DESCRIBE the `project` it is serving \u2014 its\n * manifest's own parse/validation failure, naming the failing key. `null`\n * when the project reads fine, and `undefined` from a probe that did not ask\n * See {@link servedProjectAnswer}: a session in\n * this state is still THIS project's session, and callers must report the\n * reason rather than treat it as no session at all.\n */\n manifestError?: string | null;\n}\n\nasync function fetchJson(url: string, timeoutMs: number): Promise<unknown> {\n const res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });\n if (!res.ok) throw new Error(`GET /__editor/project returned HTTP ${res.status}`);\n return await res.json();\n}\n\nfunction baseUrl(session: EditorSessionInfo): string {\n return session.url ?? `http://127.0.0.1:${session.port}`;\n}\n\nexport interface SessionListingTransport { listSessions(timeoutMs: number): Promise<EditorSessionInfo[]>; }\nexport class HttpSessionDiscovery implements SessionListingTransport {\n async listSessions(timeoutMs: number): Promise<EditorSessionInfo[]> {\n const registered = readLiveRegisteredSessions();\n const perProbeTimeout = Math.min(EDITOR_PROBE_TIMEOUT_MS, Math.max(200, timeoutMs));\n const failures: string[] = [];\n const probes = await Promise.all(\n registered.map(async (s) => {\n try {\n const body = await fetchJson(\n `${baseUrl({ port: s.port, project: null, pid: s.pid })}/__editor/project`,\n perProbeTimeout,\n );\n const served = servedProjectAnswer(body);\n const info: EditorSessionInfo = {\n port: s.port,\n project: served.path,\n pid: s.pid,\n manifestError: served.manifestError,\n };\n return info;\n } catch (error) {\n // Name the step that did not answer, not a guess at why: an aborted fetch here means\n // the server took the request to /__editor/project and sent nothing back in time.\n const timedOut = (error as { name?: string } | null)?.name === 'TimeoutError';\n const detail = timedOut\n ? `GET /__editor/project sent no answer within ${perProbeTimeout} ms (pid ${s.pid})`\n : error instanceof Error ? `${error.name}: ${error.message}` : String(error);\n failures.push(`port ${s.port}: ${detail}`);\n return undefined;\n }\n }),\n );\n const sessions = probes.filter((p): p is EditorSessionInfo => p !== undefined);\n // A readable registry with registered live processes is not an empty\n // registry just because every HTTP discovery request failed. Keep any\n // verified sessions usable, but refuse an entirely unanswered inventory\n // rather than telling the caller to launch a second editor.\n if (registered.length > 0 && sessions.length === 0) {\n throw new Error(`Registered editor sessions could not be inspected: ${failures.join('; ')}`);\n }\n return sessions;\n }\n}\n"],
5
- "mappings": ";;;;;;AAIO,IAAM,sCAAsC;AAE5C,IAAM,0BAA0B;AAEhC,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAAY,OAAe,IAAY;AACrC,UAAM,GAAG,KAAK,oBAAoB,EAAE,IAAI;AACxC,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,YAAe,SAAqB,IAAY,OAA2B;AACzF,SAAO,IAAI,QAAW,CAAC,gBAAgB,WAAW;AAChD,UAAM,QAAQ,WAAW,MAAM,OAAO,IAAI,mBAAmB,OAAO,EAAE,CAAC,GAAG,EAAE;AAC5E,YAAQ;AAAA,MACN,CAAC,MAAM;AACL,qBAAa,KAAK;AAClB,uBAAe,CAAC;AAAA,MAClB;AAAA,MACA,CAAC,QAAQ;AACP,qBAAa,KAAK;AAClB,eAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MAC5D;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAsBA,eAAe,UAAU,KAAa,WAAqC;AACzE,QAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,YAAY,QAAQ,SAAS,EAAE,CAAC;AACvE,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,uCAAuC,IAAI,MAAM,EAAE;AAChF,SAAO,MAAM,IAAI,KAAK;AACxB;AAEA,SAAS,QAAQ,SAAoC;AACnD,SAAO,QAAQ,OAAO,oBAAoB,QAAQ,IAAI;AACxD;AAGO,IAAM,uBAAN,MAA8D;AAAA,EACnE,MAAM,aAAa,WAAiD;AAClE,UAAM,aAAa,2BAA2B;AAC9C,UAAM,kBAAkB,KAAK,IAAI,yBAAyB,KAAK,IAAI,KAAK,SAAS,CAAC;AAClF,UAAM,WAAqB,CAAC;AAC5B,UAAM,SAAS,MAAM,QAAQ;AAAA,MAC3B,WAAW,IAAI,OAAO,MAAM;AAC1B,YAAI;AACF,gBAAM,OAAO,MAAM;AAAA,YACjB,GAAG,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC;AAAA,YACvD;AAAA,UACF;AACA,gBAAM,SAAS,oBAAoB,IAAI;AACvC,gBAAM,OAA0B;AAAA,YAC9B,MAAM,EAAE;AAAA,YACR,SAAS,OAAO;AAAA,YAChB,KAAK,EAAE;AAAA,YACP,eAAe,OAAO;AAAA,UACxB;AACA,iBAAO;AAAA,QACT,SAAS,OAAO;AAGd,gBAAM,WAAY,OAAoC,SAAS;AAC/D,gBAAM,SAAS,WACX,+CAA+C,eAAe,YAAY,EAAE,GAAG,MAC/E,iBAAiB,QAAQ,GAAG,MAAM,IAAI,KAAK,MAAM,OAAO,KAAK,OAAO,KAAK;AAC7E,mBAAS,KAAK,QAAQ,EAAE,IAAI,KAAK,MAAM,EAAE;AACzC,iBAAO;AAAA,QACT;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,WAAW,OAAO,OAAO,CAAC,MAA8B,MAAM,MAAS;AAK7E,QAAI,WAAW,SAAS,KAAK,SAAS,WAAW,GAAG;AAClD,YAAM,IAAI,MAAM,sDAAsD,SAAS,KAAK,IAAI,CAAC,EAAE;AAAA,IAC7F;AACA,WAAO;AAAA,EACT;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/registry-format.ts"],
4
- "sourcesContent": ["/**\n * Shared editor-session registry format: entry shape, guards, path,\n * liveness-filtered reads, served-project parsing and pending launches.\n * The server\u2019s session-registry module owns writes to this format.\n */\n\nimport {\n mkdirSync,\n readdirSync,\n readFileSync,\n realpathSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join, resolve } from 'node:path';\nimport { setTimeout as delay } from 'node:timers/promises';\n\n/** One registry entry, exactly as the server's write half records it. */\nexport interface EditorSessionEntry {\n /** Canonical project root currently open, or null when none. */\n project: string | null;\n port: number;\n /** PID of the dev-server process. */\n pid: number;\n /** ISO timestamp of server start. */\n startedAt: string;\n sessionId: string | null;\n /** Private loopback control credential \u2014 never returned by an editor HTTP\n * route; exists only in this user-private registry. */\n controlSecret?: string | null;\n repositoryId: string | null;\n worktreeId: string | null;\n worktreeRoot: string | null;\n projectRelativePath: string | null;\n branch: string | null;\n headCommit: string | null;\n baseCommit: string | null;\n}\n\nexport const EDITOR_SESSIONS_REGISTRY_FILE = join(homedir(), '.volter', 'editor-sessions.json');\n\nexport function isEditorSessionEntry(v: unknown): v is EditorSessionEntry {\n if (typeof v !== 'object' || v === null) return false;\n const s = v as Record<string, unknown>;\n const optionalIdentity = (value: unknown) =>\n value === undefined || value === null || typeof value === 'string';\n return (\n (typeof s['project'] === 'string' || s['project'] === null) &&\n typeof s['port'] === 'number' &&\n typeof s['pid'] === 'number' &&\n typeof s['startedAt'] === 'string' &&\n optionalIdentity(s['sessionId']) &&\n optionalIdentity(s['controlSecret']) &&\n optionalIdentity(s['repositoryId']) &&\n optionalIdentity(s['worktreeId']) &&\n optionalIdentity(s['worktreeRoot']) &&\n optionalIdentity(s['projectRelativePath']) &&\n optionalIdentity(s['branch']) &&\n optionalIdentity(s['headCommit']) &&\n optionalIdentity(s['baseCommit'])\n );\n}\n\nexport function normalizeEditorSessionEntry(session: EditorSessionEntry): EditorSessionEntry {\n return {\n ...session,\n sessionId: session.sessionId ?? null,\n controlSecret: session.controlSecret ?? null,\n repositoryId: session.repositoryId ?? null,\n worktreeId: session.worktreeId ?? null,\n worktreeRoot: session.worktreeRoot ?? null,\n projectRelativePath: session.projectRelativePath ?? null,\n branch: session.branch ?? null,\n headCommit: session.headCommit ?? null,\n baseCommit: session.baseCommit ?? null,\n };\n}\n\n/** Whether pid exists. Signal 0's EPERM means it exists but this caller cannot signal it. */\nexport function pidAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\n/** The registry's live entries \u2014 shape-validated and PID-liveness-filtered. */\nexport function readLiveRegisteredSessions(): EditorSessionEntry[] {\n try {\n const raw: unknown = JSON.parse(readFileSync(EDITOR_SESSIONS_REGISTRY_FILE, 'utf8'));\n return Array.isArray(raw)\n ? raw\n .filter(isEditorSessionEntry)\n .map(normalizeEditorSessionEntry)\n .filter((s) => pidAlive(s.pid))\n : [];\n } catch {\n return [];\n }\n}\n\n/** Announce a launch before the server can register. Each launcher owns its file. */\nexport function announceEditorLaunch(project: string): () => void {\n const directory = join(project, '.volter', 'editor-launches');\n mkdirSync(directory, { recursive: true });\n const file = join(directory, `${process.pid}.json`);\n writeFileSync(file, JSON.stringify({ pid: process.pid }));\n const clear = () => {\n process.removeListener('exit', clear);\n try {\n unlinkSync(file);\n } catch {\n // Already cleared when the server became ready.\n }\n };\n process.once('exit', clear);\n return clear;\n}\n\nfunction hasPendingEditorLaunch(project: string): boolean {\n const directory = join(project, '.volter', 'editor-launches');\n try {\n return readdirSync(directory).some((name) => {\n if (!/^\\d+\\.json$/.test(name)) return false;\n const pid = Number(name.slice(0, -5));\n return pid !== process.pid && pidAlive(pid);\n });\n } catch {\n return false;\n }\n}\n\n/** Wait only for an observed live launcher, never for a missing editor. */\nexport async function waitForPendingEditorLaunch(project: string): Promise<void> {\n const canon = canonicalizeProjectPath(project);\n const ready = () =>\n readLiveRegisteredSessions().some(\n (session) => session.project !== null && canonicalizeProjectPath(session.project) === canon,\n );\n if (ready() || !hasPendingEditorLaunch(canon)) return;\n const deadline = Date.now() + 180_000;\n while (!ready()) {\n if (!hasPendingEditorLaunch(canon)) {\n // The launcher clears its announcement only after registration.\n if (ready()) return;\n throw new Error(`The editor launch for ${canon} ended before registering a session.`);\n }\n if (Date.now() >= deadline) {\n throw new Error(\n `The editor launch for ${canon} is still running but has not registered within 180s.`,\n );\n }\n await delay(100);\n }\n}\n\n/** Read the project identity reported by /__editor/project. A serving path\n * identifies an open project even when its manifest cannot be parsed or\n * validated; project: null alone denotes no described project. */\nexport function servedProjectAnswer(body: unknown): {\n path: string | null;\n manifestError: string | null;\n} {\n const b = body as {\n project?: { path?: string } | null;\n serving?: { path?: string; error?: string } | null;\n };\n return {\n path: b.project?.path ?? b.serving?.path ?? null,\n manifestError: b.project ? null : (b.serving?.error ?? null),\n };\n}\n\n/** Canonical (realpathed when possible) absolute form of a project path. */\nfunction canonicalizeProjectPath(p: string): string {\n const absolute = resolve(p);\n try {\n return realpathSync(absolute);\n } catch {\n return absolute;\n }\n}\n"],
5
- "mappings": ";AAMA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,eAAe;AACxB,SAAS,MAAM,eAAe;AAC9B,SAAS,cAAc,aAAa;AAwB7B,IAAM,gCAAgC,KAAK,QAAQ,GAAG,WAAW,sBAAsB;AAEvF,SAAS,qBAAqB,GAAqC;AACxE,MAAI,OAAO,MAAM,YAAY,MAAM,KAAM,QAAO;AAChD,QAAM,IAAI;AACV,QAAM,mBAAmB,CAAC,UACxB,UAAU,UAAa,UAAU,QAAQ,OAAO,UAAU;AAC5D,UACG,OAAO,EAAE,SAAS,MAAM,YAAY,EAAE,SAAS,MAAM,SACtD,OAAO,EAAE,MAAM,MAAM,YACrB,OAAO,EAAE,KAAK,MAAM,YACpB,OAAO,EAAE,WAAW,MAAM,YAC1B,iBAAiB,EAAE,WAAW,CAAC,KAC/B,iBAAiB,EAAE,eAAe,CAAC,KACnC,iBAAiB,EAAE,cAAc,CAAC,KAClC,iBAAiB,EAAE,YAAY,CAAC,KAChC,iBAAiB,EAAE,cAAc,CAAC,KAClC,iBAAiB,EAAE,qBAAqB,CAAC,KACzC,iBAAiB,EAAE,QAAQ,CAAC,KAC5B,iBAAiB,EAAE,YAAY,CAAC,KAChC,iBAAiB,EAAE,YAAY,CAAC;AAEpC;AAEO,SAAS,4BAA4B,SAAiD;AAC3F,SAAO;AAAA,IACL,GAAG;AAAA,IACH,WAAW,QAAQ,aAAa;AAAA,IAChC,eAAe,QAAQ,iBAAiB;AAAA,IACxC,cAAc,QAAQ,gBAAgB;AAAA,IACtC,YAAY,QAAQ,cAAc;AAAA,IAClC,cAAc,QAAQ,gBAAgB;AAAA,IACtC,qBAAqB,QAAQ,uBAAuB;AAAA,IACpD,QAAQ,QAAQ,UAAU;AAAA,IAC1B,YAAY,QAAQ,cAAc;AAAA,IAClC,YAAY,QAAQ,cAAc;AAAA,EACpC;AACF;AAGO,SAAS,SAAS,KAAsB;AAC7C,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAQ,MAAgC,SAAS;AAAA,EACnD;AACF;AAGO,SAAS,6BAAmD;AACjE,MAAI;AACF,UAAM,MAAe,KAAK,MAAM,aAAa,+BAA+B,MAAM,CAAC;AACnF,WAAO,MAAM,QAAQ,GAAG,IACpB,IACG,OAAO,oBAAoB,EAC3B,IAAI,2BAA2B,EAC/B,OAAO,CAAC,MAAM,SAAS,EAAE,GAAG,CAAC,IAChC,CAAC;AAAA,EACP,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAGO,SAAS,qBAAqB,SAA6B;AAChE,QAAM,YAAY,KAAK,SAAS,WAAW,iBAAiB;AAC5D,YAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,OAAO,KAAK,WAAW,GAAG,QAAQ,GAAG,OAAO;AAClD,gBAAc,MAAM,KAAK,UAAU,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC;AACxD,QAAM,QAAQ,MAAM;AAClB,YAAQ,eAAe,QAAQ,KAAK;AACpC,QAAI;AACF,iBAAW,IAAI;AAAA,IACjB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,UAAQ,KAAK,QAAQ,KAAK;AAC1B,SAAO;AACT;AAEA,SAAS,uBAAuB,SAA0B;AACxD,QAAM,YAAY,KAAK,SAAS,WAAW,iBAAiB;AAC5D,MAAI;AACF,WAAO,YAAY,SAAS,EAAE,KAAK,CAAC,SAAS;AAC3C,UAAI,CAAC,cAAc,KAAK,IAAI,EAAG,QAAO;AACtC,YAAM,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC;AACpC,aAAO,QAAQ,QAAQ,OAAO,SAAS,GAAG;AAAA,IAC5C,CAAC;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,2BAA2B,SAAgC;AAC/E,QAAM,QAAQ,wBAAwB,OAAO;AAC7C,QAAM,QAAQ,MACZ,2BAA2B,EAAE;AAAA,IAC3B,CAAC,YAAY,QAAQ,YAAY,QAAQ,wBAAwB,QAAQ,OAAO,MAAM;AAAA,EACxF;AACF,MAAI,MAAM,KAAK,CAAC,uBAAuB,KAAK,EAAG;AAC/C,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,SAAO,CAAC,MAAM,GAAG;AACf,QAAI,CAAC,uBAAuB,KAAK,GAAG;AAElC,UAAI,MAAM,EAAG;AACb,YAAM,IAAI,MAAM,yBAAyB,KAAK,sCAAsC;AAAA,IACtF;AACA,QAAI,KAAK,IAAI,KAAK,UAAU;AAC1B,YAAM,IAAI;AAAA,QACR,yBAAyB,KAAK;AAAA,MAChC;AAAA,IACF;AACA,UAAM,MAAM,GAAG;AAAA,EACjB;AACF;AAKO,SAAS,oBAAoB,MAGlC;AACA,QAAM,IAAI;AAIV,SAAO;AAAA,IACL,MAAM,EAAE,SAAS,QAAQ,EAAE,SAAS,QAAQ;AAAA,IAC5C,eAAe,EAAE,UAAU,OAAQ,EAAE,SAAS,SAAS;AAAA,EACzD;AACF;AAGA,SAAS,wBAAwB,GAAmB;AAClD,QAAM,WAAW,QAAQ,CAAC;AAC1B,MAAI;AACF,WAAO,aAAa,QAAQ;AAAA,EAC9B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
- "names": []
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/volter-manifest.ts", "../../sdk/src/volter-contributions.ts"],
4
- "sourcesContent": ["import { readFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { readPageEntry, readVolterContributions } from '@volter/sdk/volter-contributions';\n\n/**\n * THE NODE READER of a package's `package.json` as Volter reads it: its name, its dependency\n * names and its `contributes.volter` declarations, which `@volter/sdk/volter-contributions` reads as the workbench and\n * the page read them. The build's product plugin, the session's catalog and\n * serving doors, the shared-React scope and the pre-bundle crawl all read a package here, so a\n * declaration means one thing everywhere it is read.\n *\n * It parses and does no policy. What an unusable declaration costs is the caller's to say: a\n * product build refuses it, the catalog reports it as a load error, a scope decision treats it\n * as no declaration. {@link declaredContributions} is the one test of \"this package contributes\".\n */\nexport interface VolterManifest {\n /** The `package.json`, absolute. */\n readonly path: string;\n /** Its package's directory. */\n readonly dir: string;\n readonly name: string | null;\n readonly version: string | null;\n readonly private: boolean;\n readonly bin: unknown;\n /** Every name in `dependencies`, `devDependencies` and `optionalDependencies`, in that order,\n * each once. */\n readonly dependencyNames: readonly string[];\n /** `package.json#contributes.volter`, unchecked: the package's contribution modules, an object\n * keyed by kind, and its declarations (`serving`, `viewRoutes`, `product`). It is where Code-OSS's extension\n * service reads them too, through the kit's `volter` extension point. */\n readonly contributions: unknown;\n /** `package.json#contributes.languageModelTools`, unchecked: the agent tools Code-OSS registers from it. */\n readonly languageModelTools: unknown;\n /** `package.json#contributes.volter.product`, a product's declaration, read and checked by `product-locator.ts`;\n * `undefined` when the package declares none or it is not an object. */\n readonly product: ProductDeclaration | undefined;\n}\n\n/** `package.json#contributes.volter.product`, as declared and unchecked. */\nexport interface ProductDeclaration {\n readonly entry?: unknown;\n readonly composes?: unknown;\n readonly colorTheme?: unknown;\n readonly workbench?: unknown;\n readonly displayName?: unknown;\n readonly install?: unknown;\n readonly upgrade?: unknown;\n readonly adapterRequired?: unknown;\n}\n\n/** `path`'s manifest, or `null` when there is no file there or it is not a JSON object. */\nexport function readVolterManifest(path: string): VolterManifest | null {\n let json: unknown;\n try {\n json = JSON.parse(readFileSync(path, 'utf8'));\n } catch {\n return null;\n }\n if (json === null || typeof json !== 'object' || Array.isArray(json)) return null;\n const record = json as Record<string, unknown>;\n const names = (key: string): string[] => {\n const section = record[key];\n return section !== null && typeof section === 'object' ? Object.keys(section) : [];\n };\n const contributes = record['contributes'];\n const contributions = contributes !== null && typeof contributes === 'object' ? (contributes as Record<string, unknown>)['volter'] : undefined;\n const declared = contributions !== null && typeof contributions === 'object' ? (contributions as Record<string, unknown>) : {};\n const product = declared['product'];\n return {\n path,\n dir: dirname(path),\n name: typeof record['name'] === 'string' ? record['name'] : null,\n version: typeof record['version'] === 'string' ? record['version'] : null,\n private: record['private'] === true,\n bin: record['bin'],\n dependencyNames: [...new Set([...names('dependencies'), ...names('devDependencies'), ...names('optionalDependencies')])],\n contributions,\n languageModelTools: contributes !== null && typeof contributes === 'object' ? (contributes as Record<string, unknown>)['languageModelTools'] : undefined,\n product: product !== null && typeof product === 'object' && !Array.isArray(product) ? (product as ProductDeclaration) : undefined,\n };\n}\n\n/**\n * The package's declared modules, each contribution's and its page half's, with why any entry is refused; `null` when\n * it declares none (an absent `contributes.volter`, or one that is not an object). A package with the object, even an\n * empty one, is a contributing package: its modules are editor-tree code.\n */\nexport function declaredContributions(manifest: VolterManifest | null): { readonly modules: readonly string[]; readonly errors: readonly string[] } | null {\n const declared = manifest?.contributions;\n if (declared === null || typeof declared !== 'object' || Array.isArray(declared)) return null;\n const { entries, errors } = readVolterContributions(declared);\n const page = readPageEntry(manifest?.name ?? manifest?.path ?? '', (declared as Record<string, unknown>)['page']);\n return {\n modules: [...entries.map(({ entry }) => entry), ...('entry' in page && page.entry !== null ? [page.entry] : [])],\n errors: 'malformed' in page ? [...errors, page.malformed] : errors,\n };\n}\n", "/**\n * THE ONE READER of a package's `contributes.volter`: what each key declares, the modules it names, and the activation\n * events its contributions imply. The workbench's `volter` extension point (`workbench/src/volterExtensions.ts`, through\n * the copy the overlay writes beside it, `volterGeneratedContributions.ts`), the page's activation (editor-core's `page-activation.ts`) and\n * Node's manifest reader (`@volter/live/volter-manifest`) all read a package here, so a declaration means one thing\n * everywhere it is read.\n *\n * It imports nothing: the workbench half is compiled inside the Code-OSS fork, which resolves no package, so\n * `scripts/workbench/overlay.mjs` copies this file into it at every overlay.\n *\n * The keys are an EDITOR's (`editors`), a VIEW's by container (`views.panel`, `views.statusbar`), an INSPECTOR's, a LOOK's,\n * a FINDER's and a SERVICE's. `page` is the package's page half, and `product`, `serving`, `viewRoutes` and\n * `projectAdapters` are declarations read in Node ({@link DECLARATION_KEYS}); none of them is a contribution kind.\n *\n * `activationEvents` is Code-OSS's top-level key, stated here by a package whose only code is its page half: Code-OSS\n * refuses an extension that lists top-level `activationEvents` without a `main` or `browser` entry\n * (`extensionValidator.ts`'s `validateExtensionManifest`), so such a package lists its explicit events under\n * `contributes.volter` instead, and they join the ones its contributions imply.\n */\n\n/** The `contributes.volter` keys that are declarations, not contribution kinds: the product's declaration (`product`,\n * `@volter/live/product-locator`), the server half (`serving`, editor-core's `server/session-product.ts`), the\n * limited view's module (`viewRoutes`) and the adapters a package supplies for projects like a kind it knows\n * (`projectAdapters`, editor-core's `server/session-product.ts`). */\nconst DECLARATION_KEYS: ReadonlySet<string> = new Set(['product', 'serving', 'viewRoutes', 'projectAdapters']);\n\n/**\n * One editor a package declares under `contributes.volter.editors` with the files it opens (Code-OSS's `selector` and\n * `priority`, as `contributes.notebooks` states them). Code-OSS's `customEditors` is not used: it means a webview editor.\n */\nexport interface VolterEditorDeclaration {\n /** The editor's id in the resolver (`volter.<integration>.model`). */\n readonly viewType: string;\n /** What \"Reopen Editor With\u2026\" lists. */\n readonly displayName: string;\n readonly filenamePatterns: readonly string[];\n readonly priority: 'default' | 'option';\n}\n\n/**\n * One panel view a package declares under `contributes.volter.views.panel`: `{ id, title, module }`, as an editor states\n * its `viewType` and `displayName`. The workbench registers its panel view from this before any of the package's code\n * loads, and the view's first showing is the package's `onView:<id>`.\n */\nexport interface VolterUtilityDeclaration {\n readonly id: string;\n readonly title: string;\n}\n\n/** One contribution module a package declares: the key it is listed under (`views.panel` for a panel view), its\n * `./`-relative module, and a panel view's or a finder's own statement. */\nexport interface VolterContribution {\n readonly key: string;\n readonly entry: string;\n readonly declared?: VolterUtilityDeclaration;\n readonly finder?: string;\n}\n\n/** A package's `contributes.volter`, read. */\ninterface VolterContributions {\n /** Each contribution module, in declaration order; a refused entry is left out. */\n readonly entries: readonly VolterContribution[];\n /** The file editors, for the workbench's editor resolver. */\n readonly editors: readonly VolterEditorDeclaration[];\n /** The panel views. */\n readonly utilities: readonly VolterUtilityDeclaration[];\n /** The activation events the contributions imply ({@link activationEvents}). */\n readonly events: readonly string[];\n /** Why each refused entry is refused, as `contributes.volter.<key>: <why>.` */\n readonly errors: readonly string[];\n}\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : null;\n}\n\n/** A declared list's string entries. */\nfunction strings(list: unknown): string[] {\n return Array.isArray(list) ? list.filter((entry): entry is string => typeof entry === 'string') : [];\n}\n\n/** A contribution module's id, its base name without its extension: `contributions/timeline.document.tsx` is\n * `timeline.document`. An editor's `document` implies `onDocument:<id>`, and the loader records a module by it. */\nexport function contributionIdFromPath(entry: string): string {\n return (entry.split(/[\\\\/]/).pop() ?? entry).replace(/\\.[jt]sx?$/, '');\n}\n\n/** One `contributes.volter.editors` entry, read. */\ninterface ReadEditor {\n readonly declaration?: VolterEditorDeclaration;\n /** The document module the page loads; absent for a kind or an asset route the package registers in code. */\n readonly document?: string;\n readonly kind?: string;\n readonly views?: readonly string[];\n readonly assetRoute?: string;\n}\n\nfunction readEditor(value: unknown): ReadEditor | string {\n const editor = record(value);\n if (editor === null) return 'an entry is not an object';\n const { viewType, displayName, document, selector, priority, kind, views, assetRoute } = editor;\n if (document !== undefined && typeof document !== 'string') return 'a document is a ./-relative module';\n if (kind !== undefined && (typeof kind !== 'string' || kind === '')) return 'a kind is a non-empty string';\n if (views !== undefined && (kind === undefined || !Array.isArray(views) || views.some((view) => typeof view !== 'string'))) return \"views are a kind's list of view document ids\";\n if (assetRoute !== undefined && (typeof assetRoute !== 'string' || assetRoute === '')) return 'an assetRoute is a non-empty string';\n if (document === undefined && kind === undefined && assetRoute === undefined) return 'an entry names a document, a kind or an assetRoute';\n const read = {\n ...(document === undefined ? {} : { document }),\n ...(kind === undefined ? {} : { kind }),\n ...(views === undefined ? {} : { views: views as string[] }),\n ...(assetRoute === undefined ? {} : { assetRoute }),\n };\n if (viewType === undefined && displayName === undefined && selector === undefined && priority === undefined) return read;\n if (typeof viewType !== 'string' || typeof displayName !== 'string' || typeof document !== 'string') return 'a file editor needs a viewType, a displayName and a document module';\n const filenamePatterns = Array.isArray(selector) ? selector.map((item) => record(item)?.['filenamePattern']) : [];\n if (filenamePatterns.length === 0 || filenamePatterns.some((pattern) => typeof pattern !== 'string')) return `${viewType}'s selector must list its { filenamePattern }s`;\n if (priority !== undefined && priority !== 'default' && priority !== 'option') return `${viewType}'s priority is 'default' or 'option'`;\n return { ...read, declaration: { viewType, displayName, filenamePatterns: filenamePatterns as string[], priority: priority ?? 'default' } };\n}\n\n/** One `contributes.volter.services` entry, read: a module, or `{ module, sourceAnalyzer?, designTimeMount? }`. */\nfunction readService(value: unknown): { readonly module: string; readonly sourceAnalyzer?: string; readonly designTimeMount?: string } | string {\n if (typeof value === 'string') return { module: value };\n const { module, sourceAnalyzer, designTimeMount } = record(value) ?? {};\n if (typeof module !== 'string' || [sourceAnalyzer, designTimeMount].some((id) => id !== undefined && (typeof id !== 'string' || id === ''))) {\n return 'an entry is a ./-relative module, or { module, sourceAnalyzer?, designTimeMount? }';\n }\n return {\n module,\n ...(typeof sourceAnalyzer === 'string' ? { sourceAnalyzer } : {}),\n ...(typeof designTimeMount === 'string' ? { designTimeMount } : {}),\n };\n}\n\n/** One `contributes.volter.finders` entry, read. */\nfunction readFinder(value: unknown): { readonly name: string; readonly module: string } | string {\n const { name, module } = record(value) ?? {};\n if (typeof name !== 'string' || name === '' || typeof module !== 'string') return 'an entry is { name, module }, each a non-empty string';\n return { name, module };\n}\n\n/** One `contributes.volter.views.panel` entry, read. */\nfunction readUtility(value: unknown): { readonly declaration: VolterUtilityDeclaration; readonly module: string } | string {\n const { id, title, module } = record(value) ?? {};\n if (typeof id !== 'string' || id === '' || typeof title !== 'string' || title.trim() === '' || typeof module !== 'string') return 'an entry is { id, title, module }, each a non-empty string';\n return { declaration: { id, title: title.trim() }, module };\n}\n\n/**\n * Read a package's `contributes.volter` (`declared`, unchecked). Each key is a list; the views are keyed by container,\n * as Code-OSS's `contributes.views` is. An entry that is refused is left out and named in `errors`.\n *\n * The implied activation events are Code-OSS's for an extension's contributions (`ImplicitActivationEvents`), carried\n * the same way by Volter's:\n * - for each `editors` entry, `onCustomEditor:<viewType>`, `onDocument:<id>` for its `document` (the module's base\n * name), `onDocument:<kind>` and `onDocument:<view>` for its `kind` and `views`, and `onAssetViewer:<route>` for its\n * `assetRoute`;\n * - `onView:<id>` for each `views.panel` entry;\n * - `onFinder:<name>` for each `finders` entry;\n * - for each `services` entry, `onSourceAnalysis:<id>` and `onDesignTimeMount:<medium>`;\n * - and each event `activationEvents` lists.\n */\nexport function readVolterContributions(declared: unknown): VolterContributions {\n const entries: VolterContribution[] = [];\n const editors: VolterEditorDeclaration[] = [];\n const utilities: VolterUtilityDeclaration[] = [];\n const events: string[] = [];\n const errors: string[] = [];\n const kinds = record(declared);\n if (kinds === null) return { entries, editors, utilities, events, errors: ['`contributes.volter` must be an object of kinds, each a list of ./-relative modules.'] };\n const listed: [string, unknown][] = [];\n for (const [key, list] of Object.entries(kinds)) {\n if (key === 'page' || DECLARATION_KEYS.has(key)) continue;\n if (key === 'activationEvents') {\n if (!Array.isArray(list) || list.some((event) => typeof event !== 'string')) errors.push('`contributes.volter.activationEvents` must be a list of activation events.');\n else events.push(...(list as string[]));\n continue;\n }\n if (key !== 'views') { listed.push([key, list]); continue; }\n const containers = record(list);\n if (containers === null) { errors.push('`contributes.volter.views` must be an object of containers: `panel`, `statusbar`.'); continue; }\n for (const [container, views] of Object.entries(containers)) listed.push([`views.${container}`, views]);\n }\n for (const [key, list] of listed) {\n if (!Array.isArray(list)) { errors.push(`\\`contributes.volter.${key}\\` must be a list.`); continue; }\n for (const entry of list) {\n const refuse = (why: string): void => { errors.push(`\\`contributes.volter.${key}\\`: ${why}.`); };\n if (key === 'views.panel') {\n const view = readUtility(entry);\n if (typeof view === 'string') { refuse(view); continue; }\n utilities.push(view.declaration);\n entries.push({ key, entry: view.module, declared: view.declaration });\n events.push(`onView:${view.declaration.id}`);\n } else if (key === 'finders') {\n const finder = readFinder(entry);\n if (typeof finder === 'string') { refuse(finder); continue; }\n entries.push({ key, entry: finder.module, finder: finder.name });\n events.push(`onFinder:${finder.name}`);\n } else if (key === 'services') {\n const service = readService(entry);\n if (typeof service === 'string') { refuse(service); continue; }\n entries.push({ key, entry: service.module });\n if (service.sourceAnalyzer !== undefined) events.push(`onSourceAnalysis:${service.sourceAnalyzer}`);\n if (service.designTimeMount !== undefined) events.push(`onDesignTimeMount:${service.designTimeMount}`);\n } else if (key === 'editors') {\n // An editor's document module is a document contribution like any other; a kind or an asset route the package\n // registers in code names no module, and implies its activation events alone.\n const editor = readEditor(entry);\n if (typeof editor === 'string') { refuse(editor); continue; }\n if (editor.declaration) { editors.push(editor.declaration); events.push(`onCustomEditor:${editor.declaration.viewType}`); }\n if (editor.document !== undefined) { entries.push({ key, entry: editor.document }); events.push(`onDocument:${contributionIdFromPath(editor.document)}`); }\n if (editor.kind !== undefined) events.push(`onDocument:${editor.kind}`);\n for (const view of editor.views ?? []) events.push(`onDocument:${view}`);\n if (editor.assetRoute !== undefined) events.push(`onAssetViewer:${editor.assetRoute}`);\n } else if (typeof entry !== 'string') {\n refuse(`${JSON.stringify(entry)} is not a ./-relative module`);\n } else {\n entries.push({ key, entry });\n }\n }\n }\n return { entries, editors, utilities, events, errors };\n}\n\n/** `contributes[key]`, unchecked. */\nfunction contributed(packageJSON: Readonly<Record<string, unknown>>, key: string): unknown {\n return record(packageJSON['contributes'])?.[key];\n}\n\n/** The commands a package declares (`contributes.commands`), by id. */\nfunction commandIds(packageJSON: Readonly<Record<string, unknown>>): string[] {\n const commands = contributed(packageJSON, 'commands');\n return (Array.isArray(commands) ? commands : []).flatMap((command) => {\n const id = record(command)?.['command'];\n return typeof id === 'string' ? [id] : [];\n });\n}\n\n/**\n * A package's activation events, as Code-OSS forms an extension's: the explicit ones it lists in its top-level\n * `activationEvents`, `onCommand:<id>` for each `contributes.commands` entry, and the ones its `contributes.volter`\n * implies ({@link readVolterContributions}). One list governs the package's page half and its extension-host half, as\n * one Code-OSS extension's `main` and `browser` entries share theirs.\n */\nexport function activationEvents(packageJSON: Readonly<Record<string, unknown>>): readonly string[] {\n return [\n ...strings(packageJSON['activationEvents']),\n ...commandIds(packageJSON).map((id) => `onCommand:${id}`),\n ...readVolterContributions(contributed(packageJSON, 'volter') ?? {}).events,\n ];\n}\n\n/**\n * The commands a package's page half answers: those it declares (`contributes.commands`), less, in a package with an\n * extension-host half (`main` or `browser`), the ones its `activationEvents` name (`onCommand:<id>`), which that half\n * registers.\n */\nexport function pageCommandIds(packageJSON: Readonly<Record<string, unknown>>): string[] {\n const hosted = packageJSON['main'] !== undefined || packageJSON['browser'] !== undefined;\n const hostCommands = new Set(hosted ? strings(packageJSON['activationEvents']) : []);\n return commandIds(packageJSON).filter((id) => !hostCommands.has(`onCommand:${id}`));\n}\n\n/** `contributes.volter.page`, checked: one `./`-relative module, or none. */\nexport function readPageEntry(name: string, page: unknown): { readonly entry: string | null } | { readonly malformed: string } {\n if (page === undefined) return { entry: null };\n const listed = Array.isArray(page) ? page : null;\n if (listed === null || listed.length > 1 || (listed.length === 1 && (typeof listed[0] !== 'string' || !listed[0].startsWith('./') || listed[0].includes('..')))) {\n return {\n malformed:\n `${name}'s package.json#contributes.volter.page is ${JSON.stringify(page)}: it lists one page entry, ` +\n 'a ./-relative module without parent traversal that exports activate(context).',\n };\n }\n return { entry: (listed[0] as string | undefined) ?? null };\n}\n\n/** A declaration's one module (`serving`, `viewRoutes`: a one-entry list), unchecked; `undefined` when the package\n * declares none and `null` when the declaration is not a one-entry list. */\nexport function declaredModule(declared: unknown, key: 'serving' | 'viewRoutes'): unknown {\n const listed = record(declared)?.[key];\n if (listed === undefined) return undefined;\n return Array.isArray(listed) && listed.length === 1 ? listed[0] : null;\n}\n"],
5
- "mappings": "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",
6
- "names": ["record"]
7
- }