@crvouga/mockingbird-service-docker 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import {
5
5
  createRuntime,
6
6
  isRunning,
7
7
  jsonRes
8
- } from "./chunk-5I542RFL.js";
8
+ } from "./chunk-NSZYARVW.js";
9
9
 
10
10
  // src/attach.ts
11
11
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -615,6 +615,7 @@ var serveTarget = {
615
615
  defaultPort: DEFAULT_PORT,
616
616
  create: (_values, common) => createRuntime({
617
617
  ...common.adminKey !== void 0 ? { adminKey: common.adminKey } : {},
618
+ ...common.adminControls !== void 0 ? { adminControls: common.adminControls } : {},
618
619
  ...common.seed !== void 0 ? { seed: common.seed } : {},
619
620
  ...common.onLog ? { onLog: common.onLog } : {}
620
621
  }),
@@ -626,4 +627,4 @@ export {
626
627
  createServer2 as createServer,
627
628
  serveTarget
628
629
  };
629
- //# sourceMappingURL=chunk-TR75ZFCZ.js.map
630
+ //# sourceMappingURL=chunk-Y4JH3Q6Z.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/attach.ts", "../src/streams.ts", "../src/transport.ts", "../src/server.ts"],
4
- "sourcesContent": ["import { AsyncLocalStorage } from \"node:async_hooks\"\nimport type { IncomingMessage } from \"node:http\"\nimport { STATUS_CODES } from \"node:http\"\nimport type { Duplex } from \"node:stream\"\nimport { annotateResponse, jsonRes } from \"@crvouga/mockingbird-service\"\nimport type { DockerAPI } from \"./index.js\"\nimport { booleanQuery } from \"./observations.js\"\nimport type { DockerRuntime } from \"./runtime.js\"\nimport { DockerInputError } from \"./state.js\"\nimport { type AttachSelection, type AttachStreamOptions, createAttachStreams } from \"./streams.js\"\n\nexport type AttachHandshakeOptions = {\n /** Deterministic mock writes; network packet boundaries remain OS-controlled. */\n chunkBytes?: number\n /** Synthetic already-framed output delivered with the final header fragment. */\n initialStreamBytes?: Uint8Array\n}\ntype Admission = { selection?: AttachSelection }\nconst header = Buffer.from(\n \"HTTP/1.1 101 UPGRADED\\r\\nContent-Type: application/vnd.docker.multiplexed-stream\\r\\nConnection: Upgrade\\r\\nUpgrade: tcp\\r\\n\\r\\n\",\n)\n\nexport const createAttachHandshake = (\n options: AttachHandshakeOptions = {},\n streamOptions: AttachStreamOptions = {},\n) => {\n const streams = createAttachStreams(streamOptions)\n const chunkBytes = options.chunkBytes ?? header.length\n if (!Number.isSafeInteger(chunkBytes) || chunkBytes < 1 || chunkBytes > 4096)\n throw new RangeError(\"attach chunkBytes must be 1..4096\")\n if ((options.initialStreamBytes?.byteLength ?? 0) > 1024 * 1024)\n throw new RangeError(\"initialStreamBytes exceeds 1 MiB\")\n const first = Buffer.from(options.initialStreamBytes ?? [])\n const admission = new AsyncLocalStorage<Admission>()\n const prepare = (api: DockerAPI, request: Request): Response => {\n const context = admission.getStore()\n if (!context) return jsonRes(501, { message: \"Mockingbird: attach requires Node HTTP upgrade\" })\n const url = new URL(request.url)\n if (\n !booleanQuery(url, \"stream\") ||\n booleanQuery(url, \"logs\") ||\n url.searchParams.has(\"detachKeys\") ||\n [...url.searchParams.keys()].some(\n (key) => ![\"stream\", \"logs\", \"stdin\", \"stdout\", \"stderr\"].includes(key),\n )\n )\n return jsonRes(501, { message: \"Mockingbird: attach mode is not implemented\" })\n const match = /^\\/containers\\/([^/]+)\\/attach$/.exec(url.pathname)\n if (!match?.[1]) return jsonRes(404, { message: \"page not found\" })\n let name: string\n try {\n name = decodeURIComponent(match[1])\n } catch {\n return jsonRes(400, { message: \"invalid container name encoding\" })\n }\n try {\n const c = api.state.find(name)\n if (c.status === \"paused\")\n throw new DockerInputError(\n 409,\n `container ${name} is paused, unpause the container before attach`,\n )\n if (c.status === \"restarting\")\n throw new DockerInputError(\n 409,\n `container ${name} is restarting, wait until the container is running`,\n )\n if (c.config?.Tty === true)\n return jsonRes(501, { message: \"Mockingbird: TTY attach is not implemented\" })\n context.selection = {\n api,\n generation: api.generation,\n containerId: c.id,\n namespace: request.headers.get(\"x-mockingbird-namespace\") ?? \"default\",\n branch: request.headers.get(\"x-mockingbird-branch\") ?? \"main\",\n stdin: booleanQuery(url, \"stdin\") && c.config?.OpenStdin === true,\n stdinOnce: c.config?.StdinOnce === true,\n stdout: booleanQuery(url, \"stdout\"),\n stderr: booleanQuery(url, \"stderr\"),\n }\n return annotateResponse(new Response(null), { wireStatus: 101, ids: { containerId: c.id } })\n } catch (error) {\n if (error instanceof DockerInputError)\n return new Response(`${error.message}\\r\\n`, {\n status: error.status,\n headers: { \"content-type\": \"application/vnd.docker.raw-stream\" },\n })\n throw error\n }\n }\n const write = (socket: Duplex, bytes: Uint8Array) =>\n new Promise<boolean>((resolve) => {\n const finish = (ok: boolean) => {\n socket.off(\"close\", closed)\n resolve(ok)\n }\n const closed = () => finish(false)\n socket.once(\"close\", closed)\n if (socket.destroyed) {\n finish(false)\n return\n }\n socket.write(bytes, (error) => finish(!error))\n })\n const upgrade = async (\n runtime: DockerRuntime,\n req: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n ) => {\n socket.on(\"error\", () => socket.destroy())\n socket.pause()\n try {\n if (\n req.method !== \"POST\" ||\n req.headers.upgrade?.toLowerCase() !== \"tcp\" ||\n !req.headers.connection\n ?.toLowerCase()\n .split(/\\s*,\\s*/)\n .includes(\"upgrade\") ||\n Number(req.headers[\"content-length\"] ?? 0) !== 0 ||\n req.headers[\"transfer-encoding\"] !== undefined\n ) {\n await write(\n socket,\n Buffer.from(\n \"HTTP/1.1 501 Not Implemented\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n\",\n ),\n )\n socket.end()\n return\n }\n const target = new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), \"http://docker.mock\")\n if (\n !/^(?:\\/__admin\\/ns\\/[^/]+)?(?:\\/v[0-9]+\\.[0-9]+)?\\/containers\\/[^/]+\\/attach$/.test(\n target.pathname,\n )\n ) {\n await write(\n socket,\n Buffer.from(\"HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n\"),\n )\n socket.end()\n return\n }\n const headers = new Headers()\n for (let i = 0; i < req.rawHeaders.length; i += 2) {\n const name = req.rawHeaders[i],\n value = req.rawHeaders[i + 1]\n if (name && value !== undefined) headers.append(name, value)\n }\n const request = new Request(\n new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), \"http://docker.mock\"),\n { method: \"POST\", headers },\n )\n const context: Admission = {}\n const response = await admission.run(context, () => runtime.fetch(request))\n if (socket.destroyed) return\n if (!context.selection || response.status !== 200) {\n const body = Buffer.from(await response.arrayBuffer())\n const type = response.headers.get(\"content-type\") ?? \"application/json\"\n await write(\n socket,\n Buffer.concat([\n Buffer.from(\n `HTTP/1.1 ${response.status} ${STATUS_CODES[response.status] ?? \"Error\"}\\r\\nContent-Type: ${type}\\r\\nConnection: close\\r\\nContent-Length: ${body.length}\\r\\n\\r\\n`,\n ),\n body,\n ]),\n )\n socket.end()\n return\n }\n const session = streams.connect(context.selection, socket, runtime)\n for (let offset = 0; offset < header.length; offset += chunkBytes) {\n const end = Math.min(offset + chunkBytes, header.length)\n const chunk = header.subarray(offset, end)\n if (!(await write(socket, end === header.length ? Buffer.concat([chunk, first]) : chunk)))\n return\n if (end < header.length) await new Promise<void>((resolve) => setImmediate(resolve))\n }\n session.start(head)\n } catch {\n socket.destroy()\n }\n }\n return { prepare, upgrade, attachments: streams.list }\n}\n", "import type { Duplex } from \"node:stream\"\nimport type { DockerAPI } from \"./index.js\"\nimport type { DockerRuntime } from \"./runtime.js\"\nimport { isRunning } from \"./state.js\"\n\nexport type AttachStreamOptions = {\n frameChunkBytes?: number\n maxQueuedBytes?: number\n maxStdinBytes?: number\n}\nexport type AttachSelection = {\n api: DockerAPI\n generation: number\n containerId: string\n namespace: string\n branch: string\n stdin: boolean\n stdout: boolean\n stderr: boolean\n stdinOnce: boolean\n}\n/** Node-owned live handle. Retaining one across restore never addresses a successor. */\nexport type DockerAttachment = {\n readonly id: number\n readonly containerId: string\n readonly namespace: string\n readonly branch: string\n readonly closed: boolean\n readonly stdinClosed: boolean\n readonly queuedBytes: number\n write(channel: \"stdout\" | \"stderr\", data: Uint8Array): Promise<void>\n takeStdin(): Uint8Array\n end(): Promise<void>\n cancel(): void\n}\nconst limit = (value: number, name: string) => {\n if (!Number.isSafeInteger(value) || value < 1 || value > 16 * 1024 * 1024)\n throw new RangeError(`${name} must be 1..16777216`)\n return value\n}\nexport const createAttachStreams = (options: AttachStreamOptions = {}) => {\n const chunkBytes = limit(options.frameChunkBytes ?? 64 * 1024, \"frameChunkBytes\")\n const maxQueued = limit(options.maxQueuedBytes ?? 1024 * 1024, \"maxQueuedBytes\")\n const maxStdin = limit(options.maxStdinBytes ?? 1024 * 1024, \"maxStdinBytes\")\n const sessions = new Set<DockerAttachment>()\n let sequence = 0\n const connect = (selection: AttachSelection, socket: Duplex, runtime: DockerRuntime) => {\n const { api, containerId, namespace, branch } = selection\n let closed = false,\n outputClosed = false,\n retired = false,\n inputEnded = false,\n queuedBytes = 0,\n inputSize = 0\n let input: Buffer[] = []\n let tail: Promise<void> = Promise.resolve()\n let releaseReady = (_ready: boolean) => {}\n const ready = new Promise<boolean>((resolve) => {\n releaseReady = resolve\n })\n const valid = () => !closed && api.generation === selection.generation\n const closeInput = () => {\n inputEnded = true\n if (!valid() || retired || !selection.stdin || !selection.stdinOnce) return\n const c = api.state.containers.get(containerId)\n if (c && !c.stdinClosed) {\n api.state.containers.update(containerId, { ...c, stdinClosed: true })\n runtime.checkpoint(namespace, branch)\n }\n }\n let detachInvalidation = () => {},\n detachExit = () => {}\n const release = () => {\n if (closed) return\n closed = true\n outputClosed = true\n releaseReady(false)\n sessions.delete(handle)\n detachInvalidation()\n detachExit()\n socket.off(\"data\", onData)\n socket.off(\"end\", onEnd)\n }\n const cancel = () => {\n closeInput()\n release()\n input = []\n inputSize = 0\n socket.destroy()\n }\n const send = (bytes: Uint8Array) =>\n new Promise<void>((resolve, reject) => {\n const close = () => {\n socket.off(\"close\", close)\n reject(new Error(\"Attachment closed\"))\n }\n if (!valid() || socket.destroyed) {\n close()\n return\n }\n socket.once(\"close\", close)\n socket.write(bytes, (error) => {\n socket.off(\"close\", close)\n if (error) reject(error)\n else resolve()\n })\n })\n const onData = (chunk: Buffer) => {\n if (!valid() || retired) return\n if (!selection.stdin || api.state.containers.get(containerId)?.stdinClosed) return\n if (inputSize + chunk.length > maxStdin) {\n cancel()\n return\n }\n input.push(Buffer.from(chunk))\n inputSize += chunk.length\n }\n const onEnd = () => {\n closeInput()\n if (selection.stdin && !selection.stdinOnce) void handle.end().catch(cancel)\n }\n const finish = async () => {\n await tail\n if (valid() && !socket.destroyed) socket.end()\n }\n const handle: DockerAttachment = {\n id: ++sequence,\n containerId,\n namespace,\n branch,\n get closed() {\n return closed\n },\n get stdinClosed() {\n return inputEnded || api.state.containers.get(containerId)?.stdinClosed === true\n },\n get queuedBytes() {\n return queuedBytes\n },\n async write(channel, data) {\n if (!valid() || outputClosed) throw new Error(\"Attachment closed\")\n if (channel !== \"stdout\" && channel !== \"stderr\")\n throw new TypeError(\"Unknown output channel\")\n if (!selection[channel]) return\n const size = data.byteLength + 8\n if (size + queuedBytes > maxQueued)\n throw new RangeError(\"Attachment output queue limit exceeded\")\n const frame = Buffer.allocUnsafe(size)\n frame[0] = channel === \"stdout\" ? 1 : 2\n frame.fill(0, 1, 4)\n frame.writeUInt32BE(data.byteLength, 4)\n frame.set(data, 8)\n queuedBytes += size\n const result = tail.then(async () => {\n if (!(await ready) || !valid()) throw new Error(\"Attachment closed\")\n for (let at = 0; at < frame.length; at += chunkBytes)\n await send(frame.subarray(at, at + chunkBytes))\n })\n tail = result.catch(() => {})\n try {\n await result\n } finally {\n queuedBytes -= size\n }\n },\n takeStdin() {\n const result = Buffer.concat(input, inputSize)\n input = []\n inputSize = 0\n return result\n },\n async end() {\n if (!valid() || outputClosed) return\n outputClosed = true\n closeInput()\n const c = api.state.containers.get(containerId)\n if (selection.stdinOnce && c && isRunning(c)) return\n await finish()\n },\n cancel,\n }\n socket.once(\"close\", () => {\n if (valid()) closeInput()\n release()\n })\n detachInvalidation = api.onInvalidate(cancel)\n detachExit = api.lifecycle.onExit((id) => {\n if (id === containerId) {\n retired = true\n input = []\n inputSize = 0\n inputEnded = true\n outputClosed = true\n void finish().catch(cancel)\n }\n })\n sessions.add(handle)\n if (!valid()) {\n cancel()\n }\n return {\n handle,\n start(head: Buffer) {\n if (!valid()) return\n socket.on(\"data\", onData)\n socket.once(\"end\", onEnd)\n if (head.length) onData(head)\n releaseReady(true)\n socket.resume()\n if (!selection.stdin && !selection.stdout && !selection.stderr)\n void handle.end().catch(cancel)\n },\n cancel,\n }\n }\n return { connect, list: () => [...sessions] }\n}\n", "import { lstat } from \"node:fs/promises\"\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport type { Socket } from \"node:net\"\nimport { isAbsolute } from \"node:path\"\nimport type { DockerRuntime } from \"./runtime.js\"\n\nexport type TransportOptions = {\n port?: number\n host?: string\n socketPath?: string\n /** Maximum buffered request bytes. Default 1 MiB. */\n maxBodyBytes?: number\n /** Deadline for receiving a body, independent of long-running responses. */\n bodyTimeoutMs?: number\n /** Maximum simultaneous owned connections. Default 128. */\n maxConnections?: number\n}\nconst positive = (value: number, name: string) => {\n if (!Number.isSafeInteger(value) || value <= 0)\n throw new RangeError(`${name} must be a positive integer`)\n return value\n}\n\nexport const listenDocker = async (\n runtime: DockerRuntime,\n options: TransportOptions,\n upgrade?: (\n req: IncomingMessage,\n socket: import(\"node:stream\").Duplex,\n head: Buffer,\n ) => Promise<void>,\n) => {\n const maxBody = positive(options.maxBodyBytes ?? 1024 * 1024, \"maxBodyBytes\")\n const bodyTimeout = positive(options.bodyTimeoutMs ?? 30_000, \"bodyTimeoutMs\")\n const maxConnections = positive(options.maxConnections ?? 128, \"maxConnections\")\n const path = options.socketPath\n if (path !== undefined) {\n if (!isAbsolute(path) || path.includes(\"\\0\") || Buffer.byteLength(path) > 103)\n throw new Error(\"socketPath must be an absolute Unix path of at most 103 bytes\")\n if (options.port !== undefined || options.host !== undefined)\n throw new Error(\"socketPath cannot be combined with port or host\")\n const existing = await lstat(path).then(\n () => true,\n (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return false\n throw error\n },\n )\n if (existing) throw new Error(\"Refusing existing socketPath\")\n }\n const sockets = new Set<Socket>()\n const handle = async (req: IncomingMessage, res: ServerResponse) => {\n const abort = new AbortController()\n const onClose = () => {\n if (!res.writableFinished) abort.abort()\n }\n res.once(\"close\", onClose)\n const reject = (status: number) => {\n res.writeHead(status, { \"content-type\": \"application/json\", connection: \"close\" })\n res.end(JSON.stringify({ message: `Mockingbird transport rejected request (${status})` }))\n }\n try {\n if (Number(req.headers[\"content-length\"]) > maxBody) {\n reject(413)\n return\n }\n const body = await new Promise<Buffer | undefined>((resolve, rejectBody) => {\n const chunks: Buffer[] = []\n let size = 0\n const cleanup = () => {\n clearTimeout(timer)\n req.off(\"data\", data)\n req.off(\"end\", end)\n req.off(\"error\", error)\n abort.signal.removeEventListener(\"abort\", canceled)\n }\n const canceled = () => {\n cleanup()\n rejectBody(new Error(\"Request disconnected\"))\n }\n const error = (e: Error) => {\n cleanup()\n rejectBody(e)\n }\n const end = () => {\n cleanup()\n resolve(Buffer.concat(chunks, size))\n }\n const data = (chunk: Buffer) => {\n size += chunk.length\n if (size > maxBody) {\n cleanup()\n req.pause()\n reject(413)\n resolve(undefined)\n return\n }\n chunks.push(chunk)\n }\n const timer = setTimeout(() => {\n cleanup()\n req.pause()\n reject(408)\n resolve(undefined)\n }, bodyTimeout)\n req.on(\"data\", data)\n req.once(\"end\", end)\n req.once(\"error\", error)\n abort.signal.addEventListener(\"abort\", canceled, { once: true })\n if (abort.signal.aborted) canceled()\n })\n if (body === undefined || abort.signal.aborted) return\n const headers = new Headers()\n for (let i = 0; i < req.rawHeaders.length; i += 2) {\n const name = req.rawHeaders[i],\n value = req.rawHeaders[i + 1]\n if (name !== undefined && value !== undefined) headers.append(name, value)\n }\n const method = req.method ?? \"GET\"\n const request = new Request(\n new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), \"http://docker.mock\"),\n {\n method,\n headers,\n signal: abort.signal,\n ...(method !== \"GET\" && method !== \"HEAD\" && body.length\n ? { body: new Uint8Array(body) }\n : {}),\n },\n )\n const response = await runtime.fetch(request)\n if (abort.signal.aborted) {\n await response.body?.cancel()\n return\n }\n const outgoing: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length) outgoing[\"set-cookie\"] = cookies\n res.writeHead(response.status, outgoing)\n if (!response.body) {\n res.end()\n return\n }\n res.flushHeaders()\n const reader = response.body.getReader()\n const cancel = () => {\n void reader.cancel().catch(() => {})\n }\n abort.signal.addEventListener(\"abort\", cancel, { once: true })\n try {\n while (!abort.signal.aborted) {\n const { done, value } = await reader.read()\n if (done) break\n if (!res.write(value))\n await new Promise<void>((resolve) => {\n const finish = () => {\n res.off(\"drain\", finish)\n res.off(\"close\", finish)\n resolve()\n }\n res.once(\"drain\", finish)\n res.once(\"close\", finish)\n if (res.destroyed) finish()\n })\n }\n if (!res.destroyed) res.end()\n } finally {\n abort.signal.removeEventListener(\"abort\", cancel)\n reader.releaseLock()\n }\n } catch (error) {\n if (\n (error as { code?: string })?.code === \"MOCKINGBIRD_DROP\" ||\n abort.signal.aborted ||\n res.headersSent\n )\n res.destroy()\n else reject(500)\n } finally {\n res.off(\"close\", onClose)\n }\n }\n const busy = new WeakSet<Socket>()\n const server = createServer(\n {\n maxHeaderSize: 16 * 1024,\n headersTimeout: 30_000,\n requestTimeout: 30_000,\n keepAliveTimeout: 5_000,\n },\n (req, res) => {\n // Sequential keep-alive is supported; pipelining cannot accumulate handlers.\n if (req.socket.destroyed || busy.has(req.socket)) {\n req.socket.destroy()\n return\n }\n busy.add(req.socket)\n const release = () => {\n busy.delete(req.socket)\n res.off(\"finish\", release)\n res.off(\"close\", release)\n }\n res.once(\"finish\", release)\n res.once(\"close\", release)\n void handle(req, res)\n },\n )\n if (upgrade)\n server.on(\"upgrade\", (req, socket, head) => {\n if (socket.destroyed || busy.has(socket as Socket) || !sockets.has(socket as Socket)) {\n socket.destroy()\n return\n }\n busy.add(socket as Socket)\n void upgrade(req, socket, head)\n })\n server.on(\"connection\", (socket) => {\n if (sockets.size >= maxConnections) {\n socket.destroy()\n return\n }\n sockets.add(socket)\n socket.once(\"close\", () => sockets.delete(socket))\n })\n server.once(\"close\", () => runtime.close())\n const host = options.host ?? \"127.0.0.1\"\n await new Promise<void>((resolve, reject) => {\n const error = (error: Error) => {\n server.off(\"listening\", ready)\n reject(error)\n }\n const ready = () => {\n server.off(\"error\", error)\n resolve()\n }\n server.once(\"error\", error)\n server.once(\"listening\", ready)\n if (path !== undefined) server.listen(path)\n else server.listen(options.port ?? 0, host)\n })\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : 0\n let closing: Promise<void> | undefined\n return {\n server,\n host,\n port,\n ...(path !== undefined ? { socketPath: path } : {}),\n url:\n path === undefined\n ? `http://${host.includes(\":\") ? `[${host}]` : host}:${port}`\n : \"http://docker.mock\",\n close: () =>\n (closing ??= new Promise<void>((resolve, reject) => {\n runtime.close()\n server.close((error) => {\n if (error && (error as NodeJS.ErrnoException).code !== \"ERR_SERVER_NOT_RUNNING\")\n reject(error)\n else resolve()\n })\n for (const socket of sockets) socket.destroy()\n })),\n }\n}\n", "/// <reference types=\"node\" />\nimport type { Listening, ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { type AttachHandshakeOptions, createAttachHandshake } from \"./attach.js\"\nimport { createRuntime, type DockerRuntime, type DockerRuntimeOptions } from \"./runtime.js\"\nimport type { AttachStreamOptions, DockerAttachment } from \"./streams.js\"\n\nexport type { AttachStreamOptions, DockerAttachment } from \"./streams.js\"\n\nimport { listenDocker, type TransportOptions } from \"./transport.js\"\n\nexport const DEFAULT_PORT = 8826\nexport type DockerServerOptions = Omit<DockerRuntimeOptions, \"onAttach\"> &\n TransportOptions & {\n attachHandshake?: AttachHandshakeOptions\n attachStreams?: AttachStreamOptions\n }\nexport type DockerServer = Listening & {\n runtime: DockerRuntime\n socketPath?: string\n attachments(): DockerAttachment[]\n}\n\n/** Node-only bounded HTTP transport over TCP or a test-owned Unix socket. */\nexport const createServer = async (options: DockerServerOptions = {}): Promise<DockerServer> => {\n const {\n port,\n host,\n socketPath,\n maxBodyBytes,\n bodyTimeoutMs,\n maxConnections,\n attachHandshake,\n attachStreams,\n ...rest\n } = options\n const attach = createAttachHandshake(attachHandshake, attachStreams)\n const runtime = createRuntime({ ...rest, onAttach: attach.prepare })\n try {\n const listening = await listenDocker(\n runtime,\n {\n ...(port !== undefined ? { port } : {}),\n ...(host !== undefined ? { host } : {}),\n ...(socketPath !== undefined ? { socketPath } : {}),\n ...(maxBodyBytes !== undefined ? { maxBodyBytes } : {}),\n ...(bodyTimeoutMs !== undefined ? { bodyTimeoutMs } : {}),\n ...(maxConnections !== undefined ? { maxConnections } : {}),\n },\n (req, socket, head) => attach.upgrade(runtime, req, socket, head),\n )\n return { ...listening, runtime, attachments: attach.attachments }\n } catch (error) {\n runtime.close()\n throw error\n }\n}\n\nexport const serveTarget: ServeTarget = {\n name: \"docker\",\n defaultPort: DEFAULT_PORT,\n create: (_values, common) =>\n createRuntime({\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n }),\n banner: () => [\"Docker lifecycle: explicit simulated completion; no image execution or attach\"],\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { AsyncLocalStorage } from \"node:async_hooks\"\nimport type { IncomingMessage } from \"node:http\"\nimport { STATUS_CODES } from \"node:http\"\nimport type { Duplex } from \"node:stream\"\nimport { annotateResponse, jsonRes } from \"@crvouga/mockingbird-service\"\nimport type { DockerAPI } from \"./index.js\"\nimport { booleanQuery } from \"./observations.js\"\nimport type { DockerRuntime } from \"./runtime.js\"\nimport { DockerInputError } from \"./state.js\"\nimport { type AttachSelection, type AttachStreamOptions, createAttachStreams } from \"./streams.js\"\n\nexport type AttachHandshakeOptions = {\n /** Deterministic mock writes; network packet boundaries remain OS-controlled. */\n chunkBytes?: number\n /** Synthetic already-framed output delivered with the final header fragment. */\n initialStreamBytes?: Uint8Array\n}\ntype Admission = { selection?: AttachSelection }\nconst header = Buffer.from(\n \"HTTP/1.1 101 UPGRADED\\r\\nContent-Type: application/vnd.docker.multiplexed-stream\\r\\nConnection: Upgrade\\r\\nUpgrade: tcp\\r\\n\\r\\n\",\n)\n\nexport const createAttachHandshake = (\n options: AttachHandshakeOptions = {},\n streamOptions: AttachStreamOptions = {},\n) => {\n const streams = createAttachStreams(streamOptions)\n const chunkBytes = options.chunkBytes ?? header.length\n if (!Number.isSafeInteger(chunkBytes) || chunkBytes < 1 || chunkBytes > 4096)\n throw new RangeError(\"attach chunkBytes must be 1..4096\")\n if ((options.initialStreamBytes?.byteLength ?? 0) > 1024 * 1024)\n throw new RangeError(\"initialStreamBytes exceeds 1 MiB\")\n const first = Buffer.from(options.initialStreamBytes ?? [])\n const admission = new AsyncLocalStorage<Admission>()\n const prepare = (api: DockerAPI, request: Request): Response => {\n const context = admission.getStore()\n if (!context) return jsonRes(501, { message: \"Mockingbird: attach requires Node HTTP upgrade\" })\n const url = new URL(request.url)\n if (\n !booleanQuery(url, \"stream\") ||\n booleanQuery(url, \"logs\") ||\n url.searchParams.has(\"detachKeys\") ||\n [...url.searchParams.keys()].some(\n (key) => ![\"stream\", \"logs\", \"stdin\", \"stdout\", \"stderr\"].includes(key),\n )\n )\n return jsonRes(501, { message: \"Mockingbird: attach mode is not implemented\" })\n const match = /^\\/containers\\/([^/]+)\\/attach$/.exec(url.pathname)\n if (!match?.[1]) return jsonRes(404, { message: \"page not found\" })\n let name: string\n try {\n name = decodeURIComponent(match[1])\n } catch {\n return jsonRes(400, { message: \"invalid container name encoding\" })\n }\n try {\n const c = api.state.find(name)\n if (c.status === \"paused\")\n throw new DockerInputError(\n 409,\n `container ${name} is paused, unpause the container before attach`,\n )\n if (c.status === \"restarting\")\n throw new DockerInputError(\n 409,\n `container ${name} is restarting, wait until the container is running`,\n )\n if (c.config?.Tty === true)\n return jsonRes(501, { message: \"Mockingbird: TTY attach is not implemented\" })\n context.selection = {\n api,\n generation: api.generation,\n containerId: c.id,\n namespace: request.headers.get(\"x-mockingbird-namespace\") ?? \"default\",\n branch: request.headers.get(\"x-mockingbird-branch\") ?? \"main\",\n stdin: booleanQuery(url, \"stdin\") && c.config?.OpenStdin === true,\n stdinOnce: c.config?.StdinOnce === true,\n stdout: booleanQuery(url, \"stdout\"),\n stderr: booleanQuery(url, \"stderr\"),\n }\n return annotateResponse(new Response(null), { wireStatus: 101, ids: { containerId: c.id } })\n } catch (error) {\n if (error instanceof DockerInputError)\n return new Response(`${error.message}\\r\\n`, {\n status: error.status,\n headers: { \"content-type\": \"application/vnd.docker.raw-stream\" },\n })\n throw error\n }\n }\n const write = (socket: Duplex, bytes: Uint8Array) =>\n new Promise<boolean>((resolve) => {\n const finish = (ok: boolean) => {\n socket.off(\"close\", closed)\n resolve(ok)\n }\n const closed = () => finish(false)\n socket.once(\"close\", closed)\n if (socket.destroyed) {\n finish(false)\n return\n }\n socket.write(bytes, (error) => finish(!error))\n })\n const upgrade = async (\n runtime: DockerRuntime,\n req: IncomingMessage,\n socket: Duplex,\n head: Buffer,\n ) => {\n socket.on(\"error\", () => socket.destroy())\n socket.pause()\n try {\n if (\n req.method !== \"POST\" ||\n req.headers.upgrade?.toLowerCase() !== \"tcp\" ||\n !req.headers.connection\n ?.toLowerCase()\n .split(/\\s*,\\s*/)\n .includes(\"upgrade\") ||\n Number(req.headers[\"content-length\"] ?? 0) !== 0 ||\n req.headers[\"transfer-encoding\"] !== undefined\n ) {\n await write(\n socket,\n Buffer.from(\n \"HTTP/1.1 501 Not Implemented\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n\",\n ),\n )\n socket.end()\n return\n }\n const target = new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), \"http://docker.mock\")\n if (\n !/^(?:\\/__admin\\/ns\\/[^/]+)?(?:\\/v[0-9]+\\.[0-9]+)?\\/containers\\/[^/]+\\/attach$/.test(\n target.pathname,\n )\n ) {\n await write(\n socket,\n Buffer.from(\"HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\nContent-Length: 0\\r\\n\\r\\n\"),\n )\n socket.end()\n return\n }\n const headers = new Headers()\n for (let i = 0; i < req.rawHeaders.length; i += 2) {\n const name = req.rawHeaders[i],\n value = req.rawHeaders[i + 1]\n if (name && value !== undefined) headers.append(name, value)\n }\n const request = new Request(\n new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), \"http://docker.mock\"),\n { method: \"POST\", headers },\n )\n const context: Admission = {}\n const response = await admission.run(context, () => runtime.fetch(request))\n if (socket.destroyed) return\n if (!context.selection || response.status !== 200) {\n const body = Buffer.from(await response.arrayBuffer())\n const type = response.headers.get(\"content-type\") ?? \"application/json\"\n await write(\n socket,\n Buffer.concat([\n Buffer.from(\n `HTTP/1.1 ${response.status} ${STATUS_CODES[response.status] ?? \"Error\"}\\r\\nContent-Type: ${type}\\r\\nConnection: close\\r\\nContent-Length: ${body.length}\\r\\n\\r\\n`,\n ),\n body,\n ]),\n )\n socket.end()\n return\n }\n const session = streams.connect(context.selection, socket, runtime)\n for (let offset = 0; offset < header.length; offset += chunkBytes) {\n const end = Math.min(offset + chunkBytes, header.length)\n const chunk = header.subarray(offset, end)\n if (!(await write(socket, end === header.length ? Buffer.concat([chunk, first]) : chunk)))\n return\n if (end < header.length) await new Promise<void>((resolve) => setImmediate(resolve))\n }\n session.start(head)\n } catch {\n socket.destroy()\n }\n }\n return { prepare, upgrade, attachments: streams.list }\n}\n", "import type { Duplex } from \"node:stream\"\nimport type { DockerAPI } from \"./index.js\"\nimport type { DockerRuntime } from \"./runtime.js\"\nimport { isRunning } from \"./state.js\"\n\nexport type AttachStreamOptions = {\n frameChunkBytes?: number\n maxQueuedBytes?: number\n maxStdinBytes?: number\n}\nexport type AttachSelection = {\n api: DockerAPI\n generation: number\n containerId: string\n namespace: string\n branch: string\n stdin: boolean\n stdout: boolean\n stderr: boolean\n stdinOnce: boolean\n}\n/** Node-owned live handle. Retaining one across restore never addresses a successor. */\nexport type DockerAttachment = {\n readonly id: number\n readonly containerId: string\n readonly namespace: string\n readonly branch: string\n readonly closed: boolean\n readonly stdinClosed: boolean\n readonly queuedBytes: number\n write(channel: \"stdout\" | \"stderr\", data: Uint8Array): Promise<void>\n takeStdin(): Uint8Array\n end(): Promise<void>\n cancel(): void\n}\nconst limit = (value: number, name: string) => {\n if (!Number.isSafeInteger(value) || value < 1 || value > 16 * 1024 * 1024)\n throw new RangeError(`${name} must be 1..16777216`)\n return value\n}\nexport const createAttachStreams = (options: AttachStreamOptions = {}) => {\n const chunkBytes = limit(options.frameChunkBytes ?? 64 * 1024, \"frameChunkBytes\")\n const maxQueued = limit(options.maxQueuedBytes ?? 1024 * 1024, \"maxQueuedBytes\")\n const maxStdin = limit(options.maxStdinBytes ?? 1024 * 1024, \"maxStdinBytes\")\n const sessions = new Set<DockerAttachment>()\n let sequence = 0\n const connect = (selection: AttachSelection, socket: Duplex, runtime: DockerRuntime) => {\n const { api, containerId, namespace, branch } = selection\n let closed = false,\n outputClosed = false,\n retired = false,\n inputEnded = false,\n queuedBytes = 0,\n inputSize = 0\n let input: Buffer[] = []\n let tail: Promise<void> = Promise.resolve()\n let releaseReady = (_ready: boolean) => {}\n const ready = new Promise<boolean>((resolve) => {\n releaseReady = resolve\n })\n const valid = () => !closed && api.generation === selection.generation\n const closeInput = () => {\n inputEnded = true\n if (!valid() || retired || !selection.stdin || !selection.stdinOnce) return\n const c = api.state.containers.get(containerId)\n if (c && !c.stdinClosed) {\n api.state.containers.update(containerId, { ...c, stdinClosed: true })\n runtime.checkpoint(namespace, branch)\n }\n }\n let detachInvalidation = () => {},\n detachExit = () => {}\n const release = () => {\n if (closed) return\n closed = true\n outputClosed = true\n releaseReady(false)\n sessions.delete(handle)\n detachInvalidation()\n detachExit()\n socket.off(\"data\", onData)\n socket.off(\"end\", onEnd)\n }\n const cancel = () => {\n closeInput()\n release()\n input = []\n inputSize = 0\n socket.destroy()\n }\n const send = (bytes: Uint8Array) =>\n new Promise<void>((resolve, reject) => {\n const close = () => {\n socket.off(\"close\", close)\n reject(new Error(\"Attachment closed\"))\n }\n if (!valid() || socket.destroyed) {\n close()\n return\n }\n socket.once(\"close\", close)\n socket.write(bytes, (error) => {\n socket.off(\"close\", close)\n if (error) reject(error)\n else resolve()\n })\n })\n const onData = (chunk: Buffer) => {\n if (!valid() || retired) return\n if (!selection.stdin || api.state.containers.get(containerId)?.stdinClosed) return\n if (inputSize + chunk.length > maxStdin) {\n cancel()\n return\n }\n input.push(Buffer.from(chunk))\n inputSize += chunk.length\n }\n const onEnd = () => {\n closeInput()\n if (selection.stdin && !selection.stdinOnce) void handle.end().catch(cancel)\n }\n const finish = async () => {\n await tail\n if (valid() && !socket.destroyed) socket.end()\n }\n const handle: DockerAttachment = {\n id: ++sequence,\n containerId,\n namespace,\n branch,\n get closed() {\n return closed\n },\n get stdinClosed() {\n return inputEnded || api.state.containers.get(containerId)?.stdinClosed === true\n },\n get queuedBytes() {\n return queuedBytes\n },\n async write(channel, data) {\n if (!valid() || outputClosed) throw new Error(\"Attachment closed\")\n if (channel !== \"stdout\" && channel !== \"stderr\")\n throw new TypeError(\"Unknown output channel\")\n if (!selection[channel]) return\n const size = data.byteLength + 8\n if (size + queuedBytes > maxQueued)\n throw new RangeError(\"Attachment output queue limit exceeded\")\n const frame = Buffer.allocUnsafe(size)\n frame[0] = channel === \"stdout\" ? 1 : 2\n frame.fill(0, 1, 4)\n frame.writeUInt32BE(data.byteLength, 4)\n frame.set(data, 8)\n queuedBytes += size\n const result = tail.then(async () => {\n if (!(await ready) || !valid()) throw new Error(\"Attachment closed\")\n for (let at = 0; at < frame.length; at += chunkBytes)\n await send(frame.subarray(at, at + chunkBytes))\n })\n tail = result.catch(() => {})\n try {\n await result\n } finally {\n queuedBytes -= size\n }\n },\n takeStdin() {\n const result = Buffer.concat(input, inputSize)\n input = []\n inputSize = 0\n return result\n },\n async end() {\n if (!valid() || outputClosed) return\n outputClosed = true\n closeInput()\n const c = api.state.containers.get(containerId)\n if (selection.stdinOnce && c && isRunning(c)) return\n await finish()\n },\n cancel,\n }\n socket.once(\"close\", () => {\n if (valid()) closeInput()\n release()\n })\n detachInvalidation = api.onInvalidate(cancel)\n detachExit = api.lifecycle.onExit((id) => {\n if (id === containerId) {\n retired = true\n input = []\n inputSize = 0\n inputEnded = true\n outputClosed = true\n void finish().catch(cancel)\n }\n })\n sessions.add(handle)\n if (!valid()) {\n cancel()\n }\n return {\n handle,\n start(head: Buffer) {\n if (!valid()) return\n socket.on(\"data\", onData)\n socket.once(\"end\", onEnd)\n if (head.length) onData(head)\n releaseReady(true)\n socket.resume()\n if (!selection.stdin && !selection.stdout && !selection.stderr)\n void handle.end().catch(cancel)\n },\n cancel,\n }\n }\n return { connect, list: () => [...sessions] }\n}\n", "import { lstat } from \"node:fs/promises\"\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\"\nimport type { Socket } from \"node:net\"\nimport { isAbsolute } from \"node:path\"\nimport type { DockerRuntime } from \"./runtime.js\"\n\nexport type TransportOptions = {\n port?: number\n host?: string\n socketPath?: string\n /** Maximum buffered request bytes. Default 1 MiB. */\n maxBodyBytes?: number\n /** Deadline for receiving a body, independent of long-running responses. */\n bodyTimeoutMs?: number\n /** Maximum simultaneous owned connections. Default 128. */\n maxConnections?: number\n}\nconst positive = (value: number, name: string) => {\n if (!Number.isSafeInteger(value) || value <= 0)\n throw new RangeError(`${name} must be a positive integer`)\n return value\n}\n\nexport const listenDocker = async (\n runtime: DockerRuntime,\n options: TransportOptions,\n upgrade?: (\n req: IncomingMessage,\n socket: import(\"node:stream\").Duplex,\n head: Buffer,\n ) => Promise<void>,\n) => {\n const maxBody = positive(options.maxBodyBytes ?? 1024 * 1024, \"maxBodyBytes\")\n const bodyTimeout = positive(options.bodyTimeoutMs ?? 30_000, \"bodyTimeoutMs\")\n const maxConnections = positive(options.maxConnections ?? 128, \"maxConnections\")\n const path = options.socketPath\n if (path !== undefined) {\n if (!isAbsolute(path) || path.includes(\"\\0\") || Buffer.byteLength(path) > 103)\n throw new Error(\"socketPath must be an absolute Unix path of at most 103 bytes\")\n if (options.port !== undefined || options.host !== undefined)\n throw new Error(\"socketPath cannot be combined with port or host\")\n const existing = await lstat(path).then(\n () => true,\n (error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return false\n throw error\n },\n )\n if (existing) throw new Error(\"Refusing existing socketPath\")\n }\n const sockets = new Set<Socket>()\n const handle = async (req: IncomingMessage, res: ServerResponse) => {\n const abort = new AbortController()\n const onClose = () => {\n if (!res.writableFinished) abort.abort()\n }\n res.once(\"close\", onClose)\n const reject = (status: number) => {\n res.writeHead(status, { \"content-type\": \"application/json\", connection: \"close\" })\n res.end(JSON.stringify({ message: `Mockingbird transport rejected request (${status})` }))\n }\n try {\n if (Number(req.headers[\"content-length\"]) > maxBody) {\n reject(413)\n return\n }\n const body = await new Promise<Buffer | undefined>((resolve, rejectBody) => {\n const chunks: Buffer[] = []\n let size = 0\n const cleanup = () => {\n clearTimeout(timer)\n req.off(\"data\", data)\n req.off(\"end\", end)\n req.off(\"error\", error)\n abort.signal.removeEventListener(\"abort\", canceled)\n }\n const canceled = () => {\n cleanup()\n rejectBody(new Error(\"Request disconnected\"))\n }\n const error = (e: Error) => {\n cleanup()\n rejectBody(e)\n }\n const end = () => {\n cleanup()\n resolve(Buffer.concat(chunks, size))\n }\n const data = (chunk: Buffer) => {\n size += chunk.length\n if (size > maxBody) {\n cleanup()\n req.pause()\n reject(413)\n resolve(undefined)\n return\n }\n chunks.push(chunk)\n }\n const timer = setTimeout(() => {\n cleanup()\n req.pause()\n reject(408)\n resolve(undefined)\n }, bodyTimeout)\n req.on(\"data\", data)\n req.once(\"end\", end)\n req.once(\"error\", error)\n abort.signal.addEventListener(\"abort\", canceled, { once: true })\n if (abort.signal.aborted) canceled()\n })\n if (body === undefined || abort.signal.aborted) return\n const headers = new Headers()\n for (let i = 0; i < req.rawHeaders.length; i += 2) {\n const name = req.rawHeaders[i],\n value = req.rawHeaders[i + 1]\n if (name !== undefined && value !== undefined) headers.append(name, value)\n }\n const method = req.method ?? \"GET\"\n const request = new Request(\n new URL((req.url ?? \"/\").replace(/^\\/+/, \"/\"), \"http://docker.mock\"),\n {\n method,\n headers,\n signal: abort.signal,\n ...(method !== \"GET\" && method !== \"HEAD\" && body.length\n ? { body: new Uint8Array(body) }\n : {}),\n },\n )\n const response = await runtime.fetch(request)\n if (abort.signal.aborted) {\n await response.body?.cancel()\n return\n }\n const outgoing: Record<string, string | string[]> = Object.fromEntries(response.headers)\n const cookies = response.headers.getSetCookie()\n if (cookies.length) outgoing[\"set-cookie\"] = cookies\n res.writeHead(response.status, outgoing)\n if (!response.body) {\n res.end()\n return\n }\n res.flushHeaders()\n const reader = response.body.getReader()\n const cancel = () => {\n void reader.cancel().catch(() => {})\n }\n abort.signal.addEventListener(\"abort\", cancel, { once: true })\n try {\n while (!abort.signal.aborted) {\n const { done, value } = await reader.read()\n if (done) break\n if (!res.write(value))\n await new Promise<void>((resolve) => {\n const finish = () => {\n res.off(\"drain\", finish)\n res.off(\"close\", finish)\n resolve()\n }\n res.once(\"drain\", finish)\n res.once(\"close\", finish)\n if (res.destroyed) finish()\n })\n }\n if (!res.destroyed) res.end()\n } finally {\n abort.signal.removeEventListener(\"abort\", cancel)\n reader.releaseLock()\n }\n } catch (error) {\n if (\n (error as { code?: string })?.code === \"MOCKINGBIRD_DROP\" ||\n abort.signal.aborted ||\n res.headersSent\n )\n res.destroy()\n else reject(500)\n } finally {\n res.off(\"close\", onClose)\n }\n }\n const busy = new WeakSet<Socket>()\n const server = createServer(\n {\n maxHeaderSize: 16 * 1024,\n headersTimeout: 30_000,\n requestTimeout: 30_000,\n keepAliveTimeout: 5_000,\n },\n (req, res) => {\n // Sequential keep-alive is supported; pipelining cannot accumulate handlers.\n if (req.socket.destroyed || busy.has(req.socket)) {\n req.socket.destroy()\n return\n }\n busy.add(req.socket)\n const release = () => {\n busy.delete(req.socket)\n res.off(\"finish\", release)\n res.off(\"close\", release)\n }\n res.once(\"finish\", release)\n res.once(\"close\", release)\n void handle(req, res)\n },\n )\n if (upgrade)\n server.on(\"upgrade\", (req, socket, head) => {\n if (socket.destroyed || busy.has(socket as Socket) || !sockets.has(socket as Socket)) {\n socket.destroy()\n return\n }\n busy.add(socket as Socket)\n void upgrade(req, socket, head)\n })\n server.on(\"connection\", (socket) => {\n if (sockets.size >= maxConnections) {\n socket.destroy()\n return\n }\n sockets.add(socket)\n socket.once(\"close\", () => sockets.delete(socket))\n })\n server.once(\"close\", () => runtime.close())\n const host = options.host ?? \"127.0.0.1\"\n await new Promise<void>((resolve, reject) => {\n const error = (error: Error) => {\n server.off(\"listening\", ready)\n reject(error)\n }\n const ready = () => {\n server.off(\"error\", error)\n resolve()\n }\n server.once(\"error\", error)\n server.once(\"listening\", ready)\n if (path !== undefined) server.listen(path)\n else server.listen(options.port ?? 0, host)\n })\n const address = server.address()\n const port = typeof address === \"object\" && address !== null ? address.port : 0\n let closing: Promise<void> | undefined\n return {\n server,\n host,\n port,\n ...(path !== undefined ? { socketPath: path } : {}),\n url:\n path === undefined\n ? `http://${host.includes(\":\") ? `[${host}]` : host}:${port}`\n : \"http://docker.mock\",\n close: () =>\n (closing ??= new Promise<void>((resolve, reject) => {\n runtime.close()\n server.close((error) => {\n if (error && (error as NodeJS.ErrnoException).code !== \"ERR_SERVER_NOT_RUNNING\")\n reject(error)\n else resolve()\n })\n for (const socket of sockets) socket.destroy()\n })),\n }\n}\n", "/// <reference types=\"node\" />\nimport type { Listening, ServeTarget } from \"@crvouga/mockingbird-adapter-node\"\nimport { type AttachHandshakeOptions, createAttachHandshake } from \"./attach.js\"\nimport { createRuntime, type DockerRuntime, type DockerRuntimeOptions } from \"./runtime.js\"\nimport type { AttachStreamOptions, DockerAttachment } from \"./streams.js\"\n\nexport type { AttachStreamOptions, DockerAttachment } from \"./streams.js\"\n\nimport { listenDocker, type TransportOptions } from \"./transport.js\"\n\nexport const DEFAULT_PORT = 8826\nexport type DockerServerOptions = Omit<DockerRuntimeOptions, \"onAttach\"> &\n TransportOptions & {\n attachHandshake?: AttachHandshakeOptions\n attachStreams?: AttachStreamOptions\n }\nexport type DockerServer = Listening & {\n runtime: DockerRuntime\n socketPath?: string\n attachments(): DockerAttachment[]\n}\n\n/** Node-only bounded HTTP transport over TCP or a test-owned Unix socket. */\nexport const createServer = async (options: DockerServerOptions = {}): Promise<DockerServer> => {\n const {\n port,\n host,\n socketPath,\n maxBodyBytes,\n bodyTimeoutMs,\n maxConnections,\n attachHandshake,\n attachStreams,\n ...rest\n } = options\n const attach = createAttachHandshake(attachHandshake, attachStreams)\n const runtime = createRuntime({ ...rest, onAttach: attach.prepare })\n try {\n const listening = await listenDocker(\n runtime,\n {\n ...(port !== undefined ? { port } : {}),\n ...(host !== undefined ? { host } : {}),\n ...(socketPath !== undefined ? { socketPath } : {}),\n ...(maxBodyBytes !== undefined ? { maxBodyBytes } : {}),\n ...(bodyTimeoutMs !== undefined ? { bodyTimeoutMs } : {}),\n ...(maxConnections !== undefined ? { maxConnections } : {}),\n },\n (req, socket, head) => attach.upgrade(runtime, req, socket, head),\n )\n return { ...listening, runtime, attachments: attach.attachments }\n } catch (error) {\n runtime.close()\n throw error\n }\n}\n\nexport const serveTarget: ServeTarget = {\n name: \"docker\",\n defaultPort: DEFAULT_PORT,\n create: (_values, common) =>\n createRuntime({\n ...(common.adminKey !== undefined ? { adminKey: common.adminKey } : {}),\n ...(common.adminControls !== undefined ? { adminControls: common.adminControls } : {}),\n ...(common.seed !== undefined ? { seed: common.seed } : {}),\n ...(common.onLog ? { onLog: common.onLog } : {}),\n }),\n banner: () => [\"Docker lifecycle: explicit simulated completion; no image execution or attach\"],\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["error", "createServer"]
7
7
  }
package/dist/cli.js CHANGED
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  serveTarget
4
- } from "./chunk-TR75ZFCZ.js";
4
+ } from "./chunk-Y4JH3Q6Z.js";
5
5
  import {
6
6
  createClock,
7
+ normalizeBootControls,
7
8
  parseDuration,
8
- resolveAdminPrefix
9
- } from "./chunk-5I542RFL.js";
9
+ parseFaultHeader,
10
+ resolveAdminPrefix,
11
+ restoreClock
12
+ } from "./chunk-NSZYARVW.js";
10
13
 
11
14
  // ../../adapters/node/dist/cli.js
12
15
  import { readFile as readFile2 } from "node:fs/promises";
@@ -53,6 +56,8 @@ var serve = async (api, options = {}) => {
53
56
  req.socket.destroy();
54
57
  return;
55
58
  }
59
+ if (aborted.signal.aborted)
60
+ return;
56
61
  res.writeHead(500, { "content-type": "application/json" });
57
62
  res.end(JSON.stringify({
58
63
  error: {
@@ -84,6 +89,7 @@ var serve = async (api, options = {}) => {
84
89
  }
85
90
  res.end();
86
91
  } catch {
92
+ await new Promise((resolve2) => setImmediate(resolve2));
87
93
  res.destroy();
88
94
  } finally {
89
95
  reader.releaseLock();
@@ -167,11 +173,11 @@ var httpChild = async (target, entry, options) => {
167
173
  adminKey: entry.adminKey,
168
174
  seed: entry.seed,
169
175
  onLog: options.onLog,
176
+ ...entry.adminControls !== void 0 ? { adminControls: entry.adminControls } : {},
170
177
  ...entry.fixtures !== void 0 ? { fixtures: entry.fixtures } : {},
171
178
  ...entry.baseUrl !== void 0 ? { baseUrl: entry.baseUrl } : {}
172
179
  });
173
180
  const adminPrefix = resolveAdminPrefix(entry.adminPrefix);
174
- runtime.isolateNamespaces();
175
181
  const locked = /* @__PURE__ */ new Set();
176
182
  const active = /* @__PURE__ */ new Map();
177
183
  const api = {
@@ -433,19 +439,12 @@ async function startFleet(config, options) {
433
439
  return json(400, { error: "freeze must be boolean" });
434
440
  const response2 = await transaction(namespace, selected, async (_name, child) => {
435
441
  const childClock = child.clock(namespace);
436
- childClock.freeze();
437
- childClock.set(target);
438
- if (!frozen)
439
- childClock.unfreeze();
442
+ restoreClock(childClock, { now: target, frozen });
440
443
  await child.settle?.(namespace);
441
444
  return childClock.state();
442
445
  });
443
- if (response2.ok) {
444
- current.freeze();
445
- current.set(target);
446
- if (!frozen)
447
- current.unfreeze();
448
- }
446
+ if (response2.ok)
447
+ restoreClock(current, { now: target, frozen });
449
448
  return response2;
450
449
  }
451
450
  if (!route)
@@ -464,13 +463,8 @@ async function startFleet(config, options) {
464
463
  return json(404, { error: "no snapshot in this namespace" });
465
464
  const restored = new Map([...point.values].map(([name]) => [name, children.get(name)]));
466
465
  const response2 = await transaction(namespace, restored, async (name, child) => child.restore(namespace, point.values.get(name)));
467
- if (response2.ok) {
468
- const current = clock(namespace);
469
- current.freeze();
470
- current.set(point.clock.now);
471
- if (!point.clock.frozen)
472
- current.unfreeze();
473
- }
466
+ if (response2.ok)
467
+ restoreClock(clock(namespace), point.clock);
474
468
  return response2;
475
469
  }
476
470
  const snapshotId = randomUUID();
@@ -496,6 +490,8 @@ async function startFleet(config, options) {
496
490
  for (const [name, entry] of Object.entries(config.services)) {
497
491
  if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name))
498
492
  throw new Error("invalid service name");
493
+ if (config.adminControls !== void 0 || entry.adminControls !== void 0)
494
+ normalizeBootControls({ ...config.adminControls, ...entry.adminControls }, `services.${name}.adminControls`);
499
495
  try {
500
496
  const target = await options.load(name);
501
497
  if (entry.protocol && entry.protocol !== ("protocol" in target ? target.protocol : "http"))
@@ -504,7 +500,9 @@ async function startFleet(config, options) {
504
500
  const child = "start" in target ? await target.start(entry) : await httpChild(target, {
505
501
  ...entry,
506
502
  adminPrefix: resolveAdminPrefix(entry.adminPrefix ?? adminPrefix),
507
- ...adminKey !== void 0 ? { adminKey } : {}
503
+ ...adminKey !== void 0 ? { adminKey } : {},
504
+ // Fleet-wide controls, with the service's own keys replacing them.
505
+ ...config.adminControls !== void 0 || entry.adminControls !== void 0 ? { adminControls: { ...config.adminControls, ...entry.adminControls } } : {}
508
506
  }, options);
509
507
  children.set(name, child);
510
508
  if (!await child.ready())
@@ -637,7 +635,51 @@ var COMMON_SERVE_OPTIONS = {
637
635
  value: "<key>",
638
636
  description: "Require x-mockingbird-admin-key on /__admin/* (env MOCKINGBIRD_ADMIN_KEY)"
639
637
  },
640
- seed: { type: "string", value: "<seed>", description: "Seed for every random choice" },
638
+ seed: {
639
+ type: "string",
640
+ value: "<seed>",
641
+ description: "Seed for every random choice (env MOCKINGBIRD_SEED)"
642
+ },
643
+ "admin-controls": {
644
+ type: "string",
645
+ value: "<json|file>",
646
+ description: "Admin controls to boot with, as JSON or a JSON file: clock, latency, faults, snapshot, namespaces (env MOCKINGBIRD_ADMIN_CONTROLS)"
647
+ },
648
+ clock: {
649
+ type: "string",
650
+ value: "<instant>",
651
+ description: "Start the clock at this instant (epoch ms or ISO-8601)"
652
+ },
653
+ freeze: { type: "boolean", description: "Freeze the clock: it moves only when told to" },
654
+ "auto-advance": {
655
+ type: "string",
656
+ value: "<duration>",
657
+ description: "Step the clock this much before every request (e.g. 1s)"
658
+ },
659
+ latency: {
660
+ type: "string",
661
+ value: "<duration>",
662
+ description: "Baseline response time for every request (e.g. 250ms)"
663
+ },
664
+ jitter: {
665
+ type: "string",
666
+ value: "<duration>",
667
+ description: "Seeded extra latency between 0 and this"
668
+ },
669
+ "virtual-time": {
670
+ type: "boolean",
671
+ description: "Spend latency, delays and timeouts on the mock clock instead of really waiting"
672
+ },
673
+ fault: {
674
+ type: "string",
675
+ value: "<fault>",
676
+ description: 'A fault to boot with, as in x-mockingbird-fault: 503, rate_limited, "status=503; delay=2s"'
677
+ },
678
+ snapshot: {
679
+ type: "string",
680
+ value: "<file>",
681
+ description: "Start from a snapshot document (GET /__admin/snapshots/:id)"
682
+ },
641
683
  log: {
642
684
  type: "string",
643
685
  value: "<pretty|json|off>",
@@ -684,6 +726,68 @@ var formatLog = (format) => {
684
726
  };
685
727
  };
686
728
  var asString = (value) => typeof value === "string" ? value : void 0;
729
+ var readJsonFile = async (path, label) => {
730
+ try {
731
+ return JSON.parse(await readFile2(path, "utf8"));
732
+ } catch (error) {
733
+ throw new Error(`${label}: cannot read ${path}: ${error instanceof Error ? error.message : error}`);
734
+ }
735
+ };
736
+ var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
737
+ var resolveServeControls = async (values, env = process.env, base) => {
738
+ const source = asString(values["admin-controls"]) ?? env.MOCKINGBIRD_ADMIN_CONTROLS;
739
+ let document = isObject2(base) ? { ...base } : {};
740
+ if (source !== void 0 && source.trim() !== "") {
741
+ let parsed;
742
+ if (source.trim().startsWith("{")) {
743
+ try {
744
+ parsed = JSON.parse(source);
745
+ } catch {
746
+ throw new Error("--admin-controls: not valid JSON");
747
+ }
748
+ } else
749
+ parsed = await readJsonFile(source, "--admin-controls");
750
+ if (!isObject2(parsed))
751
+ throw new Error("--admin-controls: expected a JSON object");
752
+ document = { ...document, ...parsed };
753
+ }
754
+ const clock = isObject2(document.clock) ? { ...document.clock } : {};
755
+ const now = asString(values.clock);
756
+ if (now !== void 0)
757
+ clock.now = now;
758
+ if (values.freeze === true)
759
+ clock.frozen = true;
760
+ const autoAdvance = asString(values["auto-advance"]);
761
+ if (autoAdvance !== void 0)
762
+ clock.autoAdvance = autoAdvance;
763
+ if (Object.keys(clock).length > 0)
764
+ document.clock = clock;
765
+ const delay = asString(values.latency);
766
+ const jitter = asString(values.jitter);
767
+ if (delay !== void 0 || jitter !== void 0) {
768
+ const latency = isObject2(document.latency) ? { ...document.latency } : document.latency !== void 0 ? { delay: document.latency } : {};
769
+ if (delay !== void 0)
770
+ latency.delay = delay;
771
+ if (jitter !== void 0)
772
+ latency.jitter = jitter;
773
+ document.latency = latency;
774
+ }
775
+ if (values["virtual-time"] === true)
776
+ document.virtualTime = true;
777
+ const fault = asString(values.fault);
778
+ if (fault !== void 0)
779
+ document.faults = [
780
+ ...Array.isArray(document.faults) ? document.faults : [],
781
+ parseFaultHeader(fault, "--fault")
782
+ ];
783
+ const snapshot = asString(values.snapshot);
784
+ if (snapshot !== void 0)
785
+ document.snapshot = await readJsonFile(snapshot, "--snapshot");
786
+ if (Object.keys(document).length === 0)
787
+ return void 0;
788
+ normalizeBootControls(document, "adminControls");
789
+ return document;
790
+ };
687
791
  var loadTarget = async (name, own) => {
688
792
  if (name === own.name)
689
793
  return own;
@@ -710,7 +814,8 @@ var start = async (target, values, config) => {
710
814
  adminPrefix,
711
815
  adminKey: config.adminKey,
712
816
  seed: config.seed,
713
- onLog: formatLog(config.log)
817
+ onLog: formatLog(config.log),
818
+ ...config.adminControls !== void 0 ? { adminControls: config.adminControls } : {}
714
819
  });
715
820
  const listening = await listen(runtime, { port: config.port, host: config.host });
716
821
  target.listening?.(listening);
@@ -750,6 +855,9 @@ var serveCommand = (target) => ({
750
855
  const adminKey2 = config.adminKey ?? asString(values["admin-key"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY;
751
856
  if (adminKey2 !== void 0)
752
857
  config.adminKey = adminKey2;
858
+ const adminControls = await resolveServeControls(values, process.env, config.adminControls);
859
+ if (adminControls !== void 0)
860
+ config.adminControls = adminControls;
753
861
  const fleet = await startFleet(config, {
754
862
  load: (name) => loadTarget(name, target),
755
863
  onLog: formatLog(config.log ?? log),
@@ -775,10 +883,12 @@ var serveCommand = (target) => ({
775
883
  }
776
884
  const port = asString(values.port);
777
885
  const adminKey = asString(values["admin-key"]) ?? process.env.MOCKINGBIRD_ADMIN_KEY;
778
- const seed = asString(values.seed);
886
+ const seed = asString(values.seed) ?? process.env.MOCKINGBIRD_SEED;
779
887
  let listening;
780
888
  try {
889
+ const adminControls = await resolveServeControls(values);
781
890
  listening = await start(target, values, {
891
+ ...adminControls !== void 0 ? { adminControls } : {},
782
892
  adminPrefix: resolveAdminPrefix(asString(values["admin-prefix"]) ?? process.env.MOCKINGBIRD_ADMIN_PREFIX),
783
893
  port: port === void 0 ? target.defaultPort : Number.parseInt(port, 10),
784
894
  host: asString(values.host) ?? "127.0.0.1",