@mostra/sdk 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundles.json +18 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/events.ts +3 -1
- package/src/mostra.ts +4 -1
package/dist/bundles.json
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.
|
|
4
|
-
"release": "https://github.com/mostra-dev/mostra/releases/download/v0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"release": "https://github.com/mostra-dev/mostra/releases/download/v0.5.0",
|
|
5
5
|
"assets": {
|
|
6
6
|
"darwin-arm64": {
|
|
7
|
-
"file": "mostra-0.
|
|
8
|
-
"sha256": "
|
|
9
|
-
"size":
|
|
10
|
-
"unpackedSize":
|
|
7
|
+
"file": "mostra-0.5.0-darwin-arm64.tar.gz",
|
|
8
|
+
"sha256": "d7b9581b0b6bbf0c3498ed1c294854aaf138139adddf7fda9e0e3249dd29891f",
|
|
9
|
+
"size": 198891076,
|
|
10
|
+
"unpackedSize": 782535407
|
|
11
11
|
},
|
|
12
12
|
"darwin-x64": {
|
|
13
|
-
"file": "mostra-0.
|
|
14
|
-
"sha256": "
|
|
15
|
-
"size":
|
|
16
|
-
"unpackedSize":
|
|
13
|
+
"file": "mostra-0.5.0-darwin-x64.tar.gz",
|
|
14
|
+
"sha256": "e60c605bf6bcac0e32d73bf38ad7ecb3f972842105b5197ba3cedb8e1b333351",
|
|
15
|
+
"size": 201970818,
|
|
16
|
+
"unpackedSize": 790633271
|
|
17
17
|
},
|
|
18
18
|
"linux-x64": {
|
|
19
|
-
"file": "mostra-0.
|
|
20
|
-
"sha256": "
|
|
21
|
-
"size":
|
|
22
|
-
"unpackedSize":
|
|
19
|
+
"file": "mostra-0.5.0-linux-x64.tar.gz",
|
|
20
|
+
"sha256": "191ca8bb83ff7487930ce7ed133fab6aba71df34d3922a9e68746b3dda2cec1c",
|
|
21
|
+
"size": 206187524,
|
|
22
|
+
"unpackedSize": 793491763
|
|
23
23
|
},
|
|
24
24
|
"linux-arm64": {
|
|
25
|
-
"file": "mostra-0.
|
|
26
|
-
"sha256": "
|
|
27
|
-
"size":
|
|
28
|
-
"unpackedSize":
|
|
25
|
+
"file": "mostra-0.5.0-linux-arm64.tar.gz",
|
|
26
|
+
"sha256": "ba91da81c7eb59573ec7fdd422a100f4c538204a5c727468c2f787bea04e080f",
|
|
27
|
+
"size": 204286151,
|
|
28
|
+
"unpackedSize": 783799716
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/bundle/download.ts","../src/bundle/setup.ts","../src/bundle/cache.ts","../src/bundle/tree.ts","../src/runtime.ts","../src/control.ts","../src/events.ts","../src/mostra.ts","../src/platform.ts","../src/version.ts"],"mappings":";;;;UAqBiB;;WAEN;;WAEA;;;wBAmMK,UAAU;;;;KCtMd,cAAc,SAAS;;UAGlB;;WAEN;;WAEA,MAAM;;;KAIL;;UAGK;;WAEN,MAAM;;WAEN,SAAS;;WAET,WAAW,MAAM;;WAEjB;;;wBAIK,YAAY;;wBAKZ,QAAQ,KAAK;;wBAYP,WAAW,eAAe,QAAQ;;wBA4BlC,UAAU,QAAQ,SAAS,QAAQ;;wBAkBzC,YAAY;;wBAYN,WAAW,QAAQ,SAAS;;wBAkB5B,aAAa,QAAQ,SAAS;;;;;;;wBAiG9B,YAAY,cAAc,UAAS,eAAoB,QAAQ;;;;wBChMrE,UAAU,KAAK,aAAa;;wBAU5B,UAAU,KAAK,aAAa;;KAKhC;WACI;IAAqB;WACtB;;WACA,MAAM;;;UAGJ;;WAEN,KAAK;;WAEL,UAAU;;WAEV,SAAS;;WAET,eAAe,UAAU;;;;;;wBAUd,cAAc,MAAM,YAAY,SAAS,iBAAiB,QAAQ;;;;wBCzElE,SAAS,eAAe;EAAU;EAAgB;;;wBAwDxD,gBAAgB,cAAc,cAAc;;wBAQ5C,aAAa;;;;;;wBAUP,aACpB,cACA,WAAU,2BACT,QAAQ;;KAqBC;;;;;;wBAOU,UACpB,cACA,UAAU,aACV;WAAoB,cAAc;WAAmC;IACpE,QAAQ;;;;UC7FM;;WAEN;;WAEA;;WAEA;;WAEA;;WAEA,SAAS;;;UAIH;;WAEN;;WAEA,MAAM;;WAEN,SAAS;;WAET,eAAe,UAAU;;;wBAkBpB,SAAS,KAAK,cAAc;;wBAqB5B;;wBAuBA,UAAU,QAAQ,SAAS;;;;;;;wBA+BrB,eAAe,UAAS,iBAAsB,QAAQ;;;;UCvH3D;;WAEN,UAAU;;WAEV,MAAM;;WAEN;;WAEA;;;;;;;wBAwEK,eAAe,kBAAkB,UAAS,iBAAsB,QAAQ;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/bundle/download.ts","../src/bundle/setup.ts","../src/bundle/cache.ts","../src/bundle/tree.ts","../src/runtime.ts","../src/control.ts","../src/events.ts","../src/mostra.ts","../src/platform.ts","../src/version.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as safeRelative, S as linkStaysInside, _ as setupBundle, a as sdkVersion, b as describeTree, c as bundleDir, d as browsersDir, f as checkBrowser, g as readSetup, h as readBundle, i as runtimeOf, l as cacheRoot, m as offline, n as readPins, o as bundlePlatform, p as checkFiles, r as resolveRuntime, s as currentPlatform, t as enclosingBundle, u as installPinned, v as setupExtras, w as redactUrl, x as hashFile, y as checkTree } from "./runtime-CUbnK5Za.js";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
-
import { ARTIFACT_PROGRESS, EVENTS_NOTIFY, EventsNotifyParams, HOST_PROTOCOL_REVISIONS, LineSplitter, RpcError, RpcPeer, WAIT_MAX_MS, artifactPublishMethod, artifactVerifyMethod, createDiagnostic, encodeLine, eventsAckMethod, eventsSubscribeMethod, eventsUnsubscribeMethod, livePushMethod, mostraInitializeMethod, portDraftsMirrorMethod, portIdentityResolveMethod, portSecretsGetMethod, portSubmissionsDeliverMethod, portTrustedControlsActivatedMethod, portTrustedControlsPolicyMethod, resultsGetMethod, stateGetMethod, statePatchMethod, viewUrlMethod, waitMethod } from "@mostra/protocol";
|
|
3
|
+
import { ARTIFACT_PROGRESS, EVENTS_NOTIFY, EventsNotifyParams, HOST_PROTOCOL_REVISIONS, LineSplitter, RpcError, RpcPeer, WAIT_MAX_MS, artifactPublishMethod, artifactVerifyMethod, createDiagnostic, encodeLine, eventsAckMethod, eventsSubscribeMethod, eventsUnsubscribeMethod, livePushMethod, mostraInitializeMethod, portDraftsMirrorMethod, portIdentityResolveMethod, portSecretsGetMethod, portSubmissionsDeliverMethod, portTrustedControlsActivatedMethod, portTrustedControlsPolicyMethod, receivable, resultsGetMethod, stateGetMethod, statePatchMethod, viewUrlMethod, waitMethod } from "@mostra/protocol";
|
|
4
4
|
import { existsSync, realpathSync } from "node:fs";
|
|
5
5
|
import { Duplex } from "node:stream";
|
|
6
6
|
import { spawn } from "node:child_process";
|
|
@@ -174,7 +174,7 @@ var EventStream = class {
|
|
|
174
174
|
options.signal?.addEventListener("abort", () => void this.return(), { once: true });
|
|
175
175
|
}
|
|
176
176
|
#receive(params) {
|
|
177
|
-
const parsed = EventsNotifyParams.safeParse(params);
|
|
177
|
+
const parsed = receivable(EventsNotifyParams).safeParse(params);
|
|
178
178
|
if (!parsed.success) return;
|
|
179
179
|
if (parsed.data.subscriptionId !== this.#subscription) {
|
|
180
180
|
this.#early?.push(parsed.data);
|
|
@@ -472,7 +472,7 @@ async function createMostra(options) {
|
|
|
472
472
|
const definition = Object.values(PORT_METHODS).flat().find((candidate) => candidate.method === method);
|
|
473
473
|
const handler = definition && portHandler(ports, method);
|
|
474
474
|
if (!definition || !handler) throw new RpcError(createDiagnostic("MOSTRA_HOST_METHOD_UNKNOWN", { message: `This host serves no method ${method}.` }), -32601);
|
|
475
|
-
const parsed = definition.params.safeParse(params);
|
|
475
|
+
const parsed = receivable(definition.params).safeParse(params);
|
|
476
476
|
if (!parsed.success) throw new RpcError(createDiagnostic("MOSTRA_HOST_INVALID_PARAMS", { message: `The params of ${method} don't match its schema.` }), -32602);
|
|
477
477
|
const answer = await handler(parsed.data);
|
|
478
478
|
const checked = definition.result.safeParse(answer ?? null);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/control.ts","../src/events.ts","../src/mostra.ts"],"sourcesContent":["/**\n * Opening a home's control endpoint as a client (D45, D81, D150): the socket or pipe that\n * `instance.json` names, where a home's daemon serves the host protocol.\n *\n * - **POSIX:** a Unix socket in the owner-only runtime directory, opened with `node:net`.\n * - **Windows:** an owner-only named pipe, opened through the runtime's helper, `mostra-win\n * connect`. A pipe's old name in `instance.json` may be free for another user to take, and\n * Node.js can neither check who created a pipe nor stop its server acting as the client, so the\n * helper connects without letting the server impersonate the client, refuses a pipe whose owner\n * isn't this user, and relays the bytes over its standard streams (Opus review of WP-W1).\n *\n * The helper is the one beside the runtime the caller resolved: `MOSTRA_WIN_HELPER`, else the\n * runtime bundle's `bin/mostra-win.exe`, else a development checkout's Cargo build.\n */\nimport { spawn } from \"node:child_process\";\nimport { existsSync, realpathSync } from \"node:fs\";\nimport { createConnection, type Socket } from \"node:net\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Duplex } from \"node:stream\";\nimport { createDiagnostic, RpcError } from \"@mostra/protocol\";\nimport type { Environment } from \"./bundle/setup.ts\";\nimport type { Runtime } from \"./runtime.ts\";\n\n/** Options of {@link connectControl}. */\nexport interface ControlOptions {\n /** The runtime whose helper opens a Windows pipe; required on Windows. */\n readonly runtime?: Runtime | undefined;\n /** The environment: `MOSTRA_WIN_HELPER`. */\n readonly env?: Environment | undefined;\n /** How long to wait for the endpoint to answer. Default 5000. */\n readonly timeoutMs?: number | undefined;\n /** The operating system, `process.platform` by default. */\n readonly platform?: string | undefined;\n}\n\nconst PIPE_PREFIX = \"\\\\\\\\.\\\\pipe\\\\\";\n\n/** The helper's exit code for a pipe another user owns (`crates/mostra-win/src/win/connect.rs`). */\nconst NOT_OWNED_EXIT = 3;\n\nfunction unreachable(endpoint: string, why: string): RpcError {\n return new RpcError(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `Couldn't reach the instance at ${endpoint} (${why}).`,\n }),\n );\n}\n\n/**\n * The Windows helper beside `runtime`: `MOSTRA_WIN_HELPER`, else the bundle's\n * `bin/mostra-win.exe`, else, for a checkout's `packages/runtime/dist/bin.js`, Cargo's\n * `target/release` then `target/debug`. Throws `MOSTRA_ENV_HELPER_MISSING` when none exists.\n */\nexport function windowsHelper(runtime: Runtime | undefined, env: Environment): string {\n const override = env[\"MOSTRA_WIN_HELPER\"];\n if (override) {\n if (existsSync(override)) return override;\n throw missingHelper(`MOSTRA_WIN_HELPER names ${override}, which doesn't exist.`);\n }\n if (runtime === undefined) {\n throw missingHelper(\"No runtime was resolved, so Mostra's Windows helper can't be found.\");\n }\n const candidates =\n runtime.bundle !== undefined\n ? [join(runtime.bundle.root, \"bin\", \"mostra-win.exe\")]\n : [\"release\", \"debug\"].map((profile) =>\n resolve(\n // The runtime's own order (`helperPath`) starts from where its code really is, so a\n // link to its entry (a relative `MOSTRA_RUNTIME_BIN` through a junction) changes nothing.\n dirname(realEntry(runtime.runtimeEntry)),\n \"..\",\n \"..\",\n \"..\",\n \"target\",\n profile,\n \"mostra-win.exe\",\n ),\n );\n const found = candidates.find((candidate) => existsSync(candidate));\n if (found === undefined) {\n throw missingHelper(\n `Mostra's Windows helper isn't installed; looked for ${candidates.join(\", \")}.`,\n );\n }\n return found;\n}\n\nfunction realEntry(entry: string): string {\n try {\n return realpathSync.native(entry);\n } catch {\n return entry;\n }\n}\n\nfunction missingHelper(message: string): RpcError {\n return new RpcError(createDiagnostic(\"MOSTRA_ENV_HELPER_MISSING\", { message }));\n}\n\n/**\n * Connects to a home's control endpoint. Rejects with `MOSTRA_HOST_CONNECTION_CLOSED` when nothing\n * serves it in time or, on Windows, when another user owns the pipe; with\n * `MOSTRA_ENV_HELPER_MISSING` when the Windows helper can't be found.\n */\nexport function connectControl(endpoint: string, options: ControlOptions = {}): Promise<Duplex> {\n const timeoutMs = options.timeoutMs ?? 5000;\n if ((options.platform ?? process.platform) === \"win32\") {\n return connectPipe(\n endpoint,\n windowsHelper(options.runtime, options.env ?? process.env),\n timeoutMs,\n );\n }\n return new Promise((resolvePromise, reject) => {\n const socket: Socket = createConnection(endpoint);\n const timer = setTimeout(() => {\n socket.destroy();\n reject(unreachable(endpoint, \"timed out\"));\n }, timeoutMs);\n socket.once(\"error\", (error: NodeJS.ErrnoException) => {\n clearTimeout(timer);\n reject(unreachable(endpoint, error.code ?? \"failed\"));\n });\n socket.once(\"connect\", () => {\n clearTimeout(timer);\n resolvePromise(socket);\n });\n });\n}\n\nfunction connectPipe(endpoint: string, helper: string, timeoutMs: number): Promise<Duplex> {\n const name = endpoint.startsWith(PIPE_PREFIX) ? endpoint.slice(PIPE_PREFIX.length) : endpoint;\n return new Promise((resolvePromise, reject) => {\n const child = spawn(helper, [\"connect\", name], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n windowsHide: true,\n });\n let settled = false;\n const fail = (why: string): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n child.kill();\n reject(unreachable(endpoint, why));\n };\n const timer = setTimeout(() => fail(\"timed out\"), timeoutMs);\n child.once(\"error\", (error) => fail(error.message));\n child.once(\"exit\", (code) =>\n fail(code === NOT_OWNED_EXIT ? \"another user owns that pipe\" : `the helper exited ${code}`),\n );\n let text = \"\";\n child.stderr.setEncoding(\"utf8\");\n child.stderr.on(\"data\", (chunk: string) => {\n if (settled) return;\n text += chunk;\n const end = text.indexOf(\"\\n\");\n if (end < 0) return;\n let line: { connected?: boolean; code?: string; error?: string } = {};\n try {\n line = JSON.parse(text.slice(0, end)) as typeof line;\n } catch {\n // Treated as a failure below.\n }\n if (line.connected !== true) {\n fail(\n line.code === \"not-owned\"\n ? \"another user owns that pipe\"\n : (line.error ?? text.slice(0, end)),\n );\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolvePromise(Duplex.from({ readable: child.stdout, writable: child.stdin }));\n });\n });\n}\n","/**\n * Events as an async iterator (IN-08, D101): pages from `events/subscribe`, in cursor order. A\n * consumer that falls behind pauses the subscription instead of buffering without bound or\n * stalling the connection's other calls: past `highWaterMark` pages it unsubscribes, and once it\n * has caught up it subscribes again after the last cursor it received, so nothing is missed or\n * repeated. With a `name`, a page is acknowledged (`events/ack`) when the next one is asked for,\n * which is when the consumer has handled it; a stream of the same name later resumes there.\n */\nimport {\n createDiagnostic,\n type Diagnostic,\n type EventFilter,\n type EventRecord,\n EventsNotifyParams,\n eventsAckMethod,\n eventsSubscribeMethod,\n eventsUnsubscribeMethod,\n type MethodDefinition,\n RpcError,\n} from \"@mostra/protocol\";\n\n/** One page of events. */\nexport interface EventPage {\n /** The events, in cursor order. */\n readonly events: readonly EventRecord[];\n /** Everything up to this cursor was examined: the cursor to resume after. */\n readonly cursor: string;\n /** Set when compaction removed events in this range (`MOSTRA_HOST_CURSOR_COMPACTED`). */\n readonly compacted?: Diagnostic;\n}\n\n/** Options of a stream. */\nexport interface EventStreamOptions {\n /** Which events: artifacts and type patterns. Default: every event. */\n readonly filter?: EventFilter;\n /** Start after this cursor. Default: a named stream's acknowledged cursor, else now. */\n readonly after?: string;\n /** A durable subscriber name: pages are acknowledged under it as they are handled. */\n readonly name?: string;\n /** Pages held before the subscription pauses. Default 16. */\n readonly highWaterMark?: number;\n /** Ends the stream. */\n readonly signal?: AbortSignal;\n}\n\n/** What a stream needs of the session. */\nexport interface EventSession {\n call<P, R>(method: MethodDefinition<P, R>, params: P): Promise<R>;\n /** Registers a handler for `events/notify`; returns its removal. */\n onNotification(handler: (params: unknown) => void): () => void;\n /** Resolves when the session ends. */\n readonly closed: Promise<void>;\n}\n\n/** Opens a stream; nothing is subscribed until the first page is asked for. */\nexport function openEventStream(\n session: EventSession,\n options: EventStreamOptions,\n): AsyncIterable<EventPage> {\n return {\n [Symbol.asyncIterator]: () => new EventStream(session, options),\n };\n}\n\nclass EventStream implements AsyncIterator<EventPage> {\n readonly #session: EventSession;\n readonly #options: EventStreamOptions;\n readonly #high: number;\n readonly #queue: EventPage[] = [];\n #subscription: string | undefined;\n /** The cursor of the last page received, where a resumed subscription starts. */\n #received: string | undefined;\n /** The cursor of the last page handed out; handled once the next page is asked for. */\n #handedOut: string | undefined;\n /** The cursor of the last page the consumer handled, and of the last acknowledged. */\n #handled: string | undefined;\n #acked: string | undefined;\n #paused = false;\n /**\n * Pages that arrived while a subscribe was in flight, for a subscription not known yet: the\n * answer and its first page can reach the session in one chunk, and the page is dispatched\n * before the code awaiting the answer resumes. Kept until the answer names its ID.\n */\n #early: EventsNotifyParams[] | undefined;\n /** The unsubscribe of a pause; its answer means no page of that subscription follows. */\n #pausing: Promise<void> | undefined;\n /** A subscribe whose answer is on its way (`#subscribe`). */\n #subscribing: Promise<void> | undefined;\n #started = false;\n #ended: { error?: unknown } | undefined;\n #wake: (() => void) | undefined;\n readonly #removeHandler: () => void;\n\n constructor(session: EventSession, options: EventStreamOptions) {\n this.#session = session;\n this.#options = options;\n this.#high = Math.max(1, options.highWaterMark ?? 16);\n this.#removeHandler = session.onNotification((params) => this.#receive(params));\n void session.closed.then(() =>\n this.#end(\n new RpcError(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: \"The Mostra instance exited while its events were being read.\",\n }),\n ),\n ),\n );\n options.signal?.addEventListener(\"abort\", () => void this.return(), { once: true });\n }\n\n #receive(params: unknown): void {\n const parsed = EventsNotifyParams.safeParse(params);\n if (!parsed.success) return;\n if (parsed.data.subscriptionId !== this.#subscription) {\n this.#early?.push(parsed.data);\n return;\n }\n this.#accept(parsed.data);\n }\n\n #accept({ events, cursor, compacted }: EventsNotifyParams): void {\n this.#queue.push({ events, cursor, ...(compacted ? { compacted } : {}) });\n this.#received = cursor;\n if (this.#queue.length >= this.#high && !this.#paused) this.#pausing = this.#pause();\n this.#wake?.();\n }\n\n /**\n * Subscribes after `after`. While the answer is on its way the call is `#subscribing`, so\n * `return()` can wait for it and end the subscription it names (GPT review finding 5).\n */\n #subscribe(after: string | undefined): Promise<void> {\n const opening = this.#open(after);\n this.#subscribing = opening;\n return opening.finally(() => {\n if (this.#subscribing === opening) this.#subscribing = undefined;\n });\n }\n\n async #open(after: string | undefined): Promise<void> {\n this.#early = [];\n let answer: { subscriptionId: string; cursor: string };\n let early: EventsNotifyParams[];\n try {\n answer = await this.#session.call(eventsSubscribeMethod, {\n ...(this.#options.filter ? { filter: this.#options.filter } : {}),\n ...(after !== undefined ? { after } : {}),\n ...(this.#options.name ? { name: this.#options.name } : {}),\n });\n } finally {\n early = this.#early;\n this.#early = undefined;\n }\n // Synchronous from here: no page can be dispatched before the ID is known.\n this.#subscription = answer.subscriptionId;\n this.#received ??= answer.cursor;\n // The pages of this subscription that came before its answer was handled, in order.\n for (const page of early) if (page.subscriptionId === answer.subscriptionId) this.#accept(page);\n }\n\n async #pause(): Promise<void> {\n this.#paused = true;\n const id = this.#subscription;\n if (id === undefined) return;\n // What the consumer handled is acknowledged while the subscription still exists: the ack goes\n // out before the unsubscribe on the connection, which keeps their order.\n const acked = this.#sendAck(id);\n // Pages already on their way still arrive under this ID and are kept. The answer comes after\n // them on the connection, so once it is here the subscription is over.\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: id })\n .catch(() => undefined);\n // A pause runs from a page's arrival, where nobody awaits it, so it never rejects (Opus review\n // finding 7): an ack the instance refused (or a connection gone) ends the stream with that\n // error, which the consumer's next() then throws.\n await acked.catch((error: unknown) => this.#end(error));\n if (this.#subscription === id) this.#subscription = undefined;\n }\n\n async #resume(): Promise<void> {\n await this.#pausing;\n this.#pausing = undefined;\n this.#paused = false;\n await this.#subscribe(this.#received);\n }\n\n /**\n * Acknowledges what the consumer handled under subscription `id`, if a named stream has handled\n * more than it acknowledged. The line is written when this is called (the session writes each\n * call at once), so it is ordered before anything sent after it.\n */\n #sendAck(id: string): Promise<void> {\n const cursor = this.#handled;\n if (!this.#options.name || cursor === undefined || cursor === this.#acked)\n return Promise.resolve();\n this.#acked = cursor;\n return this.#session\n .call(eventsAckMethod, { subscriptionId: id, cursor })\n .then(() => undefined);\n }\n\n #end(error?: unknown): void {\n if (this.#ended) return;\n this.#ended = error === undefined ? {} : { error };\n this.#removeHandler();\n this.#wake?.();\n }\n\n async next(): Promise<IteratorResult<EventPage>> {\n if (!this.#started) {\n this.#started = true;\n await this.#subscribe(this.#options.after);\n }\n // Asking for the next page means the consumer handled the one before it.\n if (this.#handedOut !== undefined) this.#handled = this.#handedOut;\n if (this.#paused && !this.#ended && this.#queue.length <= this.#high / 2) await this.#resume();\n // Never under a subscription being paused: its unsubscribe may already be on its way, and\n // the pause acknowledges what was handled before it.\n if (!this.#paused && this.#subscription !== undefined) await this.#sendAck(this.#subscription);\n for (;;) {\n const page = this.#queue.shift();\n if (page !== undefined) {\n this.#handedOut = page.cursor;\n return { done: false, value: page };\n }\n if (this.#ended) {\n if (this.#ended.error !== undefined) throw this.#ended.error;\n return { done: true, value: undefined };\n }\n if (this.#paused) {\n await this.#resume();\n await this.#sendAck(this.#subscription as string);\n }\n await new Promise<void>((done) => {\n this.#wake = done;\n });\n this.#wake = undefined;\n }\n }\n\n /**\n * Ends the stream: the subscription ends, and a named stream acknowledges every page the\n * consumer handled, never the last one handed out (the loop may have stopped inside it).\n */\n async return(): Promise<IteratorResult<EventPage>> {\n this.#end();\n this.#queue.length = 0;\n await this.#pausing;\n // A subscribe on its way names a subscription that exists on the instance once it is\n // answered: wait for it, so it is ended below like any other.\n await this.#subscribing?.catch(() => undefined);\n const id = this.#subscription;\n if (id !== undefined) {\n this.#subscription = undefined;\n const acked = this.#sendAck(id);\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: id })\n .catch(() => undefined);\n await acked.catch(() => undefined);\n } else if (this.#options.name && this.#handled !== undefined && this.#handled !== this.#acked) {\n // Paused when it ended, with pages handled since: a subscription of the name, just long\n // enough to acknowledge them (its pages are ignored; the handler is gone).\n await this.#subscribe(this.#received).catch(() => undefined);\n const again = this.#subscription;\n if (again !== undefined) {\n this.#subscription = undefined;\n const acked = this.#sendAck(again);\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: again })\n .catch(() => undefined);\n await acked.catch(() => undefined);\n }\n }\n return { done: true, value: undefined };\n }\n}\n","/**\n * `createMostra` (HO-04, D25, D58, D59): a JavaScript host's handle on its own Mostra instance.\n * It finds a verified runtime (the pinned bundle, downloaded once), spawns `mostra-runtime host`\n * for the host's home over stdio, opens the protocol session, serves the host's ports and offers\n * the calls every host makes. The instance lives exactly as long as this handle: `close()` ends\n * it, and so does the host's process exiting.\n *\n * ```ts\n * const mostra = await createMostra({\n * home: \"/var/lib/inbox/mostra\",\n * client: { name: \"inbox-app\", version: \"1.4.0\" },\n * ports: { submissions: { deliver: async (envelope) => ({ accepted: true }) } },\n * });\n * const receipt = await mostra.publish(\"/work/inbox\");\n * const outcome = await mostra.wait(receipt.artifactId, \"submitted\");\n * await mostra.close();\n * ```\n */\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { resolve } from \"node:path\";\nimport {\n type AnyMethod,\n ARTIFACT_PROGRESS,\n type ArtifactRef,\n type ArtifactSummary,\n artifactPublishMethod,\n artifactVerifyMethod,\n type CapabilityLink,\n type ControlsPolicy,\n createDiagnostic,\n type DependencyPolicy,\n type Diagnostic,\n type DraftMirror,\n EVENTS_NOTIFY,\n encodeLine,\n HOST_PROTOCOL_REVISIONS,\n type HomeConfig,\n type InstanceCapabilities,\n type InstanceInfo,\n LineSplitter,\n type LivePushParams,\n type LivePushResult,\n livePushMethod,\n type MethodDefinition,\n type MostraInitializeResult,\n mostraInitializeMethod,\n type PortIdentityResolveParams,\n type PortName,\n type PortSecretsGetParams,\n type PortTrustedControlsActivatedParams,\n type PublishReceipt,\n portDraftsMirrorMethod,\n portIdentityResolveMethod,\n portSecretsGetMethod,\n portSubmissionsDeliverMethod,\n portTrustedControlsActivatedMethod,\n portTrustedControlsPolicyMethod,\n type Results,\n type ResultsMarkdown,\n RpcError,\n RpcPeer,\n resultsGetMethod,\n type SourceRef,\n type StateGetResult,\n type StatePatchParams,\n type StatePatchResult,\n type SubmissionEnvelope,\n type SubmissionPolicy,\n type SubmissionsDeliverResult,\n type SubmitChannel,\n stateGetMethod,\n statePatchMethod,\n type VerifyVerdict,\n type ViewerSpec,\n type ViewUrlParams,\n viewUrlMethod,\n WAIT_MAX_MS,\n type WaitCondition,\n type WaitResult,\n waitMethod,\n} from \"@mostra/protocol\";\nimport type { InstallProgress } from \"./bundle/cache.ts\";\nimport type { Environment } from \"./bundle/setup.ts\";\nimport { type EventPage, type EventStreamOptions, openEventStream } from \"./events.ts\";\nimport { type Runtime, resolveRuntime } from \"./runtime.ts\";\nimport { sdkVersion } from \"./version.ts\";\n\n/**\n * The ports a host may serve (INTERFACES §7). Each is optional and has a standalone default;\n * Mostra calls a port only on the instance this handle spawned, and every call is idempotent by\n * its stable ID, so a port may see the same call again after a crash.\n */\nexport interface Ports {\n /**\n * Names the person behind a link minted without a viewer, from the request's headers (D143):\n * `{ ref, name? }` (one viewer per `ref`), or `null` for the home's local user. Answer within\n * 5 s; a failure refuses the link rather than guess.\n */\n readonly identity?: {\n resolve(request: PortIdentityResolveParams): ViewerSpec | null | Promise<ViewerSpec | null>;\n };\n /** Receives each submission once it is durable (D58, D105). Answer within 30 s. */\n readonly submissions?: {\n deliver(\n envelope: SubmissionEnvelope,\n ): SubmissionsDeliverResult | Promise<SubmissionsDeliverResult>;\n };\n /** Receives each viewer draft's latest revision, best effort, never retried (IN-06). */\n readonly drafts?: { mirror(draft: DraftMirror): void | Promise<void> };\n /** Answers `secret()` first, before the home's store (D131); `null` falls back. */\n readonly secrets?: {\n get(request: PortSecretsGetParams): string | null | Promise<string | null>;\n };\n /** Decides each view's submit channels and trusted controls (D108). Answer within 5 s. */\n readonly trustedControls?: {\n policy(artifact: ArtifactSummary): ControlsPolicy | Promise<ControlsPolicy>;\n activated?(event: PortTrustedControlsActivatedParams): void | Promise<void>;\n };\n}\n\n/** Home settings a host chooses (INTERFACES §6 `HomeConfig`), kept in the home's `config.json`. */\nexport interface HomeSettings {\n /** The submission policy for artifacts whose publish names none (D102). */\n readonly submission?: SubmissionPolicy;\n /** The channels that may submit (D40, D108); absent, both. */\n readonly submitChannels?: readonly SubmitChannel[];\n /** How packages enter an artifact's lockfile (D43, D95). */\n readonly dependencies?: DependencyPolicy;\n}\n\n/** Options of {@link createMostra}. */\nexport interface MostraOptions {\n /** The home this host owns (D45). Created 0700 when missing. */\n readonly home: string;\n /**\n * Who the host is. The name owns the host's deliveries across restarts (D105), so keep it\n * stable; it defaults to `mostra-sdk`.\n */\n readonly client?: { readonly name: string; readonly version?: string };\n /** The ports this host serves. */\n readonly ports?: Ports;\n /**\n * Whether submissions made while this host is away wait for it (D105): the registered ports\n * are recorded as ports this host always provides. Default `true`; `false` leaves the home's\n * record as it is.\n */\n readonly expectPorts?: boolean;\n /** Home settings, kept in the home's `config.json` for every later client. */\n readonly settings?: HomeSettings;\n /** An extracted runtime bundle to use instead of the pinned one. */\n readonly bundle?: string;\n /** The gateway's bind address and URL host (D99); loopback by default. */\n readonly bind?: string;\n /** The host name URLs use, which a wildcard `bind` needs (D99). */\n readonly urlHost?: string;\n /** The instance's environment. Default: this process's. */\n readonly env?: Environment;\n /** Each line the instance logs (JSON, on its stderr). A callback that throws is ignored. */\n readonly onLog?: (line: string) => void;\n /** Progress of the first download or setup of the runtime bundle. A throw is ignored. */\n readonly onProgress?: (progress: InstallProgress) => void;\n /**\n * An exception a port threw; the instance got `MOSTRA_HOST_INTERNAL_ERROR` and will retry. A\n * callback that throws is ignored, and the instance is answered all the same.\n */\n readonly onPortError?: (error: unknown, method: string) => void;\n /** Gives up finding the runtime and starting the instance. */\n readonly signal?: AbortSignal;\n}\n\n/** How the instance ended. */\nexport interface InstanceExit {\n /** Its exit code: 0 after a clean stop. */\n readonly code: number | null;\n /** The signal that ended it, if one did. */\n readonly signal: NodeJS.Signals | null;\n}\n\n/** An artifact by ID (`art_…`), by source path, or as the protocol's reference. */\nexport type ArtifactInput = string | ArtifactRef;\n\n/** A host's handle on its instance. */\nexport interface Mostra {\n /** The instance: release, home, platform, PID and mode. */\n readonly instance: InstanceInfo;\n /** What the instance serves. */\n readonly capabilities: InstanceCapabilities;\n /** The negotiated host protocol revision. */\n readonly revision: string;\n /** The session's warnings (an unsupported port, for example). */\n readonly warnings: readonly Diagnostic[];\n /** The runtime it runs on. */\n readonly runtime: Runtime;\n /** Resolves when the instance has exited, for whatever reason. */\n readonly closed: Promise<InstanceExit>;\n /** Calls any host protocol method by its definition from `@mostra/protocol`. */\n call<P, R>(method: MethodDefinition<P, R>, params: P, options?: CallOptions): Promise<R>;\n /** `artifact/publish`: verifies (D41) and publishes; durable before it resolves (H-07). */\n publish(\n source: ArtifactInput,\n options?: {\n readonly force?: boolean;\n readonly policy?: SubmissionPolicy;\n readonly requestId?: string;\n },\n ): Promise<PublishReceipt>;\n /** `artifact/verify`: one verdict, never a publish (D138). */\n verify(\n source: ArtifactInput,\n options?: { readonly screenshots?: \"on-failure\" | \"always\" | \"never\" },\n ): Promise<VerifyVerdict>;\n /**\n * `wait` (IN-10, D103): until the condition holds or `timeoutMs` passes (default: the longest\n * timer, about 24.8 days). `still_waiting` carries the cursor to wait from next; an aborted wait\n * rejects with the signal's reason and loses nothing.\n */\n wait(\n artifact: ArtifactInput,\n until: WaitCondition,\n options?: {\n readonly after?: string;\n readonly timeoutMs?: number;\n readonly signal?: AbortSignal;\n },\n ): Promise<WaitResult>;\n /** `results/get`: a submission as JSON (the default) or Markdown (INTERFACES §8). */\n results(\n artifact: ArtifactInput,\n options?: { readonly submission?: string; readonly format?: \"json\" },\n ): Promise<Results>;\n /** `results/get` as Markdown, for an agent to read. */\n results(\n artifact: ArtifactInput,\n options: { readonly submission?: string; readonly format: \"md\" },\n ): Promise<ResultsMarkdown>;\n /** `state/get`: artifact-scoped fields (D63). */\n getState(artifact: ArtifactInput, ids?: readonly string[]): Promise<StateGetResult>;\n /** `state/patch`: writes artifact-scoped fields through compare-and-set (D63). */\n patchState(\n artifact: ArtifactInput,\n patch: Omit<StatePatchParams, \"artifact\">,\n ): Promise<StatePatchResult>;\n /** `live/push`: a live source's next value (D56). */\n pushLive(\n artifact: ArtifactInput,\n push: Omit<LivePushParams, \"artifact\">,\n ): Promise<LivePushResult>;\n /** `view/url`: a new capability link (D97). */\n viewUrl(\n artifact: ArtifactInput,\n options?: Omit<ViewUrlParams, \"artifact\">,\n ): Promise<CapabilityLink>;\n /**\n * The events matching `options.filter`, page by page, in cursor order, with no gap across a\n * pause (`events/subscribe`). With a `name`, each page is acknowledged when the next one is\n * asked for, so a later stream of that name resumes after the last page handled.\n */\n events(options?: EventStreamOptions): AsyncIterable<EventPage>;\n /** Ends the instance: its stdio closes, and it is stopped if it doesn't exit within 10 s. */\n close(): Promise<InstanceExit>;\n}\n\n/** Options of one call. */\nexport interface CallOptions {\n /** Gives up after this many milliseconds with `MOSTRA_HOST_TIMEOUT`. Default: none. */\n readonly timeoutMs?: number;\n}\n\n/** The port methods the SDK serves, by port. */\nconst PORT_METHODS = {\n identity: [portIdentityResolveMethod],\n submissions: [portSubmissionsDeliverMethod],\n drafts: [portDraftsMirrorMethod],\n secrets: [portSecretsGetMethod],\n trustedControls: [portTrustedControlsPolicyMethod, portTrustedControlsActivatedMethod],\n} as const satisfies Record<keyof Ports, readonly AnyMethod[]>;\n\n/** The client name of an SDK host that names none. */\nexport const SDK_CLIENT_NAME = \"mostra-sdk\";\n\n/** `art_…` is an artifact ID; anything else a source path, resolved against the working directory. */\nexport function artifactRef(input: ArtifactInput): ArtifactRef & SourceRef {\n if (typeof input !== \"string\") return input as ArtifactRef & SourceRef;\n return input.startsWith(\"art_\") ? { artifactId: input } : { path: resolve(input) };\n}\n\nfunction closedError(message: string): RpcError {\n return new RpcError(createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", { message }));\n}\n\n/** The handler for one port method, its params already checked. */\nfunction portHandler(ports: Ports, method: string): ((params: unknown) => unknown) | undefined {\n switch (method) {\n case portIdentityResolveMethod.method:\n return (\n ports.identity && ((params) => ports.identity?.resolve(params as PortIdentityResolveParams))\n );\n case portSubmissionsDeliverMethod.method:\n return (\n ports.submissions && ((params) => ports.submissions?.deliver(params as SubmissionEnvelope))\n );\n case portDraftsMirrorMethod.method:\n return ports.drafts && ((params) => ports.drafts?.mirror(params as DraftMirror));\n case portSecretsGetMethod.method:\n return ports.secrets && ((params) => ports.secrets?.get(params as PortSecretsGetParams));\n case portTrustedControlsPolicyMethod.method:\n return (\n ports.trustedControls &&\n ((params) =>\n ports.trustedControls?.policy((params as { artifact: ArtifactSummary }).artifact))\n );\n case portTrustedControlsActivatedMethod.method:\n return (\n ports.trustedControls &&\n (async (params) => {\n await ports.trustedControls?.activated?.(params as PortTrustedControlsActivatedParams);\n return {};\n })\n );\n default:\n return undefined;\n }\n}\n\n/** The port names `ports` registers; anything unknown is a mistake worth failing on. */\nfunction portNames(ports: Ports): PortName[] {\n const names: PortName[] = [];\n for (const [name, value] of Object.entries(ports)) {\n if (value === undefined) continue;\n if (!(name in PORT_METHODS)) {\n throw new TypeError(\n `createMostra: no port \"${name}\" (the SDK serves ${Object.keys(PORT_METHODS).join(\", \")}).`,\n );\n }\n names.push(name as PortName);\n }\n return names;\n}\n\n/** Ends the child: stdin closed, then SIGTERM after `graceMs`, then SIGKILL after 5 s more. */\nfunction stopChild(\n child: ChildProcess,\n exited: Promise<InstanceExit>,\n graceMs: number,\n): Promise<InstanceExit> {\n child.stdin?.end();\n const term = setTimeout(() => child.kill(\"SIGTERM\"), graceMs);\n const kill = setTimeout(() => child.kill(\"SIGKILL\"), graceMs + 5000);\n return exited.finally(() => {\n clearTimeout(term);\n clearTimeout(kill);\n });\n}\n\n/** `callback`, made unable to throw into its caller. */\nfunction guarded<A extends unknown[]>(\n callback: ((...args: A) => void) | undefined,\n): ((...args: A) => void) | undefined {\n if (callback === undefined) return undefined;\n return (...args: A) => {\n try {\n callback(...args);\n } catch {\n // The host's own code failed; Mostra's work goes on.\n }\n };\n}\n\n/**\n * Spawns an instance for `home` and opens its session. Rejects with the instance's own diagnostic\n * when it can't start (`MOSTRA_HOME_LOCKED`, `MOSTRA_PLATFORM_UNSUPPORTED` …), or with the\n * runtime's (`MOSTRA_ENV_BUNDLE_*`) when no verified runtime can be found.\n */\nexport async function createMostra(options: MostraOptions): Promise<Mostra> {\n // The host's callbacks run inside Mostra's own handlers (a pipe's data listener, the RPC\n // peer's error path, a download's progress): one that throws must never escape into the host's\n // process as an uncaught exception or unhandled rejection, nor stop an answer (Opus review\n // finding 6). A callback that throws is ignored.\n const onLog = guarded(options.onLog);\n const onProgress = guarded(options.onProgress);\n const onPortError = guarded(options.onPortError);\n const ports = options.ports ?? {};\n const names = portNames(ports);\n const env = options.env ?? process.env;\n const home = resolve(options.home);\n const runtime = await resolveRuntime({\n bundle: options.bundle,\n env,\n signal: options.signal,\n ...(onProgress ? { onProgress } : {}),\n });\n options.signal?.throwIfAborted();\n const args = [runtime.runtimeEntry, \"host\", \"--home\", home];\n if (options.bind !== undefined) args.push(\"--bind\", options.bind);\n if (options.urlHost !== undefined) args.push(\"--url-host\", options.urlHost);\n const child = spawn(runtime.node, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: env as NodeJS.ProcessEnv,\n });\n const logTail: string[] = [];\n const exited = new Promise<InstanceExit>((done) => {\n child.on(\"close\", (code, signal) => done({ code, signal }));\n child.on(\"error\", () => done({ code: null, signal: null }));\n });\n let spawnError: Error | undefined;\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n const errLines = new LineSplitter();\n child.stderr?.setEncoding(\"utf8\");\n child.stderr?.on(\"data\", (chunk: string) => {\n for (const line of errLines.push(chunk)) {\n logTail.push(line);\n if (logTail.length > 20) logTail.shift();\n onLog?.(line);\n }\n });\n\n const notificationHandlers = new Map<string, Set<(params: unknown) => void>>();\n const peer = new RpcPeer({\n send: (message) => {\n if (child.stdin?.writable) child.stdin.write(encodeLine(message));\n },\n onRequest: async (method, params) => {\n const definition = Object.values(PORT_METHODS)\n .flat()\n .find((candidate) => candidate.method === method) as\n | MethodDefinition<unknown, unknown>\n | undefined;\n const handler = definition && portHandler(ports, method);\n if (!definition || !handler) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_METHOD_UNKNOWN\", {\n message: `This host serves no method ${method}.`,\n }),\n -32601,\n );\n }\n const parsed = definition.params.safeParse(params);\n if (!parsed.success) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_INVALID_PARAMS\", {\n message: `The params of ${method} don't match its schema.`,\n }),\n -32602,\n );\n }\n const answer = await handler(parsed.data);\n const checked = definition.result.safeParse(answer ?? null);\n if (!checked.success) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_INTERNAL_ERROR\", {\n message: `The host's ${method} answered something its schema refuses.`,\n }),\n );\n }\n return checked.data;\n },\n onNotification: (method, params) => {\n for (const handler of notificationHandlers.get(method) ?? []) handler(params);\n },\n onInternalError: (error, method) => onPortError?.(error, method),\n });\n const lines = new LineSplitter();\n child.stdout?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n for (const line of lines.push(chunk)) peer.receiveLine(line);\n });\n child.stdout?.on(\"close\", () => {\n peer.close(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `The Mostra instance for ${home} exited${logTail.length > 0 ? `; its last log line: ${logTail.at(-1)}` : \"\"}.`,\n }),\n );\n });\n // A pipe to an instance that died fails its next write (EPIPE) on its own schedule. Without a\n // listener that error is an uncaught exception in the host's own process: every pipe has one,\n // and a write that failed ends the session at once, as the instance's exit does.\n child.stdin?.on(\"error\", (error: NodeJS.ErrnoException) => {\n peer.close(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `The Mostra instance for ${home} stopped reading (${error.code ?? error.message}).`,\n }),\n );\n });\n child.stdout?.on(\"error\", () => {});\n child.stderr?.on(\"error\", () => {});\n\n const abort = (): void => {\n void stopChild(child, exited, 0);\n };\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n const homeConfig: HomeConfig = {\n path: home,\n // What this host always provides, an empty list included: a host that stopped serving a port\n // clears the record, so its absence is never awaited (Opus review of WP-12, finding 7).\n ...(options.expectPorts !== false ? { ports: names } : {}),\n ...(options.settings?.submission ? { submission: options.settings.submission } : {}),\n ...(options.settings?.submitChannels\n ? { submitChannels: options.settings.submitChannels }\n : {}),\n ...(options.settings?.dependencies ? { dependencies: options.settings.dependencies } : {}),\n };\n let session: MostraInitializeResult;\n try {\n session = await peer.call(mostraInitializeMethod, {\n client: {\n name: options.client?.name ?? SDK_CLIENT_NAME,\n version: options.client?.version ?? sdkVersion(),\n },\n revisions: [...HOST_PROTOCOL_REVISIONS],\n capabilities: { notifications: [EVENTS_NOTIFY, ARTIFACT_PROGRESS] },\n ports: names,\n home: homeConfig,\n });\n } catch (error) {\n await stopChild(child, exited, 2000);\n if (spawnError) throw closedError(`Couldn't start ${runtime.node}: ${spawnError.message}`);\n throw error;\n } finally {\n options.signal?.removeEventListener(\"abort\", abort);\n }\n\n const call = <P, R>(\n method: MethodDefinition<P, R>,\n params: P,\n callOptions?: CallOptions,\n ): Promise<R> =>\n peer.call(\n method,\n params,\n callOptions?.timeoutMs !== undefined ? { timeoutMs: callOptions.timeoutMs } : {},\n );\n let closing: Promise<InstanceExit> | undefined;\n\n const mostra: Mostra = {\n instance: session.instance,\n capabilities: session.capabilities,\n revision: session.revision,\n warnings: session.warnings,\n runtime,\n closed: exited,\n call,\n publish: (source, publishOptions = {}) =>\n call(artifactPublishMethod, { source: artifactRef(source), ...publishOptions }),\n verify: (source, verifyOptions = {}) =>\n call(artifactVerifyMethod, { source: artifactRef(source), ...verifyOptions }),\n wait: (artifact, until, waitOptions = {}) => {\n const signal = waitOptions.signal;\n // Checked before the request goes out: nothing is sent for a wait already given up, so no\n // answer is left without a handler (GPT review finding 4).\n if (signal?.aborted) return Promise.reject(signal.reason);\n const pending = call(waitMethod, {\n artifact: artifactRef(artifact),\n until,\n timeoutMs: waitOptions.timeoutMs ?? WAIT_MAX_MS,\n ...(waitOptions.after !== undefined ? { after: waitOptions.after } : {}),\n });\n if (!signal) return pending;\n // The instance's wait runs on until its own timeout; its late answer, or the connection's\n // end, is handled here and dropped.\n return new Promise<WaitResult>((done, fail) => {\n const onAbort = (): void => fail(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n pending.then(done, fail).finally(() => signal.removeEventListener(\"abort\", onAbort));\n });\n },\n results: ((\n artifact: ArtifactInput,\n resultsOptions: { submission?: string; format?: \"json\" | \"md\" } = {},\n ) =>\n call(resultsGetMethod, {\n artifact: artifactRef(artifact),\n ...resultsOptions,\n })) as Mostra[\"results\"],\n getState: (artifact, ids) =>\n call(stateGetMethod, { artifact: artifactRef(artifact), ...(ids ? { ids: [...ids] } : {}) }),\n patchState: (artifact, patch) =>\n call(statePatchMethod, { artifact: artifactRef(artifact), ...patch }),\n pushLive: (artifact, push) =>\n call(livePushMethod, { artifact: artifactRef(artifact), ...push }),\n viewUrl: (artifact, urlOptions = {}) =>\n call(viewUrlMethod, { artifact: artifactRef(artifact), ...urlOptions }),\n events: (streamOptions = {}) =>\n openEventStream(\n {\n call,\n onNotification: (handler) => {\n let set = notificationHandlers.get(EVENTS_NOTIFY);\n if (!set) {\n set = new Set();\n notificationHandlers.set(EVENTS_NOTIFY, set);\n }\n set.add(handler);\n return () => set?.delete(handler);\n },\n closed: exited.then(() => undefined),\n },\n streamOptions,\n ),\n close: () => {\n closing ??= stopChild(child, exited, 10_000);\n return closing;\n },\n };\n return mostra;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,cAAc;;AAGpB,MAAM,iBAAiB;AAEvB,SAAS,YAAY,UAAkB,KAAuB;CAC5D,OAAO,IAAI,SACT,iBAAiB,iCAAiC,EAChD,SAAS,kCAAkC,SAAS,IAAI,IAAI,IAC9D,CAAC,CACH;AACF;;;;;;AAOA,SAAgB,cAAc,SAA8B,KAA0B;CACpF,MAAM,WAAW,IAAI;CACrB,IAAI,UAAU;EACZ,IAAI,WAAW,QAAQ,GAAG,OAAO;EACjC,MAAM,cAAc,2BAA2B,SAAS,uBAAuB;CACjF;CACA,IAAI,YAAY,KAAA,GACd,MAAM,cAAc,qEAAqE;CAE3F,MAAM,aACJ,QAAQ,WAAW,KAAA,IACf,CAAC,KAAK,QAAQ,OAAO,MAAM,OAAO,gBAAgB,CAAC,IACnD,CAAC,WAAW,OAAO,CAAC,CAAC,KAAK,YACxB,QAGE,QAAQ,UAAU,QAAQ,YAAY,CAAC,GACvC,MACA,MACA,MACA,UACA,SACA,gBACF,CACF;CACN,MAAM,QAAQ,WAAW,MAAM,cAAc,WAAW,SAAS,CAAC;CAClE,IAAI,UAAU,KAAA,GACZ,MAAM,cACJ,uDAAuD,WAAW,KAAK,IAAI,EAAE,EAC/E;CAEF,OAAO;AACT;AAEA,SAAS,UAAU,OAAuB;CACxC,IAAI;EACF,OAAO,aAAa,OAAO,KAAK;CAClC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,SAA2B;CAChD,OAAO,IAAI,SAAS,iBAAiB,6BAA6B,EAAE,QAAQ,CAAC,CAAC;AAChF;;;;;;AAOA,SAAgB,eAAe,UAAkB,UAA0B,CAAC,GAAoB;CAC9F,MAAM,YAAY,QAAQ,aAAa;CACvC,KAAK,QAAQ,YAAY,QAAQ,cAAc,SAC7C,OAAO,YACL,UACA,cAAc,QAAQ,SAAS,QAAQ,OAAO,QAAQ,GAAG,GACzD,SACF;CAEF,OAAO,IAAI,SAAS,gBAAgB,WAAW;EAC7C,MAAM,SAAiB,iBAAiB,QAAQ;EAChD,MAAM,QAAQ,iBAAiB;GAC7B,OAAO,QAAQ;GACf,OAAO,YAAY,UAAU,WAAW,CAAC;EAC3C,GAAG,SAAS;EACZ,OAAO,KAAK,UAAU,UAAiC;GACrD,aAAa,KAAK;GAClB,OAAO,YAAY,UAAU,MAAM,QAAQ,QAAQ,CAAC;EACtD,CAAC;EACD,OAAO,KAAK,iBAAiB;GAC3B,aAAa,KAAK;GAClB,eAAe,MAAM;EACvB,CAAC;CACH,CAAC;AACH;AAEA,SAAS,YAAY,UAAkB,QAAgB,WAAoC;CACzF,MAAM,OAAO,SAAS,WAAW,WAAW,IAAI,SAAS,MAAM,CAAkB,IAAI;CACrF,OAAO,IAAI,SAAS,gBAAgB,WAAW;EAC7C,MAAM,QAAQ,MAAM,QAAQ,CAAC,WAAW,IAAI,GAAG;GAC7C,OAAO;IAAC;IAAQ;IAAQ;GAAM;GAC9B,aAAa;EACf,CAAC;EACD,IAAI,UAAU;EACd,MAAM,QAAQ,QAAsB;GAClC,IAAI,SAAS;GACb,UAAU;GACV,aAAa,KAAK;GAClB,MAAM,KAAK;GACX,OAAO,YAAY,UAAU,GAAG,CAAC;EACnC;EACA,MAAM,QAAQ,iBAAiB,KAAK,WAAW,GAAG,SAAS;EAC3D,MAAM,KAAK,UAAU,UAAU,KAAK,MAAM,OAAO,CAAC;EAClD,MAAM,KAAK,SAAS,SAClB,KAAK,SAAS,iBAAiB,gCAAgC,qBAAqB,MAAM,CAC5F;EACA,IAAI,OAAO;EACX,MAAM,OAAO,YAAY,MAAM;EAC/B,MAAM,OAAO,GAAG,SAAS,UAAkB;GACzC,IAAI,SAAS;GACb,QAAQ;GACR,MAAM,MAAM,KAAK,QAAQ,IAAI;GAC7B,IAAI,MAAM,GAAG;GACb,IAAI,OAA+D,CAAC;GACpE,IAAI;IACF,OAAO,KAAK,MAAM,KAAK,MAAM,GAAG,GAAG,CAAC;GACtC,QAAQ,CAER;GACA,IAAI,KAAK,cAAc,MAAM;IAC3B,KACE,KAAK,SAAS,cACV,gCACC,KAAK,SAAS,KAAK,MAAM,GAAG,GAAG,CACtC;IACA;GACF;GACA,UAAU;GACV,aAAa,KAAK;GAClB,eAAe,OAAO,KAAK;IAAE,UAAU,MAAM;IAAQ,UAAU,MAAM;GAAM,CAAC,CAAC;EAC/E,CAAC;CACH,CAAC;AACH;;;;;;;;;;;;ACzHA,SAAgB,gBACd,SACA,SAC0B;CAC1B,OAAO,GACJ,OAAO,sBAAsB,IAAI,YAAY,SAAS,OAAO,EAChE;AACF;AAEA,IAAM,cAAN,MAAsD;CACpD;CACA;CACA;CACA,SAA+B,CAAC;CAChC;;CAEA;;CAEA;;CAEA;CACA;CACA,UAAU;;;;;;CAMV;;CAEA;;CAEA;CACA,WAAW;CACX;CACA;CACA;CAEA,YAAY,SAAuB,SAA6B;EAC9D,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,QAAQ,KAAK,IAAI,GAAG,QAAQ,iBAAiB,EAAE;EACpD,KAAK,iBAAiB,QAAQ,gBAAgB,WAAW,KAAK,SAAS,MAAM,CAAC;EAC9E,QAAa,OAAO,WAClB,KAAK,KACH,IAAI,SACF,iBAAiB,iCAAiC,EAChD,SAAS,+DACX,CAAC,CACH,CACF,CACF;EACA,QAAQ,QAAQ,iBAAiB,eAAe,KAAK,KAAK,OAAO,GAAG,EAAE,MAAM,KAAK,CAAC;CACpF;CAEA,SAAS,QAAuB;EAC9B,MAAM,SAAS,mBAAmB,UAAU,MAAM;EAClD,IAAI,CAAC,OAAO,SAAS;EACrB,IAAI,OAAO,KAAK,mBAAmB,KAAK,eAAe;GACrD,KAAK,QAAQ,KAAK,OAAO,IAAI;GAC7B;EACF;EACA,KAAK,QAAQ,OAAO,IAAI;CAC1B;CAEA,QAAQ,EAAE,QAAQ,QAAQ,aAAuC;EAC/D,KAAK,OAAO,KAAK;GAAE;GAAQ;GAAQ,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EAAG,CAAC;EACxE,KAAK,YAAY;EACjB,IAAI,KAAK,OAAO,UAAU,KAAK,SAAS,CAAC,KAAK,SAAS,KAAK,WAAW,KAAK,OAAO;EACnF,KAAK,QAAQ;CACf;;;;;CAMA,WAAW,OAA0C;EACnD,MAAM,UAAU,KAAK,MAAM,KAAK;EAChC,KAAK,eAAe;EACpB,OAAO,QAAQ,cAAc;GAC3B,IAAI,KAAK,iBAAiB,SAAS,KAAK,eAAe,KAAA;EACzD,CAAC;CACH;CAEA,MAAM,MAAM,OAA0C;EACpD,KAAK,SAAS,CAAC;EACf,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,KAAK,SAAS,KAAK,uBAAuB;IACvD,GAAI,KAAK,SAAS,SAAS,EAAE,QAAQ,KAAK,SAAS,OAAO,IAAI,CAAC;IAC/D,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;IACvC,GAAI,KAAK,SAAS,OAAO,EAAE,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC;GAC3D,CAAC;EACH,UAAU;GACR,QAAQ,KAAK;GACb,KAAK,SAAS,KAAA;EAChB;EAEA,KAAK,gBAAgB,OAAO;EAC5B,KAAK,cAAc,OAAO;EAE1B,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,mBAAmB,OAAO,gBAAgB,KAAK,QAAQ,IAAI;CAChG;CAEA,MAAM,SAAwB;EAC5B,KAAK,UAAU;EACf,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,KAAA,GAAW;EAGtB,MAAM,QAAQ,KAAK,SAAS,EAAE;EAG9B,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,GAAG,CAAC,CAAC,CACrD,YAAY,KAAA,CAAS;EAIxB,MAAM,MAAM,OAAO,UAAmB,KAAK,KAAK,KAAK,CAAC;EACtD,IAAI,KAAK,kBAAkB,IAAI,KAAK,gBAAgB,KAAA;CACtD;CAEA,MAAM,UAAyB;EAC7B,MAAM,KAAK;EACX,KAAK,WAAW,KAAA;EAChB,KAAK,UAAU;EACf,MAAM,KAAK,WAAW,KAAK,SAAS;CACtC;;;;;;CAOA,SAAS,IAA2B;EAClC,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,KAAK,SAAS,QAAQ,WAAW,KAAA,KAAa,WAAW,KAAK,QACjE,OAAO,QAAQ,QAAQ;EACzB,KAAK,SAAS;EACd,OAAO,KAAK,SACT,KAAK,iBAAiB;GAAE,gBAAgB;GAAI;EAAO,CAAC,CAAC,CACrD,WAAW,KAAA,CAAS;CACzB;CAEA,KAAK,OAAuB;EAC1B,IAAI,KAAK,QAAQ;EACjB,KAAK,SAAS,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACjD,KAAK,eAAe;EACpB,KAAK,QAAQ;CACf;CAEA,MAAM,OAA2C;EAC/C,IAAI,CAAC,KAAK,UAAU;GAClB,KAAK,WAAW;GAChB,MAAM,KAAK,WAAW,KAAK,SAAS,KAAK;EAC3C;EAEA,IAAI,KAAK,eAAe,KAAA,GAAW,KAAK,WAAW,KAAK;EACxD,IAAI,KAAK,WAAW,CAAC,KAAK,UAAU,KAAK,OAAO,UAAU,KAAK,QAAQ,GAAG,MAAM,KAAK,QAAQ;EAG7F,IAAI,CAAC,KAAK,WAAW,KAAK,kBAAkB,KAAA,GAAW,MAAM,KAAK,SAAS,KAAK,aAAa;EAC7F,SAAS;GACP,MAAM,OAAO,KAAK,OAAO,MAAM;GAC/B,IAAI,SAAS,KAAA,GAAW;IACtB,KAAK,aAAa,KAAK;IACvB,OAAO;KAAE,MAAM;KAAO,OAAO;IAAK;GACpC;GACA,IAAI,KAAK,QAAQ;IACf,IAAI,KAAK,OAAO,UAAU,KAAA,GAAW,MAAM,KAAK,OAAO;IACvD,OAAO;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU;GACxC;GACA,IAAI,KAAK,SAAS;IAChB,MAAM,KAAK,QAAQ;IACnB,MAAM,KAAK,SAAS,KAAK,aAAuB;GAClD;GACA,MAAM,IAAI,SAAe,SAAS;IAChC,KAAK,QAAQ;GACf,CAAC;GACD,KAAK,QAAQ,KAAA;EACf;CACF;;;;;CAMA,MAAM,SAA6C;EACjD,KAAK,KAAK;EACV,KAAK,OAAO,SAAS;EACrB,MAAM,KAAK;EAGX,MAAM,KAAK,cAAc,YAAY,KAAA,CAAS;EAC9C,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,KAAA,GAAW;GACpB,KAAK,gBAAgB,KAAA;GACrB,MAAM,QAAQ,KAAK,SAAS,EAAE;GAC9B,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,GAAG,CAAC,CAAC,CACrD,YAAY,KAAA,CAAS;GACxB,MAAM,MAAM,YAAY,KAAA,CAAS;EACnC,OAAO,IAAI,KAAK,SAAS,QAAQ,KAAK,aAAa,KAAA,KAAa,KAAK,aAAa,KAAK,QAAQ;GAG7F,MAAM,KAAK,WAAW,KAAK,SAAS,CAAC,CAAC,YAAY,KAAA,CAAS;GAC3D,MAAM,QAAQ,KAAK;GACnB,IAAI,UAAU,KAAA,GAAW;IACvB,KAAK,gBAAgB,KAAA;IACrB,MAAM,QAAQ,KAAK,SAAS,KAAK;IACjC,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,MAAM,CAAC,CAAC,CACxD,YAAY,KAAA,CAAS;IACxB,MAAM,MAAM,YAAY,KAAA,CAAS;GACnC;EACF;EACA,OAAO;GAAE,MAAM;GAAM,OAAO,KAAA;EAAU;CACxC;AACF;;;;;;;;;;;;;;;;;;;;;;ACNA,MAAM,eAAe;CACnB,UAAU,CAAC,yBAAyB;CACpC,aAAa,CAAC,4BAA4B;CAC1C,QAAQ,CAAC,sBAAsB;CAC/B,SAAS,CAAC,oBAAoB;CAC9B,iBAAiB,CAAC,iCAAiC,kCAAkC;AACvF;;AAGA,MAAa,kBAAkB;;AAG/B,SAAgB,YAAY,OAA+C;CACzE,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAO,MAAM,WAAW,MAAM,IAAI,EAAE,YAAY,MAAM,IAAI,EAAE,MAAM,QAAQ,KAAK,EAAE;AACnF;AAEA,SAAS,YAAY,SAA2B;CAC9C,OAAO,IAAI,SAAS,iBAAiB,iCAAiC,EAAE,QAAQ,CAAC,CAAC;AACpF;;AAGA,SAAS,YAAY,OAAc,QAA4D;CAC7F,QAAQ,QAAR;EACE,KAAK,0BAA0B,QAC7B,OACE,MAAM,cAAc,WAAW,MAAM,UAAU,QAAQ,MAAmC;EAE9F,KAAK,6BAA6B,QAChC,OACE,MAAM,iBAAiB,WAAW,MAAM,aAAa,QAAQ,MAA4B;EAE7F,KAAK,uBAAuB,QAC1B,OAAO,MAAM,YAAY,WAAW,MAAM,QAAQ,OAAO,MAAqB;EAChF,KAAK,qBAAqB,QACxB,OAAO,MAAM,aAAa,WAAW,MAAM,SAAS,IAAI,MAA8B;EACxF,KAAK,gCAAgC,QACnC,OACE,MAAM,qBACJ,WACA,MAAM,iBAAiB,OAAQ,OAAyC,QAAQ;EAEtF,KAAK,mCAAmC,QACtC,OACE,MAAM,oBACL,OAAO,WAAW;GACjB,MAAM,MAAM,iBAAiB,YAAY,MAA4C;GACrF,OAAO,CAAC;EACV;EAEJ,SACE;CACJ;AACF;;AAGA,SAAS,UAAU,OAA0B;CAC3C,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,GAAG;EACjD,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,EAAE,QAAQ,eACZ,MAAM,IAAI,UACR,0BAA0B,KAAK,oBAAoB,OAAO,KAAK,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,GAC1F;EAEF,MAAM,KAAK,IAAgB;CAC7B;CACA,OAAO;AACT;;AAGA,SAAS,UACP,OACA,QACA,SACuB;CACvB,MAAM,OAAO,IAAI;CACjB,MAAM,OAAO,iBAAiB,MAAM,KAAK,SAAS,GAAG,OAAO;CAC5D,MAAM,OAAO,iBAAiB,MAAM,KAAK,SAAS,GAAG,UAAU,GAAI;CACnE,OAAO,OAAO,cAAc;EAC1B,aAAa,IAAI;EACjB,aAAa,IAAI;CACnB,CAAC;AACH;;AAGA,SAAS,QACP,UACoC;CACpC,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;CACnC,QAAQ,GAAG,SAAY;EACrB,IAAI;GACF,SAAS,GAAG,IAAI;EAClB,QAAQ,CAER;CACF;AACF;;;;;;AAOA,eAAsB,aAAa,SAAyC;CAK1E,MAAM,QAAQ,QAAQ,QAAQ,KAAK;CACnC,MAAM,aAAa,QAAQ,QAAQ,UAAU;CAC7C,MAAM,cAAc,QAAQ,QAAQ,WAAW;CAC/C,MAAM,QAAQ,QAAQ,SAAS,CAAC;CAChC,MAAM,QAAQ,UAAU,KAAK;CAC7B,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,OAAO,QAAQ,QAAQ,IAAI;CACjC,MAAM,UAAU,MAAM,eAAe;EACnC,QAAQ,QAAQ;EAChB;EACA,QAAQ,QAAQ;EAChB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;CACrC,CAAC;CACD,QAAQ,QAAQ,eAAe;CAC/B,MAAM,OAAO;EAAC,QAAQ;EAAc;EAAQ;EAAU;CAAI;CAC1D,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,KAAK,UAAU,QAAQ,IAAI;CAChE,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,KAAK,cAAc,QAAQ,OAAO;CAC1E,MAAM,QAAQ,MAAM,QAAQ,MAAM,MAAM;EACtC,OAAO;GAAC;GAAQ;GAAQ;EAAM;EACzB;CACP,CAAC;CACD,MAAM,UAAoB,CAAC;CAC3B,MAAM,SAAS,IAAI,SAAuB,SAAS;EACjD,MAAM,GAAG,UAAU,MAAM,WAAW,KAAK;GAAE;GAAM;EAAO,CAAC,CAAC;EAC1D,MAAM,GAAG,eAAe,KAAK;GAAE,MAAM;GAAM,QAAQ;EAAK,CAAC,CAAC;CAC5D,CAAC;CACD,IAAI;CACJ,MAAM,GAAG,UAAU,UAAU;EAC3B,aAAa;CACf,CAAC;CACD,MAAM,WAAW,IAAI,aAAa;CAClC,MAAM,QAAQ,YAAY,MAAM;CAChC,MAAM,QAAQ,GAAG,SAAS,UAAkB;EAC1C,KAAK,MAAM,QAAQ,SAAS,KAAK,KAAK,GAAG;GACvC,QAAQ,KAAK,IAAI;GACjB,IAAI,QAAQ,SAAS,IAAI,QAAQ,MAAM;GACvC,QAAQ,IAAI;EACd;CACF,CAAC;CAED,MAAM,uCAAuB,IAAI,IAA4C;CAC7E,MAAM,OAAO,IAAI,QAAQ;EACvB,OAAO,YAAY;GACjB,IAAI,MAAM,OAAO,UAAU,MAAM,MAAM,MAAM,WAAW,OAAO,CAAC;EAClE;EACA,WAAW,OAAO,QAAQ,WAAW;GACnC,MAAM,aAAa,OAAO,OAAO,YAAY,CAAC,CAC3C,KAAK,CAAC,CACN,MAAM,cAAc,UAAU,WAAW,MAAM;GAGlD,MAAM,UAAU,cAAc,YAAY,OAAO,MAAM;GACvD,IAAI,CAAC,cAAc,CAAC,SAClB,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,8BAA8B,OAAO,GAChD,CAAC,GACD,MACF;GAEF,MAAM,SAAS,WAAW,OAAO,UAAU,MAAM;GACjD,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,iBAAiB,OAAO,0BACnC,CAAC,GACD,MACF;GAEF,MAAM,SAAS,MAAM,QAAQ,OAAO,IAAI;GACxC,MAAM,UAAU,WAAW,OAAO,UAAU,UAAU,IAAI;GAC1D,IAAI,CAAC,QAAQ,SACX,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,cAAc,OAAO,yCAChC,CAAC,CACH;GAEF,OAAO,QAAQ;EACjB;EACA,iBAAiB,QAAQ,WAAW;GAClC,KAAK,MAAM,WAAW,qBAAqB,IAAI,MAAM,KAAK,CAAC,GAAG,QAAQ,MAAM;EAC9E;EACA,kBAAkB,OAAO,WAAW,cAAc,OAAO,MAAM;CACjE,CAAC;CACD,MAAM,QAAQ,IAAI,aAAa;CAC/B,MAAM,QAAQ,YAAY,MAAM;CAChC,MAAM,QAAQ,GAAG,SAAS,UAAkB;EAC1C,KAAK,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG,KAAK,YAAY,IAAI;CAC7D,CAAC;CACD,MAAM,QAAQ,GAAG,eAAe;EAC9B,KAAK,MACH,iBAAiB,iCAAiC,EAChD,SAAS,2BAA2B,KAAK,SAAS,QAAQ,SAAS,IAAI,wBAAwB,QAAQ,GAAG,EAAE,MAAM,GAAG,GACvH,CAAC,CACH;CACF,CAAC;CAID,MAAM,OAAO,GAAG,UAAU,UAAiC;EACzD,KAAK,MACH,iBAAiB,iCAAiC,EAChD,SAAS,2BAA2B,KAAK,oBAAoB,MAAM,QAAQ,MAAM,QAAQ,IAC3F,CAAC,CACH;CACF,CAAC;CACD,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC;CAClC,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC;CAElC,MAAM,cAAoB;EACxB,UAAe,OAAO,QAAQ,CAAC;CACjC;CACA,QAAQ,QAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CAC/D,MAAM,aAAyB;EAC7B,MAAM;EAGN,GAAI,QAAQ,gBAAgB,QAAQ,EAAE,OAAO,MAAM,IAAI,CAAC;EACxD,GAAI,QAAQ,UAAU,aAAa,EAAE,YAAY,QAAQ,SAAS,WAAW,IAAI,CAAC;EAClF,GAAI,QAAQ,UAAU,iBAClB,EAAE,gBAAgB,QAAQ,SAAS,eAAe,IAClD,CAAC;EACL,GAAI,QAAQ,UAAU,eAAe,EAAE,cAAc,QAAQ,SAAS,aAAa,IAAI,CAAC;CAC1F;CACA,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,KAAK,KAAK,wBAAwB;GAChD,QAAQ;IACN,MAAM,QAAQ,QAAQ,QAAA;IACtB,SAAS,QAAQ,QAAQ,WAAW,WAAW;GACjD;GACA,WAAW,CAAC,GAAG,uBAAuB;GACtC,cAAc,EAAE,eAAe,CAAC,eAAe,iBAAiB,EAAE;GAClE,OAAO;GACP,MAAM;EACR,CAAC;CACH,SAAS,OAAO;EACd,MAAM,UAAU,OAAO,QAAQ,GAAI;EACnC,IAAI,YAAY,MAAM,YAAY,kBAAkB,QAAQ,KAAK,IAAI,WAAW,SAAS;EACzF,MAAM;CACR,UAAU;EACR,QAAQ,QAAQ,oBAAoB,SAAS,KAAK;CACpD;CAEA,MAAM,QACJ,QACA,QACA,gBAEA,KAAK,KACH,QACA,QACA,aAAa,cAAc,KAAA,IAAY,EAAE,WAAW,YAAY,UAAU,IAAI,CAAC,CACjF;CACF,IAAI;CAwEJ,OAAO;EArEL,UAAU,QAAQ;EAClB,cAAc,QAAQ;EACtB,UAAU,QAAQ;EAClB,UAAU,QAAQ;EAClB;EACA,QAAQ;EACR;EACA,UAAU,QAAQ,iBAAiB,CAAC,MAClC,KAAK,uBAAuB;GAAE,QAAQ,YAAY,MAAM;GAAG,GAAG;EAAe,CAAC;EAChF,SAAS,QAAQ,gBAAgB,CAAC,MAChC,KAAK,sBAAsB;GAAE,QAAQ,YAAY,MAAM;GAAG,GAAG;EAAc,CAAC;EAC9E,OAAO,UAAU,OAAO,cAAc,CAAC,MAAM;GAC3C,MAAM,SAAS,YAAY;GAG3B,IAAI,QAAQ,SAAS,OAAO,QAAQ,OAAO,OAAO,MAAM;GACxD,MAAM,UAAU,KAAK,YAAY;IAC/B,UAAU,YAAY,QAAQ;IAC9B;IACA,WAAW,YAAY,aAAa;IACpC,GAAI,YAAY,UAAU,KAAA,IAAY,EAAE,OAAO,YAAY,MAAM,IAAI,CAAC;GACxE,CAAC;GACD,IAAI,CAAC,QAAQ,OAAO;GAGpB,OAAO,IAAI,SAAqB,MAAM,SAAS;IAC7C,MAAM,gBAAsB,KAAK,OAAO,MAAM;IAC9C,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;IACxD,QAAQ,KAAK,MAAM,IAAI,CAAC,CAAC,cAAc,OAAO,oBAAoB,SAAS,OAAO,CAAC;GACrF,CAAC;EACH;EACA,WACE,UACA,iBAAkE,CAAC,MAEnE,KAAK,kBAAkB;GACrB,UAAU,YAAY,QAAQ;GAC9B,GAAG;EACL,CAAC;EACH,WAAW,UAAU,QACnB,KAAK,gBAAgB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAI,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC;EAAG,CAAC;EAC7F,aAAa,UAAU,UACrB,KAAK,kBAAkB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAM,CAAC;EACtE,WAAW,UAAU,SACnB,KAAK,gBAAgB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAK,CAAC;EACnE,UAAU,UAAU,aAAa,CAAC,MAChC,KAAK,eAAe;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAW,CAAC;EACxE,SAAS,gBAAgB,CAAC,MACxB,gBACE;GACE;GACA,iBAAiB,YAAY;IAC3B,IAAI,MAAM,qBAAqB,IAAI,aAAa;IAChD,IAAI,CAAC,KAAK;KACR,sBAAM,IAAI,IAAI;KACd,qBAAqB,IAAI,eAAe,GAAG;IAC7C;IACA,IAAI,IAAI,OAAO;IACf,aAAa,KAAK,OAAO,OAAO;GAClC;GACA,QAAQ,OAAO,WAAW,KAAA,CAAS;EACrC,GACA,aACF;EACF,aAAa;GACX,YAAY,UAAU,OAAO,QAAQ,GAAM;GAC3C,OAAO;EACT;CAEU;AACd"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/control.ts","../src/events.ts","../src/mostra.ts"],"sourcesContent":["/**\n * Opening a home's control endpoint as a client (D45, D81, D150): the socket or pipe that\n * `instance.json` names, where a home's daemon serves the host protocol.\n *\n * - **POSIX:** a Unix socket in the owner-only runtime directory, opened with `node:net`.\n * - **Windows:** an owner-only named pipe, opened through the runtime's helper, `mostra-win\n * connect`. A pipe's old name in `instance.json` may be free for another user to take, and\n * Node.js can neither check who created a pipe nor stop its server acting as the client, so the\n * helper connects without letting the server impersonate the client, refuses a pipe whose owner\n * isn't this user, and relays the bytes over its standard streams (Opus review of WP-W1).\n *\n * The helper is the one beside the runtime the caller resolved: `MOSTRA_WIN_HELPER`, else the\n * runtime bundle's `bin/mostra-win.exe`, else a development checkout's Cargo build.\n */\nimport { spawn } from \"node:child_process\";\nimport { existsSync, realpathSync } from \"node:fs\";\nimport { createConnection, type Socket } from \"node:net\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Duplex } from \"node:stream\";\nimport { createDiagnostic, RpcError } from \"@mostra/protocol\";\nimport type { Environment } from \"./bundle/setup.ts\";\nimport type { Runtime } from \"./runtime.ts\";\n\n/** Options of {@link connectControl}. */\nexport interface ControlOptions {\n /** The runtime whose helper opens a Windows pipe; required on Windows. */\n readonly runtime?: Runtime | undefined;\n /** The environment: `MOSTRA_WIN_HELPER`. */\n readonly env?: Environment | undefined;\n /** How long to wait for the endpoint to answer. Default 5000. */\n readonly timeoutMs?: number | undefined;\n /** The operating system, `process.platform` by default. */\n readonly platform?: string | undefined;\n}\n\nconst PIPE_PREFIX = \"\\\\\\\\.\\\\pipe\\\\\";\n\n/** The helper's exit code for a pipe another user owns (`crates/mostra-win/src/win/connect.rs`). */\nconst NOT_OWNED_EXIT = 3;\n\nfunction unreachable(endpoint: string, why: string): RpcError {\n return new RpcError(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `Couldn't reach the instance at ${endpoint} (${why}).`,\n }),\n );\n}\n\n/**\n * The Windows helper beside `runtime`: `MOSTRA_WIN_HELPER`, else the bundle's\n * `bin/mostra-win.exe`, else, for a checkout's `packages/runtime/dist/bin.js`, Cargo's\n * `target/release` then `target/debug`. Throws `MOSTRA_ENV_HELPER_MISSING` when none exists.\n */\nexport function windowsHelper(runtime: Runtime | undefined, env: Environment): string {\n const override = env[\"MOSTRA_WIN_HELPER\"];\n if (override) {\n if (existsSync(override)) return override;\n throw missingHelper(`MOSTRA_WIN_HELPER names ${override}, which doesn't exist.`);\n }\n if (runtime === undefined) {\n throw missingHelper(\"No runtime was resolved, so Mostra's Windows helper can't be found.\");\n }\n const candidates =\n runtime.bundle !== undefined\n ? [join(runtime.bundle.root, \"bin\", \"mostra-win.exe\")]\n : [\"release\", \"debug\"].map((profile) =>\n resolve(\n // The runtime's own order (`helperPath`) starts from where its code really is, so a\n // link to its entry (a relative `MOSTRA_RUNTIME_BIN` through a junction) changes nothing.\n dirname(realEntry(runtime.runtimeEntry)),\n \"..\",\n \"..\",\n \"..\",\n \"target\",\n profile,\n \"mostra-win.exe\",\n ),\n );\n const found = candidates.find((candidate) => existsSync(candidate));\n if (found === undefined) {\n throw missingHelper(\n `Mostra's Windows helper isn't installed; looked for ${candidates.join(\", \")}.`,\n );\n }\n return found;\n}\n\nfunction realEntry(entry: string): string {\n try {\n return realpathSync.native(entry);\n } catch {\n return entry;\n }\n}\n\nfunction missingHelper(message: string): RpcError {\n return new RpcError(createDiagnostic(\"MOSTRA_ENV_HELPER_MISSING\", { message }));\n}\n\n/**\n * Connects to a home's control endpoint. Rejects with `MOSTRA_HOST_CONNECTION_CLOSED` when nothing\n * serves it in time or, on Windows, when another user owns the pipe; with\n * `MOSTRA_ENV_HELPER_MISSING` when the Windows helper can't be found.\n */\nexport function connectControl(endpoint: string, options: ControlOptions = {}): Promise<Duplex> {\n const timeoutMs = options.timeoutMs ?? 5000;\n if ((options.platform ?? process.platform) === \"win32\") {\n return connectPipe(\n endpoint,\n windowsHelper(options.runtime, options.env ?? process.env),\n timeoutMs,\n );\n }\n return new Promise((resolvePromise, reject) => {\n const socket: Socket = createConnection(endpoint);\n const timer = setTimeout(() => {\n socket.destroy();\n reject(unreachable(endpoint, \"timed out\"));\n }, timeoutMs);\n socket.once(\"error\", (error: NodeJS.ErrnoException) => {\n clearTimeout(timer);\n reject(unreachable(endpoint, error.code ?? \"failed\"));\n });\n socket.once(\"connect\", () => {\n clearTimeout(timer);\n resolvePromise(socket);\n });\n });\n}\n\nfunction connectPipe(endpoint: string, helper: string, timeoutMs: number): Promise<Duplex> {\n const name = endpoint.startsWith(PIPE_PREFIX) ? endpoint.slice(PIPE_PREFIX.length) : endpoint;\n return new Promise((resolvePromise, reject) => {\n const child = spawn(helper, [\"connect\", name], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n windowsHide: true,\n });\n let settled = false;\n const fail = (why: string): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n child.kill();\n reject(unreachable(endpoint, why));\n };\n const timer = setTimeout(() => fail(\"timed out\"), timeoutMs);\n child.once(\"error\", (error) => fail(error.message));\n child.once(\"exit\", (code) =>\n fail(code === NOT_OWNED_EXIT ? \"another user owns that pipe\" : `the helper exited ${code}`),\n );\n let text = \"\";\n child.stderr.setEncoding(\"utf8\");\n child.stderr.on(\"data\", (chunk: string) => {\n if (settled) return;\n text += chunk;\n const end = text.indexOf(\"\\n\");\n if (end < 0) return;\n let line: { connected?: boolean; code?: string; error?: string } = {};\n try {\n line = JSON.parse(text.slice(0, end)) as typeof line;\n } catch {\n // Treated as a failure below.\n }\n if (line.connected !== true) {\n fail(\n line.code === \"not-owned\"\n ? \"another user owns that pipe\"\n : (line.error ?? text.slice(0, end)),\n );\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolvePromise(Duplex.from({ readable: child.stdout, writable: child.stdin }));\n });\n });\n}\n","/**\n * Events as an async iterator (IN-08, D101): pages from `events/subscribe`, in cursor order. A\n * consumer that falls behind pauses the subscription instead of buffering without bound or\n * stalling the connection's other calls: past `highWaterMark` pages it unsubscribes, and once it\n * has caught up it subscribes again after the last cursor it received, so nothing is missed or\n * repeated. With a `name`, a page is acknowledged (`events/ack`) when the next one is asked for,\n * which is when the consumer has handled it; a stream of the same name later resumes there.\n */\nimport {\n createDiagnostic,\n type Diagnostic,\n type EventFilter,\n type EventRecord,\n EventsNotifyParams,\n eventsAckMethod,\n eventsSubscribeMethod,\n eventsUnsubscribeMethod,\n type MethodDefinition,\n RpcError,\n receivable,\n} from \"@mostra/protocol\";\n\n/** One page of events. */\nexport interface EventPage {\n /** The events, in cursor order. */\n readonly events: readonly EventRecord[];\n /** Everything up to this cursor was examined: the cursor to resume after. */\n readonly cursor: string;\n /** Set when compaction removed events in this range (`MOSTRA_HOST_CURSOR_COMPACTED`). */\n readonly compacted?: Diagnostic;\n}\n\n/** Options of a stream. */\nexport interface EventStreamOptions {\n /** Which events: artifacts and type patterns. Default: every event. */\n readonly filter?: EventFilter;\n /** Start after this cursor. Default: a named stream's acknowledged cursor, else now. */\n readonly after?: string;\n /** A durable subscriber name: pages are acknowledged under it as they are handled. */\n readonly name?: string;\n /** Pages held before the subscription pauses. Default 16. */\n readonly highWaterMark?: number;\n /** Ends the stream. */\n readonly signal?: AbortSignal;\n}\n\n/** What a stream needs of the session. */\nexport interface EventSession {\n call<P, R>(method: MethodDefinition<P, R>, params: P): Promise<R>;\n /** Registers a handler for `events/notify`; returns its removal. */\n onNotification(handler: (params: unknown) => void): () => void;\n /** Resolves when the session ends. */\n readonly closed: Promise<void>;\n}\n\n/** Opens a stream; nothing is subscribed until the first page is asked for. */\nexport function openEventStream(\n session: EventSession,\n options: EventStreamOptions,\n): AsyncIterable<EventPage> {\n return {\n [Symbol.asyncIterator]: () => new EventStream(session, options),\n };\n}\n\nclass EventStream implements AsyncIterator<EventPage> {\n readonly #session: EventSession;\n readonly #options: EventStreamOptions;\n readonly #high: number;\n readonly #queue: EventPage[] = [];\n #subscription: string | undefined;\n /** The cursor of the last page received, where a resumed subscription starts. */\n #received: string | undefined;\n /** The cursor of the last page handed out; handled once the next page is asked for. */\n #handedOut: string | undefined;\n /** The cursor of the last page the consumer handled, and of the last acknowledged. */\n #handled: string | undefined;\n #acked: string | undefined;\n #paused = false;\n /**\n * Pages that arrived while a subscribe was in flight, for a subscription not known yet: the\n * answer and its first page can reach the session in one chunk, and the page is dispatched\n * before the code awaiting the answer resumes. Kept until the answer names its ID.\n */\n #early: EventsNotifyParams[] | undefined;\n /** The unsubscribe of a pause; its answer means no page of that subscription follows. */\n #pausing: Promise<void> | undefined;\n /** A subscribe whose answer is on its way (`#subscribe`). */\n #subscribing: Promise<void> | undefined;\n #started = false;\n #ended: { error?: unknown } | undefined;\n #wake: (() => void) | undefined;\n readonly #removeHandler: () => void;\n\n constructor(session: EventSession, options: EventStreamOptions) {\n this.#session = session;\n this.#options = options;\n this.#high = Math.max(1, options.highWaterMark ?? 16);\n this.#removeHandler = session.onNotification((params) => this.#receive(params));\n void session.closed.then(() =>\n this.#end(\n new RpcError(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: \"The Mostra instance exited while its events were being read.\",\n }),\n ),\n ),\n );\n options.signal?.addEventListener(\"abort\", () => void this.return(), { once: true });\n }\n\n #receive(params: unknown): void {\n // Events of types a newer instance added arrive too, never dropping their page (#45).\n const parsed = receivable(EventsNotifyParams).safeParse(params);\n if (!parsed.success) return;\n if (parsed.data.subscriptionId !== this.#subscription) {\n this.#early?.push(parsed.data);\n return;\n }\n this.#accept(parsed.data);\n }\n\n #accept({ events, cursor, compacted }: EventsNotifyParams): void {\n this.#queue.push({ events, cursor, ...(compacted ? { compacted } : {}) });\n this.#received = cursor;\n if (this.#queue.length >= this.#high && !this.#paused) this.#pausing = this.#pause();\n this.#wake?.();\n }\n\n /**\n * Subscribes after `after`. While the answer is on its way the call is `#subscribing`, so\n * `return()` can wait for it and end the subscription it names (GPT review finding 5).\n */\n #subscribe(after: string | undefined): Promise<void> {\n const opening = this.#open(after);\n this.#subscribing = opening;\n return opening.finally(() => {\n if (this.#subscribing === opening) this.#subscribing = undefined;\n });\n }\n\n async #open(after: string | undefined): Promise<void> {\n this.#early = [];\n let answer: { subscriptionId: string; cursor: string };\n let early: EventsNotifyParams[];\n try {\n answer = await this.#session.call(eventsSubscribeMethod, {\n ...(this.#options.filter ? { filter: this.#options.filter } : {}),\n ...(after !== undefined ? { after } : {}),\n ...(this.#options.name ? { name: this.#options.name } : {}),\n });\n } finally {\n early = this.#early;\n this.#early = undefined;\n }\n // Synchronous from here: no page can be dispatched before the ID is known.\n this.#subscription = answer.subscriptionId;\n this.#received ??= answer.cursor;\n // The pages of this subscription that came before its answer was handled, in order.\n for (const page of early) if (page.subscriptionId === answer.subscriptionId) this.#accept(page);\n }\n\n async #pause(): Promise<void> {\n this.#paused = true;\n const id = this.#subscription;\n if (id === undefined) return;\n // What the consumer handled is acknowledged while the subscription still exists: the ack goes\n // out before the unsubscribe on the connection, which keeps their order.\n const acked = this.#sendAck(id);\n // Pages already on their way still arrive under this ID and are kept. The answer comes after\n // them on the connection, so once it is here the subscription is over.\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: id })\n .catch(() => undefined);\n // A pause runs from a page's arrival, where nobody awaits it, so it never rejects (Opus review\n // finding 7): an ack the instance refused (or a connection gone) ends the stream with that\n // error, which the consumer's next() then throws.\n await acked.catch((error: unknown) => this.#end(error));\n if (this.#subscription === id) this.#subscription = undefined;\n }\n\n async #resume(): Promise<void> {\n await this.#pausing;\n this.#pausing = undefined;\n this.#paused = false;\n await this.#subscribe(this.#received);\n }\n\n /**\n * Acknowledges what the consumer handled under subscription `id`, if a named stream has handled\n * more than it acknowledged. The line is written when this is called (the session writes each\n * call at once), so it is ordered before anything sent after it.\n */\n #sendAck(id: string): Promise<void> {\n const cursor = this.#handled;\n if (!this.#options.name || cursor === undefined || cursor === this.#acked)\n return Promise.resolve();\n this.#acked = cursor;\n return this.#session\n .call(eventsAckMethod, { subscriptionId: id, cursor })\n .then(() => undefined);\n }\n\n #end(error?: unknown): void {\n if (this.#ended) return;\n this.#ended = error === undefined ? {} : { error };\n this.#removeHandler();\n this.#wake?.();\n }\n\n async next(): Promise<IteratorResult<EventPage>> {\n if (!this.#started) {\n this.#started = true;\n await this.#subscribe(this.#options.after);\n }\n // Asking for the next page means the consumer handled the one before it.\n if (this.#handedOut !== undefined) this.#handled = this.#handedOut;\n if (this.#paused && !this.#ended && this.#queue.length <= this.#high / 2) await this.#resume();\n // Never under a subscription being paused: its unsubscribe may already be on its way, and\n // the pause acknowledges what was handled before it.\n if (!this.#paused && this.#subscription !== undefined) await this.#sendAck(this.#subscription);\n for (;;) {\n const page = this.#queue.shift();\n if (page !== undefined) {\n this.#handedOut = page.cursor;\n return { done: false, value: page };\n }\n if (this.#ended) {\n if (this.#ended.error !== undefined) throw this.#ended.error;\n return { done: true, value: undefined };\n }\n if (this.#paused) {\n await this.#resume();\n await this.#sendAck(this.#subscription as string);\n }\n await new Promise<void>((done) => {\n this.#wake = done;\n });\n this.#wake = undefined;\n }\n }\n\n /**\n * Ends the stream: the subscription ends, and a named stream acknowledges every page the\n * consumer handled, never the last one handed out (the loop may have stopped inside it).\n */\n async return(): Promise<IteratorResult<EventPage>> {\n this.#end();\n this.#queue.length = 0;\n await this.#pausing;\n // A subscribe on its way names a subscription that exists on the instance once it is\n // answered: wait for it, so it is ended below like any other.\n await this.#subscribing?.catch(() => undefined);\n const id = this.#subscription;\n if (id !== undefined) {\n this.#subscription = undefined;\n const acked = this.#sendAck(id);\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: id })\n .catch(() => undefined);\n await acked.catch(() => undefined);\n } else if (this.#options.name && this.#handled !== undefined && this.#handled !== this.#acked) {\n // Paused when it ended, with pages handled since: a subscription of the name, just long\n // enough to acknowledge them (its pages are ignored; the handler is gone).\n await this.#subscribe(this.#received).catch(() => undefined);\n const again = this.#subscription;\n if (again !== undefined) {\n this.#subscription = undefined;\n const acked = this.#sendAck(again);\n await this.#session\n .call(eventsUnsubscribeMethod, { subscriptionId: again })\n .catch(() => undefined);\n await acked.catch(() => undefined);\n }\n }\n return { done: true, value: undefined };\n }\n}\n","/**\n * `createMostra` (HO-04, D25, D58, D59): a JavaScript host's handle on its own Mostra instance.\n * It finds a verified runtime (the pinned bundle, downloaded once), spawns `mostra-runtime host`\n * for the host's home over stdio, opens the protocol session, serves the host's ports and offers\n * the calls every host makes. The instance lives exactly as long as this handle: `close()` ends\n * it, and so does the host's process exiting.\n *\n * ```ts\n * const mostra = await createMostra({\n * home: \"/var/lib/inbox/mostra\",\n * client: { name: \"inbox-app\", version: \"1.4.0\" },\n * ports: { submissions: { deliver: async (envelope) => ({ accepted: true }) } },\n * });\n * const receipt = await mostra.publish(\"/work/inbox\");\n * const outcome = await mostra.wait(receipt.artifactId, \"submitted\");\n * await mostra.close();\n * ```\n */\nimport { type ChildProcess, spawn } from \"node:child_process\";\nimport { resolve } from \"node:path\";\nimport {\n type AnyMethod,\n ARTIFACT_PROGRESS,\n type ArtifactRef,\n type ArtifactSummary,\n artifactPublishMethod,\n artifactVerifyMethod,\n type CapabilityLink,\n type ControlsPolicy,\n createDiagnostic,\n type DependencyPolicy,\n type Diagnostic,\n type DraftMirror,\n EVENTS_NOTIFY,\n encodeLine,\n HOST_PROTOCOL_REVISIONS,\n type HomeConfig,\n type InstanceCapabilities,\n type InstanceInfo,\n LineSplitter,\n type LivePushParams,\n type LivePushResult,\n livePushMethod,\n type MethodDefinition,\n type MostraInitializeResult,\n mostraInitializeMethod,\n type PortIdentityResolveParams,\n type PortName,\n type PortSecretsGetParams,\n type PortTrustedControlsActivatedParams,\n type PublishReceipt,\n portDraftsMirrorMethod,\n portIdentityResolveMethod,\n portSecretsGetMethod,\n portSubmissionsDeliverMethod,\n portTrustedControlsActivatedMethod,\n portTrustedControlsPolicyMethod,\n type Results,\n type ResultsMarkdown,\n RpcError,\n RpcPeer,\n receivable,\n resultsGetMethod,\n type SourceRef,\n type StateGetResult,\n type StatePatchParams,\n type StatePatchResult,\n type SubmissionEnvelope,\n type SubmissionPolicy,\n type SubmissionsDeliverResult,\n type SubmitChannel,\n stateGetMethod,\n statePatchMethod,\n type VerifyVerdict,\n type ViewerSpec,\n type ViewUrlParams,\n viewUrlMethod,\n WAIT_MAX_MS,\n type WaitCondition,\n type WaitResult,\n waitMethod,\n} from \"@mostra/protocol\";\nimport type { InstallProgress } from \"./bundle/cache.ts\";\nimport type { Environment } from \"./bundle/setup.ts\";\nimport { type EventPage, type EventStreamOptions, openEventStream } from \"./events.ts\";\nimport { type Runtime, resolveRuntime } from \"./runtime.ts\";\nimport { sdkVersion } from \"./version.ts\";\n\n/**\n * The ports a host may serve (INTERFACES §7). Each is optional and has a standalone default;\n * Mostra calls a port only on the instance this handle spawned, and every call is idempotent by\n * its stable ID, so a port may see the same call again after a crash.\n */\nexport interface Ports {\n /**\n * Names the person behind a link minted without a viewer, from the request's headers (D143):\n * `{ ref, name? }` (one viewer per `ref`), or `null` for the home's local user. Answer within\n * 5 s; a failure refuses the link rather than guess.\n */\n readonly identity?: {\n resolve(request: PortIdentityResolveParams): ViewerSpec | null | Promise<ViewerSpec | null>;\n };\n /** Receives each submission once it is durable (D58, D105). Answer within 30 s. */\n readonly submissions?: {\n deliver(\n envelope: SubmissionEnvelope,\n ): SubmissionsDeliverResult | Promise<SubmissionsDeliverResult>;\n };\n /** Receives each viewer draft's latest revision, best effort, never retried (IN-06). */\n readonly drafts?: { mirror(draft: DraftMirror): void | Promise<void> };\n /** Answers `secret()` first, before the home's store (D131); `null` falls back. */\n readonly secrets?: {\n get(request: PortSecretsGetParams): string | null | Promise<string | null>;\n };\n /** Decides each view's submit channels and trusted controls (D108). Answer within 5 s. */\n readonly trustedControls?: {\n policy(artifact: ArtifactSummary): ControlsPolicy | Promise<ControlsPolicy>;\n activated?(event: PortTrustedControlsActivatedParams): void | Promise<void>;\n };\n}\n\n/** Home settings a host chooses (INTERFACES §6 `HomeConfig`), kept in the home's `config.json`. */\nexport interface HomeSettings {\n /** The submission policy for artifacts whose publish names none (D102). */\n readonly submission?: SubmissionPolicy;\n /** The channels that may submit (D40, D108); absent, both. */\n readonly submitChannels?: readonly SubmitChannel[];\n /** How packages enter an artifact's lockfile (D43, D95). */\n readonly dependencies?: DependencyPolicy;\n}\n\n/** Options of {@link createMostra}. */\nexport interface MostraOptions {\n /** The home this host owns (D45). Created 0700 when missing. */\n readonly home: string;\n /**\n * Who the host is. The name owns the host's deliveries across restarts (D105), so keep it\n * stable; it defaults to `mostra-sdk`.\n */\n readonly client?: { readonly name: string; readonly version?: string };\n /** The ports this host serves. */\n readonly ports?: Ports;\n /**\n * Whether submissions made while this host is away wait for it (D105): the registered ports\n * are recorded as ports this host always provides. Default `true`; `false` leaves the home's\n * record as it is.\n */\n readonly expectPorts?: boolean;\n /** Home settings, kept in the home's `config.json` for every later client. */\n readonly settings?: HomeSettings;\n /** An extracted runtime bundle to use instead of the pinned one. */\n readonly bundle?: string;\n /** The gateway's bind address and URL host (D99); loopback by default. */\n readonly bind?: string;\n /** The host name URLs use, which a wildcard `bind` needs (D99). */\n readonly urlHost?: string;\n /** The instance's environment. Default: this process's. */\n readonly env?: Environment;\n /** Each line the instance logs (JSON, on its stderr). A callback that throws is ignored. */\n readonly onLog?: (line: string) => void;\n /** Progress of the first download or setup of the runtime bundle. A throw is ignored. */\n readonly onProgress?: (progress: InstallProgress) => void;\n /**\n * An exception a port threw; the instance got `MOSTRA_HOST_INTERNAL_ERROR` and will retry. A\n * callback that throws is ignored, and the instance is answered all the same.\n */\n readonly onPortError?: (error: unknown, method: string) => void;\n /** Gives up finding the runtime and starting the instance. */\n readonly signal?: AbortSignal;\n}\n\n/** How the instance ended. */\nexport interface InstanceExit {\n /** Its exit code: 0 after a clean stop. */\n readonly code: number | null;\n /** The signal that ended it, if one did. */\n readonly signal: NodeJS.Signals | null;\n}\n\n/** An artifact by ID (`art_…`), by source path, or as the protocol's reference. */\nexport type ArtifactInput = string | ArtifactRef;\n\n/** A host's handle on its instance. */\nexport interface Mostra {\n /** The instance: release, home, platform, PID and mode. */\n readonly instance: InstanceInfo;\n /** What the instance serves. */\n readonly capabilities: InstanceCapabilities;\n /** The negotiated host protocol revision. */\n readonly revision: string;\n /** The session's warnings (an unsupported port, for example). */\n readonly warnings: readonly Diagnostic[];\n /** The runtime it runs on. */\n readonly runtime: Runtime;\n /** Resolves when the instance has exited, for whatever reason. */\n readonly closed: Promise<InstanceExit>;\n /** Calls any host protocol method by its definition from `@mostra/protocol`. */\n call<P, R>(method: MethodDefinition<P, R>, params: P, options?: CallOptions): Promise<R>;\n /** `artifact/publish`: verifies (D41) and publishes; durable before it resolves (H-07). */\n publish(\n source: ArtifactInput,\n options?: {\n readonly force?: boolean;\n readonly policy?: SubmissionPolicy;\n readonly requestId?: string;\n },\n ): Promise<PublishReceipt>;\n /** `artifact/verify`: one verdict, never a publish (D138). */\n verify(\n source: ArtifactInput,\n options?: { readonly screenshots?: \"on-failure\" | \"always\" | \"never\" },\n ): Promise<VerifyVerdict>;\n /**\n * `wait` (IN-10, D103): until the condition holds or `timeoutMs` passes (default: the longest\n * timer, about 24.8 days). `still_waiting` carries the cursor to wait from next; an aborted wait\n * rejects with the signal's reason and loses nothing.\n */\n wait(\n artifact: ArtifactInput,\n until: WaitCondition,\n options?: {\n readonly after?: string;\n readonly timeoutMs?: number;\n readonly signal?: AbortSignal;\n },\n ): Promise<WaitResult>;\n /** `results/get`: a submission as JSON (the default) or Markdown (INTERFACES §8). */\n results(\n artifact: ArtifactInput,\n options?: { readonly submission?: string; readonly format?: \"json\" },\n ): Promise<Results>;\n /** `results/get` as Markdown, for an agent to read. */\n results(\n artifact: ArtifactInput,\n options: { readonly submission?: string; readonly format: \"md\" },\n ): Promise<ResultsMarkdown>;\n /** `state/get`: artifact-scoped fields (D63). */\n getState(artifact: ArtifactInput, ids?: readonly string[]): Promise<StateGetResult>;\n /** `state/patch`: writes artifact-scoped fields through compare-and-set (D63). */\n patchState(\n artifact: ArtifactInput,\n patch: Omit<StatePatchParams, \"artifact\">,\n ): Promise<StatePatchResult>;\n /** `live/push`: a live source's next value (D56). */\n pushLive(\n artifact: ArtifactInput,\n push: Omit<LivePushParams, \"artifact\">,\n ): Promise<LivePushResult>;\n /** `view/url`: a new capability link (D97). */\n viewUrl(\n artifact: ArtifactInput,\n options?: Omit<ViewUrlParams, \"artifact\">,\n ): Promise<CapabilityLink>;\n /**\n * The events matching `options.filter`, page by page, in cursor order, with no gap across a\n * pause (`events/subscribe`). With a `name`, each page is acknowledged when the next one is\n * asked for, so a later stream of that name resumes after the last page handled.\n */\n events(options?: EventStreamOptions): AsyncIterable<EventPage>;\n /** Ends the instance: its stdio closes, and it is stopped if it doesn't exit within 10 s. */\n close(): Promise<InstanceExit>;\n}\n\n/** Options of one call. */\nexport interface CallOptions {\n /** Gives up after this many milliseconds with `MOSTRA_HOST_TIMEOUT`. Default: none. */\n readonly timeoutMs?: number;\n}\n\n/** The port methods the SDK serves, by port. */\nconst PORT_METHODS = {\n identity: [portIdentityResolveMethod],\n submissions: [portSubmissionsDeliverMethod],\n drafts: [portDraftsMirrorMethod],\n secrets: [portSecretsGetMethod],\n trustedControls: [portTrustedControlsPolicyMethod, portTrustedControlsActivatedMethod],\n} as const satisfies Record<keyof Ports, readonly AnyMethod[]>;\n\n/** The client name of an SDK host that names none. */\nexport const SDK_CLIENT_NAME = \"mostra-sdk\";\n\n/** `art_…` is an artifact ID; anything else a source path, resolved against the working directory. */\nexport function artifactRef(input: ArtifactInput): ArtifactRef & SourceRef {\n if (typeof input !== \"string\") return input as ArtifactRef & SourceRef;\n return input.startsWith(\"art_\") ? { artifactId: input } : { path: resolve(input) };\n}\n\nfunction closedError(message: string): RpcError {\n return new RpcError(createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", { message }));\n}\n\n/** The handler for one port method, its params already checked. */\nfunction portHandler(ports: Ports, method: string): ((params: unknown) => unknown) | undefined {\n switch (method) {\n case portIdentityResolveMethod.method:\n return (\n ports.identity && ((params) => ports.identity?.resolve(params as PortIdentityResolveParams))\n );\n case portSubmissionsDeliverMethod.method:\n return (\n ports.submissions && ((params) => ports.submissions?.deliver(params as SubmissionEnvelope))\n );\n case portDraftsMirrorMethod.method:\n return ports.drafts && ((params) => ports.drafts?.mirror(params as DraftMirror));\n case portSecretsGetMethod.method:\n return ports.secrets && ((params) => ports.secrets?.get(params as PortSecretsGetParams));\n case portTrustedControlsPolicyMethod.method:\n return (\n ports.trustedControls &&\n ((params) =>\n ports.trustedControls?.policy((params as { artifact: ArtifactSummary }).artifact))\n );\n case portTrustedControlsActivatedMethod.method:\n return (\n ports.trustedControls &&\n (async (params) => {\n await ports.trustedControls?.activated?.(params as PortTrustedControlsActivatedParams);\n return {};\n })\n );\n default:\n return undefined;\n }\n}\n\n/** The port names `ports` registers; anything unknown is a mistake worth failing on. */\nfunction portNames(ports: Ports): PortName[] {\n const names: PortName[] = [];\n for (const [name, value] of Object.entries(ports)) {\n if (value === undefined) continue;\n if (!(name in PORT_METHODS)) {\n throw new TypeError(\n `createMostra: no port \"${name}\" (the SDK serves ${Object.keys(PORT_METHODS).join(\", \")}).`,\n );\n }\n names.push(name as PortName);\n }\n return names;\n}\n\n/** Ends the child: stdin closed, then SIGTERM after `graceMs`, then SIGKILL after 5 s more. */\nfunction stopChild(\n child: ChildProcess,\n exited: Promise<InstanceExit>,\n graceMs: number,\n): Promise<InstanceExit> {\n child.stdin?.end();\n const term = setTimeout(() => child.kill(\"SIGTERM\"), graceMs);\n const kill = setTimeout(() => child.kill(\"SIGKILL\"), graceMs + 5000);\n return exited.finally(() => {\n clearTimeout(term);\n clearTimeout(kill);\n });\n}\n\n/** `callback`, made unable to throw into its caller. */\nfunction guarded<A extends unknown[]>(\n callback: ((...args: A) => void) | undefined,\n): ((...args: A) => void) | undefined {\n if (callback === undefined) return undefined;\n return (...args: A) => {\n try {\n callback(...args);\n } catch {\n // The host's own code failed; Mostra's work goes on.\n }\n };\n}\n\n/**\n * Spawns an instance for `home` and opens its session. Rejects with the instance's own diagnostic\n * when it can't start (`MOSTRA_HOME_LOCKED`, `MOSTRA_PLATFORM_UNSUPPORTED` …), or with the\n * runtime's (`MOSTRA_ENV_BUNDLE_*`) when no verified runtime can be found.\n */\nexport async function createMostra(options: MostraOptions): Promise<Mostra> {\n // The host's callbacks run inside Mostra's own handlers (a pipe's data listener, the RPC\n // peer's error path, a download's progress): one that throws must never escape into the host's\n // process as an uncaught exception or unhandled rejection, nor stop an answer (Opus review\n // finding 6). A callback that throws is ignored.\n const onLog = guarded(options.onLog);\n const onProgress = guarded(options.onProgress);\n const onPortError = guarded(options.onPortError);\n const ports = options.ports ?? {};\n const names = portNames(ports);\n const env = options.env ?? process.env;\n const home = resolve(options.home);\n const runtime = await resolveRuntime({\n bundle: options.bundle,\n env,\n signal: options.signal,\n ...(onProgress ? { onProgress } : {}),\n });\n options.signal?.throwIfAborted();\n const args = [runtime.runtimeEntry, \"host\", \"--home\", home];\n if (options.bind !== undefined) args.push(\"--bind\", options.bind);\n if (options.urlHost !== undefined) args.push(\"--url-host\", options.urlHost);\n const child = spawn(runtime.node, args, {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: env as NodeJS.ProcessEnv,\n });\n const logTail: string[] = [];\n const exited = new Promise<InstanceExit>((done) => {\n child.on(\"close\", (code, signal) => done({ code, signal }));\n child.on(\"error\", () => done({ code: null, signal: null }));\n });\n let spawnError: Error | undefined;\n child.on(\"error\", (error) => {\n spawnError = error;\n });\n const errLines = new LineSplitter();\n child.stderr?.setEncoding(\"utf8\");\n child.stderr?.on(\"data\", (chunk: string) => {\n for (const line of errLines.push(chunk)) {\n logTail.push(line);\n if (logTail.length > 20) logTail.shift();\n onLog?.(line);\n }\n });\n\n const notificationHandlers = new Map<string, Set<(params: unknown) => void>>();\n const peer = new RpcPeer({\n send: (message) => {\n if (child.stdin?.writable) child.stdin.write(encodeLine(message));\n },\n onRequest: async (method, params) => {\n const definition = Object.values(PORT_METHODS)\n .flat()\n .find((candidate) => candidate.method === method) as\n | MethodDefinition<unknown, unknown>\n | undefined;\n const handler = definition && portHandler(ports, method);\n if (!definition || !handler) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_METHOD_UNKNOWN\", {\n message: `This host serves no method ${method}.`,\n }),\n -32601,\n );\n }\n // A newer instance may send enum values and union members this host's schemas don't\n // name yet (#45): the port's handler meets them, as it meets any value it can't act on.\n const parsed = receivable(definition.params).safeParse(params);\n if (!parsed.success) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_INVALID_PARAMS\", {\n message: `The params of ${method} don't match its schema.`,\n }),\n -32602,\n );\n }\n const answer = await handler(parsed.data);\n const checked = definition.result.safeParse(answer ?? null);\n if (!checked.success) {\n throw new RpcError(\n createDiagnostic(\"MOSTRA_HOST_INTERNAL_ERROR\", {\n message: `The host's ${method} answered something its schema refuses.`,\n }),\n );\n }\n return checked.data;\n },\n onNotification: (method, params) => {\n for (const handler of notificationHandlers.get(method) ?? []) handler(params);\n },\n onInternalError: (error, method) => onPortError?.(error, method),\n });\n const lines = new LineSplitter();\n child.stdout?.setEncoding(\"utf8\");\n child.stdout?.on(\"data\", (chunk: string) => {\n for (const line of lines.push(chunk)) peer.receiveLine(line);\n });\n child.stdout?.on(\"close\", () => {\n peer.close(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `The Mostra instance for ${home} exited${logTail.length > 0 ? `; its last log line: ${logTail.at(-1)}` : \"\"}.`,\n }),\n );\n });\n // A pipe to an instance that died fails its next write (EPIPE) on its own schedule. Without a\n // listener that error is an uncaught exception in the host's own process: every pipe has one,\n // and a write that failed ends the session at once, as the instance's exit does.\n child.stdin?.on(\"error\", (error: NodeJS.ErrnoException) => {\n peer.close(\n createDiagnostic(\"MOSTRA_HOST_CONNECTION_CLOSED\", {\n message: `The Mostra instance for ${home} stopped reading (${error.code ?? error.message}).`,\n }),\n );\n });\n child.stdout?.on(\"error\", () => {});\n child.stderr?.on(\"error\", () => {});\n\n const abort = (): void => {\n void stopChild(child, exited, 0);\n };\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n const homeConfig: HomeConfig = {\n path: home,\n // What this host always provides, an empty list included: a host that stopped serving a port\n // clears the record, so its absence is never awaited (Opus review of WP-12, finding 7).\n ...(options.expectPorts !== false ? { ports: names } : {}),\n ...(options.settings?.submission ? { submission: options.settings.submission } : {}),\n ...(options.settings?.submitChannels\n ? { submitChannels: options.settings.submitChannels }\n : {}),\n ...(options.settings?.dependencies ? { dependencies: options.settings.dependencies } : {}),\n };\n let session: MostraInitializeResult;\n try {\n session = await peer.call(mostraInitializeMethod, {\n client: {\n name: options.client?.name ?? SDK_CLIENT_NAME,\n version: options.client?.version ?? sdkVersion(),\n },\n revisions: [...HOST_PROTOCOL_REVISIONS],\n capabilities: { notifications: [EVENTS_NOTIFY, ARTIFACT_PROGRESS] },\n ports: names,\n home: homeConfig,\n });\n } catch (error) {\n await stopChild(child, exited, 2000);\n if (spawnError) throw closedError(`Couldn't start ${runtime.node}: ${spawnError.message}`);\n throw error;\n } finally {\n options.signal?.removeEventListener(\"abort\", abort);\n }\n\n const call = <P, R>(\n method: MethodDefinition<P, R>,\n params: P,\n callOptions?: CallOptions,\n ): Promise<R> =>\n peer.call(\n method,\n params,\n callOptions?.timeoutMs !== undefined ? { timeoutMs: callOptions.timeoutMs } : {},\n );\n let closing: Promise<InstanceExit> | undefined;\n\n const mostra: Mostra = {\n instance: session.instance,\n capabilities: session.capabilities,\n revision: session.revision,\n warnings: session.warnings,\n runtime,\n closed: exited,\n call,\n publish: (source, publishOptions = {}) =>\n call(artifactPublishMethod, { source: artifactRef(source), ...publishOptions }),\n verify: (source, verifyOptions = {}) =>\n call(artifactVerifyMethod, { source: artifactRef(source), ...verifyOptions }),\n wait: (artifact, until, waitOptions = {}) => {\n const signal = waitOptions.signal;\n // Checked before the request goes out: nothing is sent for a wait already given up, so no\n // answer is left without a handler (GPT review finding 4).\n if (signal?.aborted) return Promise.reject(signal.reason);\n const pending = call(waitMethod, {\n artifact: artifactRef(artifact),\n until,\n timeoutMs: waitOptions.timeoutMs ?? WAIT_MAX_MS,\n ...(waitOptions.after !== undefined ? { after: waitOptions.after } : {}),\n });\n if (!signal) return pending;\n // The instance's wait runs on until its own timeout; its late answer, or the connection's\n // end, is handled here and dropped.\n return new Promise<WaitResult>((done, fail) => {\n const onAbort = (): void => fail(signal.reason);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n pending.then(done, fail).finally(() => signal.removeEventListener(\"abort\", onAbort));\n });\n },\n results: ((\n artifact: ArtifactInput,\n resultsOptions: { submission?: string; format?: \"json\" | \"md\" } = {},\n ) =>\n call(resultsGetMethod, {\n artifact: artifactRef(artifact),\n ...resultsOptions,\n })) as Mostra[\"results\"],\n getState: (artifact, ids) =>\n call(stateGetMethod, { artifact: artifactRef(artifact), ...(ids ? { ids: [...ids] } : {}) }),\n patchState: (artifact, patch) =>\n call(statePatchMethod, { artifact: artifactRef(artifact), ...patch }),\n pushLive: (artifact, push) =>\n call(livePushMethod, { artifact: artifactRef(artifact), ...push }),\n viewUrl: (artifact, urlOptions = {}) =>\n call(viewUrlMethod, { artifact: artifactRef(artifact), ...urlOptions }),\n events: (streamOptions = {}) =>\n openEventStream(\n {\n call,\n onNotification: (handler) => {\n let set = notificationHandlers.get(EVENTS_NOTIFY);\n if (!set) {\n set = new Set();\n notificationHandlers.set(EVENTS_NOTIFY, set);\n }\n set.add(handler);\n return () => set?.delete(handler);\n },\n closed: exited.then(() => undefined),\n },\n streamOptions,\n ),\n close: () => {\n closing ??= stopChild(child, exited, 10_000);\n return closing;\n },\n };\n return mostra;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,cAAc;;AAGpB,MAAM,iBAAiB;AAEvB,SAAS,YAAY,UAAkB,KAAuB;CAC5D,OAAO,IAAI,SACT,iBAAiB,iCAAiC,EAChD,SAAS,kCAAkC,SAAS,IAAI,IAAI,IAC9D,CAAC,CACH;AACF;;;;;;AAOA,SAAgB,cAAc,SAA8B,KAA0B;CACpF,MAAM,WAAW,IAAI;CACrB,IAAI,UAAU;EACZ,IAAI,WAAW,QAAQ,GAAG,OAAO;EACjC,MAAM,cAAc,2BAA2B,SAAS,uBAAuB;CACjF;CACA,IAAI,YAAY,KAAA,GACd,MAAM,cAAc,qEAAqE;CAE3F,MAAM,aACJ,QAAQ,WAAW,KAAA,IACf,CAAC,KAAK,QAAQ,OAAO,MAAM,OAAO,gBAAgB,CAAC,IACnD,CAAC,WAAW,OAAO,CAAC,CAAC,KAAK,YACxB,QAGE,QAAQ,UAAU,QAAQ,YAAY,CAAC,GACvC,MACA,MACA,MACA,UACA,SACA,gBACF,CACF;CACN,MAAM,QAAQ,WAAW,MAAM,cAAc,WAAW,SAAS,CAAC;CAClE,IAAI,UAAU,KAAA,GACZ,MAAM,cACJ,uDAAuD,WAAW,KAAK,IAAI,EAAE,EAC/E;CAEF,OAAO;AACT;AAEA,SAAS,UAAU,OAAuB;CACxC,IAAI;EACF,OAAO,aAAa,OAAO,KAAK;CAClC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,cAAc,SAA2B;CAChD,OAAO,IAAI,SAAS,iBAAiB,6BAA6B,EAAE,QAAQ,CAAC,CAAC;AAChF;;;;;;AAOA,SAAgB,eAAe,UAAkB,UAA0B,CAAC,GAAoB;CAC9F,MAAM,YAAY,QAAQ,aAAa;CACvC,KAAK,QAAQ,YAAY,QAAQ,cAAc,SAC7C,OAAO,YACL,UACA,cAAc,QAAQ,SAAS,QAAQ,OAAO,QAAQ,GAAG,GACzD,SACF;CAEF,OAAO,IAAI,SAAS,gBAAgB,WAAW;EAC7C,MAAM,SAAiB,iBAAiB,QAAQ;EAChD,MAAM,QAAQ,iBAAiB;GAC7B,OAAO,QAAQ;GACf,OAAO,YAAY,UAAU,WAAW,CAAC;EAC3C,GAAG,SAAS;EACZ,OAAO,KAAK,UAAU,UAAiC;GACrD,aAAa,KAAK;GAClB,OAAO,YAAY,UAAU,MAAM,QAAQ,QAAQ,CAAC;EACtD,CAAC;EACD,OAAO,KAAK,iBAAiB;GAC3B,aAAa,KAAK;GAClB,eAAe,MAAM;EACvB,CAAC;CACH,CAAC;AACH;AAEA,SAAS,YAAY,UAAkB,QAAgB,WAAoC;CACzF,MAAM,OAAO,SAAS,WAAW,WAAW,IAAI,SAAS,MAAM,CAAkB,IAAI;CACrF,OAAO,IAAI,SAAS,gBAAgB,WAAW;EAC7C,MAAM,QAAQ,MAAM,QAAQ,CAAC,WAAW,IAAI,GAAG;GAC7C,OAAO;IAAC;IAAQ;IAAQ;GAAM;GAC9B,aAAa;EACf,CAAC;EACD,IAAI,UAAU;EACd,MAAM,QAAQ,QAAsB;GAClC,IAAI,SAAS;GACb,UAAU;GACV,aAAa,KAAK;GAClB,MAAM,KAAK;GACX,OAAO,YAAY,UAAU,GAAG,CAAC;EACnC;EACA,MAAM,QAAQ,iBAAiB,KAAK,WAAW,GAAG,SAAS;EAC3D,MAAM,KAAK,UAAU,UAAU,KAAK,MAAM,OAAO,CAAC;EAClD,MAAM,KAAK,SAAS,SAClB,KAAK,SAAS,iBAAiB,gCAAgC,qBAAqB,MAAM,CAC5F;EACA,IAAI,OAAO;EACX,MAAM,OAAO,YAAY,MAAM;EAC/B,MAAM,OAAO,GAAG,SAAS,UAAkB;GACzC,IAAI,SAAS;GACb,QAAQ;GACR,MAAM,MAAM,KAAK,QAAQ,IAAI;GAC7B,IAAI,MAAM,GAAG;GACb,IAAI,OAA+D,CAAC;GACpE,IAAI;IACF,OAAO,KAAK,MAAM,KAAK,MAAM,GAAG,GAAG,CAAC;GACtC,QAAQ,CAER;GACA,IAAI,KAAK,cAAc,MAAM;IAC3B,KACE,KAAK,SAAS,cACV,gCACC,KAAK,SAAS,KAAK,MAAM,GAAG,GAAG,CACtC;IACA;GACF;GACA,UAAU;GACV,aAAa,KAAK;GAClB,eAAe,OAAO,KAAK;IAAE,UAAU,MAAM;IAAQ,UAAU,MAAM;GAAM,CAAC,CAAC;EAC/E,CAAC;CACH,CAAC;AACH;;;;;;;;;;;;ACxHA,SAAgB,gBACd,SACA,SAC0B;CAC1B,OAAO,GACJ,OAAO,sBAAsB,IAAI,YAAY,SAAS,OAAO,EAChE;AACF;AAEA,IAAM,cAAN,MAAsD;CACpD;CACA;CACA;CACA,SAA+B,CAAC;CAChC;;CAEA;;CAEA;;CAEA;CACA;CACA,UAAU;;;;;;CAMV;;CAEA;;CAEA;CACA,WAAW;CACX;CACA;CACA;CAEA,YAAY,SAAuB,SAA6B;EAC9D,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,QAAQ,KAAK,IAAI,GAAG,QAAQ,iBAAiB,EAAE;EACpD,KAAK,iBAAiB,QAAQ,gBAAgB,WAAW,KAAK,SAAS,MAAM,CAAC;EAC9E,QAAa,OAAO,WAClB,KAAK,KACH,IAAI,SACF,iBAAiB,iCAAiC,EAChD,SAAS,+DACX,CAAC,CACH,CACF,CACF;EACA,QAAQ,QAAQ,iBAAiB,eAAe,KAAK,KAAK,OAAO,GAAG,EAAE,MAAM,KAAK,CAAC;CACpF;CAEA,SAAS,QAAuB;EAE9B,MAAM,SAAS,WAAW,kBAAkB,CAAC,CAAC,UAAU,MAAM;EAC9D,IAAI,CAAC,OAAO,SAAS;EACrB,IAAI,OAAO,KAAK,mBAAmB,KAAK,eAAe;GACrD,KAAK,QAAQ,KAAK,OAAO,IAAI;GAC7B;EACF;EACA,KAAK,QAAQ,OAAO,IAAI;CAC1B;CAEA,QAAQ,EAAE,QAAQ,QAAQ,aAAuC;EAC/D,KAAK,OAAO,KAAK;GAAE;GAAQ;GAAQ,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EAAG,CAAC;EACxE,KAAK,YAAY;EACjB,IAAI,KAAK,OAAO,UAAU,KAAK,SAAS,CAAC,KAAK,SAAS,KAAK,WAAW,KAAK,OAAO;EACnF,KAAK,QAAQ;CACf;;;;;CAMA,WAAW,OAA0C;EACnD,MAAM,UAAU,KAAK,MAAM,KAAK;EAChC,KAAK,eAAe;EACpB,OAAO,QAAQ,cAAc;GAC3B,IAAI,KAAK,iBAAiB,SAAS,KAAK,eAAe,KAAA;EACzD,CAAC;CACH;CAEA,MAAM,MAAM,OAA0C;EACpD,KAAK,SAAS,CAAC;EACf,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,KAAK,SAAS,KAAK,uBAAuB;IACvD,GAAI,KAAK,SAAS,SAAS,EAAE,QAAQ,KAAK,SAAS,OAAO,IAAI,CAAC;IAC/D,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;IACvC,GAAI,KAAK,SAAS,OAAO,EAAE,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC;GAC3D,CAAC;EACH,UAAU;GACR,QAAQ,KAAK;GACb,KAAK,SAAS,KAAA;EAChB;EAEA,KAAK,gBAAgB,OAAO;EAC5B,KAAK,cAAc,OAAO;EAE1B,KAAK,MAAM,QAAQ,OAAO,IAAI,KAAK,mBAAmB,OAAO,gBAAgB,KAAK,QAAQ,IAAI;CAChG;CAEA,MAAM,SAAwB;EAC5B,KAAK,UAAU;EACf,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,KAAA,GAAW;EAGtB,MAAM,QAAQ,KAAK,SAAS,EAAE;EAG9B,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,GAAG,CAAC,CAAC,CACrD,YAAY,KAAA,CAAS;EAIxB,MAAM,MAAM,OAAO,UAAmB,KAAK,KAAK,KAAK,CAAC;EACtD,IAAI,KAAK,kBAAkB,IAAI,KAAK,gBAAgB,KAAA;CACtD;CAEA,MAAM,UAAyB;EAC7B,MAAM,KAAK;EACX,KAAK,WAAW,KAAA;EAChB,KAAK,UAAU;EACf,MAAM,KAAK,WAAW,KAAK,SAAS;CACtC;;;;;;CAOA,SAAS,IAA2B;EAClC,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,KAAK,SAAS,QAAQ,WAAW,KAAA,KAAa,WAAW,KAAK,QACjE,OAAO,QAAQ,QAAQ;EACzB,KAAK,SAAS;EACd,OAAO,KAAK,SACT,KAAK,iBAAiB;GAAE,gBAAgB;GAAI;EAAO,CAAC,CAAC,CACrD,WAAW,KAAA,CAAS;CACzB;CAEA,KAAK,OAAuB;EAC1B,IAAI,KAAK,QAAQ;EACjB,KAAK,SAAS,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACjD,KAAK,eAAe;EACpB,KAAK,QAAQ;CACf;CAEA,MAAM,OAA2C;EAC/C,IAAI,CAAC,KAAK,UAAU;GAClB,KAAK,WAAW;GAChB,MAAM,KAAK,WAAW,KAAK,SAAS,KAAK;EAC3C;EAEA,IAAI,KAAK,eAAe,KAAA,GAAW,KAAK,WAAW,KAAK;EACxD,IAAI,KAAK,WAAW,CAAC,KAAK,UAAU,KAAK,OAAO,UAAU,KAAK,QAAQ,GAAG,MAAM,KAAK,QAAQ;EAG7F,IAAI,CAAC,KAAK,WAAW,KAAK,kBAAkB,KAAA,GAAW,MAAM,KAAK,SAAS,KAAK,aAAa;EAC7F,SAAS;GACP,MAAM,OAAO,KAAK,OAAO,MAAM;GAC/B,IAAI,SAAS,KAAA,GAAW;IACtB,KAAK,aAAa,KAAK;IACvB,OAAO;KAAE,MAAM;KAAO,OAAO;IAAK;GACpC;GACA,IAAI,KAAK,QAAQ;IACf,IAAI,KAAK,OAAO,UAAU,KAAA,GAAW,MAAM,KAAK,OAAO;IACvD,OAAO;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU;GACxC;GACA,IAAI,KAAK,SAAS;IAChB,MAAM,KAAK,QAAQ;IACnB,MAAM,KAAK,SAAS,KAAK,aAAuB;GAClD;GACA,MAAM,IAAI,SAAe,SAAS;IAChC,KAAK,QAAQ;GACf,CAAC;GACD,KAAK,QAAQ,KAAA;EACf;CACF;;;;;CAMA,MAAM,SAA6C;EACjD,KAAK,KAAK;EACV,KAAK,OAAO,SAAS;EACrB,MAAM,KAAK;EAGX,MAAM,KAAK,cAAc,YAAY,KAAA,CAAS;EAC9C,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,KAAA,GAAW;GACpB,KAAK,gBAAgB,KAAA;GACrB,MAAM,QAAQ,KAAK,SAAS,EAAE;GAC9B,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,GAAG,CAAC,CAAC,CACrD,YAAY,KAAA,CAAS;GACxB,MAAM,MAAM,YAAY,KAAA,CAAS;EACnC,OAAO,IAAI,KAAK,SAAS,QAAQ,KAAK,aAAa,KAAA,KAAa,KAAK,aAAa,KAAK,QAAQ;GAG7F,MAAM,KAAK,WAAW,KAAK,SAAS,CAAC,CAAC,YAAY,KAAA,CAAS;GAC3D,MAAM,QAAQ,KAAK;GACnB,IAAI,UAAU,KAAA,GAAW;IACvB,KAAK,gBAAgB,KAAA;IACrB,MAAM,QAAQ,KAAK,SAAS,KAAK;IACjC,MAAM,KAAK,SACR,KAAK,yBAAyB,EAAE,gBAAgB,MAAM,CAAC,CAAC,CACxD,YAAY,KAAA,CAAS;IACxB,MAAM,MAAM,YAAY,KAAA,CAAS;GACnC;EACF;EACA,OAAO;GAAE,MAAM;GAAM,OAAO,KAAA;EAAU;CACxC;AACF;;;;;;;;;;;;;;;;;;;;;;ACPA,MAAM,eAAe;CACnB,UAAU,CAAC,yBAAyB;CACpC,aAAa,CAAC,4BAA4B;CAC1C,QAAQ,CAAC,sBAAsB;CAC/B,SAAS,CAAC,oBAAoB;CAC9B,iBAAiB,CAAC,iCAAiC,kCAAkC;AACvF;;AAGA,MAAa,kBAAkB;;AAG/B,SAAgB,YAAY,OAA+C;CACzE,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAO,MAAM,WAAW,MAAM,IAAI,EAAE,YAAY,MAAM,IAAI,EAAE,MAAM,QAAQ,KAAK,EAAE;AACnF;AAEA,SAAS,YAAY,SAA2B;CAC9C,OAAO,IAAI,SAAS,iBAAiB,iCAAiC,EAAE,QAAQ,CAAC,CAAC;AACpF;;AAGA,SAAS,YAAY,OAAc,QAA4D;CAC7F,QAAQ,QAAR;EACE,KAAK,0BAA0B,QAC7B,OACE,MAAM,cAAc,WAAW,MAAM,UAAU,QAAQ,MAAmC;EAE9F,KAAK,6BAA6B,QAChC,OACE,MAAM,iBAAiB,WAAW,MAAM,aAAa,QAAQ,MAA4B;EAE7F,KAAK,uBAAuB,QAC1B,OAAO,MAAM,YAAY,WAAW,MAAM,QAAQ,OAAO,MAAqB;EAChF,KAAK,qBAAqB,QACxB,OAAO,MAAM,aAAa,WAAW,MAAM,SAAS,IAAI,MAA8B;EACxF,KAAK,gCAAgC,QACnC,OACE,MAAM,qBACJ,WACA,MAAM,iBAAiB,OAAQ,OAAyC,QAAQ;EAEtF,KAAK,mCAAmC,QACtC,OACE,MAAM,oBACL,OAAO,WAAW;GACjB,MAAM,MAAM,iBAAiB,YAAY,MAA4C;GACrF,OAAO,CAAC;EACV;EAEJ,SACE;CACJ;AACF;;AAGA,SAAS,UAAU,OAA0B;CAC3C,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,KAAK,GAAG;EACjD,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,EAAE,QAAQ,eACZ,MAAM,IAAI,UACR,0BAA0B,KAAK,oBAAoB,OAAO,KAAK,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,GAC1F;EAEF,MAAM,KAAK,IAAgB;CAC7B;CACA,OAAO;AACT;;AAGA,SAAS,UACP,OACA,QACA,SACuB;CACvB,MAAM,OAAO,IAAI;CACjB,MAAM,OAAO,iBAAiB,MAAM,KAAK,SAAS,GAAG,OAAO;CAC5D,MAAM,OAAO,iBAAiB,MAAM,KAAK,SAAS,GAAG,UAAU,GAAI;CACnE,OAAO,OAAO,cAAc;EAC1B,aAAa,IAAI;EACjB,aAAa,IAAI;CACnB,CAAC;AACH;;AAGA,SAAS,QACP,UACoC;CACpC,IAAI,aAAa,KAAA,GAAW,OAAO,KAAA;CACnC,QAAQ,GAAG,SAAY;EACrB,IAAI;GACF,SAAS,GAAG,IAAI;EAClB,QAAQ,CAER;CACF;AACF;;;;;;AAOA,eAAsB,aAAa,SAAyC;CAK1E,MAAM,QAAQ,QAAQ,QAAQ,KAAK;CACnC,MAAM,aAAa,QAAQ,QAAQ,UAAU;CAC7C,MAAM,cAAc,QAAQ,QAAQ,WAAW;CAC/C,MAAM,QAAQ,QAAQ,SAAS,CAAC;CAChC,MAAM,QAAQ,UAAU,KAAK;CAC7B,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,MAAM,OAAO,QAAQ,QAAQ,IAAI;CACjC,MAAM,UAAU,MAAM,eAAe;EACnC,QAAQ,QAAQ;EAChB;EACA,QAAQ,QAAQ;EAChB,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;CACrC,CAAC;CACD,QAAQ,QAAQ,eAAe;CAC/B,MAAM,OAAO;EAAC,QAAQ;EAAc;EAAQ;EAAU;CAAI;CAC1D,IAAI,QAAQ,SAAS,KAAA,GAAW,KAAK,KAAK,UAAU,QAAQ,IAAI;CAChE,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,KAAK,cAAc,QAAQ,OAAO;CAC1E,MAAM,QAAQ,MAAM,QAAQ,MAAM,MAAM;EACtC,OAAO;GAAC;GAAQ;GAAQ;EAAM;EACzB;CACP,CAAC;CACD,MAAM,UAAoB,CAAC;CAC3B,MAAM,SAAS,IAAI,SAAuB,SAAS;EACjD,MAAM,GAAG,UAAU,MAAM,WAAW,KAAK;GAAE;GAAM;EAAO,CAAC,CAAC;EAC1D,MAAM,GAAG,eAAe,KAAK;GAAE,MAAM;GAAM,QAAQ;EAAK,CAAC,CAAC;CAC5D,CAAC;CACD,IAAI;CACJ,MAAM,GAAG,UAAU,UAAU;EAC3B,aAAa;CACf,CAAC;CACD,MAAM,WAAW,IAAI,aAAa;CAClC,MAAM,QAAQ,YAAY,MAAM;CAChC,MAAM,QAAQ,GAAG,SAAS,UAAkB;EAC1C,KAAK,MAAM,QAAQ,SAAS,KAAK,KAAK,GAAG;GACvC,QAAQ,KAAK,IAAI;GACjB,IAAI,QAAQ,SAAS,IAAI,QAAQ,MAAM;GACvC,QAAQ,IAAI;EACd;CACF,CAAC;CAED,MAAM,uCAAuB,IAAI,IAA4C;CAC7E,MAAM,OAAO,IAAI,QAAQ;EACvB,OAAO,YAAY;GACjB,IAAI,MAAM,OAAO,UAAU,MAAM,MAAM,MAAM,WAAW,OAAO,CAAC;EAClE;EACA,WAAW,OAAO,QAAQ,WAAW;GACnC,MAAM,aAAa,OAAO,OAAO,YAAY,CAAC,CAC3C,KAAK,CAAC,CACN,MAAM,cAAc,UAAU,WAAW,MAAM;GAGlD,MAAM,UAAU,cAAc,YAAY,OAAO,MAAM;GACvD,IAAI,CAAC,cAAc,CAAC,SAClB,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,8BAA8B,OAAO,GAChD,CAAC,GACD,MACF;GAIF,MAAM,SAAS,WAAW,WAAW,MAAM,CAAC,CAAC,UAAU,MAAM;GAC7D,IAAI,CAAC,OAAO,SACV,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,iBAAiB,OAAO,0BACnC,CAAC,GACD,MACF;GAEF,MAAM,SAAS,MAAM,QAAQ,OAAO,IAAI;GACxC,MAAM,UAAU,WAAW,OAAO,UAAU,UAAU,IAAI;GAC1D,IAAI,CAAC,QAAQ,SACX,MAAM,IAAI,SACR,iBAAiB,8BAA8B,EAC7C,SAAS,cAAc,OAAO,yCAChC,CAAC,CACH;GAEF,OAAO,QAAQ;EACjB;EACA,iBAAiB,QAAQ,WAAW;GAClC,KAAK,MAAM,WAAW,qBAAqB,IAAI,MAAM,KAAK,CAAC,GAAG,QAAQ,MAAM;EAC9E;EACA,kBAAkB,OAAO,WAAW,cAAc,OAAO,MAAM;CACjE,CAAC;CACD,MAAM,QAAQ,IAAI,aAAa;CAC/B,MAAM,QAAQ,YAAY,MAAM;CAChC,MAAM,QAAQ,GAAG,SAAS,UAAkB;EAC1C,KAAK,MAAM,QAAQ,MAAM,KAAK,KAAK,GAAG,KAAK,YAAY,IAAI;CAC7D,CAAC;CACD,MAAM,QAAQ,GAAG,eAAe;EAC9B,KAAK,MACH,iBAAiB,iCAAiC,EAChD,SAAS,2BAA2B,KAAK,SAAS,QAAQ,SAAS,IAAI,wBAAwB,QAAQ,GAAG,EAAE,MAAM,GAAG,GACvH,CAAC,CACH;CACF,CAAC;CAID,MAAM,OAAO,GAAG,UAAU,UAAiC;EACzD,KAAK,MACH,iBAAiB,iCAAiC,EAChD,SAAS,2BAA2B,KAAK,oBAAoB,MAAM,QAAQ,MAAM,QAAQ,IAC3F,CAAC,CACH;CACF,CAAC;CACD,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC;CAClC,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC;CAElC,MAAM,cAAoB;EACxB,UAAe,OAAO,QAAQ,CAAC;CACjC;CACA,QAAQ,QAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CAC/D,MAAM,aAAyB;EAC7B,MAAM;EAGN,GAAI,QAAQ,gBAAgB,QAAQ,EAAE,OAAO,MAAM,IAAI,CAAC;EACxD,GAAI,QAAQ,UAAU,aAAa,EAAE,YAAY,QAAQ,SAAS,WAAW,IAAI,CAAC;EAClF,GAAI,QAAQ,UAAU,iBAClB,EAAE,gBAAgB,QAAQ,SAAS,eAAe,IAClD,CAAC;EACL,GAAI,QAAQ,UAAU,eAAe,EAAE,cAAc,QAAQ,SAAS,aAAa,IAAI,CAAC;CAC1F;CACA,IAAI;CACJ,IAAI;EACF,UAAU,MAAM,KAAK,KAAK,wBAAwB;GAChD,QAAQ;IACN,MAAM,QAAQ,QAAQ,QAAA;IACtB,SAAS,QAAQ,QAAQ,WAAW,WAAW;GACjD;GACA,WAAW,CAAC,GAAG,uBAAuB;GACtC,cAAc,EAAE,eAAe,CAAC,eAAe,iBAAiB,EAAE;GAClE,OAAO;GACP,MAAM;EACR,CAAC;CACH,SAAS,OAAO;EACd,MAAM,UAAU,OAAO,QAAQ,GAAI;EACnC,IAAI,YAAY,MAAM,YAAY,kBAAkB,QAAQ,KAAK,IAAI,WAAW,SAAS;EACzF,MAAM;CACR,UAAU;EACR,QAAQ,QAAQ,oBAAoB,SAAS,KAAK;CACpD;CAEA,MAAM,QACJ,QACA,QACA,gBAEA,KAAK,KACH,QACA,QACA,aAAa,cAAc,KAAA,IAAY,EAAE,WAAW,YAAY,UAAU,IAAI,CAAC,CACjF;CACF,IAAI;CAwEJ,OAAO;EArEL,UAAU,QAAQ;EAClB,cAAc,QAAQ;EACtB,UAAU,QAAQ;EAClB,UAAU,QAAQ;EAClB;EACA,QAAQ;EACR;EACA,UAAU,QAAQ,iBAAiB,CAAC,MAClC,KAAK,uBAAuB;GAAE,QAAQ,YAAY,MAAM;GAAG,GAAG;EAAe,CAAC;EAChF,SAAS,QAAQ,gBAAgB,CAAC,MAChC,KAAK,sBAAsB;GAAE,QAAQ,YAAY,MAAM;GAAG,GAAG;EAAc,CAAC;EAC9E,OAAO,UAAU,OAAO,cAAc,CAAC,MAAM;GAC3C,MAAM,SAAS,YAAY;GAG3B,IAAI,QAAQ,SAAS,OAAO,QAAQ,OAAO,OAAO,MAAM;GACxD,MAAM,UAAU,KAAK,YAAY;IAC/B,UAAU,YAAY,QAAQ;IAC9B;IACA,WAAW,YAAY,aAAa;IACpC,GAAI,YAAY,UAAU,KAAA,IAAY,EAAE,OAAO,YAAY,MAAM,IAAI,CAAC;GACxE,CAAC;GACD,IAAI,CAAC,QAAQ,OAAO;GAGpB,OAAO,IAAI,SAAqB,MAAM,SAAS;IAC7C,MAAM,gBAAsB,KAAK,OAAO,MAAM;IAC9C,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;IACxD,QAAQ,KAAK,MAAM,IAAI,CAAC,CAAC,cAAc,OAAO,oBAAoB,SAAS,OAAO,CAAC;GACrF,CAAC;EACH;EACA,WACE,UACA,iBAAkE,CAAC,MAEnE,KAAK,kBAAkB;GACrB,UAAU,YAAY,QAAQ;GAC9B,GAAG;EACL,CAAC;EACH,WAAW,UAAU,QACnB,KAAK,gBAAgB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAI,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC;EAAG,CAAC;EAC7F,aAAa,UAAU,UACrB,KAAK,kBAAkB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAM,CAAC;EACtE,WAAW,UAAU,SACnB,KAAK,gBAAgB;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAK,CAAC;EACnE,UAAU,UAAU,aAAa,CAAC,MAChC,KAAK,eAAe;GAAE,UAAU,YAAY,QAAQ;GAAG,GAAG;EAAW,CAAC;EACxE,SAAS,gBAAgB,CAAC,MACxB,gBACE;GACE;GACA,iBAAiB,YAAY;IAC3B,IAAI,MAAM,qBAAqB,IAAI,aAAa;IAChD,IAAI,CAAC,KAAK;KACR,sBAAM,IAAI,IAAI;KACd,qBAAqB,IAAI,eAAe,GAAG;IAC7C;IACA,IAAI,IAAI,OAAO;IACf,aAAa,KAAK,OAAO,OAAO;GAClC;GACA,QAAQ,OAAO,WAAW,KAAA,CAAS;EACrC,GACA,aACF;EACF,aAAa;GACX,YAAY,UAAU,OAAO,QAAQ,GAAM;GAC3C,OAAO;EACT;CAEU;AACd"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostra/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Embed Mostra in a JavaScript host: createMostra({ home, ports }) spawns a verified runtime bundle, serves your ports and speaks the host protocol.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "The Mostra Authors",
|
|
@@ -34,12 +34,12 @@
|
|
|
34
34
|
"provenance": true
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@mostra/protocol": "0.
|
|
37
|
+
"@mostra/protocol": "0.5.0",
|
|
38
38
|
"tar": "7.5.22",
|
|
39
|
-
"undici": "8.
|
|
39
|
+
"undici": "8.12.2"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@mostra/runtime": "0.
|
|
42
|
+
"@mostra/runtime": "0.5.0",
|
|
43
43
|
"@mostra/tools": "0.0.0",
|
|
44
44
|
"@types/node": "24.19.1",
|
|
45
45
|
"tsdown": "0.23.0",
|
package/src/events.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
eventsUnsubscribeMethod,
|
|
18
18
|
type MethodDefinition,
|
|
19
19
|
RpcError,
|
|
20
|
+
receivable,
|
|
20
21
|
} from "@mostra/protocol";
|
|
21
22
|
|
|
22
23
|
/** One page of events. */
|
|
@@ -109,7 +110,8 @@ class EventStream implements AsyncIterator<EventPage> {
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
#receive(params: unknown): void {
|
|
112
|
-
|
|
113
|
+
// Events of types a newer instance added arrive too, never dropping their page (#45).
|
|
114
|
+
const parsed = receivable(EventsNotifyParams).safeParse(params);
|
|
113
115
|
if (!parsed.success) return;
|
|
114
116
|
if (parsed.data.subscriptionId !== this.#subscription) {
|
|
115
117
|
this.#early?.push(parsed.data);
|
package/src/mostra.ts
CHANGED
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
type ResultsMarkdown,
|
|
60
60
|
RpcError,
|
|
61
61
|
RpcPeer,
|
|
62
|
+
receivable,
|
|
62
63
|
resultsGetMethod,
|
|
63
64
|
type SourceRef,
|
|
64
65
|
type StateGetResult,
|
|
@@ -436,7 +437,9 @@ export async function createMostra(options: MostraOptions): Promise<Mostra> {
|
|
|
436
437
|
-32601,
|
|
437
438
|
);
|
|
438
439
|
}
|
|
439
|
-
|
|
440
|
+
// A newer instance may send enum values and union members this host's schemas don't
|
|
441
|
+
// name yet (#45): the port's handler meets them, as it meets any value it can't act on.
|
|
442
|
+
const parsed = receivable(definition.params).safeParse(params);
|
|
440
443
|
if (!parsed.success) {
|
|
441
444
|
throw new RpcError(
|
|
442
445
|
createDiagnostic("MOSTRA_HOST_INVALID_PARAMS", {
|