gesso-framework 0.6.8 → 0.6.10
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/CHANGELOG.md +13 -0
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +3 -3
- package/dist/{app-bMsNOs1H.js → app-6S4_PPB0.js} +2 -2
- package/dist/{app-bMsNOs1H.js.map → app-6S4_PPB0.js.map} +1 -1
- package/dist/{index-D5bW2GUt.d.ts → index-oHQAJMqR.d.ts} +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +7 -7
- package/dist/{persisted-i6TIWX8R.js → persisted-IXHP0d-X.js} +2 -2
- package/dist/{persisted-i6TIWX8R.js.map → persisted-IXHP0d-X.js.map} +1 -1
- package/dist/remote/index.d.ts +2 -2
- package/dist/remote/index.js +1 -1
- package/dist/{remote-gRdwYo-q.js → remote-BEGRJ1V3.js} +24 -5
- package/dist/remote-BEGRJ1V3.js.map +1 -0
- package/dist/{serveChannels-Cs22-p8X.js → serveChannels-CFzq0Hz3.js} +2 -2
- package/dist/{serveChannels-Cs22-p8X.js.map → serveChannels-CFzq0Hz3.js.map} +1 -1
- package/dist/{ui-Bs6ywsAA.d.ts → ui-e4RHm8Mt.d.ts} +10 -2
- package/dist/{webmcp-F3OOy73w.js → webmcp-Dsp4W-pi.js} +2 -2
- package/dist/{webmcp-F3OOy73w.js.map → webmcp-Dsp4W-pi.js.map} +1 -1
- package/dist/worker/index.d.ts +2 -2
- package/dist/worker/index.js +2 -2
- package/package.json +2 -2
- package/dist/remote-gRdwYo-q.js.map +0 -1
|
@@ -246,8 +246,9 @@ function agentSurface(channels, options = {}) {
|
|
|
246
246
|
served,
|
|
247
247
|
name,
|
|
248
248
|
description: schema?.description,
|
|
249
|
-
viewSchema: schema
|
|
249
|
+
viewSchema: schema === void 0 ? void 0 : withoutKeys(schema.view, schema.hidden ?? []),
|
|
250
250
|
view: { ...served.token.initial },
|
|
251
|
+
hidden: new Set(schema?.hidden ?? []),
|
|
251
252
|
commands
|
|
252
253
|
};
|
|
253
254
|
entries.push(entry);
|
|
@@ -330,7 +331,7 @@ function agentSurface(channels, options = {}) {
|
|
|
330
331
|
const entry = entries.find((candidate) => resourceUri(candidate.served.token.name) === uri);
|
|
331
332
|
if (entry === void 0) return;
|
|
332
333
|
follow();
|
|
333
|
-
return
|
|
334
|
+
return shown(entry);
|
|
334
335
|
},
|
|
335
336
|
dispose: () => {
|
|
336
337
|
for (const subscription of subscriptions) subscription.unsubscribe();
|
|
@@ -382,12 +383,30 @@ function toolName(raw) {
|
|
|
382
383
|
if (name.length > MAX_NAME) throw new Error(`The tool name '${name}' is longer than ${MAX_NAME} characters. Shorten the channel's name.`);
|
|
383
384
|
return name;
|
|
384
385
|
}
|
|
386
|
+
/** The view as an agent is given it: without its hidden keys. */
|
|
387
|
+
function shown(entry) {
|
|
388
|
+
const view = {};
|
|
389
|
+
for (const [key, value] of Object.entries(entry.view)) if (!entry.hidden.has(key)) view[key] = value;
|
|
390
|
+
return view;
|
|
391
|
+
}
|
|
392
|
+
/** An object schema without some of its properties. */
|
|
393
|
+
function withoutKeys(schema, keys) {
|
|
394
|
+
if (keys.length === 0) return schema;
|
|
395
|
+
const properties = { ...schema.properties };
|
|
396
|
+
for (const key of keys) delete properties[key];
|
|
397
|
+
const required = Array.isArray(schema.required) ? schema.required.filter((key) => !keys.includes(key)) : void 0;
|
|
398
|
+
return {
|
|
399
|
+
...schema,
|
|
400
|
+
properties,
|
|
401
|
+
...required === void 0 ? {} : { required }
|
|
402
|
+
};
|
|
403
|
+
}
|
|
385
404
|
function viewResult(entry, lead) {
|
|
386
|
-
const view =
|
|
405
|
+
const view = shown(entry);
|
|
387
406
|
return {
|
|
388
407
|
content: [{
|
|
389
408
|
type: "text",
|
|
390
|
-
text: `${lead}\n${JSON.stringify(view
|
|
409
|
+
text: `${lead}\n${JSON.stringify(view)}`
|
|
391
410
|
}],
|
|
392
411
|
structuredContent: view,
|
|
393
412
|
isError: false
|
|
@@ -612,4 +631,4 @@ function combineSurfaces(surfaces) {
|
|
|
612
631
|
//#endregion
|
|
613
632
|
export { agentSurface as a, channelSchema as c, serveAgentPort as i, describeChannel as l, combineSurfaces as n, resourceUri as o, remoteSurface as r, validate as s, AGENT_PORT as t };
|
|
614
633
|
|
|
615
|
-
//# sourceMappingURL=remote-
|
|
634
|
+
//# sourceMappingURL=remote-BEGRJ1V3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-BEGRJ1V3.js","names":[],"sources":["../src/channel/ChannelSchema.ts","../src/agent/binary.ts","../src/agent/validate.ts","../src/agent/AgentSurface.ts","../src/agent/remote.ts"],"sourcesContent":["/**\n * A channel described at run time: what its view holds and what its\n * commands take, as JSON Schema.\n *\n * A token's types are erased by the compiler, so a running application\n * cannot ask a channel what it accepts. That is no loss to a component,\n * which was typechecked against the token, and all of it to anything\n * that meets the application only at run time: an AI agent being\n * handed the channel as tools, a devtools panel, a test that drives an\n * app by its commands. The schema is the contract again, in the one\n * form those can read.\n *\n * `gesso-vite-plugin` writes it from the contract's own types and\n * JSDoc, so an application that uses the plugin describes its channels\n * by having written them. Without the plugin, `describeChannel` takes a\n * hand-written one.\n */\nimport type { ChannelToken } from './ChannelToken';\n\n/**\n * A JSON Schema (draft 2020-12) object.\n *\n * Deliberately loose: the framework stores and hands back schemas and\n * never interprets one, so a precise type would be a second JSON Schema\n * specification to keep in step with the real one.\n */\nexport type JsonSchema = { readonly [keyword: string]: unknown };\n\n/** One command, described. */\nexport interface CommandSchema {\n /** The command's JSDoc summary. */\n readonly description?: string;\n /**\n * The parameter names, in the order the command takes them.\n *\n * A command is called positionally, `move(from, to)`, while a tool\n * call names its arguments, so this is what maps one onto the other.\n */\n readonly parameters: readonly string[];\n /** The last parameter is a rest parameter, and its array is spread into the call. */\n readonly rest?: true;\n /** An object schema, one property per parameter, keyed by parameter name. */\n readonly input: JsonSchema;\n /** `@destructive`: what it does cannot be undone. */\n readonly destructive?: true;\n /** `@idempotent`: sending it twice changes nothing the first did not. */\n readonly idempotent?: true;\n /** `@confirm`: a person should approve it before anything else sends it. */\n readonly confirm?: true;\n /** `@hidden`: for the application's own components, not for an agent. */\n readonly hidden?: true;\n}\n\n/** A channel, described. */\nexport interface ChannelSchema {\n /** The token's JSDoc summary. */\n readonly description?: string;\n /** An object schema, one property per view key. */\n readonly view: JsonSchema;\n /**\n * `@hidden` view keys: the application's own plumbing, or bulk only a\n * screen can use — geometry, a clipboard, a file's text. An agent is\n * given the view without them, in its tools' results, its resource\n * and its output schema, so what it reads is what it can act on.\n */\n readonly hidden?: readonly string[];\n readonly commands: { readonly [name: string]: CommandSchema };\n}\n\n/**\n * The schemas, beside the tokens rather than on them.\n *\n * A token is a plain object both threads import and nothing should\n * grow on, and its interface is a public type that a schema field would\n * widen for every application, described or not. Keyed weakly, so a\n * token the application drops takes its schema with it.\n */\nconst schemas = new WeakMap<object, ChannelSchema>();\n\n/**\n * Attaches a schema to a token.\n *\n * `gesso-vite-plugin` calls this at the bottom of each contract module\n * it reads, so it runs once, when the module is first imported. Call it\n * yourself to describe a channel the plugin does not see. A second call\n * for the same token replaces the first, which is what a hot-replaced\n * contract module does.\n */\nexport function describeChannel(token: ChannelToken<object, object>, schema: ChannelSchema): void {\n schemas.set(token, schema);\n}\n\n/** The schema attached to a token, or undefined when nothing described it. */\nexport function channelSchema(token: ChannelToken<object, object>): ChannelSchema | undefined {\n return schemas.get(token);\n}\n","import type { JsonSchema } from '../channel/ChannelSchema';\n\n/**\n * Turns the base64 an agent wrote back into the bytes a command takes.\n *\n * A command may carry an `ArrayBuffer` or a typed array, which an agent\n * cannot write: it speaks JSON. `gesso-vite-plugin` describes such a\n * field as a base64 string tagged `x-gesso-binary` with the type the\n * command expects, the agent sends the string, and this walks the\n * arguments beside their schema and puts the bytes back before the\n * command is sent, so the application receives what its own components\n * would have sent it.\n *\n * Only what the schema tags is touched, and a value that is not a\n * string where bytes are expected is left for the command to see: the\n * validator has already said whether it was the right shape.\n */\nexport function decodeBinary(schema: JsonSchema | undefined, value: unknown, root: JsonSchema): unknown {\n if (schema === undefined) {\n return value;\n }\n const ref = schema.$ref;\n if (typeof ref === 'string') {\n const target = (root.$defs as Record<string, JsonSchema> | undefined)?.[ref.replace(/^#\\/\\$defs\\//, '')];\n return decodeBinary(target, value, root);\n }\n const kind = schema['x-gesso-binary'];\n if (typeof kind === 'string') {\n return typeof value === 'string' ? bytesOf(value, kind) : value;\n }\n if (Array.isArray(schema.anyOf)) {\n // A union holding bytes is nearly always `bytes | null`; the branch\n // that is bytes is the one a string can be.\n const binary = (schema.anyOf as JsonSchema[]).find(option => typeof option['x-gesso-binary'] === 'string');\n return binary !== undefined && typeof value === 'string' ? decodeBinary(binary, value, root) : value;\n }\n if (Array.isArray(value)) {\n const prefix = (schema.prefixItems as JsonSchema[] | undefined) ?? [];\n const items = schema.items as JsonSchema | boolean | undefined;\n return value.map((item, index) =>\n decodeBinary(index < prefix.length ? prefix[index] : typeof items === 'object' ? items : undefined, item, root)\n );\n }\n if (value !== null && typeof value === 'object') {\n const properties = (schema.properties as Record<string, JsonSchema> | undefined) ?? {};\n const out: Record<string, unknown> = {};\n for (const [key, field] of Object.entries(value)) {\n out[key] = decodeBinary(properties[key], field, root);\n }\n return out;\n }\n return value;\n}\n\nconst TYPED: Record<string, new (buffer: ArrayBuffer) => ArrayBufferView> = {\n Int8Array,\n Uint8Array,\n Uint8ClampedArray,\n Int16Array,\n Uint16Array,\n Int32Array,\n Uint32Array,\n Float32Array,\n Float64Array,\n BigInt64Array,\n BigUint64Array\n};\n\nfunction bytesOf(base64: string, kind: string): ArrayBuffer | ArrayBufferView {\n const text = atob(base64);\n const bytes = new Uint8Array(text.length);\n for (let index = 0; index < text.length; index++) {\n bytes[index] = text.charCodeAt(index);\n }\n if (kind === 'ArrayBuffer') {\n return bytes.buffer;\n }\n const Typed = TYPED[kind];\n return Typed === undefined || Typed === Uint8Array ? bytes : new Typed(bytes.buffer);\n}\n","import type { JsonSchema } from '../channel/ChannelSchema';\n\n/**\n * Checks a value against the JSON Schema a channel description uses.\n *\n * Not a general validator. It understands the keywords\n * `gesso-vite-plugin` writes (`type`, `enum`, `const`, `anyOf`,\n * `items`, `prefixItems`, `minItems`, `properties`, `required`,\n * `additionalProperties`, `not` and `$ref` into `$defs`), and passes\n * anything else. That is enough to stop the case that matters: an\n * agent sending `\"3\"` where a command takes a number, which the\n * command would accept, store, and fail on much later and somewhere\n * else.\n *\n * Returns the first problem as a sentence naming where it is, written\n * for the agent that sent the value so it can correct itself, or null.\n */\nexport function validate(schema: JsonSchema, value: unknown, root: JsonSchema = schema, at = 'input'): string | null {\n const ref = schema.$ref;\n if (typeof ref === 'string') {\n const name = ref.replace(/^#\\/\\$defs\\//, '');\n const target = (root.$defs as Record<string, JsonSchema> | undefined)?.[name];\n return target === undefined ? null : validate(target, value, root, at);\n }\n if (schema.not !== undefined && validate(schema.not as JsonSchema, value, root, at) === null) {\n return `${at} must not be given.`;\n }\n if ('const' in schema && value !== schema.const) {\n return `${at} must be ${JSON.stringify(schema.const)}.`;\n }\n if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {\n return `${at} must be one of ${schema.enum.map(option => JSON.stringify(option)).join(', ')}.`;\n }\n if (Array.isArray(schema.anyOf)) {\n const options = schema.anyOf as JsonSchema[];\n if (options.every(option => validate(option, value, root, at) !== null)) {\n return `${at} does not match any of the ${options.length} shapes it may take.`;\n }\n return null;\n }\n const type = schema.type;\n if (typeof type === 'string' && !hasType(value, type)) {\n return `${at} must be ${article(type)}, not ${describe(value)}.`;\n }\n if (Array.isArray(value)) {\n return validateArray(schema, value, root, at);\n }\n if (type === 'object' && value !== null && typeof value === 'object') {\n return validateObject(schema, value as Record<string, unknown>, root, at);\n }\n return null;\n}\n\nfunction validateArray(schema: JsonSchema, value: readonly unknown[], root: JsonSchema, at: string): string | null {\n const prefix = (schema.prefixItems as JsonSchema[] | undefined) ?? [];\n if (typeof schema.minItems === 'number' && value.length < schema.minItems) {\n return `${at} must have at least ${schema.minItems} items.`;\n }\n for (let index = 0; index < value.length; index++) {\n const item = index < prefix.length ? prefix[index] : schema.items;\n if (item === false) {\n return `${at} must have at most ${prefix.length} items.`;\n }\n if (item !== undefined && item !== true) {\n const problem = validate(item as JsonSchema, value[index], root, `${at}[${index}]`);\n if (problem !== null) {\n return problem;\n }\n }\n }\n return null;\n}\n\nfunction validateObject(\n schema: JsonSchema,\n value: Record<string, unknown>,\n root: JsonSchema,\n at: string\n): string | null {\n const properties = (schema.properties as Record<string, JsonSchema> | undefined) ?? {};\n for (const key of (schema.required as string[] | undefined) ?? []) {\n if (value[key] === undefined) {\n return `${at}.${key} is required.`;\n }\n }\n for (const [key, property] of Object.entries(value)) {\n const declared = properties[key];\n if (declared !== undefined) {\n const problem = property === undefined ? null : validate(declared, property, root, `${at}.${key}`);\n if (problem !== null) {\n return problem;\n }\n continue;\n }\n const extra = schema.additionalProperties;\n if (extra === false) {\n const known = Object.keys(properties);\n return `${at}.${key} is not expected${known.length > 0 ? `; the fields are ${known.join(', ')}` : ''}.`;\n }\n if (extra !== undefined && extra !== true) {\n const problem = validate(extra as JsonSchema, property, root, `${at}.${key}`);\n if (problem !== null) {\n return problem;\n }\n }\n }\n return null;\n}\n\nfunction hasType(value: unknown, type: string): boolean {\n switch (type) {\n case 'string':\n return typeof value === 'string';\n case 'number':\n return typeof value === 'number' && Number.isFinite(value);\n case 'integer':\n return Number.isInteger(value);\n case 'boolean':\n return typeof value === 'boolean';\n case 'null':\n return value === null;\n case 'array':\n return Array.isArray(value);\n case 'object':\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n default:\n return true;\n }\n}\n\nfunction article(type: string): string {\n return /^[aeiou]/.test(type) ? `an ${type}` : `a ${type}`;\n}\n\nfunction describe(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (Array.isArray(value)) {\n return 'an array';\n }\n return typeof value === 'object' ? 'an object' : `${typeof value} ${JSON.stringify(value)}`;\n}\n","import type { Subscription } from 'rxjs';\n\nimport { channelSchema, type CommandSchema, type JsonSchema } from '../channel/ChannelSchema';\nimport type { ServedChannel } from '../channel/serveChannels';\nimport { decodeBinary } from './binary';\nimport { validate } from './validate';\n\n/**\n * An application's channels, as an AI agent sees them.\n *\n * A channel is already the shape an agent wants. Its view is what the\n * application currently holds, as plain data, and its commands are\n * the things it can be asked to do, by name, with typed arguments. A\n * component reaches both through a replica; this reaches them from the\n * other side, through the same `{ token, source }` the application\n * already hands to `serveChannels` or `createDesktopApp`, so an agent\n * sends a command to exactly the handler a click does and sees its\n * effect in exactly the view a screen draws.\n *\n * For each channel it offers:\n *\n * - a resource, `gesso://<channel>/view`, holding the view;\n * - a tool, `<channel>_view`, returning the same thing, because more\n * agents call tools than read resources;\n * - a tool per command, `<channel>_<command>`, whose input schema is\n * the command's parameters by name. Calling it sends the command,\n * waits for the view to settle, and returns the view as it now is,\n * so the agent sees what its call did without a second round trip.\n *\n * The descriptions come from `channelSchema(token)`, which\n * `gesso-vite-plugin` writes from the contract's JSDoc. A command\n * marked `@hidden` is not offered, nor is a view key marked so, in\n * anything the agent reads; `@destructive` and `@idempotent`\n * become hints a client shows; `@confirm` means the person is asked,\n * through `confirm`, before the command is sent, and a surface given\n * no way to ask refuses it. A channel nobody described is still\n * offered, with its commands taking their arguments as a positional\n * list, and says so in its description.\n *\n * Nothing here knows about a transport. `mcpHandler` serves it over\n * MCP's HTTP transport; anything else can call `tools`, `call`,\n * `resources` and `read` directly.\n */\n\n/** A tool, in the shape MCP's `tools/list` returns. */\nexport interface AgentTool {\n readonly name: string;\n readonly title?: string;\n readonly description: string;\n readonly inputSchema: JsonSchema;\n readonly outputSchema?: JsonSchema;\n readonly annotations: {\n readonly title?: string;\n readonly readOnlyHint: boolean;\n readonly destructiveHint?: boolean;\n readonly idempotentHint?: boolean;\n readonly openWorldHint: false;\n };\n}\n\n/** A resource, in the shape MCP's `resources/list` returns. */\nexport interface AgentResource {\n readonly uri: string;\n readonly name: string;\n readonly description?: string;\n readonly mimeType: 'application/json';\n}\n\n/** What a tool call returned, in the shape MCP's `tools/call` returns. */\nexport interface AgentToolResult {\n readonly content: readonly { readonly type: 'text'; readonly text: string }[];\n readonly structuredContent?: Record<string, unknown>;\n readonly isError: boolean;\n}\n\n/** A command an agent wants to send that its contract says a person should approve. */\nexport interface AgentConfirmation {\n readonly channel: string;\n readonly command: string;\n readonly description?: string;\n /** The arguments, by parameter name. */\n readonly arguments: Readonly<Record<string, unknown>>;\n readonly destructive: boolean;\n}\n\nexport interface AgentSurfaceOptions {\n /**\n * Asks the person whether a `@confirm` command may be sent. Resolve\n * true to send it. Without this, such a command is refused, which is\n * the safe reading of a contract that asked for a person.\n */\n confirm?: (request: AgentConfirmation) => boolean | Promise<boolean>;\n /**\n * How long the view must be quiet after a command before the call\n * returns it, in milliseconds (default 50). A command whose effect is\n * synchronous settles at once; one that waits on a request settles\n * when the patch lands, or at `settleMs`. A command that changes\n * nothing in the view returns at `settleMs`.\n */\n quietMs?: number;\n /** The longest a call waits for the view to settle (default 1000). */\n settleMs?: number;\n}\n\n/**\n * Any surface an MCP server can speak for: one in this thread, or one\n * across a port whose every answer is a promise.\n */\nexport interface AgentSurfaceLike {\n tools(): readonly AgentTool[] | Promise<readonly AgentTool[]>;\n call(name: string, args: Readonly<Record<string, unknown>> | undefined): Promise<AgentToolResult>;\n resources(): readonly AgentResource[] | Promise<readonly AgentResource[]>;\n read(uri: string): Record<string, unknown> | undefined | Promise<Record<string, unknown> | undefined>;\n dispose?(): void;\n}\n\nexport interface AgentSurface extends AgentSurfaceLike {\n tools(): readonly AgentTool[];\n /** Calls a tool by name. Never throws: a failure is a result with `isError`, which an agent can read and correct. */\n call(name: string, args: Readonly<Record<string, unknown>> | undefined): Promise<AgentToolResult>;\n resources(): readonly AgentResource[];\n /** The current view of the channel a resource names, or undefined for a URI this surface does not hold. */\n read(uri: string): Record<string, unknown> | undefined;\n /** Stops following every view. */\n dispose(): void;\n}\n\n/** The longest tool name MCP clients accept. */\nconst MAX_NAME = 64;\n\ninterface Entry {\n readonly served: ServedChannel;\n readonly name: string;\n readonly description?: string;\n readonly viewSchema?: JsonSchema;\n readonly view: Record<string, unknown>;\n /** View keys kept from the agent. */\n readonly hidden: ReadonlySet<string>;\n readonly commands: ReadonlyMap<string, CommandSchema | null>;\n}\n\ntype Handler = (request: Readonly<Record<string, unknown>> | undefined) => Promise<AgentToolResult>;\n\nexport function agentSurface(channels: readonly ServedChannel[], options: AgentSurfaceOptions = {}): AgentSurface {\n const quietMs = options.quietMs ?? 50;\n const settleMs = options.settleMs ?? 1000;\n const subscriptions: Subscription[] = [];\n const entries: Entry[] = [];\n const tools: AgentTool[] = [];\n const handlers = new Map<string, Handler>();\n let lastChange = 0;\n /** Every change the view has seen, so a command can tell its own from what came before it. */\n let changes = 0;\n let following = false;\n\n /**\n * Subscribed on first use rather than at construction, so a surface\n * built at startup and never called costs the application nothing:\n * a view key may be a cold observable doing real work per subscriber.\n */\n const follow = () => {\n if (following) {\n return;\n }\n following = true;\n for (const entry of entries) {\n for (const [key, observable] of Object.entries(entry.served.source.view)) {\n subscriptions.push(\n observable.subscribe(value => {\n entry.view[key] = value;\n lastChange = Date.now();\n changes++;\n })\n );\n }\n }\n };\n\n /**\n * Waits for what a command sent when the view had seen `before`\n * changes to reach the view.\n *\n * Quiet counts only once the view has changed since the command: a\n * command that answers after a request has changed nothing yet when\n * the first quiet window ends, and returning then would hand the\n * agent the view from before it. A command that changes nothing at\n * all waits out `settleMs`, which is the price of not being able to\n * tell it from one that is still working.\n */\n const settle = async (before: number) => {\n const start = Date.now();\n for (;;) {\n await new Promise(resolve => setTimeout(resolve, quietMs));\n const now = Date.now();\n const changed = changes > before;\n if ((changed && now - lastChange >= quietMs) || now - start >= settleMs) {\n return;\n }\n }\n };\n\n const add = (tool: AgentTool, handler: Handler) => {\n if (handlers.has(tool.name)) {\n throw new Error(\n `Two tools would be called '${tool.name}'. A channel and command name pair has to be unique once joined ` +\n 'with an underscore, and a command cannot be called `view`.'\n );\n }\n tools.push(tool);\n handlers.set(tool.name, handler);\n };\n\n for (const served of channels) {\n const schema = channelSchema(served.token as never);\n const name = toolName(served.token.name);\n const commands = new Map<string, CommandSchema | null>();\n for (const command of Object.keys(served.source.commands ?? {})) {\n const described = schema?.commands[command];\n if (described?.hidden !== true) {\n commands.set(command, described ?? null);\n }\n }\n const entry: Entry = {\n served,\n name,\n description: schema?.description,\n viewSchema: schema === undefined ? undefined : withoutKeys(schema.view, schema.hidden ?? []),\n view: { ...(served.token.initial as Record<string, unknown>) },\n hidden: new Set(schema?.hidden ?? []),\n commands\n };\n entries.push(entry);\n\n const about = entry.description === undefined ? '' : `\\n\\n${entry.description}`;\n add(\n {\n name: toolName(`${served.token.name}_view`),\n title: `Read ${served.token.name}`,\n description: `What the ${served.token.name} channel currently holds.${about}`,\n inputSchema: { type: 'object', properties: {}, additionalProperties: false },\n ...(entry.viewSchema === undefined ? {} : { outputSchema: entry.viewSchema }),\n annotations: { readOnlyHint: true, openWorldHint: false }\n },\n async () => {\n follow();\n return viewResult(entry, `The ${served.token.name} view.`);\n }\n );\n\n for (const [command, described] of commands) {\n add(commandTool(entry, command, described), args => send(entry, command, described, args));\n }\n }\n\n const send = async (\n entry: Entry,\n command: string,\n described: CommandSchema | null,\n args: Readonly<Record<string, unknown>> | undefined\n ): Promise<AgentToolResult> => {\n follow();\n const channel = entry.served.token.name;\n const input = args ?? {};\n let positional: unknown[];\n if (described === null) {\n const list = input.arguments ?? [];\n if (!Array.isArray(list)) {\n return failure('arguments must be an array of the values the command takes, in order.');\n }\n positional = list;\n } else {\n const problem = validate(described.input, input);\n if (problem !== null) {\n return failure(problem);\n }\n // Bytes arrive as base64, which is all JSON can carry, and leave\n // as the typed array or buffer the command was written to take.\n const properties = (described.input.properties as Record<string, JsonSchema> | undefined) ?? {};\n positional = described.parameters.map(parameter =>\n decodeBinary(properties[parameter], input[parameter], described.input)\n );\n if (described.rest === true) {\n const spread = positional.pop();\n positional.push(...(Array.isArray(spread) ? spread : []));\n }\n while (positional.length > 0 && positional[positional.length - 1] === undefined) {\n positional.pop();\n }\n if (described.confirm === true) {\n if (options.confirm === undefined) {\n return failure(\n `${channel}.${command} asks for a person to approve it, and this application has given agents no way ` +\n 'to ask. Ask the person to do it themselves.'\n );\n }\n const approved = await options.confirm({\n channel,\n command,\n description: described.description,\n arguments: input,\n destructive: described.destructive === true\n });\n if (!approved) {\n return failure(`The person declined ${channel}.${command}. Do not send it again unless they ask.`);\n }\n }\n }\n const handler = entry.served.source.commands?.[command] as ((...values: unknown[]) => void) | undefined;\n if (handler === undefined) {\n return failure(`${channel} has no command ${command}.`);\n }\n const before = changes;\n try {\n handler(...positional);\n } catch (error) {\n return failure(`${channel}.${command} failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n await settle(before);\n return viewResult(entry, `Sent ${command} to ${channel}. The view afterwards:`);\n };\n\n return {\n tools: () => tools,\n call: async (name, args) => {\n const handler = handlers.get(name);\n if (handler === undefined) {\n return failure(`There is no tool called ${name}. The tools are ${[...handlers.keys()].join(', ')}.`);\n }\n return handler(args);\n },\n resources: () =>\n entries.map(entry => ({\n uri: resourceUri(entry.served.token.name),\n name: entry.served.token.name,\n ...(entry.description === undefined ? {} : { description: entry.description }),\n mimeType: 'application/json' as const\n })),\n read: uri => {\n const entry = entries.find(candidate => resourceUri(candidate.served.token.name) === uri);\n if (entry === undefined) {\n return undefined;\n }\n follow();\n return shown(entry);\n },\n dispose: () => {\n for (const subscription of subscriptions) {\n subscription.unsubscribe();\n }\n subscriptions.length = 0;\n following = false;\n }\n };\n}\n\n/** The URI of a channel's view. */\nexport function resourceUri(channel: string): string {\n return `gesso://${encodeURIComponent(channel)}/view`;\n}\n\nfunction commandTool(entry: Entry, command: string, described: CommandSchema | null): AgentTool {\n const channel = entry.served.token.name;\n const destructive = described?.destructive === true;\n const lines = [described?.description ?? `Sends ${command} to the ${channel} channel.`];\n if (described === null) {\n lines.push(\n `This command was not described, so its arguments are a positional list. Read ${toolName(`${channel}_view`)} ` +\n 'first to see what the channel holds.'\n );\n }\n if (described?.confirm === true) {\n lines.push('The person is asked to approve this before it is sent.');\n }\n lines.push('Returns the view after the command has taken effect.');\n return {\n name: toolName(`${channel}_${command}`),\n title: `${channel}: ${command}`,\n description: lines.join('\\n\\n'),\n inputSchema:\n described?.input ??\n ({\n type: 'object',\n properties: { arguments: { type: 'array', description: 'The arguments, in order.' } },\n additionalProperties: false\n } satisfies JsonSchema),\n ...(entry.viewSchema === undefined ? {} : { outputSchema: entry.viewSchema }),\n annotations: {\n readOnlyHint: false,\n ...(destructive ? { destructiveHint: true } : { destructiveHint: false }),\n ...(described?.idempotent === true ? { idempotentHint: true } : {}),\n openWorldHint: false\n }\n };\n}\n\n/**\n * A tool name MCP clients accept: letters, digits, underscores and\n * dashes, at most 64 characters. A channel name is the application's\n * own string, so anything else in it becomes an underscore, and one\n * too long is an error at startup rather than a tool a client drops.\n */\nfunction toolName(raw: string): string {\n const name = raw.replace(/[^A-Za-z0-9_-]/g, '_');\n if (name.length > MAX_NAME) {\n throw new Error(`The tool name '${name}' is longer than ${MAX_NAME} characters. Shorten the channel's name.`);\n }\n return name;\n}\n\n/** The view as an agent is given it: without its hidden keys. */\nfunction shown(entry: Entry): Record<string, unknown> {\n const view: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(entry.view)) {\n if (!entry.hidden.has(key)) view[key] = value;\n }\n return view;\n}\n\n/** An object schema without some of its properties. */\nfunction withoutKeys(schema: JsonSchema, keys: readonly string[]): JsonSchema {\n if (keys.length === 0) return schema;\n const properties = { ...(schema.properties as Record<string, JsonSchema> | undefined) };\n for (const key of keys) delete properties[key];\n const required = Array.isArray(schema.required)\n ? (schema.required as string[]).filter(key => !keys.includes(key))\n : undefined;\n return { ...schema, properties, ...(required === undefined ? {} : { required }) };\n}\n\nfunction viewResult(entry: Entry, lead: string): AgentToolResult {\n const view = shown(entry);\n return {\n // Compact: the same view is in structuredContent, and indenting a\n // large one doubles what the agent reads for nothing.\n content: [{ type: 'text', text: `${lead}\\n${JSON.stringify(view)}` }],\n structuredContent: view,\n isError: false\n };\n}\n\nfunction failure(text: string): AgentToolResult {\n return { content: [{ type: 'text', text }], isError: true };\n}\n","import type { AgentConfirmation, AgentResource, AgentSurfaceLike, AgentTool, AgentToolResult } from './AgentSurface';\n\n/**\n * An agent surface across a thread.\n *\n * A web application's channels are served in workers, and an agent\n * reaches the page, so the surface has to cross from one to the other.\n * The worker that serves channels answers a `gesso:agent` port with its\n * own surface (`serveAgentPort`); the page holds the other end as a\n * surface of its own (`remoteSurface`); and a thread that knows several\n * such ports, the render worker with its channel workers behind it,\n * offers them as one (`combineSurfaces`).\n *\n * The four operations cross as they are. MCP itself is spoken only at\n * the end that faces the agent, so nothing in a worker parses JSON-RPC.\n */\n\n/** The port key a thread answers with its agent surface. */\nexport const AGENT_PORT = 'gesso:agent';\n\n/** What crosses an agent port, page to worker. */\nexport type AgentPortRequest =\n | { id: number; op: 'tools' }\n | { id: number; op: 'resources' }\n | { id: number; op: 'call'; name: string; args?: Readonly<Record<string, unknown>> }\n | { id: number; op: 'read'; uri: string };\n\n/** A request before it is given an id. */\ntype Unsent<T> = T extends unknown ? Omit<T, 'id'> : never;\n\n/** What crosses back. */\nexport type AgentPortResponse = { id: number; result: unknown } | { id: number; error: string };\n\n/**\n * The question going the other way: a command marked `@confirm` needs\n * a person, the person is at the far end, so the thread that serves\n * the channel asks down the port it was asked on and waits.\n */\nexport type AgentPortConfirm = { confirm: number; request: AgentConfirmation } | { confirm: number; approved: boolean };\n\n/** How a thread asks the person, wherever the person is. */\nexport type AgentConfirm = (request: AgentConfirmation) => boolean | Promise<boolean>;\n\n/** A `MessagePort`, or anything that posts and receives like one. */\ntype AgentPort = Pick<MessagePort, 'postMessage' | 'onmessage'>;\n\n/**\n * Answers a port with a surface.\n *\n * The surface is made on the first request rather than when the port\n * arrives: a port is opened by a page that may never ask anything, and\n * a surface subscribes to every view key once it is used. It is given\n * a `confirm` that asks the far end of this port, so a command marked\n * `@confirm` reaches the person wherever they are; the far end answers\n * no when it has no way to ask.\n */\nexport function serveAgentPort(port: AgentPort, makeSurface: (confirm: AgentConfirm) => AgentSurfaceLike): () => void {\n let surface: AgentSurfaceLike | undefined;\n let nextConfirm = 1;\n const asking = new Map<number, (approved: boolean) => void>();\n const confirm: AgentConfirm = request =>\n new Promise(resolve => {\n const id = nextConfirm++;\n asking.set(id, resolve);\n port.postMessage({ confirm: id, request } satisfies AgentPortConfirm);\n });\n port.onmessage = async event => {\n const data = event.data as AgentPortRequest | AgentPortConfirm;\n if ('confirm' in data && 'approved' in data) {\n asking.get(data.confirm)?.(data.approved);\n asking.delete(data.confirm);\n return;\n }\n const request = data as AgentPortRequest;\n if (typeof request?.id !== 'number') {\n return;\n }\n surface ??= makeSurface(confirm);\n try {\n port.postMessage({ id: request.id, result: await answer(surface, request) } satisfies AgentPortResponse);\n } catch (error) {\n port.postMessage({\n id: request.id,\n error: error instanceof Error ? error.message : String(error)\n } satisfies AgentPortResponse);\n }\n };\n return () => {\n port.onmessage = null;\n surface?.dispose?.();\n for (const resolve of asking.values()) {\n resolve(false);\n }\n asking.clear();\n };\n}\n\nfunction answer(surface: AgentSurfaceLike, request: AgentPortRequest): unknown {\n switch (request.op) {\n case 'tools':\n return surface.tools();\n case 'resources':\n return surface.resources();\n case 'call':\n return surface.call(request.name, request.args);\n case 'read':\n return surface.read(request.uri) ?? null;\n }\n}\n\n/**\n * The surface at the other end of a port.\n *\n * A request nobody answers within `timeoutMs` (default 2000) is taken\n * as a thread with nothing to offer: a worker that serves no channels\n * never installs the answering side, and an agent asking the whole\n * application should hear about the threads that do rather than wait\n * on one that does not.\n */\nexport function remoteSurface(\n port: AgentPort,\n options: { timeoutMs?: number; confirm?: AgentConfirm } = {}\n): AgentSurfaceLike {\n const timeoutMs = options.timeoutMs ?? 2000;\n let next = 1;\n const pending = new Map<number, { resolve: (value: unknown) => void; reject: (error: Error) => void }>();\n port.onmessage = event => {\n const question = event.data as AgentPortConfirm;\n if (question !== null && typeof question === 'object' && 'request' in question) {\n // Asked to put a command to the person. With nobody to ask, the\n // answer is no, which is what a contract asking for a person means.\n void Promise.resolve(options.confirm?.(question.request) ?? false).then(\n approved => port.postMessage({ confirm: question.confirm, approved } satisfies AgentPortConfirm),\n () => port.postMessage({ confirm: question.confirm, approved: false } satisfies AgentPortConfirm)\n );\n return;\n }\n const response = event.data as AgentPortResponse;\n const waiting = pending.get(response?.id);\n if (waiting === undefined) {\n return;\n }\n pending.delete(response.id);\n if ('error' in response) {\n waiting.reject(new Error(response.error));\n } else {\n waiting.resolve(response.result);\n }\n };\n const ask = <T>(request: Unsent<AgentPortRequest>, fallback: T, timeout = timeoutMs): Promise<T> =>\n new Promise<T>((resolve, reject) => {\n const id = next++;\n const timer = setTimeout(() => {\n pending.delete(id);\n resolve(fallback);\n }, timeout);\n pending.set(id, {\n resolve: value => {\n clearTimeout(timer);\n resolve(value as T);\n },\n reject: error => {\n clearTimeout(timer);\n reject(error);\n }\n });\n port.postMessage({ ...request, id });\n });\n return {\n tools: () => ask<readonly AgentTool[]>({ op: 'tools' }, []),\n resources: () => ask<readonly AgentResource[]>({ op: 'resources' }, []),\n // A command may wait on the person through `confirm`, so a call is\n // given far longer than a listing before it is abandoned.\n call: (name, args) =>\n ask<AgentToolResult>(\n { op: 'call', name, args },\n { content: [{ type: 'text', text: `${name} did not answer in time.` }], isError: true },\n Math.max(timeoutMs, 120_000)\n ),\n read: async uri => (await ask<Record<string, unknown> | null>({ op: 'read', uri }, null)) ?? undefined\n };\n}\n\n/**\n * Several surfaces as one: tools and resources listed together, a call\n * or a read sent to whichever surface listed it. When two list the same\n * name, the first keeps it, as the order of the threads is the order\n * the application registered them in.\n */\nexport function combineSurfaces(surfaces: readonly AgentSurfaceLike[]): AgentSurfaceLike {\n const toolOwners = new Map<string, AgentSurfaceLike>();\n const resourceOwners = new Map<string, AgentSurfaceLike>();\n\n const tools = async () => {\n const lists = await Promise.all(surfaces.map(surface => surface.tools()));\n toolOwners.clear();\n const out: AgentTool[] = [];\n lists.forEach((list, index) => {\n for (const tool of list) {\n if (!toolOwners.has(tool.name)) {\n toolOwners.set(tool.name, surfaces[index]);\n out.push(tool);\n }\n }\n });\n return out;\n };\n\n const resources = async () => {\n const lists = await Promise.all(surfaces.map(surface => surface.resources()));\n resourceOwners.clear();\n const out: AgentResource[] = [];\n lists.forEach((list, index) => {\n for (const resource of list) {\n if (!resourceOwners.has(resource.uri)) {\n resourceOwners.set(resource.uri, surfaces[index]);\n out.push(resource);\n }\n }\n });\n return out;\n };\n\n return {\n tools,\n resources,\n call: async (name, args) => {\n if (!toolOwners.has(name)) {\n await tools();\n }\n const owner = toolOwners.get(name);\n if (owner === undefined) {\n const known = [...toolOwners.keys()].join(', ');\n return {\n content: [{ type: 'text', text: `There is no tool called ${name}. The tools are ${known}.` }],\n isError: true\n };\n }\n return owner.call(name, args);\n },\n read: async uri => {\n if (!resourceOwners.has(uri)) {\n await resources();\n }\n return resourceOwners.get(uri)?.read(uri);\n },\n dispose: () => {\n for (const surface of surfaces) {\n surface.dispose?.();\n }\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as agentSurface, i as serveAgentPort } from "./remote-
|
|
1
|
+
import { a as agentSurface, i as serveAgentPort } from "./remote-BEGRJ1V3.js";
|
|
2
2
|
import { Subscription } from "rxjs";
|
|
3
3
|
//#region src/channel/structuralEquals.ts
|
|
4
4
|
/**
|
|
@@ -949,4 +949,4 @@ function serveChannels(channels, host) {
|
|
|
949
949
|
//#endregion
|
|
950
950
|
export { structurallyEqual as A, applyPatches as C, channel as D, isChannelHostMessage as E, defineChannel as O, applyPatch as S, isChannelClientMessage as T, printPropValue as _, isPortErrorMessage as a, findUnplainPath as b, servePorts as c, isConsoleEntryMessage as d, describeStream as f, formatStream as g, formatNodeReport as h, isHubMessage as i, viewKeys as k, workerHandle as l, formatNodePath as m, serveChannels as n, isPortHandshake as o, formatAge as p, APPLICATION_WORKER as r, portHandle as s, serve as t, captureConsole as u, ProvidedChannel as v, diffProjection as w, requirePlainData as x, provide as y };
|
|
951
951
|
|
|
952
|
-
//# sourceMappingURL=serveChannels-
|
|
952
|
+
//# sourceMappingURL=serveChannels-CFzq0Hz3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serveChannels-Cs22-p8X.js","names":["MAX_DEPTH","isPlainObject"],"sources":["../src/channel/structuralEquals.ts","../src/channel/ChannelToken.ts","../src/channel/ChannelProtocol.ts","../src/channel/StorePatch.ts","../src/channel/plainData.ts","../src/channel/provide.ts","../src/app/NodeReport.ts","../src/worker/captureConsole.ts","../src/worker/WorkerPorts.ts","../src/channel/serveChannels.ts"],"sourcesContent":["/**\n * Deep comparison for store read models.\n *\n * Projections and selectors allocate a fresh value on every\n * evaluation, so reference equality reports a change on every\n * unrelated state emission. That invalidates bindings and dirties\n * nodes across the whole tree for state the view never read.\n *\n * Scope is deliberately narrow: primitives, arrays, and plain objects\n * — what a projection is allowed to return. Anything else (class\n * instances, Date, Map, Set, functions) compares by reference, which\n * is conservative: it reports a change, so the UI updates when it did\n * not need to rather than failing to update when it did.\n *\n * The same comparison becomes the equality half of the patch differ\n * in Phase E, so a store behaves identically local or remote.\n */\nconst MAX_DEPTH = 100;\n\nexport function structurallyEqual<T>(a: T, b: T): boolean {\n return compare(a, b, 0);\n}\n\nfunction compare(a: unknown, b: unknown, depth: number): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n if (depth > MAX_DEPTH) {\n // Deeper than any sane view model, or cyclic. Report \"changed\"\n // rather than risk a non-terminating walk.\n return false;\n }\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {\n return false;\n }\n\n const aIsArray = Array.isArray(a);\n if (aIsArray !== Array.isArray(b)) {\n return false;\n }\n\n if (aIsArray) {\n const left = a as unknown[];\n const right = b as unknown[];\n if (left.length !== right.length) {\n return false;\n }\n for (let i = 0; i < left.length; i++) {\n if (!compare(left[i], right[i], depth + 1)) {\n return false;\n }\n }\n return true;\n }\n\n if (!isPlainObject(a) || !isPlainObject(b)) {\n return false;\n }\n\n const left = a as Record<string, unknown>;\n const right = b as Record<string, unknown>;\n const leftKeys = Object.keys(left);\n if (leftKeys.length !== Object.keys(right).length) {\n return false;\n }\n for (const key of leftKeys) {\n if (!Object.prototype.hasOwnProperty.call(right, key)) {\n return false;\n }\n if (!compare(left[key], right[key], depth + 1)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Objects created from an object literal or a null prototype. Class\n * instances are excluded so they keep reference semantics.\n */\nfunction isPlainObject(value: object): boolean {\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","/**\n * The barrier between the application and the view, declared once.\n *\n * A token is a name, a shape and an initial value — and no\n * implementation at all. Both threads import it, which is the point:\n * the module holding it has nothing in it to bundle, so an app's api\n * client and domain logic never reach the render worker the way a\n * shared `Store` class dragged them there.\n *\n * What the framework knows about a channel ends here. It diffs plain\n * data and ships patches; where the observables came from — a single\n * subject, or an api → repository → domain → view-model stack — is the\n * application's business and the framework cannot tell the difference.\n *\n * export interface CatalogView {\n * products: ProductRow[];\n * status: 'loading' | 'ready' | 'error';\n * }\n * export interface CatalogCommands {\n * addToCart(id: string): void;\n * }\n * export const Catalog = channel<CatalogView, CatalogCommands>('catalog', {\n * products: [],\n * status: 'loading'\n * });\n */\n\n/**\n * A command a component can send back across the barrier.\n *\n * The arguments are what cross: each is structured-cloned onto the\n * owning thread, so they must be plain data. There may be as many as\n * the command needs, which is why `move(from, to)` is written the way\n * anyone would write it rather than as `move({ from, to })`; a command\n * used to carry one payload and the second argument was dropped on the\n * floor with a warning.\n *\n * Commands return nothing: the effect comes back as a patch, never as\n * a return value, since there is no synchronous answer to be had\n * across a thread.\n */\nexport type Command = (...args: never[]) => void;\n\n/**\n * The internal view of a command set: a bag of callables by name.\n *\n * Public signatures constrain to `object`, not to this. An application\n * declares its commands as an ordinary interface —\n * `interface CatalogCommands { addToCart(id: string): void }` — and an\n * interface has no index signature, so it does not satisfy a\n * `Record<string, …>` constraint however well it fits in spirit.\n * Constraining to `object` accepts what people actually write; this\n * alias is what the implementation casts to when it looks a command up\n * by name.\n */\nexport type CommandMap = Record<string, Command>;\n\n/** The declared barrier for one area of an application. */\nexport interface ChannelToken<View extends object, Commands extends object = Record<string, never>> {\n readonly name: string;\n /**\n * What every view key holds before the first patch arrives.\n *\n * Required rather than optional, so the render thread never observes\n * `undefined` for a declared key. A channel that is genuinely still\n * loading says so in its own shape — a `status` field — rather than\n * leaving the view to infer it from an absence.\n */\n readonly initial: View;\n /** Phantom, carrying the command types to `send`. Never read. */\n readonly commands?: Commands;\n}\n\n/**\n * Declares a channel.\n *\n * The name identifies it across the thread boundary and must be\n * stable; unlike a class name it survives minification, which is why\n * it is written out rather than derived.\n */\nexport function channel<View extends object, Commands extends object = Record<string, never>>(\n name: string,\n initial: View\n): ChannelToken<View, Commands> {\n if (name.length === 0) {\n throw new Error('A channel needs a name: it is how the two threads agree on which one this is.');\n }\n return { name, initial };\n}\n\n/** A channel written as one object: the view with its values, and the commands. */\nexport interface ChannelSpec<View extends object, Commands extends object> {\n /**\n * Every view key with the value it holds before the first patch.\n *\n * The type of the channel's view is the type of this object, so the\n * keys and their initial values are written once instead of in an\n * interface and again in a literal that has to agree with it.\n */\n readonly view: View;\n /**\n * The commands, as a type rather than as implementations.\n *\n * Written `{} as { addToCart(id: string, quantity: number): void }`:\n * the handlers live on the thread that owns the data and are passed\n * to `provide`, so what belongs in the token is only their shape.\n */\n readonly commands?: Commands;\n}\n\n/**\n * Declares a channel from one object.\n *\n * export const Catalog = defineChannel('catalog', {\n * view: {\n * products: [] as readonly ProductRow[],\n * status: 'loading' as ShelfStatus\n * },\n * commands: {} as {\n * addToCart(id: string, quantity: number): void;\n * move(from: number, to: number): void;\n * }\n * });\n *\n * export type CatalogView = ViewOf<typeof Catalog>;\n *\n * The same token `channel()` returns, declared once instead of three\n * times. `channel<View, Commands>(name, initial)` wrote the view as an\n * interface, then as an initial literal that had to agree with it, and\n * a key added to one and forgotten in the other was a type error in a\n * third file. Here the object is the type.\n *\n * A field whose initial value is narrower than the type it holds is\n * given the type it holds: `[]` is `never[]` and `'loading'` is\n * `string` unless it is said, which is what the `as` clauses above are\n * for. `ViewOf` and `CommandsOf` name the resulting types wherever the\n * application used to name its own interface.\n *\n * `channel()` is not deprecated and keeps working exactly as it did.\n * An application with interfaces it wants to keep, because they are\n * shared with something else or because the initial values are built\n * elsewhere, has nothing to migrate.\n */\nexport function defineChannel<View extends object, Commands extends object = Record<string, never>>(\n name: string,\n spec: ChannelSpec<View, Commands>\n): ChannelToken<View, Commands> {\n return channel<View, Commands>(name, spec.view);\n}\n\n/** The view type of a channel token, for an application that wants to name it. */\nexport type ViewOf<T> = T extends ChannelToken<infer View, object> ? View : never;\n\n/** The command type of a channel token. */\nexport type CommandsOf<T> = T extends ChannelToken<object, infer Commands> ? Commands : never;\n\n/**\n * The keys a channel publishes.\n *\n * Structural in its parameter rather than generic over the token, so\n * it does not have to agree with any particular command type to read\n * what is only ever the initial value's shape.\n */\nexport function viewKeys(token: { initial: object }): string[] {\n return Object.keys(token.initial);\n}\n","import type { Patch } from './StorePatch';\n\n/**\n * A two-way channel endpoint. `MessagePort` satisfies it, as does a\n * test double, so nothing in the channel layer depends on Worker.\n */\nexport interface ChannelPort {\n postMessage(message: unknown): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/**\n * Render thread → the thread that owns the channel.\n *\n * A command's first argument stayed `payload` when commands learned to\n * take more than one, and the rest travel beside it. That is not\n * tidiness: an older view talking to a newer application worker sends\n * no `rest`, which reads as the one-argument call it is, and an older\n * application worker ignores the field, which is exactly what it did\n * before the field existed.\n */\nexport type ChannelClientMessage =\n | { type: 'channel:sync' }\n | { type: 'channel:command'; command: string; payload: unknown; rest?: unknown[] };\n\n/** The owning thread → render thread. */\nexport type ChannelHostMessage =\n | { type: 'channel:patch'; patches: Patch[] }\n | { type: 'channel:error'; message: string; stack?: string };\n\nexport function isChannelClientMessage(value: unknown): value is ChannelClientMessage {\n const type = (value as { type?: unknown } | null)?.type;\n return type === 'channel:sync' || type === 'channel:command';\n}\n\nexport function isChannelHostMessage(value: unknown): value is ChannelHostMessage {\n const type = (value as { type?: unknown } | null)?.type;\n return type === 'channel:patch' || type === 'channel:error';\n}\n","import { structurallyEqual } from './structuralEquals';\n\nexport type PatchPath = readonly (string | number)[];\n\n/**\n * A change to one projection, as sent from a data worker to a replica.\n *\n * Paths are relative to the projection's root value, so a patch is\n * self-contained: the replica never needs the previous value to apply\n * one, only the value it already holds.\n */\nexport type Patch =\n | { op: 'set'; projection: string; path: PatchPath; value: unknown }\n | { op: 'delete'; projection: string; path: PatchPath }\n | {\n op: 'splice';\n projection: string;\n path: PatchPath;\n index: number;\n deleteCount: number;\n items: readonly unknown[];\n };\n\n/**\n * Describes how to turn `previous` into `current` for one projection.\n *\n * Returns an empty list when nothing changed, which is the common case\n * and the reason this exists: the point of a projection is that most\n * state changes do not alter it, and the ones that do usually alter a\n * small part.\n */\nexport function diffProjection(projection: string, previous: unknown, current: unknown): Patch[] {\n const patches: Patch[] = [];\n diff(projection, [], previous, current, patches);\n return patches;\n}\n\nfunction diff(projection: string, path: PatchPath, previous: unknown, current: unknown, out: Patch[]): void {\n if (structurallyEqual(previous, current)) {\n return;\n }\n\n if (Array.isArray(previous) && Array.isArray(current)) {\n diffArray(projection, path, previous, current, out);\n return;\n }\n\n if (isPlainObject(previous) && isPlainObject(current)) {\n for (const key of Object.keys(current)) {\n if (Object.prototype.hasOwnProperty.call(previous, key)) {\n diff(projection, [...path, key], previous[key], current[key], out);\n } else {\n out.push({ op: 'set', projection, path: [...path, key], value: current[key] });\n }\n }\n for (const key of Object.keys(previous)) {\n if (!Object.prototype.hasOwnProperty.call(current, key)) {\n out.push({ op: 'delete', projection, path: [...path, key] });\n }\n }\n return;\n }\n\n out.push({ op: 'set', projection, path, value: current });\n}\n\n/**\n * Diffs two arrays by trimming the common prefix and suffix.\n *\n * This is not a minimal edit script — a shuffle degrades to replacing\n * the middle wholesale. It is chosen because the operations lists\n * actually undergo (append, prepend, remove one, edit in place) all\n * reduce to a single small patch, and computing a true LCS on every\n * store change would cost more than it saves.\n */\nfunction diffArray(projection: string, path: PatchPath, previous: unknown[], current: unknown[], out: Patch[]): void {\n let start = 0;\n while (start < previous.length && start < current.length && structurallyEqual(previous[start], current[start])) {\n start++;\n }\n\n let previousEnd = previous.length - 1;\n let currentEnd = current.length - 1;\n while (previousEnd >= start && currentEnd >= start && structurallyEqual(previous[previousEnd], current[currentEnd])) {\n previousEnd--;\n currentEnd--;\n }\n\n const previousCount = previousEnd - start + 1;\n const currentCount = currentEnd - start + 1;\n\n if (previousCount === 0 && currentCount === 0) {\n return;\n }\n\n if (previousCount === currentCount) {\n // Same span on both sides: these are edits in place, so recurse\n // and let nested diffs stay small rather than resending elements.\n for (let offset = 0; offset < previousCount; offset++) {\n const index = start + offset;\n diff(projection, [...path, index], previous[index], current[index], out);\n }\n return;\n }\n\n out.push({\n op: 'splice',\n projection,\n path,\n index: start,\n deleteCount: previousCount,\n items: current.slice(start, currentEnd + 1)\n });\n}\n\n/**\n * Applies patches to a projection value, sharing structure with the\n * original everywhere the patch did not reach.\n *\n * Nothing handed in is mutated: bindings hold onto emitted values, so a\n * replica that edited in place would change data a component already\n * rendered.\n *\n * **Each container on a patched path is copied once per batch, not once\n * per patch.** A batch of N patches into one K-key object used to cost\n * N × K: every patch spread the whole object again to change one key.\n * A snapshot keyed by id is exactly that shape — gessologic published\n * `Record<netId, 0 | 1>` for ten thousand nets, about nine hundred\n * patches a publish, and the render worker's patch phase fell minutes\n * behind a 60 Hz stream it could never catch. So the batch remembers\n * the containers it has copied, and writes into those in place: they\n * are its own, made during this call and seen by nobody yet. Values\n * that arrived inside a patch are never written into, because they are\n * the patch's — a devtools log replays the same patch objects again.\n */\nexport function applyPatches(root: unknown, patches: readonly Patch[]): unknown {\n const owned = new Set<object>();\n let next = root;\n for (const patch of patches) {\n next = applyOne(next, patch, owned);\n }\n return next;\n}\n\nexport function applyPatch(root: unknown, patch: Patch): unknown {\n return applyOne(root, patch, new Set());\n}\n\n/** The containers this batch copied, and may therefore write into. */\ntype Owned = Set<object>;\n\nfunction applyOne(root: unknown, patch: Patch, owned: Owned): unknown {\n switch (patch.op) {\n case 'set':\n return setIn(root, patch.path, 0, patch.value, owned);\n case 'delete':\n if (patch.path.length === 0) {\n return undefined;\n }\n return deleteIn(root, patch.path, 0, owned);\n case 'splice':\n return updateIn(root, patch.path, 0, owned, node => {\n // Concatenation rather than `splice(..., ...items)`: spreading\n // the items passes each as an argument, and a batch of two\n // hundred thousand overflows the call stack.\n const array = Array.isArray(node) ? node : [];\n const spliced = array.slice(0, patch.index).concat(patch.items, array.slice(patch.index + patch.deleteCount));\n owned.add(spliced);\n return spliced;\n });\n }\n}\n\nfunction setIn(node: unknown, path: PatchPath, index: number, value: unknown, owned: Owned): unknown {\n if (index === path.length) {\n return value;\n }\n const key = path[index];\n const copy = cloneContainer(node, key, owned);\n setKey(copy, key, setIn(readKey(node, key), path, index + 1, value, owned));\n return copy;\n}\n\nfunction deleteIn(node: unknown, path: PatchPath, index: number, owned: Owned): unknown {\n const key = path[index];\n const copy = cloneContainer(node, key, owned);\n if (index === path.length - 1) {\n if (Array.isArray(copy)) {\n copy.splice(Number(key), 1);\n } else {\n delete (copy as Record<string, unknown>)[String(key)];\n }\n return copy;\n }\n setKey(copy, key, deleteIn(readKey(node, key), path, index + 1, owned));\n return copy;\n}\n\nfunction updateIn(\n node: unknown,\n path: PatchPath,\n index: number,\n owned: Owned,\n update: (node: unknown) => unknown\n): unknown {\n if (index === path.length) {\n return update(node);\n }\n const key = path[index];\n const copy = cloneContainer(node, key, owned);\n setKey(copy, key, updateIn(readKey(node, key), path, index + 1, owned, update));\n return copy;\n}\n\n/**\n * The container to write `key` into: `node` itself when this batch\n * already copied it, and a fresh copy, remembered, when it did not.\n */\nfunction cloneContainer(node: unknown, key: string | number, owned: Owned): unknown[] | Record<string, unknown> {\n if (typeof node === 'object' && node !== null && owned.has(node)) {\n return node as unknown[] | Record<string, unknown>;\n }\n let copy: unknown[] | Record<string, unknown>;\n if (Array.isArray(node)) {\n copy = node.slice();\n } else if (isPlainObject(node)) {\n copy = { ...node };\n } else {\n // The path runs past the end of what the replica holds, which\n // happens when a patch fills in a branch that did not exist yet.\n copy = typeof key === 'number' ? [] : {};\n }\n owned.add(copy);\n return copy;\n}\n\nfunction readKey(node: unknown, key: string | number): unknown {\n if (node === null || node === undefined) {\n return undefined;\n }\n return (node as Record<string | number, unknown>)[key];\n}\n\nfunction setKey(container: unknown[] | Record<string, unknown>, key: string | number, value: unknown): void {\n (container as Record<string | number, unknown>)[key] = value;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","/**\n * Checks that a value can cross the barrier.\n *\n * `structurallyEqual` understands primitives, arrays and plain\n * objects, and falls back to reference equality for everything else —\n * which for a freshly built value reports \"changed\" every single time.\n * A view key holding a `Date`, a `Map` or a domain object therefore\n * re-emits on every unrelated update and dirties the subtree bound to\n * it, forever, while looking perfectly correct.\n *\n * That failure is invisible in a test and shows up as a vague slowness\n * much later, so `provide` checks each key's first emission and throws\n * naming the path. The view-model layer is where rich objects become\n * flat data; this is what makes that a rule rather than a convention.\n *\n * The check runs once per key, on the first value only. It is a\n * development guard against a design mistake, not a validator on the\n * hot path.\n */\n\nconst MAX_DEPTH = 100;\n\n/**\n * Returns the path to the first value that cannot cross, or null when\n * the whole tree is plain data.\n */\nexport function findUnplainPath(value: unknown, path: readonly (string | number)[] = []): string | null {\n if (path.length > MAX_DEPTH) {\n return format(path);\n }\n if (value === null) {\n return null;\n }\n const type = typeof value;\n if (type === 'string' || type === 'number' || type === 'boolean' || type === 'undefined') {\n return null;\n }\n if (type === 'function' || type === 'symbol' || type === 'bigint') {\n return format(path);\n }\n if (Array.isArray(value)) {\n for (let index = 0; index < value.length; index++) {\n const found = findUnplainPath(value[index], [...path, index]);\n if (found !== null) {\n return found;\n }\n }\n return null;\n }\n const prototype = Object.getPrototypeOf(value as object);\n if (prototype !== Object.prototype && prototype !== null) {\n return format(path);\n }\n for (const [key, member] of Object.entries(value as Record<string, unknown>)) {\n const found = findUnplainPath(member, [...path, key]);\n if (found !== null) {\n return found;\n }\n }\n return null;\n}\n\n/**\n * Throws when `value` cannot cross the barrier, naming the channel,\n * the key and the path within it.\n */\nexport function requirePlainData(channelName: string, key: string, value: unknown): void {\n const path = findUnplainPath(value);\n if (path === null) {\n return;\n }\n const where = path === '' ? `'${key}'` : `'${key}'${path}`;\n throw new Error(\n `Channel '${channelName}' published ${where}, which is not plain data. ` +\n `Only primitives, arrays and plain objects cross the barrier: a Date, Map, Set, ` +\n `class instance or function compares by reference, so it would report a change on ` +\n `every update and rebuild the subtree bound to it. Flatten it in the view model.`\n );\n}\n\nfunction format(path: readonly (string | number)[]): string {\n return path.map(step => (typeof step === 'number' ? `[${step}]` : `.${step}`)).join('');\n}\n","import { Subscription, type Observable } from 'rxjs';\n\nimport { diffProjection, type Patch } from './StorePatch';\nimport { isChannelClientMessage, type ChannelHostMessage, type ChannelPort } from './ChannelProtocol';\nimport { requirePlainData } from './plainData';\nimport { viewKeys, type ChannelToken, type CommandMap } from './ChannelToken';\n\n/** What the owning thread supplies for a channel. */\nexport interface ChannelSource<View extends object, Commands extends object> {\n /** One observable per declared view key. */\n view: { readonly [K in keyof View]: Observable<View[K]> };\n /** One handler per declared command. */\n commands?: Commands;\n}\n\n/**\n * Publishes a channel from the thread that owns its data.\n *\n * Each view key is subscribed, diffed against what the other side last\n * saw, and sent as patches. Whatever produced the observable — a bare\n * subject or a stack of layers — stays here; only plain data crosses.\n *\n * Keys are subscribed on the first sync request, so a channel nobody\n * is watching costs nothing.\n */\nexport function provide<View extends object, Commands extends object>(\n token: ChannelToken<View, Commands>,\n source: ChannelSource<View, Commands>,\n port: ChannelPort\n): ProvidedChannel {\n return new ProvidedChannel(\n token as ChannelToken<object, CommandMap>,\n source as ChannelSource<object, CommandMap>,\n port\n );\n}\n\nexport class ProvidedChannel {\n private readonly subscriptions = new Subscription();\n /**\n * What the other side is known to hold, seeded from the token's\n * initial value — which the replica also starts from, so an app\n * whose first emission equals the initial sends nothing at all.\n */\n private readonly previous = new Map<string, unknown>();\n private readonly checked = new Set<string>();\n\n private synced = false;\n\n constructor(\n private readonly token: ChannelToken<object, CommandMap>,\n private readonly source: ChannelSource<object, CommandMap>,\n private readonly port: ChannelPort\n ) {\n for (const key of viewKeys(token)) {\n this.previous.set(key, (token.initial as Record<string, unknown>)[key]);\n }\n this.port.onmessage = event => this.receive(event.data);\n }\n\n private receive(data: unknown): void {\n if (!isChannelClientMessage(data)) {\n return;\n }\n try {\n if (data.type === 'channel:sync') {\n this.sync();\n return;\n }\n this.runCommand(data.command, data.payload, data.rest);\n } catch (error) {\n // Reported rather than thrown: this runs on the thread that owns\n // the data, and a throw here would leave the view waiting for a\n // patch that never comes, with nothing said about why.\n this.post({\n type: 'channel:error',\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined\n });\n }\n }\n\n private runCommand(name: string, payload: unknown, rest?: readonly unknown[]): void {\n const handler = this.source.commands?.[name];\n if (handler === undefined) {\n const names = Object.keys(this.source.commands ?? {})\n .sort()\n .join(', ');\n throw new Error(\n `Channel '${this.token.name}' has no command '${name}'. ` +\n `Declared commands: ${names.length > 0 ? names : '(none)'}.`\n );\n }\n // Spread rather than passed as one payload: a command carries as\n // many arguments as it declares. A client that sent none beyond\n // the first sends no `rest`, so this is the single-argument call\n // it always was.\n (handler as (...args: unknown[]) => void)(payload, ...(rest ?? []));\n }\n\n private sync(): void {\n if (this.synced) {\n this.resend();\n return;\n }\n this.synced = true;\n\n for (const key of viewKeys(this.token)) {\n const observable = (this.source.view as Record<string, Observable<unknown> | undefined>)[key];\n if (observable === undefined) {\n this.post({\n type: 'channel:error',\n message: `Channel '${this.token.name}' declares view key '${key}' but nothing was provided for it.`\n });\n continue;\n }\n this.subscriptions.add(\n observable.subscribe({\n next: value => this.publish(key, value),\n error: (error: unknown) =>\n this.post({\n type: 'channel:error',\n message: `Channel '${this.token.name}' view key '${key}' errored: ${\n error instanceof Error ? error.message : String(error)\n }`,\n stack: error instanceof Error ? error.stack : undefined\n })\n })\n );\n }\n }\n\n private publish(key: string, value: unknown): void {\n if (!this.checked.has(key)) {\n this.checked.add(key);\n try {\n // Once per key, on the first value: a design mistake, caught at\n // startup rather than felt as slowness later. See `plainData`.\n requirePlainData(this.token.name, key, value);\n } catch (error) {\n // Reported, not rethrown. This runs inside an observer's `next`,\n // and RxJS routes a throw there to its unhandled-error hook\n // rather than back to the subscriber — so a throw would surface\n // as a stray async error on this thread and say nothing at all\n // on the thread that needs to know.\n this.post({\n type: 'channel:error',\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined\n });\n return;\n }\n }\n const patches = diffProjection(key, this.previous.get(key), value);\n this.previous.set(key, value);\n if (patches.length > 0) {\n this.post({ type: 'channel:patch', patches });\n }\n }\n\n /** Re-sends every key in full, for a client that reattached. */\n private resend(): void {\n const patches: Patch[] = [];\n for (const [key, value] of this.previous) {\n patches.push({ op: 'set', projection: key, path: [], value });\n }\n if (patches.length > 0) {\n this.post({ type: 'channel:patch', patches });\n }\n }\n\n private post(message: ChannelHostMessage): void {\n this.port.postMessage(message);\n }\n\n dispose(): void {\n this.subscriptions.unsubscribe();\n this.port.onmessage = null;\n }\n}\n","/**\n * What a node is, as plain data (the inspector).\n *\n * The inspector shows a node's props, its resolved environment, its\n * bindings and their sources, its layout explanation and which\n * component owns it. Every one of those lives in the render thread,\n * and none of them can cross a `postMessage`: a `UiNode` is a live\n * object, an `Observable` is a subscription, a component instance is a\n * class. So the report is built where the tree is and only strings\n * cross, which is the same rule the existing `inspect` message follows\n * with `formatExplanation`'s text, and the same reason.\n *\n * That makes the report lossy on purpose. It is a description of a\n * node for a person to read, not a handle on one: nothing in it can be\n * clicked back to the object it came from, and nothing in it is worth\n * asserting on beyond the fact that it says what the tree says.\n */\nexport interface UiNodeReport {\n readonly id: string;\n /** `UiNodeType`, as its string value. */\n readonly type: string;\n readonly box: { readonly x: number; readonly y: number; readonly width: number; readonly height: number };\n /**\n * The components that rendered this node, nearest first.\n *\n * A node has no backpointer to a component: the builder gives every\n * component slot a Fragment anchor whose id carries `:component:`,\n * and the resolver keeps one host per anchor id. Walking up the\n * ancestors for those anchors is therefore the only way to answer\n * \"who rendered this\", and it answers it exactly, because that\n * anchor *is* the component's identity.\n */\n readonly owners: readonly UiOwnerReport[];\n readonly props: readonly UiPropReport[];\n readonly environment: readonly UiEnvironmentReport[];\n /** Attached modifier kinds, in the order the element listed them. */\n readonly modifiers: readonly string[];\n /** The event types this node has listeners for, e.g. `click`. */\n readonly listens: readonly string[];\n /**\n * What lies under the pointer beneath this node, topmost first: the\n * nodes this one is painted over and would take a press from. The\n * answer to \"I clicked the button and nothing happened\": the node the\n * inspector shows is the one that took the press, and this is the\n * button it covered. Empty when the pointer is not over the node.\n */\n readonly beneath: readonly UiBeneathReport[];\n readonly semantics?: UiSemanticsReport;\n /** `formatExplanation` of the node's layout, which is L8's answer. */\n readonly explanation: string;\n}\n\n/** A node under the inspected one at the pointer. */\nexport interface UiBeneathReport {\n readonly id: string;\n readonly type: string;\n /** The component that rendered it, if one did. */\n readonly owner?: string;\n /** The event types it listens for, so a covered button reads as one. */\n readonly listens: readonly string[];\n}\n\nexport interface UiOwnerReport {\n /** The `@Define` tag, else the class or function name. */\n readonly name: string;\n readonly anchorId: string;\n}\n\n/** As much of a node as a one-line path needs. */\nexport interface NodePathTarget {\n /** `UiNodeType`, as its string value. */\n readonly type: string;\n readonly id: string;\n /** The node's accessible name, when it has one. */\n readonly label?: string;\n}\n\n/**\n * A node as a path somebody can read: `App > TrackScreen > ActionRow >\n * Button \"Like\"`.\n *\n * Written for error messages rather than for the inspector, and the\n * difference decides the shape. A report is read beside the tree it\n * came from, so an id is a handle; an error is read in a console or an\n * overlay with no tree beside it, and `node-4821` there is a fact\n * about nothing. The owner chain is what a person recognises, because\n * it is the components they wrote.\n *\n * Owners arrive nearest-first, as `UiNodeReport.owners` computes them,\n * and read outermost-first, as a path does. The accessible name is the\n * leaf when there is one: two `Button`s in the same row are only told\n * apart by what they say.\n */\nexport function formatNodePath(owners: readonly UiOwnerReport[], node: NodePathTarget): string {\n const names = owners.map(owner => owner.name).reverse();\n if (names.length === 0) {\n // Nothing rendered it: a node built without a component around it,\n // which is rare enough that the id is the only thing to say.\n return node.label === undefined || node.label === '' ? `${node.type} ${node.id}` : `${node.type} \"${node.label}\"`;\n }\n const path = names.join(' > ');\n return node.label === undefined || node.label === '' ? path : `${path} \"${node.label}\"`;\n}\n\n/** Where a property's current value came from. */\nexport type UiPropOrigin = 'element' | 'binding' | 'modifier';\n\nexport interface UiPropReport {\n readonly name: string;\n readonly value: string;\n readonly origin: UiPropOrigin;\n /**\n * For a modifier write, the modifiers writing it and what the\n * element declared; for a binding, that it is bound. Absent for a\n * plain element value.\n */\n readonly source?: string;\n /** For a bound property, the stream feeding it. Absent otherwise. */\n readonly stream?: UiStreamReport;\n}\n\n/**\n * The stream behind a bound property: which one, what it last said,\n * and when.\n *\n * \"Is this value bound\" was the third question the inspector set\n * out to answer, and the report answered it with a yes. A yes is not\n * enough to debug with: a prop that is not updating and a prop whose\n * stream has not emitted since the screen was built look identical on\n * the node, and so did they in the report.\n *\n * `emittedAt` is on the epoch clock rather than the thread's own,\n * because the panel reading it is one or two threads away and\n * `performance.now()` counts from each thread's creation. The panel\n * subtracts its own `Date.now()`.\n *\n * An epoch stamp rather than an age for a second reason: an age\n * changes on every frame, and the runtime decides whether to re-send a\n * selected node's report by comparing it with the last one. A report\n * carrying an age would differ from itself sixty times a second and\n * the panel would be redrawn for a clock tick.\n */\nexport interface UiStreamReport {\n /**\n * What feeds it: a labelled cell's own name (`queue.current`), else\n * what kind of stream it is.\n */\n readonly source: string;\n /** `cell` when the stream named itself, `observable` when it did not. */\n readonly kind: 'cell' | 'observable';\n /** The last value the stream produced, printed. */\n readonly value: string;\n /** How many values it has produced since the property was bound. */\n readonly emissions: number;\n /** When the last value arrived, as epoch milliseconds, or null before the first. */\n readonly emittedAt: number | null;\n /** Whether the subscription is still live. A false here is a torn-down binding still on the node. */\n readonly connected: boolean;\n}\n\n/**\n * A bound property's stream, as far as the report is concerned.\n *\n * Structural rather than `UiBinding`, so this module keeps its one\n * rule: it describes a node and imports nothing that holds one.\n */\nexport interface BoundStream {\n readonly id: number;\n readonly observable: unknown;\n value(): unknown;\n emissionCount(): number;\n emittedAt(): number | null;\n connected(): boolean;\n}\n\n/**\n * Describes the stream driving a property.\n *\n * `timeOrigin` turns the binding's own reading into an epoch stamp:\n * `performance.timeOrigin` on a thread that has one, and zero where\n * the reading is already an epoch.\n */\nexport function describeStream(binding: BoundStream, timeOrigin: number): UiStreamReport {\n // A labelled cell says where the value comes from: `Card.title`,\n // `queue.current`. An anonymous pipe only has the binding's id.\n const label = (binding.observable as { label?: unknown } | null)?.label;\n const labelled = typeof label === 'string' && label !== '';\n const emittedAt = binding.emittedAt();\n return {\n source: labelled ? label : `observable #${binding.id}`,\n kind: labelled ? 'cell' : 'observable',\n value: printPropValue(binding.value()),\n emissions: binding.emissionCount(),\n emittedAt: emittedAt === null ? null : timeOrigin + emittedAt,\n connected: binding.connected()\n };\n}\n\n/** A stream as one line: what feeds the prop, and how long ago it said so. */\nexport function formatStream(stream: UiStreamReport, now: number = Date.now()): string {\n const when = stream.emittedAt === null ? 'no value yet' : `${formatAge(Math.max(0, now - stream.emittedAt))} ago`;\n const emissions = `${stream.emissions} emission${stream.emissions === 1 ? '' : 's'}`;\n return `${stream.source} · ${emissions} · ${when}${stream.connected ? '' : ' · disconnected'}`;\n}\n\n/** An age in the largest unit that stays readable. */\nexport function formatAge(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n if (ms < 60_000) {\n return `${(ms / 1000).toFixed(1)}s`;\n }\n return `${Math.round(ms / 60_000)}m`;\n}\n\nexport interface UiEnvironmentReport {\n readonly key: string;\n readonly value: string;\n /** Whether this node provides the value rather than inheriting it. */\n readonly provided: boolean;\n}\n\nexport interface UiSemanticsReport {\n readonly role?: string;\n readonly label?: string;\n readonly value?: string;\n readonly states?: readonly string[];\n}\n\n/**\n * A value as one line a person can read.\n *\n * `JSON.stringify` alone is not enough: the two values a canvas UI\n * puts in a property that it cannot handle are a `Set` (a node's\n * visual states) and a function (an event handler), and it prints both\n * as `{}`, which in an inspector reads as a bug in the application\n * rather than one in the inspector.\n */\nexport function printPropValue(value: unknown): string {\n if (value === undefined) {\n return 'undefined';\n }\n if (value === null) {\n return 'null';\n }\n if (typeof value === 'function') {\n return `ƒ ${value.name === '' ? '(anonymous)' : value.name}`;\n }\n if (typeof value === 'symbol') {\n return value.toString();\n }\n if (typeof value === 'string') {\n return value;\n }\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n if (value instanceof Set) {\n return `Set { ${[...value].map(printPropValue).join(', ')} }`;\n }\n if (value instanceof Map) {\n return `Map { ${[...value].map(([key, entry]) => `${printPropValue(key)}: ${printPropValue(entry)}`).join(', ')} }`;\n }\n if (Array.isArray(value)) {\n return `[${value.map(printPropValue).join(', ')}]`;\n }\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n // A cyclic value, or one with a throwing getter. The inspector\n // must not be the thing that breaks while explaining a break.\n return String(value);\n }\n}\n\n/** The report as text, for a console or a test failure. */\nexport function formatNodeReport(report: UiNodeReport): string {\n const lines: string[] = [`${report.type} '${report.id}'`];\n if (report.owners.length > 0) {\n lines.push(`rendered by ${report.owners.map(owner => owner.name).join(' inside ')}`);\n }\n if (report.modifiers.length > 0) {\n lines.push(`modifiers: ${report.modifiers.join(', ')}`);\n }\n if (report.listens.length > 0) {\n lines.push(`listens: ${report.listens.join(', ')}`);\n }\n if (report.beneath.length > 0) {\n lines.push('beneath, at the pointer:');\n for (const under of report.beneath) {\n const owner = under.owner === undefined ? '' : ` (${under.owner})`;\n const listens = under.listens.length === 0 ? '' : `, listens: ${under.listens.join(', ')}`;\n lines.push(` ${under.type} ${under.id}${owner}${listens}`);\n }\n }\n if (report.props.length > 0) {\n lines.push('props:');\n for (const prop of report.props) {\n lines.push(` ${prop.name} = ${prop.value}${prop.source === undefined ? '' : ` (${prop.source})`}`);\n if (prop.stream !== undefined) {\n lines.push(` ${formatStream(prop.stream)}`);\n }\n }\n }\n if (report.environment.length > 0) {\n lines.push('environment:');\n for (const entry of report.environment) {\n lines.push(` ${entry.key} = ${entry.value}${entry.provided ? ' (provided here)' : ''}`);\n }\n }\n if (report.semantics !== undefined) {\n const parts = [\n report.semantics.role === undefined ? undefined : `role ${report.semantics.role}`,\n report.semantics.label === undefined ? undefined : `label ${report.semantics.label}`,\n report.semantics.value === undefined ? undefined : `value ${report.semantics.value}`,\n report.semantics.states === undefined || report.semantics.states.length === 0\n ? undefined\n : `states ${report.semantics.states.join(', ')}`\n ].filter(part => part !== undefined);\n if (parts.length > 0) {\n lines.push(`semantics: ${parts.join(' · ')}`);\n }\n }\n lines.push(report.explanation);\n return lines.join('\\n');\n}\n","import { printPropValue } from '../app/NodeReport';\nimport type { ConsoleEntry } from '../app/DevtoolsProtocol';\n\n/**\n * Forwarding a worker's `console.*`.\n *\n * A worker's console is real, but it is in devtools' worker target,\n * which a developer has to know to select and cannot read alongside\n * the page's. So while a devtools panel asks for it, each `console.*`\n * call in the worker is also handed to a sink as plain strings, with\n * the thread named, and the panel shows the three threads as one log.\n *\n * The call still reaches the worker's own console: this is a copy,\n * not a redirect, so turning a panel on changes nothing about what\n * devtools' worker target shows.\n */\n\nexport type ConsoleLevel = ConsoleEntry['level'];\n\nconst LEVELS: readonly ConsoleLevel[] = ['log', 'info', 'warn', 'error', 'debug'];\n\n/** An entry before the shell has said which thread it came from. */\nexport type ConsoleEntryBody = Omit<ConsoleEntry, 'thread'>;\n\n/**\n * Copies every `console.*` call on `target` (the worker global's\n * console by default) to `sink`. Returns a function that restores the\n * original methods.\n *\n * Idempotent per target: a second capture on the same console replaces\n * the first's sink rather than nesting, so toggling a panel on twice\n * does not log twice.\n */\nexport function captureConsole(sink: (entry: ConsoleEntryBody) => void, target: Console = console): () => void {\n const installed = (target as Console & { [CAPTURED]?: { sink: typeof sink; restore: () => void } })[CAPTURED];\n if (installed !== undefined) {\n installed.sink = sink;\n return installed.restore;\n }\n const originals = new Map<ConsoleLevel, (...args: unknown[]) => void>();\n const state = {\n sink,\n restore: (): void => {\n for (const [level, original] of originals) {\n target[level] = original;\n }\n delete (target as Console & { [CAPTURED]?: unknown })[CAPTURED];\n }\n };\n for (const level of LEVELS) {\n const original = target[level] as (...args: unknown[]) => void;\n originals.set(level, original);\n target[level] = (...args: unknown[]): void => {\n original.apply(target, args);\n // A sink that throws must not break the application's logging,\n // least of all while a developer is trying to read it.\n try {\n state.sink({ level, args: args.map(formatConsoleArg), at: Date.now() });\n } catch {\n // Nothing to do: the original call already ran.\n }\n };\n }\n (target as Console & { [CAPTURED]?: typeof state })[CAPTURED] = state;\n return state.restore;\n}\n\nconst CAPTURED = Symbol.for('gesso:console-captured');\n\n/**\n * One console argument as the panel prints it.\n *\n * `printPropValue` already prints the values a canvas UI is likely to\n * log; an Error is the one thing it prints badly (`{}`), and the one\n * thing a developer most wants to read whole.\n */\nexport function formatConsoleArg(value: unknown): string {\n if (value instanceof Error) {\n return value.stack !== undefined && value.stack !== '' ? value.stack : `${value.name}: ${value.message}`;\n }\n return printPropValue(value);\n}\n\n/** The shell's request to an application worker to start or stop forwarding. */\nexport interface ConsoleForwardingMessage {\n type: 'gesso:console';\n enabled: boolean;\n}\n\n/** An application worker's forwarded entry. */\nexport interface ConsoleEntryMessage {\n type: 'gesso:console';\n entry: ConsoleEntryBody;\n}\n\nexport function isConsoleForwardingMessage(value: unknown): value is ConsoleForwardingMessage {\n const message = value as { type?: unknown; enabled?: unknown } | null;\n return message?.type === 'gesso:console' && typeof message.enabled === 'boolean';\n}\n\nexport function isConsoleEntryMessage(value: unknown): value is ConsoleEntryMessage {\n const message = value as { type?: unknown; entry?: unknown } | null;\n return message?.type === 'gesso:console' && typeof message.entry === 'object' && message.entry !== null;\n}\n","/**\n * Named `MessagePort`s into a worker.\n *\n * A worker's global `onmessage` is a single channel, so a worker that\n * receives messages on it can host exactly one conversation. That is\n * why a store in a data worker used to mean a worker per store: the\n * client claimed the `Worker` object itself, and a second one had\n * nowhere to go.\n *\n * A handshake fixes it. The client opens a `MessageChannel`, keeps one\n * end and transfers the other with a name; the worker serves that name\n * and the two ends talk privately from then on. The global channel is\n * used once per conversation and carries nothing else.\n *\n * Nothing here knows what travels over a port. It is the transport the\n * store replication in `../store/worker` runs on today and the barrier\n * contract will run on next.\n */\n\nimport { captureConsole, isConsoleForwardingMessage, type ConsoleEntryMessage } from './captureConsole';\n\n/** A port-shaped thing: `MessagePort` and `Worker` both satisfy it. */\nexport interface MessageEndpoint {\n postMessage(message: unknown): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/** The one message the global channel carries. */\nexport interface PortHandshake {\n type: 'gesso:port';\n key: string;\n}\n\nexport function isPortHandshake(value: unknown): value is PortHandshake {\n const message = value as { type?: unknown; key?: unknown } | null;\n return message?.type === 'gesso:port' && typeof message.key === 'string';\n}\n\n/**\n * A worker spawned at most once, serving any number of named ports.\n *\n * Handed to several `useChannel` calls, it is what lets one worker\n * hold a whole application layer instead of one channel. Spawning is\n * deferred to the first `open`, so a handle nobody uses costs nothing.\n */\nexport interface WorkerHandle {\n /**\n * Opens a private channel under `key`, spawning the worker if this\n * is the first one.\n */\n open(key: string): MessagePort;\n /** Whether the worker has been spawned. */\n readonly spawned: boolean;\n /** Stops the worker, if it was ever started. */\n terminate(): void;\n}\n\n/**\n * Stands for \"whichever worker the shell spawned for the application\".\n *\n * A registration inside the render worker cannot name that worker: it\n * is created by the shell and its port only arrives with `init`, long\n * after `useChannel` and `useService` have run. This sentinel is what a\n * registration puts there instead, and the render worker swaps it for\n * the real handle once the port shows up.\n *\n * Opening a port on it before then is a bug rather than a race, so it\n * says so.\n */\nexport const APPLICATION_WORKER: WorkerHandle = {\n open(): MessagePort {\n throw new Error(\n 'APPLICATION_WORKER was used directly. It is a placeholder the render worker ' +\n \"replaces with the shell's port; reaching it means no application-logic worker was supplied. \" +\n 'Pass appLogicWorker to createApp.'\n );\n },\n spawned: false,\n terminate(): void {}\n};\n\n/**\n * Anything a handshake can be posted to with a port attached.\n * `Worker` and `MessagePort` both satisfy it.\n */\nexport interface TransferTarget {\n postMessage(message: unknown, transfer: Transferable[]): void;\n}\n\n/**\n * A handle over an endpoint someone else owns.\n *\n * The shell spawns the application worker and hands the render worker\n * one end of a channel to it; this is what the render worker opens\n * named ports over. `terminate` is a no-op — the lifetime belongs to\n * whoever created the endpoint, and a handle that could kill a worker\n * it did not spawn would be a surprising thing to hand out.\n */\nexport function portHandle(endpoint: TransferTarget): WorkerHandle {\n return {\n open(key: string): MessagePort {\n const channel = new MessageChannel();\n endpoint.postMessage({ type: 'gesso:port', key } satisfies PortHandshake, [channel.port2]);\n return channel.port1;\n },\n get spawned(): boolean {\n return true;\n },\n terminate(): void {}\n };\n}\n\n/**\n * Routes handshakes arriving on a transferred port through the same\n * handlers as the worker's own global channel.\n *\n * The shell owns the application worker and gives the render worker a\n * port to it, so handshakes reach this worker two ways: on its global\n * channel (whoever spawned it) and on that port (whoever was given\n * it). Both should be served by the same handlers, and neither end\n * should have to know which route a channel came in on.\n */\nexport interface HubMessage {\n type: 'gesso:hub';\n}\n\nexport function isHubMessage(value: unknown): value is HubMessage {\n return (value as { type?: unknown } | null)?.type === 'gesso:hub';\n}\n\nconst BASE_INSTALLED = Symbol.for('gesso:port-base-installed');\nconst CONSOLE_RESTORE = Symbol.for('gesso:port-console-restore');\n\n/**\n * Starts or stops copying this worker's console to whoever posts to\n * it, as `ConsoleEntryMessage`s. A host with no `postMessage` (a test's\n * bare object) has nowhere to send them and forwards nothing.\n */\nfunction setConsoleForwarding(host: PortHost & { [CONSOLE_RESTORE]?: () => void }, enabled: boolean): void {\n host[CONSOLE_RESTORE]?.();\n delete host[CONSOLE_RESTORE];\n const post = (host as { postMessage?: (message: unknown) => void }).postMessage;\n if (!enabled || typeof post !== 'function') {\n return;\n }\n host[CONSOLE_RESTORE] = captureConsole(entry => {\n const message: ConsoleEntryMessage = { type: 'gesso:console', entry };\n post.call(host, message);\n });\n}\n\n/**\n * The handler every `servePorts` chain sits on top of.\n *\n * It owns the two things no individual server can: routing a hub port\n * through the whole chain, and answering a handshake that nobody\n * accepted. Both have to be innermost — the first because the chain is\n * only complete once every server has wrapped `onmessage`, the second\n * because \"nobody accepted\" is only known after every server has\n * declined.\n */\nfunction installBase(host: PortHost & { [BASE_INSTALLED]?: boolean }): void {\n if (host[BASE_INSTALLED] === true) {\n return;\n }\n host[BASE_INSTALLED] = true;\n const previous = host.onmessage;\n const registered = (host as HostWithNames)[SERVED_NAMES] ?? [];\n\n host.onmessage = event => {\n if (isConsoleForwardingMessage(event.data)) {\n // The shell, on behalf of a devtools panel, asking for this\n // worker's console. Answered here because this is the one handler\n // every application worker has, whatever it serves.\n setConsoleForwarding(host, event.data.enabled);\n return;\n }\n if (isHubMessage(event.data)) {\n const port = event.ports?.[0];\n if (port === undefined) {\n throw new Error('A hub message arrived with no port attached.');\n }\n // The whole chain, not just this handler: by now every\n // `servePorts` on this host has wrapped `onmessage`, and a\n // handshake arriving on the port should reach all of them.\n port.onmessage = host.onmessage;\n return;\n }\n if (!isPortHandshake(event.data)) {\n previous?.(event);\n return;\n }\n const port = event.ports?.[0];\n if (port === undefined) {\n throw new Error(`Port handshake for '${event.data.key}' arrived with no port attached.`);\n }\n // Reaching here means every server declined. Silence would leave\n // the client waiting forever with nothing said.\n const served = registered.flatMap(get => [...get()]).sort();\n const error: PortErrorMessage = {\n type: 'port:error',\n message: `Nothing is served under '${event.data.key}'. This worker serves: ${\n served.length > 0 ? served.join(', ') : '(nothing)'\n }.`\n };\n port.postMessage(error);\n };\n}\n\n/**\n * Wraps a worker factory so the worker is created once and shared.\n *\n * A factory rather than a URL for the same reason the render worker\n * takes one: a bundler only emits a chunk for a worker it can see\n * constructed literally in the calling module.\n *\n * const data = workerHandle(\n * () => new Worker(new URL('./data.worker.ts', import.meta.url), { type: 'module' })\n * );\n */\nexport function workerHandle(factory: () => Worker): WorkerHandle {\n let worker: Worker | undefined;\n return {\n open(key: string): MessagePort {\n worker ??= factory();\n const channel = new MessageChannel();\n // The worker may not have run its module body yet. Messages\n // posted to a worker queue until it installs a handler, so the\n // handshake is safe as long as `servePorts` is called\n // synchronously at the top level of the worker module — the same\n // guarantee `renderRoot` relies on.\n worker.postMessage({ type: 'gesso:port', key } satisfies PortHandshake, [channel.port2]);\n return channel.port1;\n },\n get spawned(): boolean {\n return worker !== undefined;\n },\n terminate(): void {\n worker?.terminate();\n worker = undefined;\n }\n };\n}\n\n/**\n * Minimal view of a worker's global scope, so this module type-checks\n * against the DOM lib without pulling in the WebWorker lib.\n */\nexport interface PortHost {\n onmessage: ((event: { data: unknown; ports?: readonly MessagePort[] }) => void) | null;\n}\n\n/**\n * What a port is answered with when no handler claimed its name.\n *\n * Its own message type rather than a store's or a channel's, because\n * the transport does not know which of them the client is: both\n * recognise it, so a mismatched name is loud either way.\n */\nexport interface PortErrorMessage {\n type: 'port:error';\n message: string;\n}\n\nexport function isPortErrorMessage(value: unknown): value is PortErrorMessage {\n const message = value as { type?: unknown; message?: unknown } | null;\n return message?.type === 'port:error' && typeof message.message === 'string';\n}\n\n/** Every name served on a host, across all `servePorts` calls on it. */\nconst SERVED_NAMES = Symbol.for('gesso:served-port-names');\n\ninterface HostWithNames extends PortHost {\n [SERVED_NAMES]?: Array<() => readonly string[]>;\n}\n\n/**\n * Serves named ports inside a worker.\n *\n * Call it synchronously at the top level of the worker module, before\n * any await, so no handshake is missed.\n *\n * `onPort` returns whether it took the port. Returning false passes\n * the handshake to whatever was serving before, which is what lets two\n * kinds of thing — stores and channels, during the migration — share\n * one worker: each answers for its own names and declines the rest.\n * When nobody accepts, the port is answered with an error naming\n * everything the worker does serve, because a handshake that silently\n * matched nothing leaves the client waiting forever with nothing said.\n *\n * `names` is only read to build that message.\n *\n * Returns a function that stops serving.\n */\nexport function servePorts(\n onPort: (key: string, port: MessagePort) => boolean,\n names: () => readonly string[],\n host: PortHost = self as unknown as PortHost\n): () => void {\n const withNames = host as HostWithNames;\n const registered = (withNames[SERVED_NAMES] ??= []);\n registered.push(names);\n // Before this handler wraps `onmessage`, so the base ends up\n // innermost: it answers a handshake only once every server above it\n // has declined.\n installBase(host);\n\n const previous = host.onmessage;\n host.onmessage = event => {\n if (!isPortHandshake(event.data)) {\n // Not ours. A worker that also speaks its own protocol on the\n // global channel keeps working.\n previous?.(event);\n return;\n }\n const port = event.ports?.[0];\n if (port === undefined) {\n throw new Error(`Port handshake for '${event.data.key}' arrived with no port attached.`);\n }\n if (onPort(event.data.key, port)) {\n return;\n }\n // Declined. Another server may take it; the base answers if none\n // does.\n previous?.(event);\n };\n return () => {\n host.onmessage = previous;\n const index = registered.indexOf(names);\n if (index >= 0) {\n registered.splice(index, 1);\n }\n };\n}\n","import type { Observable } from 'rxjs';\n\nimport { agentSurface } from '../agent/AgentSurface';\nimport { AGENT_PORT, serveAgentPort } from '../agent/remote';\nimport { servePorts, type PortHost } from '../worker/WorkerPorts';\nimport { provide, type ChannelSource, ProvidedChannel } from './provide';\nimport type { ChannelPort } from './ChannelProtocol';\nimport type { ChannelToken, Command, CommandMap } from './ChannelToken';\n\n/**\n * One channel a worker offers: its token and what feeds it.\n *\n * Types erased structurally, for the same reason `ChannelRegistration`\n * erases them — a list of channels has no single generic\n * instantiation, and making every caller cast to reach one is worse\n * than describing what is actually needed.\n */\nexport interface ServedChannel {\n token: { name: string; initial: object };\n source: {\n view: Record<string, Observable<unknown>>;\n commands?: Record<string, Command>;\n };\n}\n\n/**\n * One served channel, with its source checked against its token.\n *\n * `ServedChannel` is erased on purpose, so one list can hold channels\n * of every shape; the cost is that a view key the token declares and\n * the source forgets is found at startup, by the error `provide`\n * reports, rather than by the compiler. This is the typed seam: the\n * source must hold an Observable for every key of the token's view and\n * a handler for every command, and each handler takes the arguments\n * the token declares, so none of them needs an annotation.\n *\n * serveChannels([\n * serve(Catalog, { view: catalog, commands: { add: name => catalog.add(name) } })\n * ]);\n *\n * The view may be any object with the right observables on it, which\n * is often the domain object itself when its properties are named\n * after the keys. Only the declared keys are read from it.\n */\nexport function serve<View extends object, Commands extends object>(\n token: ChannelToken<View, Commands>,\n source: ChannelSource<View, Commands>\n): ServedChannel {\n return { token, source: source as unknown as ServedChannel['source'] };\n}\n\n/**\n * Publishes channels from an application worker.\n *\n * Call it synchronously at the top level of the worker module, before\n * any await, so no handshake is missed:\n *\n * const catalog = new CatalogViewModel(new CatalogDomain(new OpfsStore()));\n * serveChannels([\n * serve(Catalog, { view: { products: catalog.products$ }, commands: { … } })\n * ]);\n *\n * Everything above this call is the application's own — plain classes,\n * plain observables, no framework import. This function is the entire\n * seam between it and the view.\n *\n * Returns a function that stops serving and disposes what it provided.\n */\nexport function serveChannels(channels: readonly ServedChannel[], host?: PortHost): () => void {\n const byName = new Map<string, ServedChannel>();\n for (const served of channels) {\n byName.set(served.token.name, served);\n }\n const provided: ProvidedChannel[] = [];\n\n const stop = servePorts(\n (key, port) => {\n if (key === AGENT_PORT) {\n // An agent asking, through the page, what this worker serves.\n // Imported rather than loaded on demand: a worker built as an\n // IIFE, which is Vite's default, cannot split off a chunk, and\n // a dynamic import here failed every such build.\n serveAgentPort(port, confirm => agentSurface(channels, { confirm }));\n return true;\n }\n const served = byName.get(key);\n if (served === undefined) {\n // Declined rather than answered, so a worker serving more than\n // one kind of thing can pass the handshake along. `servePorts`\n // reports it if nothing takes it.\n return false;\n }\n provided.push(\n provide(\n served.token as ChannelToken<object, CommandMap>,\n served.source as unknown as ChannelSource<object, CommandMap>,\n port as unknown as ChannelPort\n )\n );\n return true;\n },\n () => [...byName.keys()],\n host\n );\n\n return () => {\n stop();\n for (const channel of provided) {\n channel.dispose();\n }\n provided.length = 0;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAiBA,MAAMA,cAAY;AAElB,SAAgB,kBAAqB,GAAM,GAAe;CACxD,OAAO,QAAQ,GAAG,GAAG,CAAC;AACxB;AAEA,SAAS,QAAQ,GAAY,GAAY,OAAwB;CAC/D,IAAI,OAAO,GAAG,GAAG,CAAC,GAChB,OAAO;CAET,IAAI,QAAQA,aAGV,OAAO;CAET,IAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,MAAM,QAAQ,MAAM,MACxE,OAAO;CAGT,MAAM,WAAW,MAAM,QAAQ,CAAC;CAChC,IAAI,aAAa,MAAM,QAAQ,CAAC,GAC9B,OAAO;CAGT,IAAI,UAAU;EACZ,MAAM,OAAO;EACb,MAAM,QAAQ;EACd,IAAI,KAAK,WAAW,MAAM,QACxB,OAAO;EAET,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAC/B,IAAI,CAAC,QAAQ,KAAK,IAAI,MAAM,IAAI,QAAQ,CAAC,GACvC,OAAO;EAGX,OAAO;CACT;CAEA,IAAI,CAACC,gBAAc,CAAC,KAAK,CAACA,gBAAc,CAAC,GACvC,OAAO;CAGT,MAAM,OAAO;CACb,MAAM,QAAQ;CACd,MAAM,WAAW,OAAO,KAAK,IAAI;CACjC,IAAI,SAAS,WAAW,OAAO,KAAK,KAAK,CAAC,CAAC,QACzC,OAAO;CAET,KAAK,MAAM,OAAO,UAAU;EAC1B,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,OAAO,GAAG,GAClD,OAAO;EAET,IAAI,CAAC,QAAQ,KAAK,MAAM,MAAM,MAAM,QAAQ,CAAC,GAC3C,OAAO;CAEX;CACA,OAAO;AACT;;;;;AAMA,SAASA,gBAAc,OAAwB;CAC7C,MAAM,YAAY,OAAO,eAAe,KAAK;CAC7C,OAAO,cAAc,OAAO,aAAa,cAAc;AACzD;;;;;;;;;;ACHA,SAAgB,QACd,MACA,SAC8B;CAC9B,IAAI,KAAK,WAAW,GAClB,MAAM,IAAI,MAAM,+EAA+E;CAEjG,OAAO;EAAE;EAAM;CAAQ;AACzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,SAAgB,cACd,MACA,MAC8B;CAC9B,OAAO,QAAwB,MAAM,KAAK,IAAI;AAChD;;;;;;;;AAeA,SAAgB,SAAS,OAAsC;CAC7D,OAAO,OAAO,KAAK,MAAM,OAAO;AAClC;;;ACvIA,SAAgB,uBAAuB,OAA+C;CACpF,MAAM,OAAQ,OAAqC;CACnD,OAAO,SAAS,kBAAkB,SAAS;AAC7C;AAEA,SAAgB,qBAAqB,OAA6C;CAChF,MAAM,OAAQ,OAAqC;CACnD,OAAO,SAAS,mBAAmB,SAAS;AAC9C;;;;;;;;;;;ACPA,SAAgB,eAAe,YAAoB,UAAmB,SAA2B;CAC/F,MAAM,UAAmB,CAAC;CAC1B,KAAK,YAAY,CAAC,GAAG,UAAU,SAAS,OAAO;CAC/C,OAAO;AACT;AAEA,SAAS,KAAK,YAAoB,MAAiB,UAAmB,SAAkB,KAAoB;CAC1G,IAAI,kBAAkB,UAAU,OAAO,GACrC;CAGF,IAAI,MAAM,QAAQ,QAAQ,KAAK,MAAM,QAAQ,OAAO,GAAG;EACrD,UAAU,YAAY,MAAM,UAAU,SAAS,GAAG;EAClD;CACF;CAEA,IAAI,cAAc,QAAQ,KAAK,cAAc,OAAO,GAAG;EACrD,KAAK,MAAM,OAAO,OAAO,KAAK,OAAO,GACnC,IAAI,OAAO,UAAU,eAAe,KAAK,UAAU,GAAG,GACpD,KAAK,YAAY,CAAC,GAAG,MAAM,GAAG,GAAG,SAAS,MAAM,QAAQ,MAAM,GAAG;OAEjE,IAAI,KAAK;GAAE,IAAI;GAAO;GAAY,MAAM,CAAC,GAAG,MAAM,GAAG;GAAG,OAAO,QAAQ;EAAK,CAAC;EAGjF,KAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,GACpC,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,SAAS,GAAG,GACpD,IAAI,KAAK;GAAE,IAAI;GAAU;GAAY,MAAM,CAAC,GAAG,MAAM,GAAG;EAAE,CAAC;EAG/D;CACF;CAEA,IAAI,KAAK;EAAE,IAAI;EAAO;EAAY;EAAM,OAAO;CAAQ,CAAC;AAC1D;;;;;;;;;;AAWA,SAAS,UAAU,YAAoB,MAAiB,UAAqB,SAAoB,KAAoB;CACnH,IAAI,QAAQ;CACZ,OAAO,QAAQ,SAAS,UAAU,QAAQ,QAAQ,UAAU,kBAAkB,SAAS,QAAQ,QAAQ,MAAM,GAC3G;CAGF,IAAI,cAAc,SAAS,SAAS;CACpC,IAAI,aAAa,QAAQ,SAAS;CAClC,OAAO,eAAe,SAAS,cAAc,SAAS,kBAAkB,SAAS,cAAc,QAAQ,WAAW,GAAG;EACnH;EACA;CACF;CAEA,MAAM,gBAAgB,cAAc,QAAQ;CAC5C,MAAM,eAAe,aAAa,QAAQ;CAE1C,IAAI,kBAAkB,KAAK,iBAAiB,GAC1C;CAGF,IAAI,kBAAkB,cAAc;EAGlC,KAAK,IAAI,SAAS,GAAG,SAAS,eAAe,UAAU;GACrD,MAAM,QAAQ,QAAQ;GACtB,KAAK,YAAY,CAAC,GAAG,MAAM,KAAK,GAAG,SAAS,QAAQ,QAAQ,QAAQ,GAAG;EACzE;EACA;CACF;CAEA,IAAI,KAAK;EACP,IAAI;EACJ;EACA;EACA,OAAO;EACP,aAAa;EACb,OAAO,QAAQ,MAAM,OAAO,aAAa,CAAC;CAC5C,CAAC;AACH;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,aAAa,MAAe,SAAoC;CAC9E,MAAM,wBAAQ,IAAI,IAAY;CAC9B,IAAI,OAAO;CACX,KAAK,MAAM,SAAS,SAClB,OAAO,SAAS,MAAM,OAAO,KAAK;CAEpC,OAAO;AACT;AAEA,SAAgB,WAAW,MAAe,OAAuB;CAC/D,OAAO,SAAS,MAAM,uBAAO,IAAI,IAAI,CAAC;AACxC;AAKA,SAAS,SAAS,MAAe,OAAc,OAAuB;CACpE,QAAQ,MAAM,IAAd;EACE,KAAK,OACH,OAAO,MAAM,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,KAAK;EACtD,KAAK;GACH,IAAI,MAAM,KAAK,WAAW,GACxB;GAEF,OAAO,SAAS,MAAM,MAAM,MAAM,GAAG,KAAK;EAC5C,KAAK,UACH,OAAO,SAAS,MAAM,MAAM,MAAM,GAAG,QAAO,SAAQ;GAIlD,MAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;GAC5C,MAAM,UAAU,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,CAAC,OAAO,MAAM,OAAO,MAAM,MAAM,MAAM,QAAQ,MAAM,WAAW,CAAC;GAC5G,MAAM,IAAI,OAAO;GACjB,OAAO;EACT,CAAC;CACL;AACF;AAEA,SAAS,MAAM,MAAe,MAAiB,OAAe,OAAgB,OAAuB;CACnG,IAAI,UAAU,KAAK,QACjB,OAAO;CAET,MAAM,MAAM,KAAK;CACjB,MAAM,OAAO,eAAe,MAAM,KAAK,KAAK;CAC5C,OAAO,MAAM,KAAK,MAAM,QAAQ,MAAM,GAAG,GAAG,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC;CAC1E,OAAO;AACT;AAEA,SAAS,SAAS,MAAe,MAAiB,OAAe,OAAuB;CACtF,MAAM,MAAM,KAAK;CACjB,MAAM,OAAO,eAAe,MAAM,KAAK,KAAK;CAC5C,IAAI,UAAU,KAAK,SAAS,GAAG;EAC7B,IAAI,MAAM,QAAQ,IAAI,GACpB,KAAK,OAAO,OAAO,GAAG,GAAG,CAAC;OAE1B,OAAQ,KAAiC,OAAO,GAAG;EAErD,OAAO;CACT;CACA,OAAO,MAAM,KAAK,SAAS,QAAQ,MAAM,GAAG,GAAG,MAAM,QAAQ,GAAG,KAAK,CAAC;CACtE,OAAO;AACT;AAEA,SAAS,SACP,MACA,MACA,OACA,OACA,QACS;CACT,IAAI,UAAU,KAAK,QACjB,OAAO,OAAO,IAAI;CAEpB,MAAM,MAAM,KAAK;CACjB,MAAM,OAAO,eAAe,MAAM,KAAK,KAAK;CAC5C,OAAO,MAAM,KAAK,SAAS,QAAQ,MAAM,GAAG,GAAG,MAAM,QAAQ,GAAG,OAAO,MAAM,CAAC;CAC9E,OAAO;AACT;;;;;AAMA,SAAS,eAAe,MAAe,KAAsB,OAAmD;CAC9G,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,MAAM,IAAI,IAAI,GAC7D,OAAO;CAET,IAAI;CACJ,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO,KAAK,MAAM;MACb,IAAI,cAAc,IAAI,GAC3B,OAAO,EAAE,GAAG,KAAK;MAIjB,OAAO,OAAO,QAAQ,WAAW,CAAC,IAAI,CAAC;CAEzC,MAAM,IAAI,IAAI;CACd,OAAO;AACT;AAEA,SAAS,QAAQ,MAAe,KAA+B;CAC7D,IAAI,SAAS,QAAQ,SAAS,KAAA,GAC5B;CAEF,OAAQ,KAA0C;AACpD;AAEA,SAAS,OAAO,WAAgD,KAAsB,OAAsB;CAC1G,UAAgD,OAAO;AACzD;AAEA,SAAS,cAAc,OAAkD;CACvE,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACpE,OAAO;CAET,MAAM,YAAY,OAAO,eAAe,KAAK;CAC7C,OAAO,cAAc,OAAO,aAAa,cAAc;AACzD;;;;;;;;;;;;;;;;;;;;;;ACzOA,MAAM,YAAY;;;;;AAMlB,SAAgB,gBAAgB,OAAgB,OAAqC,CAAC,GAAkB;CACtG,IAAI,KAAK,SAAS,WAChB,OAAO,OAAO,IAAI;CAEpB,IAAI,UAAU,MACZ,OAAO;CAET,MAAM,OAAO,OAAO;CACpB,IAAI,SAAS,YAAY,SAAS,YAAY,SAAS,aAAa,SAAS,aAC3E,OAAO;CAET,IAAI,SAAS,cAAc,SAAS,YAAY,SAAS,UACvD,OAAO,OAAO,IAAI;CAEpB,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS;GACjD,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,CAAC,GAAG,MAAM,KAAK,CAAC;GAC5D,IAAI,UAAU,MACZ,OAAO;EAEX;EACA,OAAO;CACT;CACA,MAAM,YAAY,OAAO,eAAe,KAAe;CACvD,IAAI,cAAc,OAAO,aAAa,cAAc,MAClD,OAAO,OAAO,IAAI;CAEpB,KAAK,MAAM,CAAC,KAAK,WAAW,OAAO,QAAQ,KAAgC,GAAG;EAC5E,MAAM,QAAQ,gBAAgB,QAAQ,CAAC,GAAG,MAAM,GAAG,CAAC;EACpD,IAAI,UAAU,MACZ,OAAO;CAEX;CACA,OAAO;AACT;;;;;AAMA,SAAgB,iBAAiB,aAAqB,KAAa,OAAsB;CACvF,MAAM,OAAO,gBAAgB,KAAK;CAClC,IAAI,SAAS,MACX;CAEF,MAAM,QAAQ,SAAS,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG;CACpD,MAAM,IAAI,MACR,YAAY,YAAY,cAAc,MAAM,2QAI9C;AACF;AAEA,SAAS,OAAO,MAA4C;CAC1D,OAAO,KAAK,KAAI,SAAS,OAAO,SAAS,WAAW,IAAI,KAAK,KAAK,IAAI,MAAO,CAAC,CAAC,KAAK,EAAE;AACxF;;;;;;;;;;;;;ACzDA,SAAgB,QACd,OACA,QACA,MACiB;CACjB,OAAO,IAAI,gBACT,OACA,QACA,IACF;AACF;AAEA,IAAa,kBAAb,MAA6B;CAaR;CACA;CACA;CAdnB,gBAAiC,IAAI,aAAa;;;;;;CAMlD,2BAA4B,IAAI,IAAqB;CACrD,0BAA2B,IAAI,IAAY;CAE3C,SAAiB;CAEjB,YACE,OACA,QACA,MACA;EAHiB,KAAA,QAAA;EACA,KAAA,SAAA;EACA,KAAA,OAAA;EAEjB,KAAK,MAAM,OAAO,SAAS,KAAK,GAC9B,KAAK,SAAS,IAAI,KAAM,MAAM,QAAoC,IAAI;EAExE,KAAK,KAAK,aAAY,UAAS,KAAK,QAAQ,MAAM,IAAI;CACxD;CAEA,QAAgB,MAAqB;EACnC,IAAI,CAAC,uBAAuB,IAAI,GAC9B;EAEF,IAAI;GACF,IAAI,KAAK,SAAS,gBAAgB;IAChC,KAAK,KAAK;IACV;GACF;GACA,KAAK,WAAW,KAAK,SAAS,KAAK,SAAS,KAAK,IAAI;EACvD,SAAS,OAAO;GAId,KAAK,KAAK;IACR,MAAM;IACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAC9D,OAAO,iBAAiB,QAAQ,MAAM,QAAQ,KAAA;GAChD,CAAC;EACH;CACF;CAEA,WAAmB,MAAc,SAAkB,MAAiC;EAClF,MAAM,UAAU,KAAK,OAAO,WAAW;EACvC,IAAI,YAAY,KAAA,GAAW;GACzB,MAAM,QAAQ,OAAO,KAAK,KAAK,OAAO,YAAY,CAAC,CAAC,CAAC,CAClD,KAAK,CAAC,CACN,KAAK,IAAI;GACZ,MAAM,IAAI,MACR,YAAY,KAAK,MAAM,KAAK,oBAAoB,KAAK,wBAC7B,MAAM,SAAS,IAAI,QAAQ,SAAS,EAC9D;EACF;EAKA,QAA0C,SAAS,GAAI,QAAQ,CAAC,CAAE;CACpE;CAEA,OAAqB;EACnB,IAAI,KAAK,QAAQ;GACf,KAAK,OAAO;GACZ;EACF;EACA,KAAK,SAAS;EAEd,KAAK,MAAM,OAAO,SAAS,KAAK,KAAK,GAAG;GACtC,MAAM,aAAc,KAAK,OAAO,KAAyD;GACzF,IAAI,eAAe,KAAA,GAAW;IAC5B,KAAK,KAAK;KACR,MAAM;KACN,SAAS,YAAY,KAAK,MAAM,KAAK,uBAAuB,IAAI;IAClE,CAAC;IACD;GACF;GACA,KAAK,cAAc,IACjB,WAAW,UAAU;IACnB,OAAM,UAAS,KAAK,QAAQ,KAAK,KAAK;IACtC,QAAQ,UACN,KAAK,KAAK;KACR,MAAM;KACN,SAAS,YAAY,KAAK,MAAM,KAAK,cAAc,IAAI,aACrD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;KAEvD,OAAO,iBAAiB,QAAQ,MAAM,QAAQ,KAAA;IAChD,CAAC;GACL,CAAC,CACH;EACF;CACF;CAEA,QAAgB,KAAa,OAAsB;EACjD,IAAI,CAAC,KAAK,QAAQ,IAAI,GAAG,GAAG;GAC1B,KAAK,QAAQ,IAAI,GAAG;GACpB,IAAI;IAGF,iBAAiB,KAAK,MAAM,MAAM,KAAK,KAAK;GAC9C,SAAS,OAAO;IAMd,KAAK,KAAK;KACR,MAAM;KACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;KAC9D,OAAO,iBAAiB,QAAQ,MAAM,QAAQ,KAAA;IAChD,CAAC;IACD;GACF;EACF;EACA,MAAM,UAAU,eAAe,KAAK,KAAK,SAAS,IAAI,GAAG,GAAG,KAAK;EACjE,KAAK,SAAS,IAAI,KAAK,KAAK;EAC5B,IAAI,QAAQ,SAAS,GACnB,KAAK,KAAK;GAAE,MAAM;GAAiB;EAAQ,CAAC;CAEhD;;CAGA,SAAuB;EACrB,MAAM,UAAmB,CAAC;EAC1B,KAAK,MAAM,CAAC,KAAK,UAAU,KAAK,UAC9B,QAAQ,KAAK;GAAE,IAAI;GAAO,YAAY;GAAK,MAAM,CAAC;GAAG;EAAM,CAAC;EAE9D,IAAI,QAAQ,SAAS,GACnB,KAAK,KAAK;GAAE,MAAM;GAAiB;EAAQ,CAAC;CAEhD;CAEA,KAAa,SAAmC;EAC9C,KAAK,KAAK,YAAY,OAAO;CAC/B;CAEA,UAAgB;EACd,KAAK,cAAc,YAAY;EAC/B,KAAK,KAAK,YAAY;CACxB;AACF;;;;;;;;;;;;;;;;;;;ACtFA,SAAgB,eAAe,QAAkC,MAA8B;CAC7F,MAAM,QAAQ,OAAO,KAAI,UAAS,MAAM,IAAI,CAAC,CAAC,QAAQ;CACtD,IAAI,MAAM,WAAW,GAGnB,OAAO,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,KAAK,GAAG,KAAK,KAAK,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK,IAAI,KAAK,MAAM;CAEjH,MAAM,OAAO,MAAM,KAAK,KAAK;CAC7B,OAAO,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,KAAK,OAAO,GAAG,KAAK,IAAI,KAAK,MAAM;AACvF;;;;;;;;AAgFA,SAAgB,eAAe,SAAsB,YAAoC;CAGvF,MAAM,QAAS,QAAQ,YAA2C;CAClE,MAAM,WAAW,OAAO,UAAU,YAAY,UAAU;CACxD,MAAM,YAAY,QAAQ,UAAU;CACpC,OAAO;EACL,QAAQ,WAAW,QAAQ,eAAe,QAAQ;EAClD,MAAM,WAAW,SAAS;EAC1B,OAAO,eAAe,QAAQ,MAAM,CAAC;EACrC,WAAW,QAAQ,cAAc;EACjC,WAAW,cAAc,OAAO,OAAO,aAAa;EACpD,WAAW,QAAQ,UAAU;CAC/B;AACF;;AAGA,SAAgB,aAAa,QAAwB,MAAc,KAAK,IAAI,GAAW;CACrF,MAAM,OAAO,OAAO,cAAc,OAAO,iBAAiB,GAAG,UAAU,KAAK,IAAI,GAAG,MAAM,OAAO,SAAS,CAAC,EAAE;CAC5G,MAAM,YAAY,GAAG,OAAO,UAAU,WAAW,OAAO,cAAc,IAAI,KAAK;CAC/E,OAAO,GAAG,OAAO,OAAO,KAAK,UAAU,KAAK,OAAO,OAAO,YAAY,KAAK;AAC7E;;AAGA,SAAgB,UAAU,IAAoB;CAC5C,IAAI,KAAK,KACP,OAAO,GAAG,KAAK,MAAM,EAAE,EAAE;CAE3B,IAAI,KAAK,KACP,OAAO,IAAI,KAAK,IAAA,CAAM,QAAQ,CAAC,EAAE;CAEnC,OAAO,GAAG,KAAK,MAAM,KAAK,GAAM,EAAE;AACpC;;;;;;;;;;AAyBA,SAAgB,eAAe,OAAwB;CACrD,IAAI,UAAU,KAAA,GACZ,OAAO;CAET,IAAI,UAAU,MACZ,OAAO;CAET,IAAI,OAAO,UAAU,YACnB,OAAO,KAAK,MAAM,SAAS,KAAK,gBAAgB,MAAM;CAExD,IAAI,OAAO,UAAU,UACnB,OAAO,MAAM,SAAS;CAExB,IAAI,OAAO,UAAU,UACnB,OAAO;CAET,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,aAAa,OAAO,UAAU,UAC9E,OAAO,OAAO,KAAK;CAErB,IAAI,iBAAiB,KACnB,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,KAAK,IAAI,EAAE;CAE5D,IAAI,iBAAiB,KACnB,OAAO,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,WAAW,GAAG,eAAe,GAAG,EAAE,IAAI,eAAe,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE;CAElH,IAAI,MAAM,QAAQ,KAAK,GACrB,OAAO,IAAI,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,IAAI,EAAE;CAElD,IAAI;EACF,OAAO,KAAK,UAAU,KAAK,KAAK,OAAO,KAAK;CAC9C,QAAQ;EAGN,OAAO,OAAO,KAAK;CACrB;AACF;;AAGA,SAAgB,iBAAiB,QAA8B;CAC7D,MAAM,QAAkB,CAAC,GAAG,OAAO,KAAK,IAAI,OAAO,GAAG,EAAE;CACxD,IAAI,OAAO,OAAO,SAAS,GACzB,MAAM,KAAK,eAAe,OAAO,OAAO,KAAI,UAAS,MAAM,IAAI,CAAC,CAAC,KAAK,UAAU,GAAG;CAErF,IAAI,OAAO,UAAU,SAAS,GAC5B,MAAM,KAAK,cAAc,OAAO,UAAU,KAAK,IAAI,GAAG;CAExD,IAAI,OAAO,QAAQ,SAAS,GAC1B,MAAM,KAAK,YAAY,OAAO,QAAQ,KAAK,IAAI,GAAG;CAEpD,IAAI,OAAO,QAAQ,SAAS,GAAG;EAC7B,MAAM,KAAK,0BAA0B;EACrC,KAAK,MAAM,SAAS,OAAO,SAAS;GAClC,MAAM,QAAQ,MAAM,UAAU,KAAA,IAAY,KAAK,KAAK,MAAM,MAAM;GAChE,MAAM,UAAU,MAAM,QAAQ,WAAW,IAAI,KAAK,cAAc,MAAM,QAAQ,KAAK,IAAI;GACvF,MAAM,KAAK,KAAK,MAAM,KAAK,GAAG,MAAM,KAAK,QAAQ,SAAS;EAC5D;CACF;CACA,IAAI,OAAO,MAAM,SAAS,GAAG;EAC3B,MAAM,KAAK,QAAQ;EACnB,KAAK,MAAM,QAAQ,OAAO,OAAO;GAC/B,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,QAAQ,KAAK,WAAW,KAAA,IAAY,KAAK,KAAK,KAAK,OAAO,IAAI;GAClG,IAAI,KAAK,WAAW,KAAA,GAClB,MAAM,KAAK,OAAO,aAAa,KAAK,MAAM,GAAG;EAEjD;CACF;CACA,IAAI,OAAO,YAAY,SAAS,GAAG;EACjC,MAAM,KAAK,cAAc;EACzB,KAAK,MAAM,SAAS,OAAO,aACzB,MAAM,KAAK,KAAK,MAAM,IAAI,KAAK,MAAM,QAAQ,MAAM,WAAW,qBAAqB,IAAI;CAE3F;CACA,IAAI,OAAO,cAAc,KAAA,GAAW;EAClC,MAAM,QAAQ;GACZ,OAAO,UAAU,SAAS,KAAA,IAAY,KAAA,IAAY,QAAQ,OAAO,UAAU;GAC3E,OAAO,UAAU,UAAU,KAAA,IAAY,KAAA,IAAY,SAAS,OAAO,UAAU;GAC7E,OAAO,UAAU,UAAU,KAAA,IAAY,KAAA,IAAY,SAAS,OAAO,UAAU;GAC7E,OAAO,UAAU,WAAW,KAAA,KAAa,OAAO,UAAU,OAAO,WAAW,IACxE,KAAA,IACA,UAAU,OAAO,UAAU,OAAO,KAAK,IAAI;EACjD,CAAC,CAAC,QAAO,SAAQ,SAAS,KAAA,CAAS;EACnC,IAAI,MAAM,SAAS,GACjB,MAAM,KAAK,cAAc,MAAM,KAAK,KAAK,GAAG;CAEhD;CACA,MAAM,KAAK,OAAO,WAAW;CAC7B,OAAO,MAAM,KAAK,IAAI;AACxB;;;ACnTA,MAAM,SAAkC;CAAC;CAAO;CAAQ;CAAQ;CAAS;AAAO;;;;;;;;;;AAchF,SAAgB,eAAe,MAAyC,SAAkB,SAAqB;CAC7G,MAAM,YAAa,OAAiF;CACpG,IAAI,cAAc,KAAA,GAAW;EAC3B,UAAU,OAAO;EACjB,OAAO,UAAU;CACnB;CACA,MAAM,4BAAY,IAAI,IAAgD;CACtE,MAAM,QAAQ;EACZ;EACA,eAAqB;GACnB,KAAK,MAAM,CAAC,OAAO,aAAa,WAC9B,OAAO,SAAS;GAElB,OAAQ,OAA8C;EACxD;CACF;CACA,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,WAAW,OAAO;EACxB,UAAU,IAAI,OAAO,QAAQ;EAC7B,OAAO,UAAU,GAAG,SAA0B;GAC5C,SAAS,MAAM,QAAQ,IAAI;GAG3B,IAAI;IACF,MAAM,KAAK;KAAE;KAAO,MAAM,KAAK,IAAI,gBAAgB;KAAG,IAAI,KAAK,IAAI;IAAE,CAAC;GACxE,QAAQ,CAER;EACF;CACF;CACA,OAAoD,YAAY;CAChE,OAAO,MAAM;AACf;AAEA,MAAM,WAAW,OAAO,IAAI,wBAAwB;;;;;;;;AASpD,SAAgB,iBAAiB,OAAwB;CACvD,IAAI,iBAAiB,OACnB,OAAO,MAAM,UAAU,KAAA,KAAa,MAAM,UAAU,KAAK,MAAM,QAAQ,GAAG,MAAM,KAAK,IAAI,MAAM;CAEjG,OAAO,eAAe,KAAK;AAC7B;AAcA,SAAgB,2BAA2B,OAAmD;CAC5F,MAAM,UAAU;CAChB,OAAO,SAAS,SAAS,mBAAmB,OAAO,QAAQ,YAAY;AACzE;AAEA,SAAgB,sBAAsB,OAA8C;CAClF,MAAM,UAAU;CAChB,OAAO,SAAS,SAAS,mBAAmB,OAAO,QAAQ,UAAU,YAAY,QAAQ,UAAU;AACrG;;;;;;;;;;;;;;;;;;;;;ACtEA,SAAgB,gBAAgB,OAAwC;CACtE,MAAM,UAAU;CAChB,OAAO,SAAS,SAAS,gBAAgB,OAAO,QAAQ,QAAQ;AAClE;;;;;;;;;;;;;AAiCA,MAAa,qBAAmC;CAC9C,OAAoB;EAClB,MAAM,IAAI,MACR,2MAGF;CACF;CACA,SAAS;CACT,YAAkB,CAAC;AACrB;;;;;;;;;;AAmBA,SAAgB,WAAW,UAAwC;CACjE,OAAO;EACL,KAAK,KAA0B;GAC7B,MAAM,UAAU,IAAI,eAAe;GACnC,SAAS,YAAY;IAAE,MAAM;IAAc;GAAI,GAA2B,CAAC,QAAQ,KAAK,CAAC;GACzF,OAAO,QAAQ;EACjB;EACA,IAAI,UAAmB;GACrB,OAAO;EACT;EACA,YAAkB,CAAC;CACrB;AACF;AAgBA,SAAgB,aAAa,OAAqC;CAChE,OAAQ,OAAqC,SAAS;AACxD;AAEA,MAAM,iBAAiB,OAAO,IAAI,2BAA2B;AAC7D,MAAM,kBAAkB,OAAO,IAAI,4BAA4B;;;;;;AAO/D,SAAS,qBAAqB,MAAqD,SAAwB;CACzG,KAAK,gBAAgB,GAAG;CACxB,OAAO,KAAK;CACZ,MAAM,OAAQ,KAAsD;CACpE,IAAI,CAAC,WAAW,OAAO,SAAS,YAC9B;CAEF,KAAK,mBAAmB,gBAAe,UAAS;EAC9C,MAAM,UAA+B;GAAE,MAAM;GAAiB;EAAM;EACpE,KAAK,KAAK,MAAM,OAAO;CACzB,CAAC;AACH;;;;;;;;;;;AAYA,SAAS,YAAY,MAAuD;CAC1E,IAAI,KAAK,oBAAoB,MAC3B;CAEF,KAAK,kBAAkB;CACvB,MAAM,WAAW,KAAK;CACtB,MAAM,aAAc,KAAuB,iBAAiB,CAAC;CAE7D,KAAK,aAAY,UAAS;EACxB,IAAI,2BAA2B,MAAM,IAAI,GAAG;GAI1C,qBAAqB,MAAM,MAAM,KAAK,OAAO;GAC7C;EACF;EACA,IAAI,aAAa,MAAM,IAAI,GAAG;GAC5B,MAAM,OAAO,MAAM,QAAQ;GAC3B,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,8CAA8C;GAKhE,KAAK,YAAY,KAAK;GACtB;EACF;EACA,IAAI,CAAC,gBAAgB,MAAM,IAAI,GAAG;GAChC,WAAW,KAAK;GAChB;EACF;EACA,MAAM,OAAO,MAAM,QAAQ;EAC3B,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,uBAAuB,MAAM,KAAK,IAAI,iCAAiC;EAIzF,MAAM,SAAS,WAAW,SAAQ,QAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK;EAC1D,MAAM,QAA0B;GAC9B,MAAM;GACN,SAAS,4BAA4B,MAAM,KAAK,IAAI,yBAClD,OAAO,SAAS,IAAI,OAAO,KAAK,IAAI,IAAI,YACzC;EACH;EACA,KAAK,YAAY,KAAK;CACxB;AACF;;;;;;;;;;;;AAaA,SAAgB,aAAa,SAAqC;CAChE,IAAI;CACJ,OAAO;EACL,KAAK,KAA0B;GAC7B,WAAW,QAAQ;GACnB,MAAM,UAAU,IAAI,eAAe;GAMnC,OAAO,YAAY;IAAE,MAAM;IAAc;GAAI,GAA2B,CAAC,QAAQ,KAAK,CAAC;GACvF,OAAO,QAAQ;EACjB;EACA,IAAI,UAAmB;GACrB,OAAO,WAAW,KAAA;EACpB;EACA,YAAkB;GAChB,QAAQ,UAAU;GAClB,SAAS,KAAA;EACX;CACF;AACF;AAsBA,SAAgB,mBAAmB,OAA2C;CAC5E,MAAM,UAAU;CAChB,OAAO,SAAS,SAAS,gBAAgB,OAAO,QAAQ,YAAY;AACtE;;AAGA,MAAM,eAAe,OAAO,IAAI,yBAAyB;;;;;;;;;;;;;;;;;;;AAwBzD,SAAgB,WACd,QACA,OACA,OAAiB,MACL;CACZ,MAAM,YAAY;CAClB,MAAM,aAAc,UAAU,kBAAkB,CAAC;CACjD,WAAW,KAAK,KAAK;CAIrB,YAAY,IAAI;CAEhB,MAAM,WAAW,KAAK;CACtB,KAAK,aAAY,UAAS;EACxB,IAAI,CAAC,gBAAgB,MAAM,IAAI,GAAG;GAGhC,WAAW,KAAK;GAChB;EACF;EACA,MAAM,OAAO,MAAM,QAAQ;EAC3B,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,uBAAuB,MAAM,KAAK,IAAI,iCAAiC;EAEzF,IAAI,OAAO,MAAM,KAAK,KAAK,IAAI,GAC7B;EAIF,WAAW,KAAK;CAClB;CACA,aAAa;EACX,KAAK,YAAY;EACjB,MAAM,QAAQ,WAAW,QAAQ,KAAK;EACtC,IAAI,SAAS,GACX,WAAW,OAAO,OAAO,CAAC;CAE9B;AACF;;;;;;;;;;;;;;;;;;;;;;ACjSA,SAAgB,MACd,OACA,QACe;CACf,OAAO;EAAE;EAAe;CAA6C;AACvE;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,cAAc,UAAoC,MAA6B;CAC7F,MAAM,yBAAS,IAAI,IAA2B;CAC9C,KAAK,MAAM,UAAU,UACnB,OAAO,IAAI,OAAO,MAAM,MAAM,MAAM;CAEtC,MAAM,WAA8B,CAAC;CAErC,MAAM,OAAO,YACV,KAAK,SAAS;EACb,IAAI,QAAA,eAAoB;GAKtB,eAAe,OAAM,YAAW,aAAa,UAAU,EAAE,QAAQ,CAAC,CAAC;GACnE,OAAO;EACT;EACA,MAAM,SAAS,OAAO,IAAI,GAAG;EAC7B,IAAI,WAAW,KAAA,GAIb,OAAO;EAET,SAAS,KACP,QACE,OAAO,OACP,OAAO,QACP,IACF,CACF;EACA,OAAO;CACT,SACM,CAAC,GAAG,OAAO,KAAK,CAAC,GACvB,IACF;CAEA,aAAa;EACX,KAAK;EACL,KAAK,MAAM,WAAW,UACpB,QAAQ,QAAQ;EAElB,SAAS,SAAS;CACpB;AACF"}
|
|
1
|
+
{"version":3,"file":"serveChannels-CFzq0Hz3.js","names":["MAX_DEPTH","isPlainObject"],"sources":["../src/channel/structuralEquals.ts","../src/channel/ChannelToken.ts","../src/channel/ChannelProtocol.ts","../src/channel/StorePatch.ts","../src/channel/plainData.ts","../src/channel/provide.ts","../src/app/NodeReport.ts","../src/worker/captureConsole.ts","../src/worker/WorkerPorts.ts","../src/channel/serveChannels.ts"],"sourcesContent":["/**\n * Deep comparison for store read models.\n *\n * Projections and selectors allocate a fresh value on every\n * evaluation, so reference equality reports a change on every\n * unrelated state emission. That invalidates bindings and dirties\n * nodes across the whole tree for state the view never read.\n *\n * Scope is deliberately narrow: primitives, arrays, and plain objects\n * — what a projection is allowed to return. Anything else (class\n * instances, Date, Map, Set, functions) compares by reference, which\n * is conservative: it reports a change, so the UI updates when it did\n * not need to rather than failing to update when it did.\n *\n * The same comparison becomes the equality half of the patch differ\n * in Phase E, so a store behaves identically local or remote.\n */\nconst MAX_DEPTH = 100;\n\nexport function structurallyEqual<T>(a: T, b: T): boolean {\n return compare(a, b, 0);\n}\n\nfunction compare(a: unknown, b: unknown, depth: number): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n if (depth > MAX_DEPTH) {\n // Deeper than any sane view model, or cyclic. Report \"changed\"\n // rather than risk a non-terminating walk.\n return false;\n }\n if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {\n return false;\n }\n\n const aIsArray = Array.isArray(a);\n if (aIsArray !== Array.isArray(b)) {\n return false;\n }\n\n if (aIsArray) {\n const left = a as unknown[];\n const right = b as unknown[];\n if (left.length !== right.length) {\n return false;\n }\n for (let i = 0; i < left.length; i++) {\n if (!compare(left[i], right[i], depth + 1)) {\n return false;\n }\n }\n return true;\n }\n\n if (!isPlainObject(a) || !isPlainObject(b)) {\n return false;\n }\n\n const left = a as Record<string, unknown>;\n const right = b as Record<string, unknown>;\n const leftKeys = Object.keys(left);\n if (leftKeys.length !== Object.keys(right).length) {\n return false;\n }\n for (const key of leftKeys) {\n if (!Object.prototype.hasOwnProperty.call(right, key)) {\n return false;\n }\n if (!compare(left[key], right[key], depth + 1)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Objects created from an object literal or a null prototype. Class\n * instances are excluded so they keep reference semantics.\n */\nfunction isPlainObject(value: object): boolean {\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","/**\n * The barrier between the application and the view, declared once.\n *\n * A token is a name, a shape and an initial value — and no\n * implementation at all. Both threads import it, which is the point:\n * the module holding it has nothing in it to bundle, so an app's api\n * client and domain logic never reach the render worker the way a\n * shared `Store` class dragged them there.\n *\n * What the framework knows about a channel ends here. It diffs plain\n * data and ships patches; where the observables came from — a single\n * subject, or an api → repository → domain → view-model stack — is the\n * application's business and the framework cannot tell the difference.\n *\n * export interface CatalogView {\n * products: ProductRow[];\n * status: 'loading' | 'ready' | 'error';\n * }\n * export interface CatalogCommands {\n * addToCart(id: string): void;\n * }\n * export const Catalog = channel<CatalogView, CatalogCommands>('catalog', {\n * products: [],\n * status: 'loading'\n * });\n */\n\n/**\n * A command a component can send back across the barrier.\n *\n * The arguments are what cross: each is structured-cloned onto the\n * owning thread, so they must be plain data. There may be as many as\n * the command needs, which is why `move(from, to)` is written the way\n * anyone would write it rather than as `move({ from, to })`; a command\n * used to carry one payload and the second argument was dropped on the\n * floor with a warning.\n *\n * Commands return nothing: the effect comes back as a patch, never as\n * a return value, since there is no synchronous answer to be had\n * across a thread.\n */\nexport type Command = (...args: never[]) => void;\n\n/**\n * The internal view of a command set: a bag of callables by name.\n *\n * Public signatures constrain to `object`, not to this. An application\n * declares its commands as an ordinary interface —\n * `interface CatalogCommands { addToCart(id: string): void }` — and an\n * interface has no index signature, so it does not satisfy a\n * `Record<string, …>` constraint however well it fits in spirit.\n * Constraining to `object` accepts what people actually write; this\n * alias is what the implementation casts to when it looks a command up\n * by name.\n */\nexport type CommandMap = Record<string, Command>;\n\n/** The declared barrier for one area of an application. */\nexport interface ChannelToken<View extends object, Commands extends object = Record<string, never>> {\n readonly name: string;\n /**\n * What every view key holds before the first patch arrives.\n *\n * Required rather than optional, so the render thread never observes\n * `undefined` for a declared key. A channel that is genuinely still\n * loading says so in its own shape — a `status` field — rather than\n * leaving the view to infer it from an absence.\n */\n readonly initial: View;\n /** Phantom, carrying the command types to `send`. Never read. */\n readonly commands?: Commands;\n}\n\n/**\n * Declares a channel.\n *\n * The name identifies it across the thread boundary and must be\n * stable; unlike a class name it survives minification, which is why\n * it is written out rather than derived.\n */\nexport function channel<View extends object, Commands extends object = Record<string, never>>(\n name: string,\n initial: View\n): ChannelToken<View, Commands> {\n if (name.length === 0) {\n throw new Error('A channel needs a name: it is how the two threads agree on which one this is.');\n }\n return { name, initial };\n}\n\n/** A channel written as one object: the view with its values, and the commands. */\nexport interface ChannelSpec<View extends object, Commands extends object> {\n /**\n * Every view key with the value it holds before the first patch.\n *\n * The type of the channel's view is the type of this object, so the\n * keys and their initial values are written once instead of in an\n * interface and again in a literal that has to agree with it.\n */\n readonly view: View;\n /**\n * The commands, as a type rather than as implementations.\n *\n * Written `{} as { addToCart(id: string, quantity: number): void }`:\n * the handlers live on the thread that owns the data and are passed\n * to `provide`, so what belongs in the token is only their shape.\n */\n readonly commands?: Commands;\n}\n\n/**\n * Declares a channel from one object.\n *\n * export const Catalog = defineChannel('catalog', {\n * view: {\n * products: [] as readonly ProductRow[],\n * status: 'loading' as ShelfStatus\n * },\n * commands: {} as {\n * addToCart(id: string, quantity: number): void;\n * move(from: number, to: number): void;\n * }\n * });\n *\n * export type CatalogView = ViewOf<typeof Catalog>;\n *\n * The same token `channel()` returns, declared once instead of three\n * times. `channel<View, Commands>(name, initial)` wrote the view as an\n * interface, then as an initial literal that had to agree with it, and\n * a key added to one and forgotten in the other was a type error in a\n * third file. Here the object is the type.\n *\n * A field whose initial value is narrower than the type it holds is\n * given the type it holds: `[]` is `never[]` and `'loading'` is\n * `string` unless it is said, which is what the `as` clauses above are\n * for. `ViewOf` and `CommandsOf` name the resulting types wherever the\n * application used to name its own interface.\n *\n * `channel()` is not deprecated and keeps working exactly as it did.\n * An application with interfaces it wants to keep, because they are\n * shared with something else or because the initial values are built\n * elsewhere, has nothing to migrate.\n */\nexport function defineChannel<View extends object, Commands extends object = Record<string, never>>(\n name: string,\n spec: ChannelSpec<View, Commands>\n): ChannelToken<View, Commands> {\n return channel<View, Commands>(name, spec.view);\n}\n\n/** The view type of a channel token, for an application that wants to name it. */\nexport type ViewOf<T> = T extends ChannelToken<infer View, object> ? View : never;\n\n/** The command type of a channel token. */\nexport type CommandsOf<T> = T extends ChannelToken<object, infer Commands> ? Commands : never;\n\n/**\n * The keys a channel publishes.\n *\n * Structural in its parameter rather than generic over the token, so\n * it does not have to agree with any particular command type to read\n * what is only ever the initial value's shape.\n */\nexport function viewKeys(token: { initial: object }): string[] {\n return Object.keys(token.initial);\n}\n","import type { Patch } from './StorePatch';\n\n/**\n * A two-way channel endpoint. `MessagePort` satisfies it, as does a\n * test double, so nothing in the channel layer depends on Worker.\n */\nexport interface ChannelPort {\n postMessage(message: unknown): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/**\n * Render thread → the thread that owns the channel.\n *\n * A command's first argument stayed `payload` when commands learned to\n * take more than one, and the rest travel beside it. That is not\n * tidiness: an older view talking to a newer application worker sends\n * no `rest`, which reads as the one-argument call it is, and an older\n * application worker ignores the field, which is exactly what it did\n * before the field existed.\n */\nexport type ChannelClientMessage =\n | { type: 'channel:sync' }\n | { type: 'channel:command'; command: string; payload: unknown; rest?: unknown[] };\n\n/** The owning thread → render thread. */\nexport type ChannelHostMessage =\n | { type: 'channel:patch'; patches: Patch[] }\n | { type: 'channel:error'; message: string; stack?: string };\n\nexport function isChannelClientMessage(value: unknown): value is ChannelClientMessage {\n const type = (value as { type?: unknown } | null)?.type;\n return type === 'channel:sync' || type === 'channel:command';\n}\n\nexport function isChannelHostMessage(value: unknown): value is ChannelHostMessage {\n const type = (value as { type?: unknown } | null)?.type;\n return type === 'channel:patch' || type === 'channel:error';\n}\n","import { structurallyEqual } from './structuralEquals';\n\nexport type PatchPath = readonly (string | number)[];\n\n/**\n * A change to one projection, as sent from a data worker to a replica.\n *\n * Paths are relative to the projection's root value, so a patch is\n * self-contained: the replica never needs the previous value to apply\n * one, only the value it already holds.\n */\nexport type Patch =\n | { op: 'set'; projection: string; path: PatchPath; value: unknown }\n | { op: 'delete'; projection: string; path: PatchPath }\n | {\n op: 'splice';\n projection: string;\n path: PatchPath;\n index: number;\n deleteCount: number;\n items: readonly unknown[];\n };\n\n/**\n * Describes how to turn `previous` into `current` for one projection.\n *\n * Returns an empty list when nothing changed, which is the common case\n * and the reason this exists: the point of a projection is that most\n * state changes do not alter it, and the ones that do usually alter a\n * small part.\n */\nexport function diffProjection(projection: string, previous: unknown, current: unknown): Patch[] {\n const patches: Patch[] = [];\n diff(projection, [], previous, current, patches);\n return patches;\n}\n\nfunction diff(projection: string, path: PatchPath, previous: unknown, current: unknown, out: Patch[]): void {\n if (structurallyEqual(previous, current)) {\n return;\n }\n\n if (Array.isArray(previous) && Array.isArray(current)) {\n diffArray(projection, path, previous, current, out);\n return;\n }\n\n if (isPlainObject(previous) && isPlainObject(current)) {\n for (const key of Object.keys(current)) {\n if (Object.prototype.hasOwnProperty.call(previous, key)) {\n diff(projection, [...path, key], previous[key], current[key], out);\n } else {\n out.push({ op: 'set', projection, path: [...path, key], value: current[key] });\n }\n }\n for (const key of Object.keys(previous)) {\n if (!Object.prototype.hasOwnProperty.call(current, key)) {\n out.push({ op: 'delete', projection, path: [...path, key] });\n }\n }\n return;\n }\n\n out.push({ op: 'set', projection, path, value: current });\n}\n\n/**\n * Diffs two arrays by trimming the common prefix and suffix.\n *\n * This is not a minimal edit script — a shuffle degrades to replacing\n * the middle wholesale. It is chosen because the operations lists\n * actually undergo (append, prepend, remove one, edit in place) all\n * reduce to a single small patch, and computing a true LCS on every\n * store change would cost more than it saves.\n */\nfunction diffArray(projection: string, path: PatchPath, previous: unknown[], current: unknown[], out: Patch[]): void {\n let start = 0;\n while (start < previous.length && start < current.length && structurallyEqual(previous[start], current[start])) {\n start++;\n }\n\n let previousEnd = previous.length - 1;\n let currentEnd = current.length - 1;\n while (previousEnd >= start && currentEnd >= start && structurallyEqual(previous[previousEnd], current[currentEnd])) {\n previousEnd--;\n currentEnd--;\n }\n\n const previousCount = previousEnd - start + 1;\n const currentCount = currentEnd - start + 1;\n\n if (previousCount === 0 && currentCount === 0) {\n return;\n }\n\n if (previousCount === currentCount) {\n // Same span on both sides: these are edits in place, so recurse\n // and let nested diffs stay small rather than resending elements.\n for (let offset = 0; offset < previousCount; offset++) {\n const index = start + offset;\n diff(projection, [...path, index], previous[index], current[index], out);\n }\n return;\n }\n\n out.push({\n op: 'splice',\n projection,\n path,\n index: start,\n deleteCount: previousCount,\n items: current.slice(start, currentEnd + 1)\n });\n}\n\n/**\n * Applies patches to a projection value, sharing structure with the\n * original everywhere the patch did not reach.\n *\n * Nothing handed in is mutated: bindings hold onto emitted values, so a\n * replica that edited in place would change data a component already\n * rendered.\n *\n * **Each container on a patched path is copied once per batch, not once\n * per patch.** A batch of N patches into one K-key object used to cost\n * N × K: every patch spread the whole object again to change one key.\n * A snapshot keyed by id is exactly that shape — gessologic published\n * `Record<netId, 0 | 1>` for ten thousand nets, about nine hundred\n * patches a publish, and the render worker's patch phase fell minutes\n * behind a 60 Hz stream it could never catch. So the batch remembers\n * the containers it has copied, and writes into those in place: they\n * are its own, made during this call and seen by nobody yet. Values\n * that arrived inside a patch are never written into, because they are\n * the patch's — a devtools log replays the same patch objects again.\n */\nexport function applyPatches(root: unknown, patches: readonly Patch[]): unknown {\n const owned = new Set<object>();\n let next = root;\n for (const patch of patches) {\n next = applyOne(next, patch, owned);\n }\n return next;\n}\n\nexport function applyPatch(root: unknown, patch: Patch): unknown {\n return applyOne(root, patch, new Set());\n}\n\n/** The containers this batch copied, and may therefore write into. */\ntype Owned = Set<object>;\n\nfunction applyOne(root: unknown, patch: Patch, owned: Owned): unknown {\n switch (patch.op) {\n case 'set':\n return setIn(root, patch.path, 0, patch.value, owned);\n case 'delete':\n if (patch.path.length === 0) {\n return undefined;\n }\n return deleteIn(root, patch.path, 0, owned);\n case 'splice':\n return updateIn(root, patch.path, 0, owned, node => {\n // Concatenation rather than `splice(..., ...items)`: spreading\n // the items passes each as an argument, and a batch of two\n // hundred thousand overflows the call stack.\n const array = Array.isArray(node) ? node : [];\n const spliced = array.slice(0, patch.index).concat(patch.items, array.slice(patch.index + patch.deleteCount));\n owned.add(spliced);\n return spliced;\n });\n }\n}\n\nfunction setIn(node: unknown, path: PatchPath, index: number, value: unknown, owned: Owned): unknown {\n if (index === path.length) {\n return value;\n }\n const key = path[index];\n const copy = cloneContainer(node, key, owned);\n setKey(copy, key, setIn(readKey(node, key), path, index + 1, value, owned));\n return copy;\n}\n\nfunction deleteIn(node: unknown, path: PatchPath, index: number, owned: Owned): unknown {\n const key = path[index];\n const copy = cloneContainer(node, key, owned);\n if (index === path.length - 1) {\n if (Array.isArray(copy)) {\n copy.splice(Number(key), 1);\n } else {\n delete (copy as Record<string, unknown>)[String(key)];\n }\n return copy;\n }\n setKey(copy, key, deleteIn(readKey(node, key), path, index + 1, owned));\n return copy;\n}\n\nfunction updateIn(\n node: unknown,\n path: PatchPath,\n index: number,\n owned: Owned,\n update: (node: unknown) => unknown\n): unknown {\n if (index === path.length) {\n return update(node);\n }\n const key = path[index];\n const copy = cloneContainer(node, key, owned);\n setKey(copy, key, updateIn(readKey(node, key), path, index + 1, owned, update));\n return copy;\n}\n\n/**\n * The container to write `key` into: `node` itself when this batch\n * already copied it, and a fresh copy, remembered, when it did not.\n */\nfunction cloneContainer(node: unknown, key: string | number, owned: Owned): unknown[] | Record<string, unknown> {\n if (typeof node === 'object' && node !== null && owned.has(node)) {\n return node as unknown[] | Record<string, unknown>;\n }\n let copy: unknown[] | Record<string, unknown>;\n if (Array.isArray(node)) {\n copy = node.slice();\n } else if (isPlainObject(node)) {\n copy = { ...node };\n } else {\n // The path runs past the end of what the replica holds, which\n // happens when a patch fills in a branch that did not exist yet.\n copy = typeof key === 'number' ? [] : {};\n }\n owned.add(copy);\n return copy;\n}\n\nfunction readKey(node: unknown, key: string | number): unknown {\n if (node === null || node === undefined) {\n return undefined;\n }\n return (node as Record<string | number, unknown>)[key];\n}\n\nfunction setKey(container: unknown[] | Record<string, unknown>, key: string | number, value: unknown): void {\n (container as Record<string | number, unknown>)[key] = value;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","/**\n * Checks that a value can cross the barrier.\n *\n * `structurallyEqual` understands primitives, arrays and plain\n * objects, and falls back to reference equality for everything else —\n * which for a freshly built value reports \"changed\" every single time.\n * A view key holding a `Date`, a `Map` or a domain object therefore\n * re-emits on every unrelated update and dirties the subtree bound to\n * it, forever, while looking perfectly correct.\n *\n * That failure is invisible in a test and shows up as a vague slowness\n * much later, so `provide` checks each key's first emission and throws\n * naming the path. The view-model layer is where rich objects become\n * flat data; this is what makes that a rule rather than a convention.\n *\n * The check runs once per key, on the first value only. It is a\n * development guard against a design mistake, not a validator on the\n * hot path.\n */\n\nconst MAX_DEPTH = 100;\n\n/**\n * Returns the path to the first value that cannot cross, or null when\n * the whole tree is plain data.\n */\nexport function findUnplainPath(value: unknown, path: readonly (string | number)[] = []): string | null {\n if (path.length > MAX_DEPTH) {\n return format(path);\n }\n if (value === null) {\n return null;\n }\n const type = typeof value;\n if (type === 'string' || type === 'number' || type === 'boolean' || type === 'undefined') {\n return null;\n }\n if (type === 'function' || type === 'symbol' || type === 'bigint') {\n return format(path);\n }\n if (Array.isArray(value)) {\n for (let index = 0; index < value.length; index++) {\n const found = findUnplainPath(value[index], [...path, index]);\n if (found !== null) {\n return found;\n }\n }\n return null;\n }\n const prototype = Object.getPrototypeOf(value as object);\n if (prototype !== Object.prototype && prototype !== null) {\n return format(path);\n }\n for (const [key, member] of Object.entries(value as Record<string, unknown>)) {\n const found = findUnplainPath(member, [...path, key]);\n if (found !== null) {\n return found;\n }\n }\n return null;\n}\n\n/**\n * Throws when `value` cannot cross the barrier, naming the channel,\n * the key and the path within it.\n */\nexport function requirePlainData(channelName: string, key: string, value: unknown): void {\n const path = findUnplainPath(value);\n if (path === null) {\n return;\n }\n const where = path === '' ? `'${key}'` : `'${key}'${path}`;\n throw new Error(\n `Channel '${channelName}' published ${where}, which is not plain data. ` +\n `Only primitives, arrays and plain objects cross the barrier: a Date, Map, Set, ` +\n `class instance or function compares by reference, so it would report a change on ` +\n `every update and rebuild the subtree bound to it. Flatten it in the view model.`\n );\n}\n\nfunction format(path: readonly (string | number)[]): string {\n return path.map(step => (typeof step === 'number' ? `[${step}]` : `.${step}`)).join('');\n}\n","import { Subscription, type Observable } from 'rxjs';\n\nimport { diffProjection, type Patch } from './StorePatch';\nimport { isChannelClientMessage, type ChannelHostMessage, type ChannelPort } from './ChannelProtocol';\nimport { requirePlainData } from './plainData';\nimport { viewKeys, type ChannelToken, type CommandMap } from './ChannelToken';\n\n/** What the owning thread supplies for a channel. */\nexport interface ChannelSource<View extends object, Commands extends object> {\n /** One observable per declared view key. */\n view: { readonly [K in keyof View]: Observable<View[K]> };\n /** One handler per declared command. */\n commands?: Commands;\n}\n\n/**\n * Publishes a channel from the thread that owns its data.\n *\n * Each view key is subscribed, diffed against what the other side last\n * saw, and sent as patches. Whatever produced the observable — a bare\n * subject or a stack of layers — stays here; only plain data crosses.\n *\n * Keys are subscribed on the first sync request, so a channel nobody\n * is watching costs nothing.\n */\nexport function provide<View extends object, Commands extends object>(\n token: ChannelToken<View, Commands>,\n source: ChannelSource<View, Commands>,\n port: ChannelPort\n): ProvidedChannel {\n return new ProvidedChannel(\n token as ChannelToken<object, CommandMap>,\n source as ChannelSource<object, CommandMap>,\n port\n );\n}\n\nexport class ProvidedChannel {\n private readonly subscriptions = new Subscription();\n /**\n * What the other side is known to hold, seeded from the token's\n * initial value — which the replica also starts from, so an app\n * whose first emission equals the initial sends nothing at all.\n */\n private readonly previous = new Map<string, unknown>();\n private readonly checked = new Set<string>();\n\n private synced = false;\n\n constructor(\n private readonly token: ChannelToken<object, CommandMap>,\n private readonly source: ChannelSource<object, CommandMap>,\n private readonly port: ChannelPort\n ) {\n for (const key of viewKeys(token)) {\n this.previous.set(key, (token.initial as Record<string, unknown>)[key]);\n }\n this.port.onmessage = event => this.receive(event.data);\n }\n\n private receive(data: unknown): void {\n if (!isChannelClientMessage(data)) {\n return;\n }\n try {\n if (data.type === 'channel:sync') {\n this.sync();\n return;\n }\n this.runCommand(data.command, data.payload, data.rest);\n } catch (error) {\n // Reported rather than thrown: this runs on the thread that owns\n // the data, and a throw here would leave the view waiting for a\n // patch that never comes, with nothing said about why.\n this.post({\n type: 'channel:error',\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined\n });\n }\n }\n\n private runCommand(name: string, payload: unknown, rest?: readonly unknown[]): void {\n const handler = this.source.commands?.[name];\n if (handler === undefined) {\n const names = Object.keys(this.source.commands ?? {})\n .sort()\n .join(', ');\n throw new Error(\n `Channel '${this.token.name}' has no command '${name}'. ` +\n `Declared commands: ${names.length > 0 ? names : '(none)'}.`\n );\n }\n // Spread rather than passed as one payload: a command carries as\n // many arguments as it declares. A client that sent none beyond\n // the first sends no `rest`, so this is the single-argument call\n // it always was.\n (handler as (...args: unknown[]) => void)(payload, ...(rest ?? []));\n }\n\n private sync(): void {\n if (this.synced) {\n this.resend();\n return;\n }\n this.synced = true;\n\n for (const key of viewKeys(this.token)) {\n const observable = (this.source.view as Record<string, Observable<unknown> | undefined>)[key];\n if (observable === undefined) {\n this.post({\n type: 'channel:error',\n message: `Channel '${this.token.name}' declares view key '${key}' but nothing was provided for it.`\n });\n continue;\n }\n this.subscriptions.add(\n observable.subscribe({\n next: value => this.publish(key, value),\n error: (error: unknown) =>\n this.post({\n type: 'channel:error',\n message: `Channel '${this.token.name}' view key '${key}' errored: ${\n error instanceof Error ? error.message : String(error)\n }`,\n stack: error instanceof Error ? error.stack : undefined\n })\n })\n );\n }\n }\n\n private publish(key: string, value: unknown): void {\n if (!this.checked.has(key)) {\n this.checked.add(key);\n try {\n // Once per key, on the first value: a design mistake, caught at\n // startup rather than felt as slowness later. See `plainData`.\n requirePlainData(this.token.name, key, value);\n } catch (error) {\n // Reported, not rethrown. This runs inside an observer's `next`,\n // and RxJS routes a throw there to its unhandled-error hook\n // rather than back to the subscriber — so a throw would surface\n // as a stray async error on this thread and say nothing at all\n // on the thread that needs to know.\n this.post({\n type: 'channel:error',\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined\n });\n return;\n }\n }\n const patches = diffProjection(key, this.previous.get(key), value);\n this.previous.set(key, value);\n if (patches.length > 0) {\n this.post({ type: 'channel:patch', patches });\n }\n }\n\n /** Re-sends every key in full, for a client that reattached. */\n private resend(): void {\n const patches: Patch[] = [];\n for (const [key, value] of this.previous) {\n patches.push({ op: 'set', projection: key, path: [], value });\n }\n if (patches.length > 0) {\n this.post({ type: 'channel:patch', patches });\n }\n }\n\n private post(message: ChannelHostMessage): void {\n this.port.postMessage(message);\n }\n\n dispose(): void {\n this.subscriptions.unsubscribe();\n this.port.onmessage = null;\n }\n}\n","/**\n * What a node is, as plain data (the inspector).\n *\n * The inspector shows a node's props, its resolved environment, its\n * bindings and their sources, its layout explanation and which\n * component owns it. Every one of those lives in the render thread,\n * and none of them can cross a `postMessage`: a `UiNode` is a live\n * object, an `Observable` is a subscription, a component instance is a\n * class. So the report is built where the tree is and only strings\n * cross, which is the same rule the existing `inspect` message follows\n * with `formatExplanation`'s text, and the same reason.\n *\n * That makes the report lossy on purpose. It is a description of a\n * node for a person to read, not a handle on one: nothing in it can be\n * clicked back to the object it came from, and nothing in it is worth\n * asserting on beyond the fact that it says what the tree says.\n */\nexport interface UiNodeReport {\n readonly id: string;\n /** `UiNodeType`, as its string value. */\n readonly type: string;\n readonly box: { readonly x: number; readonly y: number; readonly width: number; readonly height: number };\n /**\n * The components that rendered this node, nearest first.\n *\n * A node has no backpointer to a component: the builder gives every\n * component slot a Fragment anchor whose id carries `:component:`,\n * and the resolver keeps one host per anchor id. Walking up the\n * ancestors for those anchors is therefore the only way to answer\n * \"who rendered this\", and it answers it exactly, because that\n * anchor *is* the component's identity.\n */\n readonly owners: readonly UiOwnerReport[];\n readonly props: readonly UiPropReport[];\n readonly environment: readonly UiEnvironmentReport[];\n /** Attached modifier kinds, in the order the element listed them. */\n readonly modifiers: readonly string[];\n /** The event types this node has listeners for, e.g. `click`. */\n readonly listens: readonly string[];\n /**\n * What lies under the pointer beneath this node, topmost first: the\n * nodes this one is painted over and would take a press from. The\n * answer to \"I clicked the button and nothing happened\": the node the\n * inspector shows is the one that took the press, and this is the\n * button it covered. Empty when the pointer is not over the node.\n */\n readonly beneath: readonly UiBeneathReport[];\n readonly semantics?: UiSemanticsReport;\n /** `formatExplanation` of the node's layout, which is L8's answer. */\n readonly explanation: string;\n}\n\n/** A node under the inspected one at the pointer. */\nexport interface UiBeneathReport {\n readonly id: string;\n readonly type: string;\n /** The component that rendered it, if one did. */\n readonly owner?: string;\n /** The event types it listens for, so a covered button reads as one. */\n readonly listens: readonly string[];\n}\n\nexport interface UiOwnerReport {\n /** The `@Define` tag, else the class or function name. */\n readonly name: string;\n readonly anchorId: string;\n}\n\n/** As much of a node as a one-line path needs. */\nexport interface NodePathTarget {\n /** `UiNodeType`, as its string value. */\n readonly type: string;\n readonly id: string;\n /** The node's accessible name, when it has one. */\n readonly label?: string;\n}\n\n/**\n * A node as a path somebody can read: `App > TrackScreen > ActionRow >\n * Button \"Like\"`.\n *\n * Written for error messages rather than for the inspector, and the\n * difference decides the shape. A report is read beside the tree it\n * came from, so an id is a handle; an error is read in a console or an\n * overlay with no tree beside it, and `node-4821` there is a fact\n * about nothing. The owner chain is what a person recognises, because\n * it is the components they wrote.\n *\n * Owners arrive nearest-first, as `UiNodeReport.owners` computes them,\n * and read outermost-first, as a path does. The accessible name is the\n * leaf when there is one: two `Button`s in the same row are only told\n * apart by what they say.\n */\nexport function formatNodePath(owners: readonly UiOwnerReport[], node: NodePathTarget): string {\n const names = owners.map(owner => owner.name).reverse();\n if (names.length === 0) {\n // Nothing rendered it: a node built without a component around it,\n // which is rare enough that the id is the only thing to say.\n return node.label === undefined || node.label === '' ? `${node.type} ${node.id}` : `${node.type} \"${node.label}\"`;\n }\n const path = names.join(' > ');\n return node.label === undefined || node.label === '' ? path : `${path} \"${node.label}\"`;\n}\n\n/** Where a property's current value came from. */\nexport type UiPropOrigin = 'element' | 'binding' | 'modifier';\n\nexport interface UiPropReport {\n readonly name: string;\n readonly value: string;\n readonly origin: UiPropOrigin;\n /**\n * For a modifier write, the modifiers writing it and what the\n * element declared; for a binding, that it is bound. Absent for a\n * plain element value.\n */\n readonly source?: string;\n /** For a bound property, the stream feeding it. Absent otherwise. */\n readonly stream?: UiStreamReport;\n}\n\n/**\n * The stream behind a bound property: which one, what it last said,\n * and when.\n *\n * \"Is this value bound\" was the third question the inspector set\n * out to answer, and the report answered it with a yes. A yes is not\n * enough to debug with: a prop that is not updating and a prop whose\n * stream has not emitted since the screen was built look identical on\n * the node, and so did they in the report.\n *\n * `emittedAt` is on the epoch clock rather than the thread's own,\n * because the panel reading it is one or two threads away and\n * `performance.now()` counts from each thread's creation. The panel\n * subtracts its own `Date.now()`.\n *\n * An epoch stamp rather than an age for a second reason: an age\n * changes on every frame, and the runtime decides whether to re-send a\n * selected node's report by comparing it with the last one. A report\n * carrying an age would differ from itself sixty times a second and\n * the panel would be redrawn for a clock tick.\n */\nexport interface UiStreamReport {\n /**\n * What feeds it: a labelled cell's own name (`queue.current`), else\n * what kind of stream it is.\n */\n readonly source: string;\n /** `cell` when the stream named itself, `observable` when it did not. */\n readonly kind: 'cell' | 'observable';\n /** The last value the stream produced, printed. */\n readonly value: string;\n /** How many values it has produced since the property was bound. */\n readonly emissions: number;\n /** When the last value arrived, as epoch milliseconds, or null before the first. */\n readonly emittedAt: number | null;\n /** Whether the subscription is still live. A false here is a torn-down binding still on the node. */\n readonly connected: boolean;\n}\n\n/**\n * A bound property's stream, as far as the report is concerned.\n *\n * Structural rather than `UiBinding`, so this module keeps its one\n * rule: it describes a node and imports nothing that holds one.\n */\nexport interface BoundStream {\n readonly id: number;\n readonly observable: unknown;\n value(): unknown;\n emissionCount(): number;\n emittedAt(): number | null;\n connected(): boolean;\n}\n\n/**\n * Describes the stream driving a property.\n *\n * `timeOrigin` turns the binding's own reading into an epoch stamp:\n * `performance.timeOrigin` on a thread that has one, and zero where\n * the reading is already an epoch.\n */\nexport function describeStream(binding: BoundStream, timeOrigin: number): UiStreamReport {\n // A labelled cell says where the value comes from: `Card.title`,\n // `queue.current`. An anonymous pipe only has the binding's id.\n const label = (binding.observable as { label?: unknown } | null)?.label;\n const labelled = typeof label === 'string' && label !== '';\n const emittedAt = binding.emittedAt();\n return {\n source: labelled ? label : `observable #${binding.id}`,\n kind: labelled ? 'cell' : 'observable',\n value: printPropValue(binding.value()),\n emissions: binding.emissionCount(),\n emittedAt: emittedAt === null ? null : timeOrigin + emittedAt,\n connected: binding.connected()\n };\n}\n\n/** A stream as one line: what feeds the prop, and how long ago it said so. */\nexport function formatStream(stream: UiStreamReport, now: number = Date.now()): string {\n const when = stream.emittedAt === null ? 'no value yet' : `${formatAge(Math.max(0, now - stream.emittedAt))} ago`;\n const emissions = `${stream.emissions} emission${stream.emissions === 1 ? '' : 's'}`;\n return `${stream.source} · ${emissions} · ${when}${stream.connected ? '' : ' · disconnected'}`;\n}\n\n/** An age in the largest unit that stays readable. */\nexport function formatAge(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n if (ms < 60_000) {\n return `${(ms / 1000).toFixed(1)}s`;\n }\n return `${Math.round(ms / 60_000)}m`;\n}\n\nexport interface UiEnvironmentReport {\n readonly key: string;\n readonly value: string;\n /** Whether this node provides the value rather than inheriting it. */\n readonly provided: boolean;\n}\n\nexport interface UiSemanticsReport {\n readonly role?: string;\n readonly label?: string;\n readonly value?: string;\n readonly states?: readonly string[];\n}\n\n/**\n * A value as one line a person can read.\n *\n * `JSON.stringify` alone is not enough: the two values a canvas UI\n * puts in a property that it cannot handle are a `Set` (a node's\n * visual states) and a function (an event handler), and it prints both\n * as `{}`, which in an inspector reads as a bug in the application\n * rather than one in the inspector.\n */\nexport function printPropValue(value: unknown): string {\n if (value === undefined) {\n return 'undefined';\n }\n if (value === null) {\n return 'null';\n }\n if (typeof value === 'function') {\n return `ƒ ${value.name === '' ? '(anonymous)' : value.name}`;\n }\n if (typeof value === 'symbol') {\n return value.toString();\n }\n if (typeof value === 'string') {\n return value;\n }\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n if (value instanceof Set) {\n return `Set { ${[...value].map(printPropValue).join(', ')} }`;\n }\n if (value instanceof Map) {\n return `Map { ${[...value].map(([key, entry]) => `${printPropValue(key)}: ${printPropValue(entry)}`).join(', ')} }`;\n }\n if (Array.isArray(value)) {\n return `[${value.map(printPropValue).join(', ')}]`;\n }\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n // A cyclic value, or one with a throwing getter. The inspector\n // must not be the thing that breaks while explaining a break.\n return String(value);\n }\n}\n\n/** The report as text, for a console or a test failure. */\nexport function formatNodeReport(report: UiNodeReport): string {\n const lines: string[] = [`${report.type} '${report.id}'`];\n if (report.owners.length > 0) {\n lines.push(`rendered by ${report.owners.map(owner => owner.name).join(' inside ')}`);\n }\n if (report.modifiers.length > 0) {\n lines.push(`modifiers: ${report.modifiers.join(', ')}`);\n }\n if (report.listens.length > 0) {\n lines.push(`listens: ${report.listens.join(', ')}`);\n }\n if (report.beneath.length > 0) {\n lines.push('beneath, at the pointer:');\n for (const under of report.beneath) {\n const owner = under.owner === undefined ? '' : ` (${under.owner})`;\n const listens = under.listens.length === 0 ? '' : `, listens: ${under.listens.join(', ')}`;\n lines.push(` ${under.type} ${under.id}${owner}${listens}`);\n }\n }\n if (report.props.length > 0) {\n lines.push('props:');\n for (const prop of report.props) {\n lines.push(` ${prop.name} = ${prop.value}${prop.source === undefined ? '' : ` (${prop.source})`}`);\n if (prop.stream !== undefined) {\n lines.push(` ${formatStream(prop.stream)}`);\n }\n }\n }\n if (report.environment.length > 0) {\n lines.push('environment:');\n for (const entry of report.environment) {\n lines.push(` ${entry.key} = ${entry.value}${entry.provided ? ' (provided here)' : ''}`);\n }\n }\n if (report.semantics !== undefined) {\n const parts = [\n report.semantics.role === undefined ? undefined : `role ${report.semantics.role}`,\n report.semantics.label === undefined ? undefined : `label ${report.semantics.label}`,\n report.semantics.value === undefined ? undefined : `value ${report.semantics.value}`,\n report.semantics.states === undefined || report.semantics.states.length === 0\n ? undefined\n : `states ${report.semantics.states.join(', ')}`\n ].filter(part => part !== undefined);\n if (parts.length > 0) {\n lines.push(`semantics: ${parts.join(' · ')}`);\n }\n }\n lines.push(report.explanation);\n return lines.join('\\n');\n}\n","import { printPropValue } from '../app/NodeReport';\nimport type { ConsoleEntry } from '../app/DevtoolsProtocol';\n\n/**\n * Forwarding a worker's `console.*`.\n *\n * A worker's console is real, but it is in devtools' worker target,\n * which a developer has to know to select and cannot read alongside\n * the page's. So while a devtools panel asks for it, each `console.*`\n * call in the worker is also handed to a sink as plain strings, with\n * the thread named, and the panel shows the three threads as one log.\n *\n * The call still reaches the worker's own console: this is a copy,\n * not a redirect, so turning a panel on changes nothing about what\n * devtools' worker target shows.\n */\n\nexport type ConsoleLevel = ConsoleEntry['level'];\n\nconst LEVELS: readonly ConsoleLevel[] = ['log', 'info', 'warn', 'error', 'debug'];\n\n/** An entry before the shell has said which thread it came from. */\nexport type ConsoleEntryBody = Omit<ConsoleEntry, 'thread'>;\n\n/**\n * Copies every `console.*` call on `target` (the worker global's\n * console by default) to `sink`. Returns a function that restores the\n * original methods.\n *\n * Idempotent per target: a second capture on the same console replaces\n * the first's sink rather than nesting, so toggling a panel on twice\n * does not log twice.\n */\nexport function captureConsole(sink: (entry: ConsoleEntryBody) => void, target: Console = console): () => void {\n const installed = (target as Console & { [CAPTURED]?: { sink: typeof sink; restore: () => void } })[CAPTURED];\n if (installed !== undefined) {\n installed.sink = sink;\n return installed.restore;\n }\n const originals = new Map<ConsoleLevel, (...args: unknown[]) => void>();\n const state = {\n sink,\n restore: (): void => {\n for (const [level, original] of originals) {\n target[level] = original;\n }\n delete (target as Console & { [CAPTURED]?: unknown })[CAPTURED];\n }\n };\n for (const level of LEVELS) {\n const original = target[level] as (...args: unknown[]) => void;\n originals.set(level, original);\n target[level] = (...args: unknown[]): void => {\n original.apply(target, args);\n // A sink that throws must not break the application's logging,\n // least of all while a developer is trying to read it.\n try {\n state.sink({ level, args: args.map(formatConsoleArg), at: Date.now() });\n } catch {\n // Nothing to do: the original call already ran.\n }\n };\n }\n (target as Console & { [CAPTURED]?: typeof state })[CAPTURED] = state;\n return state.restore;\n}\n\nconst CAPTURED = Symbol.for('gesso:console-captured');\n\n/**\n * One console argument as the panel prints it.\n *\n * `printPropValue` already prints the values a canvas UI is likely to\n * log; an Error is the one thing it prints badly (`{}`), and the one\n * thing a developer most wants to read whole.\n */\nexport function formatConsoleArg(value: unknown): string {\n if (value instanceof Error) {\n return value.stack !== undefined && value.stack !== '' ? value.stack : `${value.name}: ${value.message}`;\n }\n return printPropValue(value);\n}\n\n/** The shell's request to an application worker to start or stop forwarding. */\nexport interface ConsoleForwardingMessage {\n type: 'gesso:console';\n enabled: boolean;\n}\n\n/** An application worker's forwarded entry. */\nexport interface ConsoleEntryMessage {\n type: 'gesso:console';\n entry: ConsoleEntryBody;\n}\n\nexport function isConsoleForwardingMessage(value: unknown): value is ConsoleForwardingMessage {\n const message = value as { type?: unknown; enabled?: unknown } | null;\n return message?.type === 'gesso:console' && typeof message.enabled === 'boolean';\n}\n\nexport function isConsoleEntryMessage(value: unknown): value is ConsoleEntryMessage {\n const message = value as { type?: unknown; entry?: unknown } | null;\n return message?.type === 'gesso:console' && typeof message.entry === 'object' && message.entry !== null;\n}\n","/**\n * Named `MessagePort`s into a worker.\n *\n * A worker's global `onmessage` is a single channel, so a worker that\n * receives messages on it can host exactly one conversation. That is\n * why a store in a data worker used to mean a worker per store: the\n * client claimed the `Worker` object itself, and a second one had\n * nowhere to go.\n *\n * A handshake fixes it. The client opens a `MessageChannel`, keeps one\n * end and transfers the other with a name; the worker serves that name\n * and the two ends talk privately from then on. The global channel is\n * used once per conversation and carries nothing else.\n *\n * Nothing here knows what travels over a port. It is the transport the\n * store replication in `../store/worker` runs on today and the barrier\n * contract will run on next.\n */\n\nimport { captureConsole, isConsoleForwardingMessage, type ConsoleEntryMessage } from './captureConsole';\n\n/** A port-shaped thing: `MessagePort` and `Worker` both satisfy it. */\nexport interface MessageEndpoint {\n postMessage(message: unknown): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\n/** The one message the global channel carries. */\nexport interface PortHandshake {\n type: 'gesso:port';\n key: string;\n}\n\nexport function isPortHandshake(value: unknown): value is PortHandshake {\n const message = value as { type?: unknown; key?: unknown } | null;\n return message?.type === 'gesso:port' && typeof message.key === 'string';\n}\n\n/**\n * A worker spawned at most once, serving any number of named ports.\n *\n * Handed to several `useChannel` calls, it is what lets one worker\n * hold a whole application layer instead of one channel. Spawning is\n * deferred to the first `open`, so a handle nobody uses costs nothing.\n */\nexport interface WorkerHandle {\n /**\n * Opens a private channel under `key`, spawning the worker if this\n * is the first one.\n */\n open(key: string): MessagePort;\n /** Whether the worker has been spawned. */\n readonly spawned: boolean;\n /** Stops the worker, if it was ever started. */\n terminate(): void;\n}\n\n/**\n * Stands for \"whichever worker the shell spawned for the application\".\n *\n * A registration inside the render worker cannot name that worker: it\n * is created by the shell and its port only arrives with `init`, long\n * after `useChannel` and `useService` have run. This sentinel is what a\n * registration puts there instead, and the render worker swaps it for\n * the real handle once the port shows up.\n *\n * Opening a port on it before then is a bug rather than a race, so it\n * says so.\n */\nexport const APPLICATION_WORKER: WorkerHandle = {\n open(): MessagePort {\n throw new Error(\n 'APPLICATION_WORKER was used directly. It is a placeholder the render worker ' +\n \"replaces with the shell's port; reaching it means no application-logic worker was supplied. \" +\n 'Pass appLogicWorker to createApp.'\n );\n },\n spawned: false,\n terminate(): void {}\n};\n\n/**\n * Anything a handshake can be posted to with a port attached.\n * `Worker` and `MessagePort` both satisfy it.\n */\nexport interface TransferTarget {\n postMessage(message: unknown, transfer: Transferable[]): void;\n}\n\n/**\n * A handle over an endpoint someone else owns.\n *\n * The shell spawns the application worker and hands the render worker\n * one end of a channel to it; this is what the render worker opens\n * named ports over. `terminate` is a no-op — the lifetime belongs to\n * whoever created the endpoint, and a handle that could kill a worker\n * it did not spawn would be a surprising thing to hand out.\n */\nexport function portHandle(endpoint: TransferTarget): WorkerHandle {\n return {\n open(key: string): MessagePort {\n const channel = new MessageChannel();\n endpoint.postMessage({ type: 'gesso:port', key } satisfies PortHandshake, [channel.port2]);\n return channel.port1;\n },\n get spawned(): boolean {\n return true;\n },\n terminate(): void {}\n };\n}\n\n/**\n * Routes handshakes arriving on a transferred port through the same\n * handlers as the worker's own global channel.\n *\n * The shell owns the application worker and gives the render worker a\n * port to it, so handshakes reach this worker two ways: on its global\n * channel (whoever spawned it) and on that port (whoever was given\n * it). Both should be served by the same handlers, and neither end\n * should have to know which route a channel came in on.\n */\nexport interface HubMessage {\n type: 'gesso:hub';\n}\n\nexport function isHubMessage(value: unknown): value is HubMessage {\n return (value as { type?: unknown } | null)?.type === 'gesso:hub';\n}\n\nconst BASE_INSTALLED = Symbol.for('gesso:port-base-installed');\nconst CONSOLE_RESTORE = Symbol.for('gesso:port-console-restore');\n\n/**\n * Starts or stops copying this worker's console to whoever posts to\n * it, as `ConsoleEntryMessage`s. A host with no `postMessage` (a test's\n * bare object) has nowhere to send them and forwards nothing.\n */\nfunction setConsoleForwarding(host: PortHost & { [CONSOLE_RESTORE]?: () => void }, enabled: boolean): void {\n host[CONSOLE_RESTORE]?.();\n delete host[CONSOLE_RESTORE];\n const post = (host as { postMessage?: (message: unknown) => void }).postMessage;\n if (!enabled || typeof post !== 'function') {\n return;\n }\n host[CONSOLE_RESTORE] = captureConsole(entry => {\n const message: ConsoleEntryMessage = { type: 'gesso:console', entry };\n post.call(host, message);\n });\n}\n\n/**\n * The handler every `servePorts` chain sits on top of.\n *\n * It owns the two things no individual server can: routing a hub port\n * through the whole chain, and answering a handshake that nobody\n * accepted. Both have to be innermost — the first because the chain is\n * only complete once every server has wrapped `onmessage`, the second\n * because \"nobody accepted\" is only known after every server has\n * declined.\n */\nfunction installBase(host: PortHost & { [BASE_INSTALLED]?: boolean }): void {\n if (host[BASE_INSTALLED] === true) {\n return;\n }\n host[BASE_INSTALLED] = true;\n const previous = host.onmessage;\n const registered = (host as HostWithNames)[SERVED_NAMES] ?? [];\n\n host.onmessage = event => {\n if (isConsoleForwardingMessage(event.data)) {\n // The shell, on behalf of a devtools panel, asking for this\n // worker's console. Answered here because this is the one handler\n // every application worker has, whatever it serves.\n setConsoleForwarding(host, event.data.enabled);\n return;\n }\n if (isHubMessage(event.data)) {\n const port = event.ports?.[0];\n if (port === undefined) {\n throw new Error('A hub message arrived with no port attached.');\n }\n // The whole chain, not just this handler: by now every\n // `servePorts` on this host has wrapped `onmessage`, and a\n // handshake arriving on the port should reach all of them.\n port.onmessage = host.onmessage;\n return;\n }\n if (!isPortHandshake(event.data)) {\n previous?.(event);\n return;\n }\n const port = event.ports?.[0];\n if (port === undefined) {\n throw new Error(`Port handshake for '${event.data.key}' arrived with no port attached.`);\n }\n // Reaching here means every server declined. Silence would leave\n // the client waiting forever with nothing said.\n const served = registered.flatMap(get => [...get()]).sort();\n const error: PortErrorMessage = {\n type: 'port:error',\n message: `Nothing is served under '${event.data.key}'. This worker serves: ${\n served.length > 0 ? served.join(', ') : '(nothing)'\n }.`\n };\n port.postMessage(error);\n };\n}\n\n/**\n * Wraps a worker factory so the worker is created once and shared.\n *\n * A factory rather than a URL for the same reason the render worker\n * takes one: a bundler only emits a chunk for a worker it can see\n * constructed literally in the calling module.\n *\n * const data = workerHandle(\n * () => new Worker(new URL('./data.worker.ts', import.meta.url), { type: 'module' })\n * );\n */\nexport function workerHandle(factory: () => Worker): WorkerHandle {\n let worker: Worker | undefined;\n return {\n open(key: string): MessagePort {\n worker ??= factory();\n const channel = new MessageChannel();\n // The worker may not have run its module body yet. Messages\n // posted to a worker queue until it installs a handler, so the\n // handshake is safe as long as `servePorts` is called\n // synchronously at the top level of the worker module — the same\n // guarantee `renderRoot` relies on.\n worker.postMessage({ type: 'gesso:port', key } satisfies PortHandshake, [channel.port2]);\n return channel.port1;\n },\n get spawned(): boolean {\n return worker !== undefined;\n },\n terminate(): void {\n worker?.terminate();\n worker = undefined;\n }\n };\n}\n\n/**\n * Minimal view of a worker's global scope, so this module type-checks\n * against the DOM lib without pulling in the WebWorker lib.\n */\nexport interface PortHost {\n onmessage: ((event: { data: unknown; ports?: readonly MessagePort[] }) => void) | null;\n}\n\n/**\n * What a port is answered with when no handler claimed its name.\n *\n * Its own message type rather than a store's or a channel's, because\n * the transport does not know which of them the client is: both\n * recognise it, so a mismatched name is loud either way.\n */\nexport interface PortErrorMessage {\n type: 'port:error';\n message: string;\n}\n\nexport function isPortErrorMessage(value: unknown): value is PortErrorMessage {\n const message = value as { type?: unknown; message?: unknown } | null;\n return message?.type === 'port:error' && typeof message.message === 'string';\n}\n\n/** Every name served on a host, across all `servePorts` calls on it. */\nconst SERVED_NAMES = Symbol.for('gesso:served-port-names');\n\ninterface HostWithNames extends PortHost {\n [SERVED_NAMES]?: Array<() => readonly string[]>;\n}\n\n/**\n * Serves named ports inside a worker.\n *\n * Call it synchronously at the top level of the worker module, before\n * any await, so no handshake is missed.\n *\n * `onPort` returns whether it took the port. Returning false passes\n * the handshake to whatever was serving before, which is what lets two\n * kinds of thing — stores and channels, during the migration — share\n * one worker: each answers for its own names and declines the rest.\n * When nobody accepts, the port is answered with an error naming\n * everything the worker does serve, because a handshake that silently\n * matched nothing leaves the client waiting forever with nothing said.\n *\n * `names` is only read to build that message.\n *\n * Returns a function that stops serving.\n */\nexport function servePorts(\n onPort: (key: string, port: MessagePort) => boolean,\n names: () => readonly string[],\n host: PortHost = self as unknown as PortHost\n): () => void {\n const withNames = host as HostWithNames;\n const registered = (withNames[SERVED_NAMES] ??= []);\n registered.push(names);\n // Before this handler wraps `onmessage`, so the base ends up\n // innermost: it answers a handshake only once every server above it\n // has declined.\n installBase(host);\n\n const previous = host.onmessage;\n host.onmessage = event => {\n if (!isPortHandshake(event.data)) {\n // Not ours. A worker that also speaks its own protocol on the\n // global channel keeps working.\n previous?.(event);\n return;\n }\n const port = event.ports?.[0];\n if (port === undefined) {\n throw new Error(`Port handshake for '${event.data.key}' arrived with no port attached.`);\n }\n if (onPort(event.data.key, port)) {\n return;\n }\n // Declined. Another server may take it; the base answers if none\n // does.\n previous?.(event);\n };\n return () => {\n host.onmessage = previous;\n const index = registered.indexOf(names);\n if (index >= 0) {\n registered.splice(index, 1);\n }\n };\n}\n","import type { Observable } from 'rxjs';\n\nimport { agentSurface } from '../agent/AgentSurface';\nimport { AGENT_PORT, serveAgentPort } from '../agent/remote';\nimport { servePorts, type PortHost } from '../worker/WorkerPorts';\nimport { provide, type ChannelSource, ProvidedChannel } from './provide';\nimport type { ChannelPort } from './ChannelProtocol';\nimport type { ChannelToken, Command, CommandMap } from './ChannelToken';\n\n/**\n * One channel a worker offers: its token and what feeds it.\n *\n * Types erased structurally, for the same reason `ChannelRegistration`\n * erases them — a list of channels has no single generic\n * instantiation, and making every caller cast to reach one is worse\n * than describing what is actually needed.\n */\nexport interface ServedChannel {\n token: { name: string; initial: object };\n source: {\n view: Record<string, Observable<unknown>>;\n commands?: Record<string, Command>;\n };\n}\n\n/**\n * One served channel, with its source checked against its token.\n *\n * `ServedChannel` is erased on purpose, so one list can hold channels\n * of every shape; the cost is that a view key the token declares and\n * the source forgets is found at startup, by the error `provide`\n * reports, rather than by the compiler. This is the typed seam: the\n * source must hold an Observable for every key of the token's view and\n * a handler for every command, and each handler takes the arguments\n * the token declares, so none of them needs an annotation.\n *\n * serveChannels([\n * serve(Catalog, { view: catalog, commands: { add: name => catalog.add(name) } })\n * ]);\n *\n * The view may be any object with the right observables on it, which\n * is often the domain object itself when its properties are named\n * after the keys. Only the declared keys are read from it.\n */\nexport function serve<View extends object, Commands extends object>(\n token: ChannelToken<View, Commands>,\n source: ChannelSource<View, Commands>\n): ServedChannel {\n return { token, source: source as unknown as ServedChannel['source'] };\n}\n\n/**\n * Publishes channels from an application worker.\n *\n * Call it synchronously at the top level of the worker module, before\n * any await, so no handshake is missed:\n *\n * const catalog = new CatalogViewModel(new CatalogDomain(new OpfsStore()));\n * serveChannels([\n * serve(Catalog, { view: { products: catalog.products$ }, commands: { … } })\n * ]);\n *\n * Everything above this call is the application's own — plain classes,\n * plain observables, no framework import. This function is the entire\n * seam between it and the view.\n *\n * Returns a function that stops serving and disposes what it provided.\n */\nexport function serveChannels(channels: readonly ServedChannel[], host?: PortHost): () => void {\n const byName = new Map<string, ServedChannel>();\n for (const served of channels) {\n byName.set(served.token.name, served);\n }\n const provided: ProvidedChannel[] = [];\n\n const stop = servePorts(\n (key, port) => {\n if (key === AGENT_PORT) {\n // An agent asking, through the page, what this worker serves.\n // Imported rather than loaded on demand: a worker built as an\n // IIFE, which is Vite's default, cannot split off a chunk, and\n // a dynamic import here failed every such build.\n serveAgentPort(port, confirm => agentSurface(channels, { confirm }));\n return true;\n }\n const served = byName.get(key);\n if (served === undefined) {\n // Declined rather than answered, so a worker serving more than\n // one kind of thing can pass the handshake along. `servePorts`\n // reports it if nothing takes it.\n return false;\n }\n provided.push(\n provide(\n served.token as ChannelToken<object, CommandMap>,\n served.source as unknown as ChannelSource<object, CommandMap>,\n port as unknown as ChannelPort\n )\n );\n return true;\n },\n () => [...byName.keys()],\n host\n );\n\n return () => {\n stop();\n for (const channel of provided) {\n channel.dispose();\n }\n provided.length = 0;\n };\n}\n"],"mappings":"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"}
|