@nice-code/action 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -6
- package/build/{AcceptorHandler-11-QMdx2.d.mts → AcceptorHandler-CLbwu2Pa.d.mts} +179 -18
- package/build/{AcceptorHandler-CxD0c1BE.d.cts → AcceptorHandler-Du292dpC.d.cts} +179 -18
- package/build/{ActionDevtoolsCore-37JP4bOG.d.cts → ActionDevtoolsCore-DGwzONZT.d.mts} +2 -2
- package/build/{ActionDevtoolsCore-Cgq-go1R.d.mts → ActionDevtoolsCore-dH4K4w3B.d.cts} +2 -2
- package/build/advanced/index.cjs +1 -1
- package/build/advanced/index.d.cts +12 -101
- package/build/advanced/index.d.mts +12 -101
- package/build/advanced/index.mjs +1 -1
- package/build/{createHibernatableWsServerAdapter-C07RfUTH.mjs → createHibernatableWsServerAdapter-BD5n-Ev9.mjs} +186 -83
- package/build/createHibernatableWsServerAdapter-BD5n-Ev9.mjs.map +1 -0
- package/build/{createHibernatableWsServerAdapter-BNi4k9j3.cjs → createHibernatableWsServerAdapter-j96U9vgo.cjs} +185 -82
- package/build/createHibernatableWsServerAdapter-j96U9vgo.cjs.map +1 -0
- package/build/devtools/browser/index.cjs.map +1 -1
- package/build/devtools/browser/index.d.cts +1 -1
- package/build/devtools/browser/index.d.mts +1 -1
- package/build/devtools/browser/index.mjs.map +1 -1
- package/build/devtools/server/index.d.cts +1 -1
- package/build/devtools/server/index.d.mts +1 -1
- package/build/{httpAcceptorCarrier-C3S_bDkL.cjs → httpAcceptorCarrier-By0Qa__L.cjs} +2 -2
- package/build/httpAcceptorCarrier-By0Qa__L.cjs.map +1 -0
- package/build/{httpAcceptorCarrier-DPBEuewS.mjs → httpAcceptorCarrier-moSmtBxr.mjs} +2 -2
- package/build/httpAcceptorCarrier-moSmtBxr.mjs.map +1 -0
- package/build/index.cjs +6 -2
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +2 -2
- package/build/index.d.mts +2 -2
- package/build/index.mjs +3 -3
- package/build/index.mjs.map +1 -1
- package/build/platform/cloudflare/index.cjs +45 -1
- package/build/platform/cloudflare/index.cjs.map +1 -1
- package/build/platform/cloudflare/index.d.cts +40 -2
- package/build/platform/cloudflare/index.d.mts +40 -2
- package/build/platform/cloudflare/index.mjs +45 -2
- package/build/platform/cloudflare/index.mjs.map +1 -1
- package/build/react-query/index.d.cts +1 -1
- package/build/react-query/index.d.mts +1 -1
- package/package.json +5 -4
- package/build/createHibernatableWsServerAdapter-BNi4k9j3.cjs.map +0 -1
- package/build/createHibernatableWsServerAdapter-C07RfUTH.mjs.map +0 -1
- package/build/httpAcceptorCarrier-C3S_bDkL.cjs.map +0 -1
- package/build/httpAcceptorCarrier-DPBEuewS.mjs.map +0 -1
package/build/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["normalizeFrame"],"sources":["../src/ActionDefinition/Action/Context/ActionContext.ts","../src/ActionDefinition/Action/Core/ActionCore.ts","../src/utils/isAction_Context_JsonObject.ts","../src/utils/isAction_Core_JsonObject.ts","../src/utils/isAction_Any_JsonObject.ts","../src/utils/assertIsActionJson.ts","../src/utils/isAction_Any_Instance.ts","../src/ActionDefinition/Domain/ActionDomainBase.ts","../src/ActionRuntime/ActionRuntimeManager.ts","../src/ActionDefinition/Domain/ActionRootDomain.ts","../src/ActionDefinition/Domain/ActionDomain.ts","../src/ActionDefinition/Domain/helpers/createRootActionDomain.ts","../src/ActionRuntime/Transport/Carrier/duplex/inMemory/createInMemoryChannel.ts","../src/ActionRuntime/Transport/Carrier/duplex/inMemory/inMemoryCarrier.ts","../src/ActionRuntime/Transport/Carrier/duplex/rtc/rtcDataChannelByteChannel.ts","../src/ActionRuntime/Transport/Carrier/duplex/rtc/rtcCarrier.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/err_nice_transport_ws.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/ws_util.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/webSocketByteChannel.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsCarrier.ts","../src/ActionRuntime/Transport/Carrier/exchange/http/httpCarrier.ts"],"sourcesContent":["import { RuntimeCoordinate } from \"../../../ActionRuntime/RuntimeCoordinate\";\nimport type { ActionDomain } from \"../../Domain/ActionDomain\";\nimport type {\n IActionDomain,\n TInferInputFromSchema,\n TInferOutputFromSchema,\n} from \"../../Domain/ActionDomain.types\";\nimport { ActionBase } from \"../ActionBase\";\nimport { EActionForm } from \"../ActionBase.types\";\nimport type {\n IActionContext,\n IActionContext_Data,\n IActionContext_Data_JsonObject,\n IActionContext_JsonObject,\n IActionRouteItem,\n} from \"./ActionContext.types\";\n\nexport class ActionContext<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n >\n extends ActionBase<EActionForm.context, DOM, ID>\n implements IActionContext<DOM, ID>\n{\n readonly form = EActionForm.context;\n readonly _routing: IActionRouteItem[];\n readonly timeCreated: number;\n readonly cuid: string;\n originClient: RuntimeCoordinate;\n\n constructor(\n readonly _domain: ActionDomain<DOM>,\n id: ID,\n hydrationData: IActionContext_Data,\n ) {\n super(EActionForm.context, _domain, id);\n this.timeCreated = hydrationData.timeCreated;\n this.cuid = hydrationData.cuid;\n this._routing = hydrationData.routing;\n this.originClient = hydrationData.originClient;\n }\n\n _setOriginClient(client: RuntimeCoordinate): void {\n this.originClient = client;\n }\n\n toJsonString(): string {\n return JSON.stringify(this.toJsonObject());\n }\n\n toContextDataJsonObject(): IActionContext_Data_JsonObject {\n return {\n timeCreated: this.timeCreated,\n cuid: this.cuid,\n routing: this.routing.map((item) => ({\n runtime: item.runtime.toJsonObject(),\n handler: item.handler,\n time: item.time,\n })),\n originClient: this.originClient.toJsonObject(),\n };\n }\n\n toJsonObject(): IActionContext_JsonObject<DOM, ID> {\n return {\n ...super.toJsonObject(),\n ...this.toContextDataJsonObject(),\n };\n }\n\n get routing(): IActionRouteItem[] {\n return this._routing;\n }\n\n addRouteItem(item: IActionRouteItem): void {\n this._routing.push(item);\n }\n\n deserializeInput(\n serialized: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.deserializeInput(serialized);\n }\n\n serializeInput(\n raw: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"] {\n return this.schema.serializeInput(raw);\n }\n\n validateInput(input: unknown): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.validateInput(input, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n\n validateOutput(output: unknown): TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"] {\n return this.schema.validateOutput(output, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n}\n","import { nanoid } from \"nanoid\";\nimport { RuntimeCoordinate } from \"../../../ActionRuntime/RuntimeCoordinate\";\nimport type { ActionDomain } from \"../../Domain/ActionDomain\";\nimport type {\n IActionDomain,\n TInferInputFromSchema,\n TInferOutputFromSchema,\n} from \"../../Domain/ActionDomain.types\";\nimport type { TNarrowActionType } from \"../Action.combined.types\";\nimport { ActionBase } from \"../ActionBase\";\nimport { EActionForm, type IActionBase, type IActionBase_JsonObject } from \"../ActionBase.types\";\nimport { ActionContext } from \"../Context/ActionContext\";\nimport { ActionPayload } from \"../Payload/ActionPayload\";\nimport { ActionPayload_Request } from \"../Payload/ActionPayload_Request\";\nimport type { IActionCore } from \"./ActionCore.types\";\n\nexport class ActionCore<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n >\n extends ActionBase<EActionForm.core, DOM, ID>\n implements IActionCore<DOM, ID>\n{\n readonly form = EActionForm.core;\n\n constructor(\n readonly _domain: ActionDomain<DOM>,\n id: ID,\n ) {\n super(EActionForm.core, _domain, id);\n }\n\n is<ACT extends IActionBase<any, any, any>>(\n action: ACT | unknown | null | undefined,\n ): action is TNarrowActionType<DOM, ACT, ID> {\n return (\n action instanceof ActionPayload && action.domain === this.domain && action.id === this.id\n );\n }\n\n toJsonObject(): IActionBase_JsonObject<EActionForm.core, DOM, ID> {\n return {\n id: this.id,\n form: this.form,\n domain: this.domain,\n allDomains: this.allDomains,\n };\n }\n\n request(\n ...args: [TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"]] extends [never]\n ? [input?: never]\n : [input: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"]]\n ): ActionPayload_Request<DOM, ID> {\n const input: unknown = args[0];\n const validatedInput = this.schema.validateInput(input, {\n actionId: this.id,\n domain: this.domain,\n });\n\n const context = new ActionContext(this._domain, this.id, {\n cuid: nanoid(),\n timeCreated: Date.now(),\n routing: [],\n originClient: RuntimeCoordinate.unknown,\n });\n\n return new ActionPayload_Request({ context }, validatedInput, {\n time: Date.now(),\n });\n }\n\n // async run(\n // input: TInferInputFromSchema<DOM[\"actions\"][ID]>[\"Input\"],\n // options?: IExecuteActionOptions<DOM, ID>,\n // ): Promise<RunningAction<DOM, ID>> {\n // return this.request(input).run(options);\n // }\n\n // async runToOutput(\n // input: TInferInputFromSchema<DOM[\"actions\"][ID]>[\"Input\"],\n // options?: IExecuteActionOptions<DOM, ID>,\n // ): Promise<TInferOutputFromSchema<DOM[\"actions\"][ID]>[\"Output\"]> {\n // return this.request(input).runToOutput(options);\n // }\n\n // async runToOutputSafe(\n // input: TInferInputFromSchema<DOM[\"actions\"][ID]>[\"Input\"],\n // options?: IExecuteActionOptions<DOM, ID>,\n // ): Promise<\n // TActionResult<\n // TInferOutputFromSchema<DOM[\"actions\"][ID]>[\"Output\"],\n // TInferActionError<DOM[\"actions\"][ID]>\n // >\n // > {\n // return this.request(input).runToOutputSafe(options);\n // }\n\n deserializeInput(\n serialized: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.deserializeInput(serialized);\n }\n\n serializeInput(\n raw: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"] {\n return this.schema.serializeInput(raw);\n }\n\n validateInput(input: unknown): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.validateInput(input, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n\n validateOutput(output: unknown): TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"] {\n return this.schema.validateOutput(output, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n}\n","import { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { IActionContext_JsonObject } from \"../ActionDefinition/Action/Context/ActionContext.types\";\nimport { isAction_Base_JsonObject } from \"./isAction_Base_JsonObject\";\n\nexport const isAction_Context_JsonObject = (obj: unknown): obj is IActionContext_JsonObject => {\n return isAction_Base_JsonObject(obj) && obj.form === EActionForm.context;\n};\n","import { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { IActionCore_JsonObject } from \"../ActionDefinition/Action/Core/ActionCore.types\";\nimport { isAction_Base_JsonObject } from \"./isAction_Base_JsonObject\";\n\nexport const isAction_Core_JsonObject = (obj: unknown): obj is IActionCore_JsonObject => {\n return isAction_Base_JsonObject(obj) && obj.form === EActionForm.core;\n};\n","import type { TAction_Any_JsonObject } from \"../ActionDefinition/Action/Action.combined.types\";\nimport { isAction_Context_JsonObject } from \"./isAction_Context_JsonObject\";\nimport { isAction_Core_JsonObject } from \"./isAction_Core_JsonObject\";\nimport { isActionPayload_Any_JsonObject } from \"./isActionPayload_Any_JsonObject\";\n\nexport function isAction_Any_JsonObject(obj: unknown): obj is TAction_Any_JsonObject {\n return (\n isActionPayload_Any_JsonObject(obj) ||\n isAction_Context_JsonObject(obj) ||\n isAction_Core_JsonObject(obj)\n );\n}\n","import type { IActionBase_JsonObject } from \"../ActionDefinition/Action/ActionBase.types\";\nimport { EErrId_NiceAction, err_nice_action } from \"../errors/err_nice_action\";\nimport { isAction_Any_JsonObject } from \"./isAction_Any_JsonObject\";\n\nexport function assertIsActionJson(obj: unknown): asserts obj is IActionBase_JsonObject {\n if (!isAction_Any_JsonObject(obj)) {\n throw err_nice_action.fromId(EErrId_NiceAction.wire_not_action_data);\n }\n}\n","import type { TAction_Any_Instance } from \"../ActionDefinition/Action/Action.combined.types\";\nimport type { IActionBase } from \"../ActionDefinition/Action/ActionBase.types\";\nimport { ActionContext } from \"../ActionDefinition/Action/Context/ActionContext\";\nimport { ActionCore } from \"../ActionDefinition/Action/Core/ActionCore\";\nimport { ActionPayload } from \"../ActionDefinition/Action/Payload/ActionPayload\";\n\nexport function isAction_Any_Instance<ACT extends IActionBase<any, any>>(\n value: unknown | ACT,\n): value is TAction_Any_Instance<any, any> {\n return (\n value instanceof ActionCore || value instanceof ActionPayload || value instanceof ActionContext\n );\n}\n","import type {\n TDistributeRunningActionUpdateListener,\n TRunningActionUpdateListener,\n} from \"../Action/RunningAction.types\";\nimport type { IActionDomain } from \"./ActionDomain.types\";\n\nexport abstract class ActionDomainBase<ACT_DOM extends IActionDomain = IActionDomain>\n implements IActionDomain<ACT_DOM[\"allDomains\"], ACT_DOM[\"actionSchema\"]>\n{\n readonly domain: ACT_DOM[\"domain\"];\n readonly allDomains: ACT_DOM[\"allDomains\"];\n readonly actionSchema: ACT_DOM[\"actionSchema\"];\n\n protected _listeners: TRunningActionUpdateListener<any, any>[] = [];\n\n constructor(definition: ACT_DOM) {\n this.domain = definition.domain;\n this.allDomains = definition.allDomains;\n this.actionSchema = definition.actionSchema;\n }\n\n /**\n * Add an observer that is called after every action dispatched through this domain.\n * Returns an unsubscribe function — call it to remove the listener.\n */\n addActionListener(\n listener: TDistributeRunningActionUpdateListener<\n ACT_DOM,\n keyof ACT_DOM[\"actionSchema\"] & string\n >,\n ): () => void {\n this._listeners.push(listener as TRunningActionUpdateListener<any, any>);\n return () => {\n this._listeners = this._listeners.filter((l) => l !== listener);\n };\n }\n\n /**\n * @internal\n * Observers registered directly on this domain via {@link addActionListener}.\n * Used to wire observers (e.g. devtools) onto RunningActions that aren't created\n * through the local-dispatch path — notably inbound actions pushed from a backend\n * or another client over a bidirectional transport.\n */\n _getActionObservers(): TRunningActionUpdateListener<any, any>[] {\n return this._listeners;\n }\n}\n","import type { TActionPayload_Any_Instance } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { EErrId_NiceAction, err_nice_action } from \"../errors/err_nice_action\";\nimport type { ActionRuntime } from \"./ActionRuntime\";\nimport type { IActionHandlerAndRuntime, IActionRuntimeManagerContext } from \"./ActionRuntime.types\";\nimport type { IHandleActionOptions } from \"./Handler/ActionHandler.types\";\nimport {\n type IRuntimeCoordinate,\n RuntimeCoordinate,\n type TRuntimeCoordinateStringId,\n} from \"./RuntimeCoordinate\";\nimport { runtimeCoordinateToStringIds } from \"./utils/runtimeCoordinateToStringIds\";\n\nexport class ActionRuntimeManager {\n private _runtimes: Map<TRuntimeCoordinateStringId, ActionRuntime> = new Map();\n private _preferredRuntimeClientId: TRuntimeCoordinateStringId | null = null;\n private _context: IActionRuntimeManagerContext;\n\n constructor(context?: IActionRuntimeManagerContext) {\n this._context = context ?? {};\n }\n\n registerRuntime(runtime: ActionRuntime): void {\n const runtimeId = runtime.coordinate.stringId;\n if (this._runtimes.has(runtimeId)) {\n throw err_nice_action.fromId(EErrId_NiceAction.client_runtime_already_registered, {\n context: this._context,\n client: runtime.coordinate,\n });\n }\n\n for (const id of runtime.coordinate.toStringIds()) {\n if (this._runtimes.has(id)) {\n continue;\n }\n\n this._runtimes.set(id, runtime);\n }\n }\n\n getRuntimeAndHandlerForAction(\n action: TActionPayload_Any_Instance<any, any>,\n options?: IHandleActionOptions,\n throwOnIssue?: boolean,\n ): IActionHandlerAndRuntime | undefined {\n const localRuntime = options?.targetLocalRuntime;\n\n if (localRuntime != null) {\n const runtime = throwOnIssue\n ? this.getBestRuntimeOrThrow(options?.targetLocalRuntime?.coordinate)\n : this.getBestRuntime(options?.targetLocalRuntime?.coordinate);\n\n if (runtime == null) {\n return;\n }\n\n const handler = runtime._getHandlerForAction(action, options);\n\n if (handler != null) {\n return { handler, runtime };\n }\n\n if (throwOnIssue) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\n domain: action.domain,\n actionId: action.id,\n specifiedClient: localRuntime.coordinate,\n });\n }\n }\n\n // If no client specified, try to find a runtime that can handle the action\n for (const runtime of this._runtimes.values()) {\n const handler = runtime._getHandlerForAction(action);\n if (handler) {\n return { handler, runtime };\n }\n }\n\n if (throwOnIssue) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\n domain: action.domain,\n actionId: action.id,\n specifiedClient: options?.targetLocalRuntime?.coordinate,\n });\n }\n }\n\n getRuntimeAndHandlerForActionOrThrow(\n action: TActionPayload_Any_Instance<any, any>,\n options?: IHandleActionOptions,\n ): IActionHandlerAndRuntime {\n return this.getRuntimeAndHandlerForAction(action, options, true)!;\n }\n\n setPreferredRuntime(runtime: ActionRuntime): void {\n const runtimeId = runtime.coordinate.stringId;\n this._preferredRuntimeClientId = runtimeId;\n }\n\n getPreferredRuntime(): ActionRuntime | undefined {\n if (this._preferredRuntimeClientId) {\n const runtime = this._runtimes.get(this._preferredRuntimeClientId);\n if (runtime) {\n return runtime;\n }\n }\n return this._runtimes.values().next().value;\n }\n\n getBestRuntimeForSpecifier(clientSpecifier: IRuntimeCoordinate): ActionRuntime | undefined {\n const actionClient = new RuntimeCoordinate(clientSpecifier);\n const ids = actionClient.toStringIds();\n\n for (const id of ids) {\n const runtime = this._runtimes.get(id);\n if (runtime) {\n return runtime;\n }\n }\n }\n\n getBestRuntime(clientSpecifier?: IRuntimeCoordinate): ActionRuntime | undefined {\n return clientSpecifier != null\n ? this.getBestRuntimeForSpecifier(clientSpecifier)\n : this.getPreferredRuntime();\n }\n\n hasRuntime(runtime: ActionRuntime): boolean {\n return this._runtimes.has(runtime.coordinate.stringId);\n }\n\n getBestRuntimeOrThrow(specifier?: IRuntimeCoordinate): ActionRuntime {\n const runtime = this.getBestRuntime(specifier);\n\n if (!runtime) {\n if (specifier == null) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_client_runtimes_registered, {\n context: this._context,\n });\n }\n\n throw err_nice_action.fromId(EErrId_NiceAction.client_runtime_not_registered, {\n context: this._context,\n clientStringId: runtimeCoordinateToStringIds(specifier)[0],\n });\n }\n\n return runtime;\n }\n}\n","import type { ActionRuntime } from \"../../ActionRuntime/ActionRuntime\";\nimport type { IActionHandlerAndRuntime } from \"../../ActionRuntime/ActionRuntime.types\";\nimport { ActionRuntimeManager } from \"../../ActionRuntime/ActionRuntimeManager\";\nimport type { IExecuteActionOptions } from \"../../ActionRuntime/Handler/ActionHandler.types\";\nimport type { IRuntimeCoordinate } from \"../../ActionRuntime/RuntimeCoordinate\";\nimport { EErrId_NiceAction, err_nice_action } from \"../../errors/err_nice_action\";\nimport type { ActionPayload_Request } from \"../Action/Payload/ActionPayload_Request\";\nimport { RunningAction } from \"../Action/RunningAction\";\nimport { ActionDomain } from \"./ActionDomain\";\nimport type {\n IActionDomain,\n IActionDomainChildOptions,\n IActionRootDomain,\n TActionDomainChildDef,\n} from \"./ActionDomain.types\";\nimport { ActionDomainBase } from \"./ActionDomainBase\";\n\nexport class ActionRootDomain<\n ROOT_DOM extends IActionRootDomain = IActionRootDomain,\n> extends ActionDomainBase<ROOT_DOM> {\n private _actionRuntimeManager: ActionRuntimeManager;\n\n constructor(\n readonly domainDefinition: {\n domain: ROOT_DOM[\"domain\"];\n },\n ) {\n const domainId = domainDefinition.domain;\n\n super({\n domain: domainId,\n allDomains: [domainId],\n actionSchema: {},\n } as ROOT_DOM);\n\n this._actionRuntimeManager = new ActionRuntimeManager({ domain: domainId });\n }\n\n createChildDomain<SUB_DOM extends IActionDomainChildOptions>(\n subDomainDef: SUB_DOM & {\n [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never;\n },\n ): ActionDomain<TActionDomainChildDef<ROOT_DOM, SUB_DOM>> {\n if (this.allDomains.includes(subDomainDef.domain)) {\n throw err_nice_action.fromId(EErrId_NiceAction.domain_already_exists_in_hierarchy, {\n domain: subDomainDef.domain,\n allParentDomains: this.allDomains,\n parentDomain: this.domain,\n });\n }\n\n return new ActionDomain<TActionDomainChildDef<ROOT_DOM, SUB_DOM>>(\n {\n allDomains: [...this.allDomains, subDomainDef.domain],\n domain: subDomainDef.domain,\n actionSchema: subDomainDef.actions,\n },\n { rootDomain: this },\n );\n }\n\n _registerRuntime(runtime: ActionRuntime): void {\n this._actionRuntimeManager.registerRuntime(runtime);\n }\n\n _hasRuntime(runtime: ActionRuntime): boolean {\n return this._actionRuntimeManager.hasRuntime(runtime);\n }\n\n getRuntime(clientSpecifier: IRuntimeCoordinate): ActionRuntime | undefined {\n return this._actionRuntimeManager.getBestRuntimeForSpecifier(clientSpecifier);\n }\n\n async _runAction<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n ACT extends ActionPayload_Request<DOM, ID> = ActionPayload_Request<DOM, ID>,\n >(actionPayload: ACT, options?: IExecuteActionOptions<DOM, ID>): Promise<RunningAction<DOM, ID>> {\n const allListeners = [...this._listeners, ...(options?.listeners ?? [])];\n\n let handlerAndRuntime: IActionHandlerAndRuntime;\n try {\n handlerAndRuntime = this._actionRuntimeManager.getRuntimeAndHandlerForActionOrThrow(\n actionPayload,\n options,\n );\n } catch (err) {\n const runningAction = new RunningAction<DOM, ID>({\n context: actionPayload.context,\n request: actionPayload,\n callSite: actionPayload._callSite,\n });\n runningAction.addUpdateListeners(allListeners);\n runningAction._failWithError(err);\n throw err;\n }\n\n const { handler, runtime } = handlerAndRuntime;\n\n actionPayload.context._setOriginClient(runtime.coordinate);\n\n const runningAction = await handler.handleActionRequest(actionPayload, {\n targetLocalRuntime: runtime,\n });\n\n runningAction.addUpdateListeners(allListeners);\n\n return runningAction;\n }\n}\n","import type { ActionRuntime } from \"../../ActionRuntime/ActionRuntime\";\nimport type { IExecuteActionOptions } from \"../../ActionRuntime/Handler/ActionHandler.types\";\nimport { ActionLocalHandler } from \"../../ActionRuntime/Handler/Local/ActionLocalHandler\";\nimport { RuntimeCoordinate } from \"../../ActionRuntime/RuntimeCoordinate\";\nimport { EErrId_NiceAction, err_nice_action } from \"../../errors/err_nice_action\";\nimport { assertIsActionJson } from \"../../utils/assertIsActionJson\";\nimport { isAction_Any_Instance } from \"../../utils/isAction_Any_Instance\";\nimport type {\n TAction_Any_JsonObject,\n TDistributeActionPayload_Request,\n TDistributeActionPayload_Result,\n TDistributedDomainActions,\n TNarrowActionJsonTypeToActionInstanceType,\n} from \"../Action/Action.combined.types\";\nimport { EActionForm, type IActionBase } from \"../Action/ActionBase.types\";\nimport { ActionContext } from \"../Action/Context/ActionContext\";\nimport type { IActionContext_Data_JsonObject } from \"../Action/Context/ActionContext.types\";\nimport { ActionCore } from \"../Action/Core/ActionCore\";\nimport {\n EActionPayloadType,\n type IActionPayload_Request_JsonObject,\n type IActionPayload_Result_JsonObject,\n} from \"../Action/Payload/ActionPayload.types\";\nimport { ActionPayload_Request } from \"../Action/Payload/ActionPayload_Request\";\nimport { ActionPayload_Result } from \"../Action/Payload/ActionPayload_Result\";\nimport type { RunningAction } from \"../Action/RunningAction\";\nimport type { TRunningActionUpdateListener } from \"../Action/RunningAction.types\";\nimport type {\n IActionDomain,\n IActionDomainChildOptions,\n TActionDomainChildDef,\n TWrappableDomainActionHandler,\n} from \"./ActionDomain.types\";\nimport { ActionDomainBase } from \"./ActionDomainBase\";\nimport { type ActionRootDomain } from \"./ActionRootDomain\";\n\ntype TActionMap<ACT_DOM extends IActionDomain> = {\n [K in keyof ACT_DOM[\"actionSchema\"] & string]: ActionCore<ACT_DOM, K>;\n};\n\nexport class ActionDomain<\n ACT_DOM extends IActionDomain = IActionDomain,\n> extends ActionDomainBase<ACT_DOM> {\n private _rootDomain: ActionRootDomain<any>;\n private readonly _actionMap: TActionMap<ACT_DOM>;\n\n constructor(\n definition: ACT_DOM,\n {\n rootDomain,\n }: {\n rootDomain: ActionRootDomain<any>;\n },\n ) {\n super(definition);\n this._rootDomain = rootDomain;\n this._actionMap = this.createActionMap();\n }\n\n get rootDomain() {\n return this._rootDomain;\n }\n\n /**\n * @internal\n * All action observers that should see actions on this domain: the root domain's\n * observers plus this subdomain's own. Mirrors the listener set the local-dispatch\n * path assembles in `runAction`/`_runAction`, so inbound actions (pushed from a\n * backend or another client) can be wired up identically and surface in devtools.\n */\n _collectActionObservers(): TRunningActionUpdateListener<any, any>[] {\n return [...this._rootDomain._getActionObservers(), ...this._getActionObservers()];\n }\n\n _registerRuntime(runtime: ActionRuntime): void {\n this._rootDomain._registerRuntime(runtime);\n }\n\n createChildDomain<SUB_DOM extends IActionDomainChildOptions>(\n subDomainDef: SUB_DOM & {\n [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never;\n },\n ): ActionDomain<TActionDomainChildDef<ACT_DOM, SUB_DOM>> {\n if (this.allDomains.includes(subDomainDef.domain)) {\n throw err_nice_action.fromId(EErrId_NiceAction.domain_already_exists_in_hierarchy, {\n domain: subDomainDef.domain,\n allParentDomains: this.allDomains,\n parentDomain: this.domain,\n });\n }\n\n return new ActionDomain<TActionDomainChildDef<ACT_DOM, SUB_DOM>>(\n {\n allDomains: [...this.allDomains, subDomainDef.domain],\n domain: subDomainDef.domain,\n actionSchema: subDomainDef.actions,\n },\n { rootDomain: this._rootDomain },\n );\n }\n\n get action(): TActionMap<ACT_DOM> {\n return this._actionMap;\n }\n\n actionsMap(): TActionMap<ACT_DOM> {\n return this._actionMap;\n }\n\n actionForId<ID extends keyof ACT_DOM[\"actionSchema\"] & string>(id: ID): ActionCore<ACT_DOM, ID> {\n const actionSchema = this.actionSchema[id];\n if (!actionSchema) {\n throw err_nice_action.fromId(EErrId_NiceAction.action_id_not_in_domain, {\n domain: this.domain,\n actionId: id as string,\n });\n }\n\n return new ActionCore<ACT_DOM, ID>(this, id);\n }\n\n wrapAsPartialLocalHandler(\n wrappedActionExecutor: Partial<TWrappableDomainActionHandler<ACT_DOM>>,\n ): ActionLocalHandler {\n const _handler = new ActionLocalHandler();\n const executor = wrappedActionExecutor as unknown as Record<string, (input: any) => any>;\n\n for (const actionKey in wrappedActionExecutor) {\n if (!this.actionSchema[actionKey]) {\n continue;\n }\n\n _handler.forAction(this.actionForId(actionKey), (request) =>\n executor[request.id](request.input),\n );\n }\n\n return _handler;\n }\n\n wrapAsLocalHandler(\n wrappedActionExecutor: TWrappableDomainActionHandler<ACT_DOM>,\n ): ActionLocalHandler {\n const _handler = new ActionLocalHandler();\n const executor = wrappedActionExecutor as unknown as Record<string, (input: any) => any>;\n return _handler.forDomain(this, (request) => executor[request.id](request.input));\n }\n\n hydrateContext<ID extends keyof ACT_DOM[\"actionSchema\"] & string>(\n id: ID,\n contextData: IActionContext_Data_JsonObject,\n ): ActionContext<ACT_DOM, ID> {\n return new ActionContext(this, id, {\n timeCreated: contextData.timeCreated,\n cuid: contextData.cuid,\n routing: contextData.routing.map((item) => {\n return {\n runtime: new RuntimeCoordinate(item.runtime),\n handler: item.handler,\n time: item.time,\n };\n }),\n originClient: contextData.originClient\n ? new RuntimeCoordinate(contextData.originClient)\n : RuntimeCoordinate.unknown,\n });\n }\n\n isDomainAction<ACT extends IActionBase<any, ACT_DOM, any>>(\n action: ACT | unknown | null | undefined,\n ): action is TDistributedDomainActions<ACT_DOM, ACT> {\n return isAction_Any_Instance(action) && action.domain === this.domain;\n }\n\n hydrateRequestPayload<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n P extends IActionPayload_Request_JsonObject<ACT_DOM, ID>,\n >(serialized: P): TDistributeActionPayload_Request<ACT_DOM, ID> {\n if (serialized.type !== EActionPayloadType.request) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_state_mismatch, {\n expected: EActionPayloadType.request,\n received: serialized.type,\n });\n }\n\n if (serialized.domain !== this.domain) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_domain_mismatch, {\n expected: this.domain,\n received: serialized.domain,\n });\n }\n\n const id = serialized.id;\n if (!this.actionSchema[id]) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_id_not_found, {\n domain: this.domain,\n actionId: serialized.id,\n });\n }\n\n const contextAction = this.hydrateContext(id, serialized.context);\n\n return new ActionPayload_Request(\n { context: contextAction },\n contextAction.deserializeInput(serialized.input),\n {\n time: serialized.time,\n },\n ) as TDistributeActionPayload_Request<ACT_DOM, ID>;\n }\n\n hydrateResultPayload<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n R extends IActionPayload_Result_JsonObject<ACT_DOM, ID>,\n >(serialized: R): TDistributeActionPayload_Result<ACT_DOM, ID> {\n if (serialized.type !== EActionPayloadType.result) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_state_mismatch, {\n expected: EActionPayloadType.result,\n received: serialized.type,\n });\n }\n\n if (serialized.domain !== this.domain) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_domain_mismatch, {\n expected: this.domain,\n received: serialized.domain,\n });\n }\n\n const id = serialized.id;\n\n if (!this.actionSchema[id]) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_id_not_found, {\n domain: this.domain,\n actionId: serialized.id,\n });\n }\n\n const contextAction = this.hydrateContext(id, serialized.context);\n\n const result = serialized.result.ok\n ? {\n ok: true as const,\n output: contextAction.schema.deserializeOutput(serialized.result.output),\n }\n : serialized.result;\n\n return new ActionPayload_Result({ context: contextAction }, result, {\n time: serialized.time,\n }) as TDistributeActionPayload_Result<ACT_DOM, ID>;\n }\n\n hydrateAnyAction<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n AJ extends TAction_Any_JsonObject<ACT_DOM, ID>,\n >(actionJson: AJ): TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID> {\n assertIsActionJson(actionJson);\n\n if (actionJson.form === EActionForm.data) {\n if (actionJson.type === EActionPayloadType.request) {\n return this.hydrateRequestPayload(\n actionJson,\n ) as unknown as TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID>;\n }\n\n if (actionJson.type === EActionPayloadType.result) {\n return this.hydrateResultPayload(\n actionJson,\n ) as unknown as TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID>;\n }\n }\n\n return this.actionForId(actionJson.id) as TNarrowActionJsonTypeToActionInstanceType<\n ACT_DOM,\n AJ,\n ID\n >;\n }\n\n async runAction<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n ACT extends ActionPayload_Request<ACT_DOM, ID>,\n >(\n request: ACT,\n options?: IExecuteActionOptions<ACT_DOM, ID>,\n ): Promise<RunningAction<ACT_DOM, ID>> {\n const allListeners: TRunningActionUpdateListener<any, any>[] = [\n ...(options?.listeners ?? []),\n ...this._listeners,\n ];\n\n return this._rootDomain._runAction(request, {\n ...options,\n listeners: allListeners,\n });\n }\n\n private createActionMap(): {\n [K in keyof ACT_DOM[\"actionSchema\"] & string]: ActionCore<ACT_DOM, K>;\n } {\n const map = {} as {\n [K in keyof ACT_DOM[\"actionSchema\"] & string]: ActionCore<ACT_DOM, K>;\n };\n\n for (const id in this.actionSchema) {\n map[id] = new ActionCore(this, id);\n }\n\n return map;\n }\n}\n","import type { IActionRootDomain } from \"../ActionDomain.types\";\nimport { ActionRootDomain } from \"../ActionRootDomain\";\n\nexport const createActionRootDomain = <ID extends string>(definition: {\n domain: ID;\n}): ActionRootDomain<IActionRootDomain<ID>> => {\n return new ActionRootDomain<IActionRootDomain<ID>>(definition);\n};\n","import type { IDuplexCarrier } from \"../../Carrier.types\";\r\n\r\ntype TFrame = string | ArrayBuffer | Uint8Array;\r\n\r\n/** The peer (server) end of an in-memory pair — what you feed into an `AcceptorHandler`. */\r\nexport interface IInMemoryServerEndpoint {\r\n /** Write a frame back to the client end. */\r\n send(frame: TFrame): void;\r\n /** Register the inbound handler (frames the client sent). */\r\n onMessage(handler: (frame: TFrame) => void): void;\r\n /** Close the pair from this end. */\r\n close(): void;\r\n /** Notified when the pair closes (from either end). */\r\n onClose(handler: () => void): void;\r\n}\r\n\r\nexport interface IInMemoryChannelPair {\r\n /** The client end — pass as a {@link IDuplexCarrier} to a `LinkTransport`. */\r\n clientChannel: IDuplexCarrier;\r\n /** The server end — wire into an `AcceptorHandler` (`send` + `receive`). */\r\n serverEndpoint: IInMemoryServerEndpoint;\r\n}\r\n\r\n/**\r\n * Two cross-wired in-process byte channels — a loopback carrier with no socket. The client end is a\r\n * {@link IDuplexCarrier} you hand to a {@link LinkTransport}; the server end plugs into an\r\n * `AcceptorHandler` (`send: (_, f) => serverEndpoint.send(f)`, and `serverEndpoint.onMessage(f =>\r\n * handler.receive(conn, f))`). Frames are delivered on a microtask, so each side observes the other\r\n * asynchronously — exactly like a real transport — which makes this ideal for tests and for running\r\n * two runtimes in one process (or proving a non-WS carrier end to end).\r\n */\r\nexport function createInMemoryChannelPair(): IInMemoryChannelPair {\r\n let clientMessage: ((frame: TFrame) => void) | undefined;\r\n let clientClose: (() => void) | undefined;\r\n let serverMessage: ((frame: TFrame) => void) | undefined;\r\n let serverClose: (() => void) | undefined;\r\n let open = true;\r\n\r\n const closeBoth = (): void => {\r\n if (!open) return;\r\n open = false;\r\n queueMicrotask(() => {\r\n clientClose?.();\r\n serverClose?.();\r\n });\r\n };\r\n\r\n const clientChannel: IDuplexCarrier = {\r\n ready: Promise.resolve(),\r\n isOpen: () => open,\r\n send: (frame) => {\r\n if (!open) return;\r\n queueMicrotask(() => serverMessage?.(frame));\r\n },\r\n attach: ({ onMessage, onClose }) => {\r\n clientMessage = onMessage;\r\n clientClose = onClose;\r\n },\r\n close: closeBoth,\r\n label: \"in-memory\",\r\n };\r\n\r\n const serverEndpoint: IInMemoryServerEndpoint = {\r\n send: (frame) => {\r\n if (!open) return;\r\n queueMicrotask(() => clientMessage?.(frame));\r\n },\r\n onMessage: (handler) => {\r\n serverMessage = handler;\r\n },\r\n onClose: (handler) => {\r\n serverClose = handler;\r\n },\r\n close: closeBoth,\r\n };\r\n\r\n return { clientChannel, serverEndpoint };\r\n}\r\n","import {\r\n createInMemoryChannelPair,\r\n type IInMemoryServerEndpoint,\r\n} from \"./createInMemoryChannel\";\r\nimport type { IDuplexCarrierSource } from \"../../Carrier.types\";\r\n\r\nexport interface IInMemoryCarrier {\r\n /** The connector end — pass as the `carrier` to one of `connectChannel`'s transports. */\r\n carrier: IDuplexCarrierSource;\r\n /** The acceptor end — wire into an `AcceptorHandler` (`send` + `receive`). */\r\n serverEndpoint: IInMemoryServerEndpoint;\r\n}\r\n\r\n/**\r\n * A loopback duplex carrier with no socket — two cross-wired in-process ends. The connector end is an\r\n * {@link IDuplexCarrierSource} for `connectChannel`; the acceptor end plugs into an `AcceptorHandler`.\r\n * Ideal for tests and for running two runtimes in one process, or proving a non-WS carrier end to end.\r\n */\r\nexport function inMemoryCarrier(): IInMemoryCarrier {\r\n const { clientChannel, serverEndpoint } = createInMemoryChannelPair();\r\n return {\r\n carrier: {\r\n carrierLabel: \"memory\",\r\n open: () => clientChannel,\r\n getCacheKey: () => [\"memory\"],\r\n },\r\n serverEndpoint,\r\n };\r\n}\r\n","import type { IDuplexCarrier } from \"../../Carrier.types\";\r\n\r\n/**\r\n * The slice of the `RTCDataChannel` surface this adapter uses — declared structurally so it accepts the\r\n * browser `RTCDataChannel`, React-Native (`react-native-webrtc`), or any node-webrtc shim without a hard\r\n * dependency on a specific DOM/RN type. A real `RTCDataChannel` satisfies it as-is.\r\n */\r\nexport interface IRtcDataChannelLike {\r\n readyState: string; // \"connecting\" | \"open\" | \"closing\" | \"closed\"\r\n binaryType: string;\r\n label?: string;\r\n send(data: string | ArrayBuffer | ArrayBufferView): void;\r\n close(): void;\r\n addEventListener(type: string, listener: (event: any) => void, options?: unknown): void;\r\n}\r\n\r\n/**\r\n * Adapt a WebRTC `RTCDataChannel` to the carrier-agnostic {@link IDuplexCarrier}, so two browsers\r\n * (or two mobile apps) linked peer-to-peer — no server in the middle — run the *same* secure session as\r\n * a WebSocket. Hand it to `createSecureLinkTransport({ openChannel: () => rtcDataChannelByteChannel(dc) })`.\r\n *\r\n * The data channel must already be created (its negotiation/signaling is the app's concern); this only\r\n * drives bytes over it. Binary frames are requested as `ArrayBuffer` so the binary session codec unpacks\r\n * them synchronously; a `Blob` (if the channel hands one back) is normalized to a buffer.\r\n */\r\nexport function rtcDataChannelByteChannel(dc: IRtcDataChannelLike): IDuplexCarrier {\r\n dc.binaryType = \"arraybuffer\";\r\n\r\n let intentional = false;\r\n let onMessage: ((frame: string | ArrayBuffer | Uint8Array) => void) | undefined;\r\n let onClose: (() => void) | undefined;\r\n const preAttach: (string | ArrayBuffer | Uint8Array)[] = [];\r\n\r\n const deliver = (frame: string | ArrayBuffer | Uint8Array): void => {\r\n if (onMessage != null) onMessage(frame);\r\n else preAttach.push(frame);\r\n };\r\n\r\n dc.addEventListener(\"message\", async (event: { data: unknown }) => {\r\n const frame = await normalizeFrame(event.data);\r\n if (frame !== undefined) deliver(frame);\r\n });\r\n dc.addEventListener(\"close\", () => {\r\n if (!intentional) console.error(\"RTCDataChannel closed\");\r\n onClose?.();\r\n });\r\n dc.addEventListener(\"error\", (event: unknown) => {\r\n console.error(\"RTCDataChannel error:\", event);\r\n onClose?.();\r\n });\r\n\r\n const ready = new Promise<void>((resolve, reject) => {\r\n if (dc.readyState === \"open\") {\r\n resolve();\r\n return;\r\n }\r\n dc.addEventListener(\"open\", () => resolve(), { once: true });\r\n dc.addEventListener(\"error\", (event: unknown) => reject(event), { once: true });\r\n dc.addEventListener(\"close\", () => reject(new Error(\"RTCDataChannel closed before open\")), {\r\n once: true,\r\n });\r\n });\r\n\r\n return {\r\n ready,\r\n isOpen: () => dc.readyState === \"open\",\r\n send: (frame) => {\r\n // A pooled `Uint8Array` may be backed by a `SharedArrayBuffer` that `send` rejects — copy into a\r\n // fresh view (mirrors the WebSocket `sendFrame` guard).\r\n if (typeof frame === \"string\" || frame instanceof ArrayBuffer) dc.send(frame);\r\n else dc.send(new Uint8Array(frame));\r\n },\r\n attach: (handlers) => {\r\n onMessage = handlers.onMessage;\r\n onClose = handlers.onClose;\r\n for (const frame of preAttach) handlers.onMessage(frame);\r\n preAttach.length = 0;\r\n },\r\n close: () => {\r\n intentional = true;\r\n try {\r\n dc.close();\r\n } catch {\r\n // already closing/closed — the close listener still runs cleanup\r\n }\r\n },\r\n get label() {\r\n return dc.label != null && dc.label !== \"\" ? dc.label : undefined;\r\n },\r\n };\r\n}\r\n\r\nasync function normalizeFrame(\r\n data: unknown,\r\n): Promise<string | ArrayBuffer | Uint8Array | undefined> {\r\n if (typeof data === \"string\" || data instanceof ArrayBuffer || data instanceof Uint8Array) {\r\n return data;\r\n }\r\n if (typeof Blob !== \"undefined\" && data instanceof Blob) {\r\n return await data.arrayBuffer();\r\n }\r\n return undefined;\r\n}\r\n","import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../../../Transport.types\";\r\nimport {\r\n type IRtcDataChannelLike,\r\n rtcDataChannelByteChannel,\r\n} from \"./rtcDataChannelByteChannel\";\r\nimport type { IDuplexCarrierSource } from \"../../Carrier.types\";\r\n\r\nexport interface IRtcCarrierOptions {\r\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n}\r\n\r\n/**\r\n * A WebRTC {@link IDuplexCarrierSource} over an already-negotiated `RTCDataChannel` (signaling is the\r\n * app's concern). Pass it as a `carrier` to `connectChannel` so two browsers/apps linked peer-to-peer run\r\n * the identical secure session as a WebSocket.\r\n */\r\nexport function rtcCarrier(\r\n dataChannel: IRtcDataChannelLike,\r\n options: IRtcCarrierOptions = {},\r\n): IDuplexCarrierSource {\r\n return {\r\n carrierLabel: \"webrtc\",\r\n open: () => rtcDataChannelByteChannel(dataChannel),\r\n getCacheKey: options.getTransportCacheKey ?? (() => [\"webrtc\"]),\r\n getRouteInfo: options.getRouteInfo,\r\n };\r\n}\r\n","import { err } from \"@nice-code/error\";\r\nimport { err_nice_transport } from \"../../../err_nice_transport\";\r\n\r\nexport enum EErrId_NiceTransport_WebSocket {\r\n ws_disconnected = \"ws_disconnected\",\r\n ws_create_failed = \"ws_create_failed\",\r\n ws_error = \"ws_error\",\r\n}\r\n\r\nexport const err_nice_transport_ws = err_nice_transport.createChildDomain({\r\n domain: \"ws_transport\",\r\n schema: {\r\n [EErrId_NiceTransport_WebSocket.ws_disconnected]: err<Record<string, never>>({\r\n message: () => `WebSocket transport disconnected.`,\r\n }),\r\n [EErrId_NiceTransport_WebSocket.ws_create_failed]: err<{\r\n originalError?: Error;\r\n }>({\r\n message: ({ originalError }) =>\r\n `Failed to create WebSocket transport.${originalError ? ` Original error: ${originalError.message}` : \"\"}`,\r\n }),\r\n [EErrId_NiceTransport_WebSocket.ws_error]: err<{\r\n originalError?: Error;\r\n }>({\r\n message: ({ originalError }) =>\r\n `WebSocket transport error.${originalError ? ` Original error: ${originalError.message}` : \"\"}`,\r\n }),\r\n },\r\n});\r\n","/**\r\n * Send a text or binary frame over a socket. A binary formatter may hand back a `Uint8Array` whose\r\n * backing buffer is typed as `ArrayBufferLike` (msgpackr pools buffers / may be `SharedArrayBuffer`),\r\n * which `WebSocket.send`'s `BufferSource` parameter rejects — copy it into a fresh `ArrayBuffer`-backed\r\n * view so the type (and the bytes) are safe to send.\r\n */\r\nexport function sendFrame(ws: WebSocket, data: string | Uint8Array | ArrayBuffer): void {\r\n if (typeof data === \"string\" || data instanceof ArrayBuffer) {\r\n ws.send(data);\r\n return;\r\n }\r\n ws.send(new Uint8Array(data));\r\n}\r\n\r\n/** Compact a WebSocket URL to `host/pathname` for devtools display, falling back to the raw url. */\r\nexport function shortWs(url: string): string {\r\n try {\r\n const u = new URL(url);\r\n return `${u.host}${u.pathname}`;\r\n } catch {\r\n return url;\r\n }\r\n}\r\n","import type { IDuplexCarrier } from \"../../Carrier.types\";\r\nimport { sendFrame } from \"./ws_util\";\r\n\r\n/**\r\n * Adapt a `WebSocket` to the carrier-agnostic {@link IDuplexCarrier}, so the WebSocket becomes\r\n * \"just another carrier\" under the shared secure session. It owns every WebSocket-specific concern —\r\n * awaiting `open`, normalizing `Blob` frames to bytes, suppressing the log on a deliberate `close`, and\r\n * buffering any frame that arrives before the session attaches its handler — leaving the session itself\r\n * carrier-neutral.\r\n */\r\nexport function webSocketByteChannel(ws: WebSocket): IDuplexCarrier {\r\n let intentional = false;\r\n let onMessage: ((frame: string | ArrayBuffer | Uint8Array) => void) | undefined;\r\n let onClose: (() => void) | undefined;\r\n // Frames that land before the session calls `attach` (none expected in practice, but never lose one).\r\n const preAttach: (string | ArrayBuffer | Uint8Array)[] = [];\r\n\r\n const deliver = (frame: string | ArrayBuffer | Uint8Array): void => {\r\n if (onMessage != null) onMessage(frame);\r\n else preAttach.push(frame);\r\n };\r\n\r\n ws.addEventListener(\"message\", async (event) => {\r\n const frame = await normalizeFrame((event as MessageEvent).data);\r\n if (frame !== undefined) deliver(frame);\r\n });\r\n ws.addEventListener(\"close\", (event) => {\r\n if (!intentional) console.error(\"WebSocket closed:\", event);\r\n onClose?.();\r\n });\r\n ws.addEventListener(\"error\", (event) => {\r\n console.error(\"WebSocket error:\", event);\r\n onClose?.();\r\n });\r\n\r\n const ready = new Promise<void>((resolve, reject) => {\r\n if (ws.readyState === WebSocket.OPEN) {\r\n resolve();\r\n return;\r\n }\r\n ws.addEventListener(\"open\", () => resolve(), { once: true });\r\n ws.addEventListener(\"error\", (event) => reject(event), { once: true });\r\n ws.addEventListener(\r\n \"close\",\r\n (event) =>\r\n reject(new Error(`WebSocket closed before open: code=${(event as CloseEvent).code}`)),\r\n { once: true },\r\n );\r\n });\r\n\r\n return {\r\n ready,\r\n isOpen: () => ws.readyState === WebSocket.OPEN,\r\n send: (frame) => sendFrame(ws, frame),\r\n attach: (handlers) => {\r\n onMessage = handlers.onMessage;\r\n onClose = handlers.onClose;\r\n for (const frame of preAttach) handlers.onMessage(frame);\r\n preAttach.length = 0;\r\n },\r\n close: () => {\r\n intentional = true;\r\n try {\r\n ws.close();\r\n } catch {\r\n // already closing/closed — the close listener still runs cleanup\r\n }\r\n },\r\n get label() {\r\n return ws.url != null && ws.url !== \"\" ? ws.url : undefined;\r\n },\r\n };\r\n}\r\n\r\n/** Accept text + binary frames (ArrayBuffer / Uint8Array / Blob); Blobs are converted to a buffer. */\r\nasync function normalizeFrame(\r\n data: unknown,\r\n): Promise<string | ArrayBuffer | Uint8Array | undefined> {\r\n if (typeof data === \"string\" || data instanceof ArrayBuffer || data instanceof Uint8Array) {\r\n return data;\r\n }\r\n if (typeof Blob !== \"undefined\" && data instanceof Blob) {\r\n return await data.arrayBuffer();\r\n }\r\n return undefined;\r\n}\r\n","import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../../../Transport.types\";\nimport { webSocketByteChannel } from \"./webSocketByteChannel\";\nimport { shortWs } from \"./ws_util\";\nimport type { IDuplexCarrierSource } from \"../../Carrier.types\";\n\n/** The WebSocket an action's socket is opened against (derived per action from the route params). */\nexport interface IWsCarrierRequest {\n url: string;\n}\n\nexport interface IWsCarrierOptions {\n /** Override the reuse key (defaults to `[url]`, so one socket is shared per endpoint). */\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\n /** Override the devtools route info for a specific action. */\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\n}\n\n/**\n * A WebSocket {@link IDuplexCarrierSource}: opens an `arraybuffer` socket (cached per endpoint) and adapts\n * it to a carrier. Pass it as a `carrier` to `connectChannel` — the WebSocket is now \"just another\n * carrier\" under the shared secure session, with no WS-specific transport class.\n *\n * `createRequest` derives the socket URL per action (keep it simple with `() => ({ url })`), so dynamic\n * endpoints (e.g. a per-run query param) need no `createWebSocket` escape hatch.\n */\nexport function wsCarrier(\n createRequest: (input: ITransportRouteActionParams) => IWsCarrierRequest,\n options: IWsCarrierOptions = {},\n): IDuplexCarrierSource {\n return {\n carrierLabel: \"ws\",\n open: (input) => webSocketByteChannel(defaultWebSocket(createRequest(input).url)),\n getCacheKey: options.getTransportCacheKey ?? ((input) => [createRequest(input).url]),\n getRouteInfo:\n options.getRouteInfo ??\n ((input) => {\n const { url } = createRequest(input);\n return { carrierLabel: \"ws\", url, summary: `ws ${shortWs(url)}` };\n }),\n };\n}\n\nfunction defaultWebSocket(url: string): WebSocket {\n const ws = new WebSocket(url);\n // Binary responses as ArrayBuffer (not Blob) so the session unpacks them synchronously.\n ws.binaryType = \"arraybuffer\";\n return ws;\n}\n","import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../../../Transport.types\";\r\nimport type { IExchangeCarrier, IExchangeCarrierSource, TFrame } from \"../../Carrier.types\";\r\n\r\n/** The HTTP request an action is sent over (the body is supplied by the secure exchange session). */\r\nexport interface IHttpCarrierRequest {\r\n url: string;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** The slice of `fetch` the carrier uses — a structural type so callers (and tests) needn't match the\r\n * full platform `fetch` (Bun's adds `preconnect`, etc.). The global `fetch` satisfies it. */\r\nexport type TCarrierFetch = (input: string, init?: RequestInit) => Promise<Response>;\r\n\r\nexport interface IHttpCarrierOptions {\r\n /** Override the reuse key (defaults to `[url]`, so one session is shared per endpoint). */\r\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Override the devtools route info for a specific action. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Override `fetch` (e.g. to route to an in-memory handler in tests). Defaults to global `fetch`. */\r\n fetch?: TCarrierFetch;\r\n}\r\n\r\nfunction shortPath(url: string): string {\r\n try {\r\n return new URL(url).pathname || url;\r\n } catch {\r\n return url;\r\n }\r\n}\r\n\r\n/**\r\n * An HTTP {@link IExchangeCarrierSource}: each `exchange` POSTs one frame body to the action endpoint and\r\n * resolves with the response body as the single correlated reply. Pass it as a `carrier` to\r\n * `connectChannel` — a secure HTTP transport then runs the *same* secure session as a duplex carrier\r\n * (handshake → token → encrypted frames), the request/reply correlation provided for free by the HTTP\r\n * transaction.\r\n *\r\n * `createRequest` derives the URL/headers per action (keep it simple with `() => ({ url })`). The body is\r\n * the session's responsibility, so it is never built here.\r\n */\r\nexport function httpCarrier(\r\n createRequest: (input: ITransportRouteActionParams) => IHttpCarrierRequest,\r\n options: IHttpCarrierOptions = {},\r\n): IExchangeCarrierSource {\r\n const doFetch = options.fetch ?? fetch;\r\n\r\n return {\r\n shape: \"exchange\",\r\n carrierLabel: \"http\",\r\n open: (input): IExchangeCarrier => {\r\n const request = createRequest(input);\r\n return {\r\n label: request.url,\r\n exchange: async (frame: TFrame, opts): Promise<TFrame> => {\r\n const res = await doFetch(request.url, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\", ...request.headers },\r\n body: typeof frame === \"string\" ? frame : new Uint8Array(frame),\r\n signal: opts?.signal,\r\n });\r\n return await res.text();\r\n },\r\n };\r\n },\r\n getCacheKey: options.getTransportCacheKey ?? ((input) => [createRequest(input).url]),\r\n getRouteInfo:\r\n options.getRouteInfo ??\r\n ((input) => {\r\n const { url } = createRequest(input);\r\n return { carrierLabel: \"http\", method: \"POST\", url, summary: `POST ${shortPath(url)}` };\r\n }),\r\n };\r\n}\r\n"],"mappings":";;;;;;AAiBA,IAAa,gBAAb,cAIU,WAEV;CAQa;CAPX,OAAS;CACT;CACA;CACA;CACA;CAEA,YACE,SACA,IACA,eACA;EACA,MAAA,WAA2B,SAAS,EAAE;EAJ7B,KAAA,UAAA;EAKT,KAAK,cAAc,cAAc;EACjC,KAAK,OAAO,cAAc;EAC1B,KAAK,WAAW,cAAc;EAC9B,KAAK,eAAe,cAAc;CACpC;CAEA,iBAAiB,QAAiC;EAChD,KAAK,eAAe;CACtB;CAEA,eAAuB;EACrB,OAAO,KAAK,UAAU,KAAK,aAAa,CAAC;CAC3C;CAEA,0BAA0D;EACxD,OAAO;GACL,aAAa,KAAK;GAClB,MAAM,KAAK;GACX,SAAS,KAAK,QAAQ,KAAK,UAAU;IACnC,SAAS,KAAK,QAAQ,aAAa;IACnC,SAAS,KAAK;IACd,MAAM,KAAK;GACb,EAAE;GACF,cAAc,KAAK,aAAa,aAAa;EAC/C;CACF;CAEA,eAAmD;EACjD,OAAO;GACL,GAAG,MAAM,aAAa;GACtB,GAAG,KAAK,wBAAwB;EAClC;CACF;CAEA,IAAI,UAA8B;EAChC,OAAO,KAAK;CACd;CAEA,aAAa,MAA8B;EACzC,KAAK,SAAS,KAAK,IAAI;CACzB;CAEA,iBACE,YACyD;EACzD,OAAO,KAAK,OAAO,iBAAiB,UAAU;CAChD;CAEA,eACE,KAC8D;EAC9D,OAAO,KAAK,OAAO,eAAe,GAAG;CACvC;CAEA,cAAc,OAAyE;EACrF,OAAO,KAAK,OAAO,cAAc,OAAO;GACtC,QAAQ,KAAK;GACb,UAAU,KAAK;EACjB,CAAC;CACH;CAEA,eAAe,QAA4E;EACzF,OAAO,KAAK,OAAO,eAAe,QAAQ;GACxC,QAAQ,KAAK;GACb,UAAU,KAAK;EACjB,CAAC;CACH;AACF;;;ACvFA,IAAa,aAAb,cAIU,WAEV;CAIa;CAHX,OAAS;CAET,YACE,SACA,IACA;EACA,MAAA,QAAwB,SAAS,EAAE;EAH1B,KAAA,UAAA;CAIX;CAEA,GACE,QAC2C;EAC3C,OACE,kBAAkB,iBAAiB,OAAO,WAAW,KAAK,UAAU,OAAO,OAAO,KAAK;CAE3F;CAEA,eAAkE;EAChE,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,YAAY,KAAK;EACnB;CACF;CAEA,QACE,GAAG,MAG6B;EAChC,MAAM,QAAiB,KAAK;EAC5B,MAAM,iBAAiB,KAAK,OAAO,cAAc,OAAO;GACtD,UAAU,KAAK;GACf,QAAQ,KAAK;EACf,CAAC;EASD,OAAO,IAAI,sBAAsB,EAAE,SAAA,IAPf,cAAc,KAAK,SAAS,KAAK,IAAI;GACvD,MAAM,OAAO;GACb,aAAa,KAAK,IAAI;GACtB,SAAS,CAAC;GACV,cAAc,kBAAkB;EAClC,CAEyC,EAAE,GAAG,gBAAgB,EAC5D,MAAM,KAAK,IAAI,EACjB,CAAC;CACH;CA4BA,iBACE,YACyD;EACzD,OAAO,KAAK,OAAO,iBAAiB,UAAU;CAChD;CAEA,eACE,KAC8D;EAC9D,OAAO,KAAK,OAAO,eAAe,GAAG;CACvC;CAEA,cAAc,OAAyE;EACrF,OAAO,KAAK,OAAO,cAAc,OAAO;GACtC,QAAQ,KAAK;GACb,UAAU,KAAK;EACjB,CAAC;CACH;CAEA,eAAe,QAA4E;EACzF,OAAO,KAAK,OAAO,eAAe,QAAQ;GACxC,QAAQ,KAAK;GACb,UAAU,KAAK;EACjB,CAAC;CACH;AACF;;;ACvHA,MAAa,+BAA+B,QAAmD;CAC7F,OAAO,yBAAyB,GAAG,KAAK,IAAI,SAAA;AAC9C;;;ACFA,MAAa,4BAA4B,QAAgD;CACvF,OAAO,yBAAyB,GAAG,KAAK,IAAI,SAAA;AAC9C;;;ACDA,SAAgB,wBAAwB,KAA6C;CACnF,OACE,+BAA+B,GAAG,KAClC,4BAA4B,GAAG,KAC/B,yBAAyB,GAAG;AAEhC;;;ACPA,SAAgB,mBAAmB,KAAqD;CACtF,IAAI,CAAC,wBAAwB,GAAG,GAC9B,MAAM,gBAAgB,OAAA,sBAA6C;AAEvE;;;ACFA,SAAgB,sBACd,OACyC;CACzC,OACE,iBAAiB,cAAc,iBAAiB,iBAAiB,iBAAiB;AAEtF;;;ACNA,IAAsB,mBAAtB,MAEA;CACE;CACA;CACA;CAEA,aAAiE,CAAC;CAElE,YAAY,YAAqB;EAC/B,KAAK,SAAS,WAAW;EACzB,KAAK,aAAa,WAAW;EAC7B,KAAK,eAAe,WAAW;CACjC;;;;;CAMA,kBACE,UAIY;EACZ,KAAK,WAAW,KAAK,QAAkD;EACvE,aAAa;GACX,KAAK,aAAa,KAAK,WAAW,QAAQ,MAAM,MAAM,QAAQ;EAChE;CACF;;;;;;;;CASA,sBAAgE;EAC9D,OAAO,KAAK;CACd;AACF;;;ACnCA,IAAa,uBAAb,MAAkC;CAChC,4BAAoE,IAAI,IAAI;CAC5E,4BAAuE;CACvE;CAEA,YAAY,SAAwC;EAClD,KAAK,WAAW,WAAW,CAAC;CAC9B;CAEA,gBAAgB,SAA8B;EAC5C,MAAM,YAAY,QAAQ,WAAW;EACrC,IAAI,KAAK,UAAU,IAAI,SAAS,GAC9B,MAAM,gBAAgB,OAAA,qCAA4D;GAChF,SAAS,KAAK;GACd,QAAQ,QAAQ;EAClB,CAAC;EAGH,KAAK,MAAM,MAAM,QAAQ,WAAW,YAAY,GAAG;GACjD,IAAI,KAAK,UAAU,IAAI,EAAE,GACvB;GAGF,KAAK,UAAU,IAAI,IAAI,OAAO;EAChC;CACF;CAEA,8BACE,QACA,SACA,cACsC;EACtC,MAAM,eAAe,SAAS;EAE9B,IAAI,gBAAgB,MAAM;GACxB,MAAM,UAAU,eACZ,KAAK,sBAAsB,SAAS,oBAAoB,UAAU,IAClE,KAAK,eAAe,SAAS,oBAAoB,UAAU;GAE/D,IAAI,WAAW,MACb;GAGF,MAAM,UAAU,QAAQ,qBAAqB,QAAQ,OAAO;GAE5D,IAAI,WAAW,MACb,OAAO;IAAE;IAAS;GAAQ;GAG5B,IAAI,cACF,MAAM,gBAAgB,OAAA,+BAAsD;IAC1E,QAAQ,OAAO;IACf,UAAU,OAAO;IACjB,iBAAiB,aAAa;GAChC,CAAC;EAEL;EAGA,KAAK,MAAM,WAAW,KAAK,UAAU,OAAO,GAAG;GAC7C,MAAM,UAAU,QAAQ,qBAAqB,MAAM;GACnD,IAAI,SACF,OAAO;IAAE;IAAS;GAAQ;EAE9B;EAEA,IAAI,cACF,MAAM,gBAAgB,OAAA,+BAAsD;GAC1E,QAAQ,OAAO;GACf,UAAU,OAAO;GACjB,iBAAiB,SAAS,oBAAoB;EAChD,CAAC;CAEL;CAEA,qCACE,QACA,SAC0B;EAC1B,OAAO,KAAK,8BAA8B,QAAQ,SAAS,IAAI;CACjE;CAEA,oBAAoB,SAA8B;EAChD,MAAM,YAAY,QAAQ,WAAW;EACrC,KAAK,4BAA4B;CACnC;CAEA,sBAAiD;EAC/C,IAAI,KAAK,2BAA2B;GAClC,MAAM,UAAU,KAAK,UAAU,IAAI,KAAK,yBAAyB;GACjE,IAAI,SACF,OAAO;EAEX;EACA,OAAO,KAAK,UAAU,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;CACxC;CAEA,2BAA2B,iBAAgE;EAEzF,MAAM,MAAM,IADa,kBAAkB,eACpB,CAAC,CAAC,YAAY;EAErC,KAAK,MAAM,MAAM,KAAK;GACpB,MAAM,UAAU,KAAK,UAAU,IAAI,EAAE;GACrC,IAAI,SACF,OAAO;EAEX;CACF;CAEA,eAAe,iBAAiE;EAC9E,OAAO,mBAAmB,OACtB,KAAK,2BAA2B,eAAe,IAC/C,KAAK,oBAAoB;CAC/B;CAEA,WAAW,SAAiC;EAC1C,OAAO,KAAK,UAAU,IAAI,QAAQ,WAAW,QAAQ;CACvD;CAEA,sBAAsB,WAA+C;EACnE,MAAM,UAAU,KAAK,eAAe,SAAS;EAE7C,IAAI,CAAC,SAAS;GACZ,IAAI,aAAa,MACf,MAAM,gBAAgB,OAAA,iCAAwD,EAC5E,SAAS,KAAK,SAChB,CAAC;GAGH,MAAM,gBAAgB,OAAA,iCAAwD;IAC5E,SAAS,KAAK;IACd,gBAAgB,6BAA6B,SAAS,CAAC,CAAC;GAC1D,CAAC;EACH;EAEA,OAAO;CACT;AACF;;;ACpIA,IAAa,mBAAb,cAEU,iBAA2B;CAIxB;CAHX;CAEA,YACE,kBAGA;EACA,MAAM,WAAW,iBAAiB;EAElC,MAAM;GACJ,QAAQ;GACR,YAAY,CAAC,QAAQ;GACrB,cAAc,CAAC;EACjB,CAAa;EAVJ,KAAA,mBAAA;EAYT,KAAK,wBAAwB,IAAI,qBAAqB,EAAE,QAAQ,SAAS,CAAC;CAC5E;CAEA,kBACE,cAGwD;EACxD,IAAI,KAAK,WAAW,SAAS,aAAa,MAAM,GAC9C,MAAM,gBAAgB,OAAA,sCAA6D;GACjF,QAAQ,aAAa;GACrB,kBAAkB,KAAK;GACvB,cAAc,KAAK;EACrB,CAAC;EAGH,OAAO,IAAI,aACT;GACE,YAAY,CAAC,GAAG,KAAK,YAAY,aAAa,MAAM;GACpD,QAAQ,aAAa;GACrB,cAAc,aAAa;EAC7B,GACA,EAAE,YAAY,KAAK,CACrB;CACF;CAEA,iBAAiB,SAA8B;EAC7C,KAAK,sBAAsB,gBAAgB,OAAO;CACpD;CAEA,YAAY,SAAiC;EAC3C,OAAO,KAAK,sBAAsB,WAAW,OAAO;CACtD;CAEA,WAAW,iBAAgE;EACzE,OAAO,KAAK,sBAAsB,2BAA2B,eAAe;CAC9E;CAEA,MAAM,WAIJ,eAAoB,SAA2E;EAC/F,MAAM,eAAe,CAAC,GAAG,KAAK,YAAY,GAAI,SAAS,aAAa,CAAC,CAAE;EAEvE,IAAI;EACJ,IAAI;GACF,oBAAoB,KAAK,sBAAsB,qCAC7C,eACA,OACF;EACF,SAAS,KAAK;GACZ,MAAM,gBAAgB,IAAI,cAAuB;IAC/C,SAAS,cAAc;IACvB,SAAS;IACT,UAAU,cAAc;GAC1B,CAAC;GACD,cAAc,mBAAmB,YAAY;GAC7C,cAAc,eAAe,GAAG;GAChC,MAAM;EACR;EAEA,MAAM,EAAE,SAAS,YAAY;EAE7B,cAAc,QAAQ,iBAAiB,QAAQ,UAAU;EAEzD,MAAM,gBAAgB,MAAM,QAAQ,oBAAoB,eAAe,EACrE,oBAAoB,QACtB,CAAC;EAED,cAAc,mBAAmB,YAAY;EAE7C,OAAO;CACT;AACF;;;ACrEA,IAAa,eAAb,MAAa,qBAEH,iBAA0B;CAClC;CACA;CAEA,YACE,YACA,EACE,cAIF;EACA,MAAM,UAAU;EAChB,KAAK,cAAc;EACnB,KAAK,aAAa,KAAK,gBAAgB;CACzC;CAEA,IAAI,aAAa;EACf,OAAO,KAAK;CACd;;;;;;;;CASA,0BAAoE;EAClE,OAAO,CAAC,GAAG,KAAK,YAAY,oBAAoB,GAAG,GAAG,KAAK,oBAAoB,CAAC;CAClF;CAEA,iBAAiB,SAA8B;EAC7C,KAAK,YAAY,iBAAiB,OAAO;CAC3C;CAEA,kBACE,cAGuD;EACvD,IAAI,KAAK,WAAW,SAAS,aAAa,MAAM,GAC9C,MAAM,gBAAgB,OAAA,sCAA6D;GACjF,QAAQ,aAAa;GACrB,kBAAkB,KAAK;GACvB,cAAc,KAAK;EACrB,CAAC;EAGH,OAAO,IAAI,aACT;GACE,YAAY,CAAC,GAAG,KAAK,YAAY,aAAa,MAAM;GACpD,QAAQ,aAAa;GACrB,cAAc,aAAa;EAC7B,GACA,EAAE,YAAY,KAAK,YAAY,CACjC;CACF;CAEA,IAAI,SAA8B;EAChC,OAAO,KAAK;CACd;CAEA,aAAkC;EAChC,OAAO,KAAK;CACd;CAEA,YAA+D,IAAiC;EAE9F,IAAI,CADiB,KAAK,aAAa,KAErC,MAAM,gBAAgB,OAAA,2BAAkD;GACtE,QAAQ,KAAK;GACb,UAAU;EACZ,CAAC;EAGH,OAAO,IAAI,WAAwB,MAAM,EAAE;CAC7C;CAEA,0BACE,uBACoB;EACpB,MAAM,WAAW,IAAI,mBAAmB;EACxC,MAAM,WAAW;EAEjB,KAAK,MAAM,aAAa,uBAAuB;GAC7C,IAAI,CAAC,KAAK,aAAa,YACrB;GAGF,SAAS,UAAU,KAAK,YAAY,SAAS,IAAI,YAC/C,SAAS,QAAQ,GAAG,CAAC,QAAQ,KAAK,CACpC;EACF;EAEA,OAAO;CACT;CAEA,mBACE,uBACoB;EACpB,MAAM,WAAW,IAAI,mBAAmB;EACxC,MAAM,WAAW;EACjB,OAAO,SAAS,UAAU,OAAO,YAAY,SAAS,QAAQ,GAAG,CAAC,QAAQ,KAAK,CAAC;CAClF;CAEA,eACE,IACA,aAC4B;EAC5B,OAAO,IAAI,cAAc,MAAM,IAAI;GACjC,aAAa,YAAY;GACzB,MAAM,YAAY;GAClB,SAAS,YAAY,QAAQ,KAAK,SAAS;IACzC,OAAO;KACL,SAAS,IAAI,kBAAkB,KAAK,OAAO;KAC3C,SAAS,KAAK;KACd,MAAM,KAAK;IACb;GACF,CAAC;GACD,cAAc,YAAY,eACtB,IAAI,kBAAkB,YAAY,YAAY,IAC9C,kBAAkB;EACxB,CAAC;CACH;CAEA,eACE,QACmD;EACnD,OAAO,sBAAsB,MAAM,KAAK,OAAO,WAAW,KAAK;CACjE;CAEA,sBAGE,YAA8D;EAC9D,IAAI,WAAW,SAAA,WACb,MAAM,gBAAgB,OAAA,mCAA0D;GAC9E,UAAA;GACA,UAAU,WAAW;EACvB,CAAC;EAGH,IAAI,WAAW,WAAW,KAAK,QAC7B,MAAM,gBAAgB,OAAA,6BAAoD;GACxE,UAAU,KAAK;GACf,UAAU,WAAW;EACvB,CAAC;EAGH,MAAM,KAAK,WAAW;EACtB,IAAI,CAAC,KAAK,aAAa,KACrB,MAAM,gBAAgB,OAAA,iCAAwD;GAC5E,QAAQ,KAAK;GACb,UAAU,WAAW;EACvB,CAAC;EAGH,MAAM,gBAAgB,KAAK,eAAe,IAAI,WAAW,OAAO;EAEhE,OAAO,IAAI,sBACT,EAAE,SAAS,cAAc,GACzB,cAAc,iBAAiB,WAAW,KAAK,GAC/C,EACE,MAAM,WAAW,KACnB,CACF;CACF;CAEA,qBAGE,YAA6D;EAC7D,IAAI,WAAW,SAAA,UACb,MAAM,gBAAgB,OAAA,mCAA0D;GAC9E,UAAA;GACA,UAAU,WAAW;EACvB,CAAC;EAGH,IAAI,WAAW,WAAW,KAAK,QAC7B,MAAM,gBAAgB,OAAA,6BAAoD;GACxE,UAAU,KAAK;GACf,UAAU,WAAW;EACvB,CAAC;EAGH,MAAM,KAAK,WAAW;EAEtB,IAAI,CAAC,KAAK,aAAa,KACrB,MAAM,gBAAgB,OAAA,iCAAwD;GAC5E,QAAQ,KAAK;GACb,UAAU,WAAW;EACvB,CAAC;EAGH,MAAM,gBAAgB,KAAK,eAAe,IAAI,WAAW,OAAO;EAEhE,MAAM,SAAS,WAAW,OAAO,KAC7B;GACE,IAAI;GACJ,QAAQ,cAAc,OAAO,kBAAkB,WAAW,OAAO,MAAM;EACzE,IACA,WAAW;EAEf,OAAO,IAAI,qBAAqB,EAAE,SAAS,cAAc,GAAG,QAAQ,EAClE,MAAM,WAAW,KACnB,CAAC;CACH;CAEA,iBAGE,YAA4E;EAC5E,mBAAmB,UAAU;EAE7B,IAAI,WAAW,SAAA,QAA2B;GACxC,IAAI,WAAW,SAAA,WACb,OAAO,KAAK,sBACV,UACF;GAGF,IAAI,WAAW,SAAA,UACb,OAAO,KAAK,qBACV,UACF;EAEJ;EAEA,OAAO,KAAK,YAAY,WAAW,EAAE;CAKvC;CAEA,MAAM,UAIJ,SACA,SACqC;EACrC,MAAM,eAAyD,CAC7D,GAAI,SAAS,aAAa,CAAC,GAC3B,GAAG,KAAK,UACV;EAEA,OAAO,KAAK,YAAY,WAAW,SAAS;GAC1C,GAAG;GACH,WAAW;EACb,CAAC;CACH;CAEA,kBAEE;EACA,MAAM,MAAM,CAAC;EAIb,KAAK,MAAM,MAAM,KAAK,cACpB,IAAI,MAAM,IAAI,WAAW,MAAM,EAAE;EAGnC,OAAO;CACT;AACF;;;ACnTA,MAAa,0BAA6C,eAEX;CAC7C,OAAO,IAAI,iBAAwC,UAAU;AAC/D;;;;;;;;;;;ACwBA,SAAgB,4BAAkD;CAChE,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO;CAEX,MAAM,kBAAwB;EAC5B,IAAI,CAAC,MAAM;EACX,OAAO;EACP,qBAAqB;GACnB,cAAc;GACd,cAAc;EAChB,CAAC;CACH;CA+BA,OAAO;EAAE,eAAA;GA5BP,OAAO,QAAQ,QAAQ;GACvB,cAAc;GACd,OAAO,UAAU;IACf,IAAI,CAAC,MAAM;IACX,qBAAqB,gBAAgB,KAAK,CAAC;GAC7C;GACA,SAAS,EAAE,WAAW,cAAc;IAClC,gBAAgB;IAChB,cAAc;GAChB;GACA,OAAO;GACP,OAAO;EAiBY;EAAG,gBAAA;GAbtB,OAAO,UAAU;IACf,IAAI,CAAC,MAAM;IACX,qBAAqB,gBAAgB,KAAK,CAAC;GAC7C;GACA,YAAY,YAAY;IACtB,gBAAgB;GAClB;GACA,UAAU,YAAY;IACpB,cAAc;GAChB;GACA,OAAO;EAG4B;CAAE;AACzC;;;;;;;;AC3DA,SAAgB,kBAAoC;CAClD,MAAM,EAAE,eAAe,mBAAmB,0BAA0B;CACpE,OAAO;EACL,SAAS;GACP,cAAc;GACd,YAAY;GACZ,mBAAmB,CAAC,QAAQ;EAC9B;EACA;CACF;AACF;;;;;;;;;;;;ACHA,SAAgB,0BAA0B,IAAyC;CACjF,GAAG,aAAa;CAEhB,IAAI,cAAc;CAClB,IAAI;CACJ,IAAI;CACJ,MAAM,YAAmD,CAAC;CAE1D,MAAM,WAAW,UAAmD;EAClE,IAAI,aAAa,MAAM,UAAU,KAAK;OACjC,UAAU,KAAK,KAAK;CAC3B;CAEA,GAAG,iBAAiB,WAAW,OAAO,UAA6B;EACjE,MAAM,QAAQ,MAAMA,iBAAe,MAAM,IAAI;EAC7C,IAAI,UAAU,KAAA,GAAW,QAAQ,KAAK;CACxC,CAAC;CACD,GAAG,iBAAiB,eAAe;EACjC,IAAI,CAAC,aAAa,QAAQ,MAAM,uBAAuB;EACvD,UAAU;CACZ,CAAC;CACD,GAAG,iBAAiB,UAAU,UAAmB;EAC/C,QAAQ,MAAM,yBAAyB,KAAK;EAC5C,UAAU;CACZ,CAAC;CAcD,OAAO;EACL,OAAA,IAbgB,SAAe,SAAS,WAAW;GACnD,IAAI,GAAG,eAAe,QAAQ;IAC5B,QAAQ;IACR;GACF;GACA,GAAG,iBAAiB,cAAc,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;GAC3D,GAAG,iBAAiB,UAAU,UAAmB,OAAO,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;GAC9E,GAAG,iBAAiB,eAAe,uBAAO,IAAI,MAAM,mCAAmC,CAAC,GAAG,EACzF,MAAM,KACR,CAAC;EACH,CAGM;EACJ,cAAc,GAAG,eAAe;EAChC,OAAO,UAAU;GAGf,IAAI,OAAO,UAAU,YAAY,iBAAiB,aAAa,GAAG,KAAK,KAAK;QACvE,GAAG,KAAK,IAAI,WAAW,KAAK,CAAC;EACpC;EACA,SAAS,aAAa;GACpB,YAAY,SAAS;GACrB,UAAU,SAAS;GACnB,KAAK,MAAM,SAAS,WAAW,SAAS,UAAU,KAAK;GACvD,UAAU,SAAS;EACrB;EACA,aAAa;GACX,cAAc;GACd,IAAI;IACF,GAAG,MAAM;GACX,QAAQ,CAER;EACF;EACA,IAAI,QAAQ;GACV,OAAO,GAAG,SAAS,QAAQ,GAAG,UAAU,KAAK,GAAG,QAAQ,KAAA;EAC1D;CACF;AACF;AAEA,eAAeA,iBACb,MACwD;CACxD,IAAI,OAAO,SAAS,YAAY,gBAAgB,eAAe,gBAAgB,YAC7E,OAAO;CAET,IAAI,OAAO,SAAS,eAAe,gBAAgB,MACjD,OAAO,MAAM,KAAK,YAAY;AAGlC;;;;;;;;ACrFA,SAAgB,WACd,aACA,UAA8B,CAAC,GACT;CACtB,OAAO;EACL,cAAc;EACd,YAAY,0BAA0B,WAAW;EACjD,aAAa,QAAQ,+BAA+B,CAAC,QAAQ;EAC7D,cAAc,QAAQ;CACxB;AACF;;;ACxBA,IAAY,iCAAL,yBAAA,gCAAA;CACL,+BAAA,qBAAA;CACA,+BAAA,sBAAA;CACA,+BAAA,cAAA;;AACF,EAAA,CAAA,CAAA;AAEA,MAAa,wBAAwB,mBAAmB,kBAAkB;CACxE,QAAQ;CACR,QAAQ;uBAC4C,IAA2B,EAC3E,eAAe,oCACjB,CAAC;wBACkD,IAEhD,EACD,UAAU,EAAE,oBACV,wCAAwC,gBAAgB,oBAAoB,cAAc,YAAY,KAC1G,CAAC;gBAC0C,IAExC,EACD,UAAU,EAAE,oBACV,6BAA6B,gBAAgB,oBAAoB,cAAc,YAAY,KAC/F,CAAC;CACH;AACF,CAAC;;;;;;;;;ACtBD,SAAgB,UAAU,IAAe,MAA+C;CACtF,IAAI,OAAO,SAAS,YAAY,gBAAgB,aAAa;EAC3D,GAAG,KAAK,IAAI;EACZ;CACF;CACA,GAAG,KAAK,IAAI,WAAW,IAAI,CAAC;AAC9B;;AAGA,SAAgB,QAAQ,KAAqB;CAC3C,IAAI;EACF,MAAM,IAAI,IAAI,IAAI,GAAG;EACrB,OAAO,GAAG,EAAE,OAAO,EAAE;CACvB,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;ACZA,SAAgB,qBAAqB,IAA+B;CAClE,IAAI,cAAc;CAClB,IAAI;CACJ,IAAI;CAEJ,MAAM,YAAmD,CAAC;CAE1D,MAAM,WAAW,UAAmD;EAClE,IAAI,aAAa,MAAM,UAAU,KAAK;OACjC,UAAU,KAAK,KAAK;CAC3B;CAEA,GAAG,iBAAiB,WAAW,OAAO,UAAU;EAC9C,MAAM,QAAQ,MAAM,eAAgB,MAAuB,IAAI;EAC/D,IAAI,UAAU,KAAA,GAAW,QAAQ,KAAK;CACxC,CAAC;CACD,GAAG,iBAAiB,UAAU,UAAU;EACtC,IAAI,CAAC,aAAa,QAAQ,MAAM,qBAAqB,KAAK;EAC1D,UAAU;CACZ,CAAC;CACD,GAAG,iBAAiB,UAAU,UAAU;EACtC,QAAQ,MAAM,oBAAoB,KAAK;EACvC,UAAU;CACZ,CAAC;CAiBD,OAAO;EACL,OAAA,IAhBgB,SAAe,SAAS,WAAW;GACnD,IAAI,GAAG,eAAe,UAAU,MAAM;IACpC,QAAQ;IACR;GACF;GACA,GAAG,iBAAiB,cAAc,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;GAC3D,GAAG,iBAAiB,UAAU,UAAU,OAAO,KAAK,GAAG,EAAE,MAAM,KAAK,CAAC;GACrE,GAAG,iBACD,UACC,UACC,uBAAO,IAAI,MAAM,sCAAuC,MAAqB,MAAM,CAAC,GACtF,EAAE,MAAM,KAAK,CACf;EACF,CAGM;EACJ,cAAc,GAAG,eAAe,UAAU;EAC1C,OAAO,UAAU,UAAU,IAAI,KAAK;EACpC,SAAS,aAAa;GACpB,YAAY,SAAS;GACrB,UAAU,SAAS;GACnB,KAAK,MAAM,SAAS,WAAW,SAAS,UAAU,KAAK;GACvD,UAAU,SAAS;EACrB;EACA,aAAa;GACX,cAAc;GACd,IAAI;IACF,GAAG,MAAM;GACX,QAAQ,CAER;EACF;EACA,IAAI,QAAQ;GACV,OAAO,GAAG,OAAO,QAAQ,GAAG,QAAQ,KAAK,GAAG,MAAM,KAAA;EACpD;CACF;AACF;;AAGA,eAAe,eACb,MACwD;CACxD,IAAI,OAAO,SAAS,YAAY,gBAAgB,eAAe,gBAAgB,YAC7E,OAAO;CAET,IAAI,OAAO,SAAS,eAAe,gBAAgB,MACjD,OAAO,MAAM,KAAK,YAAY;AAGlC;;;;;;;;;;;AC5DA,SAAgB,UACd,eACA,UAA6B,CAAC,GACR;CACtB,OAAO;EACL,cAAc;EACd,OAAO,UAAU,qBAAqB,iBAAiB,cAAc,KAAK,CAAC,CAAC,GAAG,CAAC;EAChF,aAAa,QAAQ,0BAA0B,UAAU,CAAC,cAAc,KAAK,CAAC,CAAC,GAAG;EAClF,cACE,QAAQ,kBACN,UAAU;GACV,MAAM,EAAE,QAAQ,cAAc,KAAK;GACnC,OAAO;IAAE,cAAc;IAAM;IAAK,SAAS,MAAM,QAAQ,GAAG;GAAI;EAClE;CACJ;AACF;AAEA,SAAS,iBAAiB,KAAwB;CAChD,MAAM,KAAK,IAAI,UAAU,GAAG;CAE5B,GAAG,aAAa;CAChB,OAAO;AACT;;;ACzBA,SAAS,UAAU,KAAqB;CACtC,IAAI;EACF,OAAO,IAAI,IAAI,GAAG,CAAC,CAAC,YAAY;CAClC,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;AAYA,SAAgB,YACd,eACA,UAA+B,CAAC,GACR;CACxB,MAAM,UAAU,QAAQ,SAAS;CAEjC,OAAO;EACL,OAAO;EACP,cAAc;EACd,OAAO,UAA4B;GACjC,MAAM,UAAU,cAAc,KAAK;GACnC,OAAO;IACL,OAAO,QAAQ;IACf,UAAU,OAAO,OAAe,SAA0B;KAOxD,OAAO,OAAM,MANK,QAAQ,QAAQ,KAAK;MACrC,QAAQ;MACR,SAAS;OAAE,gBAAgB;OAAoB,GAAG,QAAQ;MAAQ;MAClE,MAAM,OAAO,UAAU,WAAW,QAAQ,IAAI,WAAW,KAAK;MAC9D,QAAQ,MAAM;KAChB,CAAC,EAAA,CACgB,KAAK;IACxB;GACF;EACF;EACA,aAAa,QAAQ,0BAA0B,UAAU,CAAC,cAAc,KAAK,CAAC,CAAC,GAAG;EAClF,cACE,QAAQ,kBACN,UAAU;GACV,MAAM,EAAE,QAAQ,cAAc,KAAK;GACnC,OAAO;IAAE,cAAc;IAAQ,QAAQ;IAAQ;IAAK,SAAS,QAAQ,UAAU,GAAG;GAAI;EACxF;CACJ;AACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["normalizeFrame"],"sources":["../src/ActionDefinition/Action/Context/ActionContext.ts","../src/ActionDefinition/Action/Core/ActionCore.ts","../src/utils/isAction_Context_JsonObject.ts","../src/utils/isAction_Core_JsonObject.ts","../src/utils/isAction_Any_JsonObject.ts","../src/utils/assertIsActionJson.ts","../src/utils/isAction_Any_Instance.ts","../src/ActionDefinition/Domain/ActionDomainBase.ts","../src/ActionRuntime/ActionRuntimeManager.ts","../src/ActionDefinition/Domain/ActionRootDomain.ts","../src/ActionDefinition/Domain/ActionDomain.ts","../src/ActionDefinition/Domain/helpers/createRootActionDomain.ts","../src/ActionRuntime/Transport/Carrier/duplex/inMemory/createInMemoryChannel.ts","../src/ActionRuntime/Transport/Carrier/duplex/inMemory/inMemoryCarrier.ts","../src/ActionRuntime/Transport/Carrier/duplex/rtc/rtcDataChannelByteChannel.ts","../src/ActionRuntime/Transport/Carrier/duplex/rtc/rtcCarrier.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/err_nice_transport_ws.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/ws_util.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/webSocketByteChannel.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsCarrier.ts","../src/ActionRuntime/Transport/Carrier/exchange/http/httpCarrier.ts"],"sourcesContent":["import { RuntimeCoordinate } from \"../../../ActionRuntime/RuntimeCoordinate\";\nimport type { ActionDomain } from \"../../Domain/ActionDomain\";\nimport type {\n IActionDomain,\n TInferInputFromSchema,\n TInferOutputFromSchema,\n} from \"../../Domain/ActionDomain.types\";\nimport { ActionBase } from \"../ActionBase\";\nimport { EActionForm } from \"../ActionBase.types\";\nimport type {\n IActionContext,\n IActionContext_Data,\n IActionContext_Data_JsonObject,\n IActionContext_JsonObject,\n IActionRouteItem,\n} from \"./ActionContext.types\";\n\nexport class ActionContext<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n >\n extends ActionBase<EActionForm.context, DOM, ID>\n implements IActionContext<DOM, ID>\n{\n readonly form = EActionForm.context;\n readonly _routing: IActionRouteItem[];\n readonly timeCreated: number;\n readonly cuid: string;\n originClient: RuntimeCoordinate;\n\n constructor(\n readonly _domain: ActionDomain<DOM>,\n id: ID,\n hydrationData: IActionContext_Data,\n ) {\n super(EActionForm.context, _domain, id);\n this.timeCreated = hydrationData.timeCreated;\n this.cuid = hydrationData.cuid;\n this._routing = hydrationData.routing;\n this.originClient = hydrationData.originClient;\n }\n\n _setOriginClient(client: RuntimeCoordinate): void {\n this.originClient = client;\n }\n\n toJsonString(): string {\n return JSON.stringify(this.toJsonObject());\n }\n\n toContextDataJsonObject(): IActionContext_Data_JsonObject {\n return {\n timeCreated: this.timeCreated,\n cuid: this.cuid,\n routing: this.routing.map((item) => ({\n runtime: item.runtime.toJsonObject(),\n handler: item.handler,\n time: item.time,\n })),\n originClient: this.originClient.toJsonObject(),\n };\n }\n\n toJsonObject(): IActionContext_JsonObject<DOM, ID> {\n return {\n ...super.toJsonObject(),\n ...this.toContextDataJsonObject(),\n };\n }\n\n get routing(): IActionRouteItem[] {\n return this._routing;\n }\n\n addRouteItem(item: IActionRouteItem): void {\n this._routing.push(item);\n }\n\n deserializeInput(\n serialized: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.deserializeInput(serialized);\n }\n\n serializeInput(\n raw: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"] {\n return this.schema.serializeInput(raw);\n }\n\n validateInput(input: unknown): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.validateInput(input, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n\n validateOutput(output: unknown): TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"] {\n return this.schema.validateOutput(output, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n}\n","import { nanoid } from \"nanoid\";\nimport { RuntimeCoordinate } from \"../../../ActionRuntime/RuntimeCoordinate\";\nimport type { ActionDomain } from \"../../Domain/ActionDomain\";\nimport type {\n IActionDomain,\n TInferInputFromSchema,\n TInferOutputFromSchema,\n} from \"../../Domain/ActionDomain.types\";\nimport type { TNarrowActionType } from \"../Action.combined.types\";\nimport { ActionBase } from \"../ActionBase\";\nimport { EActionForm, type IActionBase, type IActionBase_JsonObject } from \"../ActionBase.types\";\nimport { ActionContext } from \"../Context/ActionContext\";\nimport { ActionPayload } from \"../Payload/ActionPayload\";\nimport { ActionPayload_Request } from \"../Payload/ActionPayload_Request\";\nimport type { IActionCore } from \"./ActionCore.types\";\n\nexport class ActionCore<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n >\n extends ActionBase<EActionForm.core, DOM, ID>\n implements IActionCore<DOM, ID>\n{\n readonly form = EActionForm.core;\n\n constructor(\n readonly _domain: ActionDomain<DOM>,\n id: ID,\n ) {\n super(EActionForm.core, _domain, id);\n }\n\n is<ACT extends IActionBase<any, any, any>>(\n action: ACT | unknown | null | undefined,\n ): action is TNarrowActionType<DOM, ACT, ID> {\n return (\n action instanceof ActionPayload && action.domain === this.domain && action.id === this.id\n );\n }\n\n toJsonObject(): IActionBase_JsonObject<EActionForm.core, DOM, ID> {\n return {\n id: this.id,\n form: this.form,\n domain: this.domain,\n allDomains: this.allDomains,\n };\n }\n\n request(\n ...args: [TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"]] extends [never]\n ? [input?: never]\n : [input: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"]]\n ): ActionPayload_Request<DOM, ID> {\n const input: unknown = args[0];\n const validatedInput = this.schema.validateInput(input, {\n actionId: this.id,\n domain: this.domain,\n });\n\n const context = new ActionContext(this._domain, this.id, {\n cuid: nanoid(),\n timeCreated: Date.now(),\n routing: [],\n originClient: RuntimeCoordinate.unknown,\n });\n\n return new ActionPayload_Request({ context }, validatedInput, {\n time: Date.now(),\n });\n }\n\n // async run(\n // input: TInferInputFromSchema<DOM[\"actions\"][ID]>[\"Input\"],\n // options?: IExecuteActionOptions<DOM, ID>,\n // ): Promise<RunningAction<DOM, ID>> {\n // return this.request(input).run(options);\n // }\n\n // async runToOutput(\n // input: TInferInputFromSchema<DOM[\"actions\"][ID]>[\"Input\"],\n // options?: IExecuteActionOptions<DOM, ID>,\n // ): Promise<TInferOutputFromSchema<DOM[\"actions\"][ID]>[\"Output\"]> {\n // return this.request(input).runToOutput(options);\n // }\n\n // async runToOutputSafe(\n // input: TInferInputFromSchema<DOM[\"actions\"][ID]>[\"Input\"],\n // options?: IExecuteActionOptions<DOM, ID>,\n // ): Promise<\n // TActionResult<\n // TInferOutputFromSchema<DOM[\"actions\"][ID]>[\"Output\"],\n // TInferActionError<DOM[\"actions\"][ID]>\n // >\n // > {\n // return this.request(input).runToOutputSafe(options);\n // }\n\n deserializeInput(\n serialized: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.deserializeInput(serialized);\n }\n\n serializeInput(\n raw: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"],\n ): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"] {\n return this.schema.serializeInput(raw);\n }\n\n validateInput(input: unknown): TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"] {\n return this.schema.validateInput(input, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n\n validateOutput(output: unknown): TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"] {\n return this.schema.validateOutput(output, {\n domain: this.domain,\n actionId: this.id,\n });\n }\n}\n","import { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { IActionContext_JsonObject } from \"../ActionDefinition/Action/Context/ActionContext.types\";\nimport { isAction_Base_JsonObject } from \"./isAction_Base_JsonObject\";\n\nexport const isAction_Context_JsonObject = (obj: unknown): obj is IActionContext_JsonObject => {\n return isAction_Base_JsonObject(obj) && obj.form === EActionForm.context;\n};\n","import { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { IActionCore_JsonObject } from \"../ActionDefinition/Action/Core/ActionCore.types\";\nimport { isAction_Base_JsonObject } from \"./isAction_Base_JsonObject\";\n\nexport const isAction_Core_JsonObject = (obj: unknown): obj is IActionCore_JsonObject => {\n return isAction_Base_JsonObject(obj) && obj.form === EActionForm.core;\n};\n","import type { TAction_Any_JsonObject } from \"../ActionDefinition/Action/Action.combined.types\";\nimport { isAction_Context_JsonObject } from \"./isAction_Context_JsonObject\";\nimport { isAction_Core_JsonObject } from \"./isAction_Core_JsonObject\";\nimport { isActionPayload_Any_JsonObject } from \"./isActionPayload_Any_JsonObject\";\n\nexport function isAction_Any_JsonObject(obj: unknown): obj is TAction_Any_JsonObject {\n return (\n isActionPayload_Any_JsonObject(obj) ||\n isAction_Context_JsonObject(obj) ||\n isAction_Core_JsonObject(obj)\n );\n}\n","import type { IActionBase_JsonObject } from \"../ActionDefinition/Action/ActionBase.types\";\nimport { EErrId_NiceAction, err_nice_action } from \"../errors/err_nice_action\";\nimport { isAction_Any_JsonObject } from \"./isAction_Any_JsonObject\";\n\nexport function assertIsActionJson(obj: unknown): asserts obj is IActionBase_JsonObject {\n if (!isAction_Any_JsonObject(obj)) {\n throw err_nice_action.fromId(EErrId_NiceAction.wire_not_action_data);\n }\n}\n","import type { TAction_Any_Instance } from \"../ActionDefinition/Action/Action.combined.types\";\nimport type { IActionBase } from \"../ActionDefinition/Action/ActionBase.types\";\nimport { ActionContext } from \"../ActionDefinition/Action/Context/ActionContext\";\nimport { ActionCore } from \"../ActionDefinition/Action/Core/ActionCore\";\nimport { ActionPayload } from \"../ActionDefinition/Action/Payload/ActionPayload\";\n\nexport function isAction_Any_Instance<ACT extends IActionBase<any, any>>(\n value: unknown | ACT,\n): value is TAction_Any_Instance<any, any> {\n return (\n value instanceof ActionCore || value instanceof ActionPayload || value instanceof ActionContext\n );\n}\n","import type {\n TDistributeRunningActionUpdateListener,\n TRunningActionUpdateListener,\n} from \"../Action/RunningAction.types\";\nimport type { IActionDomain } from \"./ActionDomain.types\";\n\nexport abstract class ActionDomainBase<ACT_DOM extends IActionDomain = IActionDomain>\n implements IActionDomain<ACT_DOM[\"allDomains\"], ACT_DOM[\"actionSchema\"]>\n{\n readonly domain: ACT_DOM[\"domain\"];\n readonly allDomains: ACT_DOM[\"allDomains\"];\n readonly actionSchema: ACT_DOM[\"actionSchema\"];\n\n protected _listeners: TRunningActionUpdateListener<any, any>[] = [];\n\n constructor(definition: ACT_DOM) {\n this.domain = definition.domain;\n this.allDomains = definition.allDomains;\n this.actionSchema = definition.actionSchema;\n }\n\n /**\n * Add an observer that is called after every action dispatched through this domain.\n * Returns an unsubscribe function — call it to remove the listener.\n */\n addActionListener(\n listener: TDistributeRunningActionUpdateListener<\n ACT_DOM,\n keyof ACT_DOM[\"actionSchema\"] & string\n >,\n ): () => void {\n this._listeners.push(listener as TRunningActionUpdateListener<any, any>);\n return () => {\n this._listeners = this._listeners.filter((l) => l !== listener);\n };\n }\n\n /**\n * @internal\n * Observers registered directly on this domain via {@link addActionListener}.\n * Used to wire observers (e.g. devtools) onto RunningActions that aren't created\n * through the local-dispatch path — notably inbound actions pushed from a backend\n * or another client over a bidirectional transport.\n */\n _getActionObservers(): TRunningActionUpdateListener<any, any>[] {\n return this._listeners;\n }\n}\n","import type { TActionPayload_Any_Instance } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { EErrId_NiceAction, err_nice_action } from \"../errors/err_nice_action\";\nimport type { ActionRuntime } from \"./ActionRuntime\";\nimport type { IActionHandlerAndRuntime, IActionRuntimeManagerContext } from \"./ActionRuntime.types\";\nimport type { IHandleActionOptions } from \"./Handler/ActionHandler.types\";\nimport {\n type IRuntimeCoordinate,\n RuntimeCoordinate,\n type TRuntimeCoordinateStringId,\n} from \"./RuntimeCoordinate\";\nimport { runtimeCoordinateToStringIds } from \"./utils/runtimeCoordinateToStringIds\";\n\nexport class ActionRuntimeManager {\n private _runtimes: Map<TRuntimeCoordinateStringId, ActionRuntime> = new Map();\n private _preferredRuntimeClientId: TRuntimeCoordinateStringId | null = null;\n private _context: IActionRuntimeManagerContext;\n\n constructor(context?: IActionRuntimeManagerContext) {\n this._context = context ?? {};\n }\n\n registerRuntime(runtime: ActionRuntime): void {\n const runtimeId = runtime.coordinate.stringId;\n if (this._runtimes.has(runtimeId)) {\n throw err_nice_action.fromId(EErrId_NiceAction.client_runtime_already_registered, {\n context: this._context,\n client: runtime.coordinate,\n });\n }\n\n for (const id of runtime.coordinate.toStringIds()) {\n if (this._runtimes.has(id)) {\n continue;\n }\n\n this._runtimes.set(id, runtime);\n }\n }\n\n getRuntimeAndHandlerForAction(\n action: TActionPayload_Any_Instance<any, any>,\n options?: IHandleActionOptions,\n throwOnIssue?: boolean,\n ): IActionHandlerAndRuntime | undefined {\n const localRuntime = options?.targetLocalRuntime;\n\n if (localRuntime != null) {\n const runtime = throwOnIssue\n ? this.getBestRuntimeOrThrow(options?.targetLocalRuntime?.coordinate)\n : this.getBestRuntime(options?.targetLocalRuntime?.coordinate);\n\n if (runtime == null) {\n return;\n }\n\n const handler = runtime._getHandlerForAction(action, options);\n\n if (handler != null) {\n return { handler, runtime };\n }\n\n if (throwOnIssue) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\n domain: action.domain,\n actionId: action.id,\n specifiedClient: localRuntime.coordinate,\n });\n }\n }\n\n // If no client specified, try to find a runtime that can handle the action\n for (const runtime of this._runtimes.values()) {\n const handler = runtime._getHandlerForAction(action);\n if (handler) {\n return { handler, runtime };\n }\n }\n\n if (throwOnIssue) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\n domain: action.domain,\n actionId: action.id,\n specifiedClient: options?.targetLocalRuntime?.coordinate,\n });\n }\n }\n\n getRuntimeAndHandlerForActionOrThrow(\n action: TActionPayload_Any_Instance<any, any>,\n options?: IHandleActionOptions,\n ): IActionHandlerAndRuntime {\n return this.getRuntimeAndHandlerForAction(action, options, true)!;\n }\n\n setPreferredRuntime(runtime: ActionRuntime): void {\n const runtimeId = runtime.coordinate.stringId;\n this._preferredRuntimeClientId = runtimeId;\n }\n\n getPreferredRuntime(): ActionRuntime | undefined {\n if (this._preferredRuntimeClientId) {\n const runtime = this._runtimes.get(this._preferredRuntimeClientId);\n if (runtime) {\n return runtime;\n }\n }\n return this._runtimes.values().next().value;\n }\n\n getBestRuntimeForSpecifier(clientSpecifier: IRuntimeCoordinate): ActionRuntime | undefined {\n const actionClient = new RuntimeCoordinate(clientSpecifier);\n const ids = actionClient.toStringIds();\n\n for (const id of ids) {\n const runtime = this._runtimes.get(id);\n if (runtime) {\n return runtime;\n }\n }\n }\n\n getBestRuntime(clientSpecifier?: IRuntimeCoordinate): ActionRuntime | undefined {\n return clientSpecifier != null\n ? this.getBestRuntimeForSpecifier(clientSpecifier)\n : this.getPreferredRuntime();\n }\n\n hasRuntime(runtime: ActionRuntime): boolean {\n return this._runtimes.has(runtime.coordinate.stringId);\n }\n\n getBestRuntimeOrThrow(specifier?: IRuntimeCoordinate): ActionRuntime {\n const runtime = this.getBestRuntime(specifier);\n\n if (!runtime) {\n if (specifier == null) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_client_runtimes_registered, {\n context: this._context,\n });\n }\n\n throw err_nice_action.fromId(EErrId_NiceAction.client_runtime_not_registered, {\n context: this._context,\n clientStringId: runtimeCoordinateToStringIds(specifier)[0],\n });\n }\n\n return runtime;\n }\n}\n","import type { ActionRuntime } from \"../../ActionRuntime/ActionRuntime\";\nimport type { IActionHandlerAndRuntime } from \"../../ActionRuntime/ActionRuntime.types\";\nimport { ActionRuntimeManager } from \"../../ActionRuntime/ActionRuntimeManager\";\nimport type { IExecuteActionOptions } from \"../../ActionRuntime/Handler/ActionHandler.types\";\nimport type { IRuntimeCoordinate } from \"../../ActionRuntime/RuntimeCoordinate\";\nimport { EErrId_NiceAction, err_nice_action } from \"../../errors/err_nice_action\";\nimport type { ActionPayload_Request } from \"../Action/Payload/ActionPayload_Request\";\nimport { RunningAction } from \"../Action/RunningAction\";\nimport { ActionDomain } from \"./ActionDomain\";\nimport type {\n IActionDomain,\n IActionDomainChildOptions,\n IActionRootDomain,\n TActionDomainChildDef,\n} from \"./ActionDomain.types\";\nimport { ActionDomainBase } from \"./ActionDomainBase\";\n\nexport class ActionRootDomain<\n ROOT_DOM extends IActionRootDomain = IActionRootDomain,\n> extends ActionDomainBase<ROOT_DOM> {\n private _actionRuntimeManager: ActionRuntimeManager;\n\n constructor(\n readonly domainDefinition: {\n domain: ROOT_DOM[\"domain\"];\n },\n ) {\n const domainId = domainDefinition.domain;\n\n super({\n domain: domainId,\n allDomains: [domainId],\n actionSchema: {},\n } as ROOT_DOM);\n\n this._actionRuntimeManager = new ActionRuntimeManager({ domain: domainId });\n }\n\n createChildDomain<SUB_DOM extends IActionDomainChildOptions>(\n subDomainDef: SUB_DOM & {\n [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never;\n },\n ): ActionDomain<TActionDomainChildDef<ROOT_DOM, SUB_DOM>> {\n if (this.allDomains.includes(subDomainDef.domain)) {\n throw err_nice_action.fromId(EErrId_NiceAction.domain_already_exists_in_hierarchy, {\n domain: subDomainDef.domain,\n allParentDomains: this.allDomains,\n parentDomain: this.domain,\n });\n }\n\n return new ActionDomain<TActionDomainChildDef<ROOT_DOM, SUB_DOM>>(\n {\n allDomains: [...this.allDomains, subDomainDef.domain],\n domain: subDomainDef.domain,\n actionSchema: subDomainDef.actions,\n },\n { rootDomain: this },\n );\n }\n\n _registerRuntime(runtime: ActionRuntime): void {\n this._actionRuntimeManager.registerRuntime(runtime);\n }\n\n _hasRuntime(runtime: ActionRuntime): boolean {\n return this._actionRuntimeManager.hasRuntime(runtime);\n }\n\n getRuntime(clientSpecifier: IRuntimeCoordinate): ActionRuntime | undefined {\n return this._actionRuntimeManager.getBestRuntimeForSpecifier(clientSpecifier);\n }\n\n async _runAction<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n ACT extends ActionPayload_Request<DOM, ID> = ActionPayload_Request<DOM, ID>,\n >(actionPayload: ACT, options?: IExecuteActionOptions<DOM, ID>): Promise<RunningAction<DOM, ID>> {\n const allListeners = [...this._listeners, ...(options?.listeners ?? [])];\n\n let handlerAndRuntime: IActionHandlerAndRuntime;\n try {\n handlerAndRuntime = this._actionRuntimeManager.getRuntimeAndHandlerForActionOrThrow(\n actionPayload,\n options,\n );\n } catch (err) {\n const runningAction = new RunningAction<DOM, ID>({\n context: actionPayload.context,\n request: actionPayload,\n callSite: actionPayload._callSite,\n });\n runningAction.addUpdateListeners(allListeners);\n runningAction._failWithError(err);\n throw err;\n }\n\n const { handler, runtime } = handlerAndRuntime;\n\n actionPayload.context._setOriginClient(runtime.coordinate);\n\n const runningAction = await handler.handleActionRequest(actionPayload, {\n targetLocalRuntime: runtime,\n });\n\n runningAction.addUpdateListeners(allListeners);\n\n return runningAction;\n }\n}\n","import type { ActionRuntime } from \"../../ActionRuntime/ActionRuntime\";\nimport type { IExecuteActionOptions } from \"../../ActionRuntime/Handler/ActionHandler.types\";\nimport { ActionLocalHandler } from \"../../ActionRuntime/Handler/Local/ActionLocalHandler\";\nimport { RuntimeCoordinate } from \"../../ActionRuntime/RuntimeCoordinate\";\nimport { EErrId_NiceAction, err_nice_action } from \"../../errors/err_nice_action\";\nimport { assertIsActionJson } from \"../../utils/assertIsActionJson\";\nimport { isAction_Any_Instance } from \"../../utils/isAction_Any_Instance\";\nimport type {\n TAction_Any_JsonObject,\n TDistributeActionPayload_Request,\n TDistributeActionPayload_Result,\n TDistributedDomainActions,\n TNarrowActionJsonTypeToActionInstanceType,\n} from \"../Action/Action.combined.types\";\nimport { EActionForm, type IActionBase } from \"../Action/ActionBase.types\";\nimport { ActionContext } from \"../Action/Context/ActionContext\";\nimport type { IActionContext_Data_JsonObject } from \"../Action/Context/ActionContext.types\";\nimport { ActionCore } from \"../Action/Core/ActionCore\";\nimport {\n EActionPayloadType,\n type IActionPayload_Request_JsonObject,\n type IActionPayload_Result_JsonObject,\n} from \"../Action/Payload/ActionPayload.types\";\nimport { ActionPayload_Request } from \"../Action/Payload/ActionPayload_Request\";\nimport { ActionPayload_Result } from \"../Action/Payload/ActionPayload_Result\";\nimport type { RunningAction } from \"../Action/RunningAction\";\nimport type { TRunningActionUpdateListener } from \"../Action/RunningAction.types\";\nimport type {\n IActionDomain,\n IActionDomainChildOptions,\n TActionDomainChildDef,\n TWrappableDomainActionHandler,\n} from \"./ActionDomain.types\";\nimport { ActionDomainBase } from \"./ActionDomainBase\";\nimport { type ActionRootDomain } from \"./ActionRootDomain\";\n\ntype TActionMap<ACT_DOM extends IActionDomain> = {\n [K in keyof ACT_DOM[\"actionSchema\"] & string]: ActionCore<ACT_DOM, K>;\n};\n\nexport class ActionDomain<\n ACT_DOM extends IActionDomain = IActionDomain,\n> extends ActionDomainBase<ACT_DOM> {\n private _rootDomain: ActionRootDomain<any>;\n private readonly _actionMap: TActionMap<ACT_DOM>;\n\n constructor(\n definition: ACT_DOM,\n {\n rootDomain,\n }: {\n rootDomain: ActionRootDomain<any>;\n },\n ) {\n super(definition);\n this._rootDomain = rootDomain;\n this._actionMap = this.createActionMap();\n }\n\n get rootDomain() {\n return this._rootDomain;\n }\n\n /**\n * @internal\n * All action observers that should see actions on this domain: the root domain's\n * observers plus this subdomain's own. Mirrors the listener set the local-dispatch\n * path assembles in `runAction`/`_runAction`, so inbound actions (pushed from a\n * backend or another client) can be wired up identically and surface in devtools.\n */\n _collectActionObservers(): TRunningActionUpdateListener<any, any>[] {\n return [...this._rootDomain._getActionObservers(), ...this._getActionObservers()];\n }\n\n _registerRuntime(runtime: ActionRuntime): void {\n this._rootDomain._registerRuntime(runtime);\n }\n\n createChildDomain<SUB_DOM extends IActionDomainChildOptions>(\n subDomainDef: SUB_DOM & {\n [K in Exclude<keyof SUB_DOM, keyof IActionDomainChildOptions>]: never;\n },\n ): ActionDomain<TActionDomainChildDef<ACT_DOM, SUB_DOM>> {\n if (this.allDomains.includes(subDomainDef.domain)) {\n throw err_nice_action.fromId(EErrId_NiceAction.domain_already_exists_in_hierarchy, {\n domain: subDomainDef.domain,\n allParentDomains: this.allDomains,\n parentDomain: this.domain,\n });\n }\n\n return new ActionDomain<TActionDomainChildDef<ACT_DOM, SUB_DOM>>(\n {\n allDomains: [...this.allDomains, subDomainDef.domain],\n domain: subDomainDef.domain,\n actionSchema: subDomainDef.actions,\n },\n { rootDomain: this._rootDomain },\n );\n }\n\n get action(): TActionMap<ACT_DOM> {\n return this._actionMap;\n }\n\n actionsMap(): TActionMap<ACT_DOM> {\n return this._actionMap;\n }\n\n actionForId<ID extends keyof ACT_DOM[\"actionSchema\"] & string>(id: ID): ActionCore<ACT_DOM, ID> {\n const actionSchema = this.actionSchema[id];\n if (!actionSchema) {\n throw err_nice_action.fromId(EErrId_NiceAction.action_id_not_in_domain, {\n domain: this.domain,\n actionId: id as string,\n });\n }\n\n return new ActionCore<ACT_DOM, ID>(this, id);\n }\n\n wrapAsPartialLocalHandler(\n wrappedActionExecutor: Partial<TWrappableDomainActionHandler<ACT_DOM>>,\n ): ActionLocalHandler {\n const _handler = new ActionLocalHandler();\n const executor = wrappedActionExecutor as unknown as Record<string, (input: any) => any>;\n\n for (const actionKey in wrappedActionExecutor) {\n if (!this.actionSchema[actionKey]) {\n continue;\n }\n\n _handler.forAction(this.actionForId(actionKey), (request) =>\n executor[request.id](request.input),\n );\n }\n\n return _handler;\n }\n\n wrapAsLocalHandler(\n wrappedActionExecutor: TWrappableDomainActionHandler<ACT_DOM>,\n ): ActionLocalHandler {\n const _handler = new ActionLocalHandler();\n const executor = wrappedActionExecutor as unknown as Record<string, (input: any) => any>;\n return _handler.forDomain(this, (request) => executor[request.id](request.input));\n }\n\n hydrateContext<ID extends keyof ACT_DOM[\"actionSchema\"] & string>(\n id: ID,\n contextData: IActionContext_Data_JsonObject,\n ): ActionContext<ACT_DOM, ID> {\n return new ActionContext(this, id, {\n timeCreated: contextData.timeCreated,\n cuid: contextData.cuid,\n routing: contextData.routing.map((item) => {\n return {\n runtime: new RuntimeCoordinate(item.runtime),\n handler: item.handler,\n time: item.time,\n };\n }),\n originClient: contextData.originClient\n ? new RuntimeCoordinate(contextData.originClient)\n : RuntimeCoordinate.unknown,\n });\n }\n\n isDomainAction<ACT extends IActionBase<any, ACT_DOM, any>>(\n action: ACT | unknown | null | undefined,\n ): action is TDistributedDomainActions<ACT_DOM, ACT> {\n return isAction_Any_Instance(action) && action.domain === this.domain;\n }\n\n hydrateRequestPayload<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n P extends IActionPayload_Request_JsonObject<ACT_DOM, ID>,\n >(serialized: P): TDistributeActionPayload_Request<ACT_DOM, ID> {\n if (serialized.type !== EActionPayloadType.request) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_state_mismatch, {\n expected: EActionPayloadType.request,\n received: serialized.type,\n });\n }\n\n if (serialized.domain !== this.domain) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_domain_mismatch, {\n expected: this.domain,\n received: serialized.domain,\n });\n }\n\n const id = serialized.id;\n if (!this.actionSchema[id]) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_id_not_found, {\n domain: this.domain,\n actionId: serialized.id,\n });\n }\n\n const contextAction = this.hydrateContext(id, serialized.context);\n\n return new ActionPayload_Request(\n { context: contextAction },\n contextAction.deserializeInput(serialized.input),\n {\n time: serialized.time,\n },\n ) as TDistributeActionPayload_Request<ACT_DOM, ID>;\n }\n\n hydrateResultPayload<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n R extends IActionPayload_Result_JsonObject<ACT_DOM, ID>,\n >(serialized: R): TDistributeActionPayload_Result<ACT_DOM, ID> {\n if (serialized.type !== EActionPayloadType.result) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_state_mismatch, {\n expected: EActionPayloadType.result,\n received: serialized.type,\n });\n }\n\n if (serialized.domain !== this.domain) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_domain_mismatch, {\n expected: this.domain,\n received: serialized.domain,\n });\n }\n\n const id = serialized.id;\n\n if (!this.actionSchema[id]) {\n throw err_nice_action.fromId(EErrId_NiceAction.hydration_action_id_not_found, {\n domain: this.domain,\n actionId: serialized.id,\n });\n }\n\n const contextAction = this.hydrateContext(id, serialized.context);\n\n const result = serialized.result.ok\n ? {\n ok: true as const,\n output: contextAction.schema.deserializeOutput(serialized.result.output),\n }\n : serialized.result;\n\n return new ActionPayload_Result({ context: contextAction }, result, {\n time: serialized.time,\n }) as TDistributeActionPayload_Result<ACT_DOM, ID>;\n }\n\n hydrateAnyAction<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n AJ extends TAction_Any_JsonObject<ACT_DOM, ID>,\n >(actionJson: AJ): TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID> {\n assertIsActionJson(actionJson);\n\n if (actionJson.form === EActionForm.data) {\n if (actionJson.type === EActionPayloadType.request) {\n return this.hydrateRequestPayload(\n actionJson,\n ) as unknown as TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID>;\n }\n\n if (actionJson.type === EActionPayloadType.result) {\n return this.hydrateResultPayload(\n actionJson,\n ) as unknown as TNarrowActionJsonTypeToActionInstanceType<ACT_DOM, AJ, ID>;\n }\n }\n\n return this.actionForId(actionJson.id) as TNarrowActionJsonTypeToActionInstanceType<\n ACT_DOM,\n AJ,\n ID\n >;\n }\n\n async runAction<\n ID extends keyof ACT_DOM[\"actionSchema\"] & string,\n ACT extends ActionPayload_Request<ACT_DOM, ID>,\n >(\n request: ACT,\n options?: IExecuteActionOptions<ACT_DOM, ID>,\n ): Promise<RunningAction<ACT_DOM, ID>> {\n const allListeners: TRunningActionUpdateListener<any, any>[] = [\n ...(options?.listeners ?? []),\n ...this._listeners,\n ];\n\n return this._rootDomain._runAction(request, {\n ...options,\n listeners: allListeners,\n });\n }\n\n private createActionMap(): {\n [K in keyof ACT_DOM[\"actionSchema\"] & string]: ActionCore<ACT_DOM, K>;\n } {\n const map = {} as {\n [K in keyof ACT_DOM[\"actionSchema\"] & string]: ActionCore<ACT_DOM, K>;\n };\n\n for (const id in this.actionSchema) {\n map[id] = new ActionCore(this, id);\n }\n\n return map;\n }\n}\n","import type { IActionRootDomain } from \"../ActionDomain.types\";\nimport { ActionRootDomain } from \"../ActionRootDomain\";\n\nexport const createActionRootDomain = <ID extends string>(definition: {\n domain: ID;\n}): ActionRootDomain<IActionRootDomain<ID>> => {\n return new ActionRootDomain<IActionRootDomain<ID>>(definition);\n};\n","import type { IDuplexCarrier } from \"../../Carrier.types\";\r\n\r\ntype TFrame = string | ArrayBuffer | Uint8Array;\r\n\r\n/** The peer (server) end of an in-memory pair — what you feed into an `AcceptorHandler`. */\r\nexport interface IInMemoryServerEndpoint {\r\n /** Write a frame back to the client end. */\r\n send(frame: TFrame): void;\r\n /** Register the inbound handler (frames the client sent). */\r\n onMessage(handler: (frame: TFrame) => void): void;\r\n /** Close the pair from this end. */\r\n close(): void;\r\n /** Notified when the pair closes (from either end). */\r\n onClose(handler: () => void): void;\r\n}\r\n\r\nexport interface IInMemoryChannelPair {\r\n /** The client end — pass as a {@link IDuplexCarrier} to a `LinkTransport`. */\r\n clientChannel: IDuplexCarrier;\r\n /** The server end — wire into an `AcceptorHandler` (`send` + `receive`). */\r\n serverEndpoint: IInMemoryServerEndpoint;\r\n}\r\n\r\n/**\r\n * Two cross-wired in-process byte channels — a loopback carrier with no socket. The client end is a\r\n * {@link IDuplexCarrier} you hand to a {@link LinkTransport}; the server end plugs into an\r\n * `AcceptorHandler` (`send: (_, f) => serverEndpoint.send(f)`, and `serverEndpoint.onMessage(f =>\r\n * handler.receive(conn, f))`). Frames are delivered on a microtask, so each side observes the other\r\n * asynchronously — exactly like a real transport — which makes this ideal for tests and for running\r\n * two runtimes in one process (or proving a non-WS carrier end to end).\r\n */\r\nexport function createInMemoryChannelPair(): IInMemoryChannelPair {\r\n let clientMessage: ((frame: TFrame) => void) | undefined;\r\n let clientClose: (() => void) | undefined;\r\n let serverMessage: ((frame: TFrame) => void) | undefined;\r\n let serverClose: (() => void) | undefined;\r\n let open = true;\r\n\r\n const closeBoth = (): void => {\r\n if (!open) return;\r\n open = false;\r\n queueMicrotask(() => {\r\n clientClose?.();\r\n serverClose?.();\r\n });\r\n };\r\n\r\n const clientChannel: IDuplexCarrier = {\r\n ready: Promise.resolve(),\r\n isOpen: () => open,\r\n send: (frame) => {\r\n if (!open) return;\r\n queueMicrotask(() => serverMessage?.(frame));\r\n },\r\n attach: ({ onMessage, onClose }) => {\r\n clientMessage = onMessage;\r\n clientClose = onClose;\r\n },\r\n close: closeBoth,\r\n label: \"in-memory\",\r\n };\r\n\r\n const serverEndpoint: IInMemoryServerEndpoint = {\r\n send: (frame) => {\r\n if (!open) return;\r\n queueMicrotask(() => clientMessage?.(frame));\r\n },\r\n onMessage: (handler) => {\r\n serverMessage = handler;\r\n },\r\n onClose: (handler) => {\r\n serverClose = handler;\r\n },\r\n close: closeBoth,\r\n };\r\n\r\n return { clientChannel, serverEndpoint };\r\n}\r\n","import {\r\n createInMemoryChannelPair,\r\n type IInMemoryServerEndpoint,\r\n} from \"./createInMemoryChannel\";\r\nimport type { IDuplexCarrierSource } from \"../../Carrier.types\";\r\n\r\nexport interface IInMemoryCarrier {\r\n /** The connector end — pass as the `carrier` to one of `connectChannel`'s transports. */\r\n carrier: IDuplexCarrierSource;\r\n /** The acceptor end — wire into an `AcceptorHandler` (`send` + `receive`). */\r\n serverEndpoint: IInMemoryServerEndpoint;\r\n}\r\n\r\n/**\r\n * A loopback duplex carrier with no socket — two cross-wired in-process ends. The connector end is an\r\n * {@link IDuplexCarrierSource} for `connectChannel`; the acceptor end plugs into an `AcceptorHandler`.\r\n * Ideal for tests and for running two runtimes in one process, or proving a non-WS carrier end to end.\r\n */\r\nexport function inMemoryCarrier(): IInMemoryCarrier {\r\n const { clientChannel, serverEndpoint } = createInMemoryChannelPair();\r\n return {\r\n carrier: {\r\n carrierLabel: \"memory\",\r\n open: () => clientChannel,\r\n getCacheKey: () => [\"memory\"],\r\n },\r\n serverEndpoint,\r\n };\r\n}\r\n","import type { IDuplexCarrier } from \"../../Carrier.types\";\r\n\r\n/**\r\n * The slice of the `RTCDataChannel` surface this adapter uses — declared structurally so it accepts the\r\n * browser `RTCDataChannel`, React-Native (`react-native-webrtc`), or any node-webrtc shim without a hard\r\n * dependency on a specific DOM/RN type. A real `RTCDataChannel` satisfies it as-is.\r\n */\r\nexport interface IRtcDataChannelLike {\r\n readyState: string; // \"connecting\" | \"open\" | \"closing\" | \"closed\"\r\n binaryType: string;\r\n label?: string;\r\n send(data: string | ArrayBuffer | ArrayBufferView): void;\r\n close(): void;\r\n addEventListener(type: string, listener: (event: any) => void, options?: unknown): void;\r\n}\r\n\r\n/**\r\n * Adapt a WebRTC `RTCDataChannel` to the carrier-agnostic {@link IDuplexCarrier}, so two browsers\r\n * (or two mobile apps) linked peer-to-peer — no server in the middle — run the *same* secure session as\r\n * a WebSocket. Hand it to `createSecureLinkTransport({ openChannel: () => rtcDataChannelByteChannel(dc) })`.\r\n *\r\n * The data channel must already be created (its negotiation/signaling is the app's concern); this only\r\n * drives bytes over it. Binary frames are requested as `ArrayBuffer` so the binary session codec unpacks\r\n * them synchronously; a `Blob` (if the channel hands one back) is normalized to a buffer.\r\n */\r\nexport function rtcDataChannelByteChannel(dc: IRtcDataChannelLike): IDuplexCarrier {\r\n dc.binaryType = \"arraybuffer\";\r\n\r\n let intentional = false;\r\n let onMessage: ((frame: string | ArrayBuffer | Uint8Array) => void) | undefined;\r\n let onClose: (() => void) | undefined;\r\n const preAttach: (string | ArrayBuffer | Uint8Array)[] = [];\r\n\r\n const deliver = (frame: string | ArrayBuffer | Uint8Array): void => {\r\n if (onMessage != null) onMessage(frame);\r\n else preAttach.push(frame);\r\n };\r\n\r\n dc.addEventListener(\"message\", async (event: { data: unknown }) => {\r\n const frame = await normalizeFrame(event.data);\r\n if (frame !== undefined) deliver(frame);\r\n });\r\n dc.addEventListener(\"close\", () => {\r\n if (!intentional) console.error(\"RTCDataChannel closed\");\r\n onClose?.();\r\n });\r\n dc.addEventListener(\"error\", (event: unknown) => {\r\n console.error(\"RTCDataChannel error:\", event);\r\n onClose?.();\r\n });\r\n\r\n const ready = new Promise<void>((resolve, reject) => {\r\n if (dc.readyState === \"open\") {\r\n resolve();\r\n return;\r\n }\r\n dc.addEventListener(\"open\", () => resolve(), { once: true });\r\n dc.addEventListener(\"error\", (event: unknown) => reject(event), { once: true });\r\n dc.addEventListener(\"close\", () => reject(new Error(\"RTCDataChannel closed before open\")), {\r\n once: true,\r\n });\r\n });\r\n\r\n return {\r\n ready,\r\n isOpen: () => dc.readyState === \"open\",\r\n send: (frame) => {\r\n // A pooled `Uint8Array` may be backed by a `SharedArrayBuffer` that `send` rejects — copy into a\r\n // fresh view (mirrors the WebSocket `sendFrame` guard).\r\n if (typeof frame === \"string\" || frame instanceof ArrayBuffer) dc.send(frame);\r\n else dc.send(new Uint8Array(frame));\r\n },\r\n attach: (handlers) => {\r\n onMessage = handlers.onMessage;\r\n onClose = handlers.onClose;\r\n for (const frame of preAttach) handlers.onMessage(frame);\r\n preAttach.length = 0;\r\n },\r\n close: () => {\r\n intentional = true;\r\n try {\r\n dc.close();\r\n } catch {\r\n // already closing/closed — the close listener still runs cleanup\r\n }\r\n },\r\n get label() {\r\n return dc.label != null && dc.label !== \"\" ? dc.label : undefined;\r\n },\r\n };\r\n}\r\n\r\nasync function normalizeFrame(\r\n data: unknown,\r\n): Promise<string | ArrayBuffer | Uint8Array | undefined> {\r\n if (typeof data === \"string\" || data instanceof ArrayBuffer || data instanceof Uint8Array) {\r\n return data;\r\n }\r\n if (typeof Blob !== \"undefined\" && data instanceof Blob) {\r\n return await data.arrayBuffer();\r\n }\r\n return undefined;\r\n}\r\n","import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../../../Transport.types\";\r\nimport {\r\n type IRtcDataChannelLike,\r\n rtcDataChannelByteChannel,\r\n} from \"./rtcDataChannelByteChannel\";\r\nimport type { IDuplexCarrierSource } from \"../../Carrier.types\";\r\n\r\nexport interface IRtcCarrierOptions {\r\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n}\r\n\r\n/**\r\n * A WebRTC {@link IDuplexCarrierSource} over an already-negotiated `RTCDataChannel` (signaling is the\r\n * app's concern). Pass it as a `carrier` to `connectChannel` so two browsers/apps linked peer-to-peer run\r\n * the identical secure session as a WebSocket.\r\n */\r\nexport function rtcCarrier(\r\n dataChannel: IRtcDataChannelLike,\r\n options: IRtcCarrierOptions = {},\r\n): IDuplexCarrierSource {\r\n return {\r\n carrierLabel: \"webrtc\",\r\n open: () => rtcDataChannelByteChannel(dataChannel),\r\n getCacheKey: options.getTransportCacheKey ?? (() => [\"webrtc\"]),\r\n getRouteInfo: options.getRouteInfo,\r\n };\r\n}\r\n","import { err } from \"@nice-code/error\";\r\nimport { err_nice_transport } from \"../../../err_nice_transport\";\r\n\r\nexport enum EErrId_NiceTransport_WebSocket {\r\n ws_disconnected = \"ws_disconnected\",\r\n ws_create_failed = \"ws_create_failed\",\r\n ws_error = \"ws_error\",\r\n}\r\n\r\nexport const err_nice_transport_ws = err_nice_transport.createChildDomain({\r\n domain: \"ws_transport\",\r\n schema: {\r\n [EErrId_NiceTransport_WebSocket.ws_disconnected]: err<Record<string, never>>({\r\n message: () => `WebSocket transport disconnected.`,\r\n }),\r\n [EErrId_NiceTransport_WebSocket.ws_create_failed]: err<{\r\n originalError?: Error;\r\n }>({\r\n message: ({ originalError }) =>\r\n `Failed to create WebSocket transport.${originalError ? ` Original error: ${originalError.message}` : \"\"}`,\r\n }),\r\n [EErrId_NiceTransport_WebSocket.ws_error]: err<{\r\n originalError?: Error;\r\n }>({\r\n message: ({ originalError }) =>\r\n `WebSocket transport error.${originalError ? ` Original error: ${originalError.message}` : \"\"}`,\r\n }),\r\n },\r\n});\r\n","/**\r\n * Send a text or binary frame over a socket. A binary formatter may hand back a `Uint8Array` whose\r\n * backing buffer is typed as `ArrayBufferLike` (msgpackr pools buffers / may be `SharedArrayBuffer`),\r\n * which `WebSocket.send`'s `BufferSource` parameter rejects — copy it into a fresh `ArrayBuffer`-backed\r\n * view so the type (and the bytes) are safe to send.\r\n */\r\nexport function sendFrame(ws: WebSocket, data: string | Uint8Array | ArrayBuffer): void {\r\n if (typeof data === \"string\" || data instanceof ArrayBuffer) {\r\n ws.send(data);\r\n return;\r\n }\r\n ws.send(new Uint8Array(data));\r\n}\r\n\r\n/** Compact a WebSocket URL to `host/pathname` for devtools display, falling back to the raw url. */\r\nexport function shortWs(url: string): string {\r\n try {\r\n const u = new URL(url);\r\n return `${u.host}${u.pathname}`;\r\n } catch {\r\n return url;\r\n }\r\n}\r\n","import type { IDuplexCarrier } from \"../../Carrier.types\";\r\nimport { sendFrame } from \"./ws_util\";\r\n\r\n/**\r\n * Adapt a `WebSocket` to the carrier-agnostic {@link IDuplexCarrier}, so the WebSocket becomes\r\n * \"just another carrier\" under the shared secure session. It owns every WebSocket-specific concern —\r\n * awaiting `open`, normalizing `Blob` frames to bytes, suppressing the log on a deliberate `close`, and\r\n * buffering any frame that arrives before the session attaches its handler — leaving the session itself\r\n * carrier-neutral.\r\n */\r\nexport function webSocketByteChannel(ws: WebSocket): IDuplexCarrier {\r\n let intentional = false;\r\n let onMessage: ((frame: string | ArrayBuffer | Uint8Array) => void) | undefined;\r\n let onClose: (() => void) | undefined;\r\n // Frames that land before the session calls `attach` (none expected in practice, but never lose one).\r\n const preAttach: (string | ArrayBuffer | Uint8Array)[] = [];\r\n\r\n const deliver = (frame: string | ArrayBuffer | Uint8Array): void => {\r\n if (onMessage != null) onMessage(frame);\r\n else preAttach.push(frame);\r\n };\r\n\r\n ws.addEventListener(\"message\", async (event) => {\r\n const frame = await normalizeFrame((event as MessageEvent).data);\r\n if (frame !== undefined) deliver(frame);\r\n });\r\n ws.addEventListener(\"close\", (event) => {\r\n if (!intentional) console.error(\"WebSocket closed:\", event);\r\n onClose?.();\r\n });\r\n ws.addEventListener(\"error\", (event) => {\r\n console.error(\"WebSocket error:\", event);\r\n onClose?.();\r\n });\r\n\r\n const ready = new Promise<void>((resolve, reject) => {\r\n if (ws.readyState === WebSocket.OPEN) {\r\n resolve();\r\n return;\r\n }\r\n ws.addEventListener(\"open\", () => resolve(), { once: true });\r\n ws.addEventListener(\"error\", (event) => reject(event), { once: true });\r\n ws.addEventListener(\r\n \"close\",\r\n (event) =>\r\n reject(new Error(`WebSocket closed before open: code=${(event as CloseEvent).code}`)),\r\n { once: true },\r\n );\r\n });\r\n\r\n return {\r\n ready,\r\n isOpen: () => ws.readyState === WebSocket.OPEN,\r\n send: (frame) => sendFrame(ws, frame),\r\n attach: (handlers) => {\r\n onMessage = handlers.onMessage;\r\n onClose = handlers.onClose;\r\n for (const frame of preAttach) handlers.onMessage(frame);\r\n preAttach.length = 0;\r\n },\r\n close: () => {\r\n intentional = true;\r\n try {\r\n ws.close();\r\n } catch {\r\n // already closing/closed — the close listener still runs cleanup\r\n }\r\n },\r\n get label() {\r\n return ws.url != null && ws.url !== \"\" ? ws.url : undefined;\r\n },\r\n };\r\n}\r\n\r\n/** Accept text + binary frames (ArrayBuffer / Uint8Array / Blob); Blobs are converted to a buffer. */\r\nasync function normalizeFrame(\r\n data: unknown,\r\n): Promise<string | ArrayBuffer | Uint8Array | undefined> {\r\n if (typeof data === \"string\" || data instanceof ArrayBuffer || data instanceof Uint8Array) {\r\n return data;\r\n }\r\n if (typeof Blob !== \"undefined\" && data instanceof Blob) {\r\n return await data.arrayBuffer();\r\n }\r\n return undefined;\r\n}\r\n","import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../../../Transport.types\";\r\nimport { webSocketByteChannel } from \"./webSocketByteChannel\";\r\nimport { shortWs } from \"./ws_util\";\r\nimport type { IDuplexCarrierSource } from \"../../Carrier.types\";\r\n\r\n/** The WebSocket an action's socket is opened against (derived per action from the route params). */\r\nexport interface IWsCarrierRequest {\r\n url: string;\r\n}\r\n\r\nexport interface IWsCarrierOptions {\r\n /** Override the reuse key (defaults to `[url]`, so one socket is shared per endpoint). */\r\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Override the devtools route info for a specific action. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n}\r\n\r\n/**\r\n * A WebSocket {@link IDuplexCarrierSource}: opens an `arraybuffer` socket (cached per endpoint) and adapts\r\n * it to a carrier. Pass it as a `carrier` to `connectChannel` — the WebSocket is now \"just another\r\n * carrier\" under the shared secure session, with no WS-specific transport class.\r\n *\r\n * `createRequest` derives the socket URL per action (keep it simple with `() => ({ url })`), so dynamic\r\n * endpoints (e.g. a per-run query param) need no `createWebSocket` escape hatch.\r\n */\r\nexport function wsCarrier(\r\n createRequest: (input: ITransportRouteActionParams) => IWsCarrierRequest,\r\n options: IWsCarrierOptions = {},\r\n): IDuplexCarrierSource {\r\n return {\r\n carrierLabel: \"ws\",\r\n open: (input) => webSocketByteChannel(defaultWebSocket(createRequest(input).url)),\r\n getCacheKey: options.getTransportCacheKey ?? ((input) => [createRequest(input).url]),\r\n getRouteInfo:\r\n options.getRouteInfo ??\r\n ((input) => {\r\n const { url } = createRequest(input);\r\n return { carrierLabel: \"ws\", url, summary: `ws ${shortWs(url)}` };\r\n }),\r\n };\r\n}\r\n\r\nfunction defaultWebSocket(url: string): WebSocket {\r\n const ws = new WebSocket(url);\r\n // Binary responses as ArrayBuffer (not Blob) so the session unpacks them synchronously.\r\n ws.binaryType = \"arraybuffer\";\r\n return ws;\r\n}\r\n","import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../../../Transport.types\";\r\nimport type { IExchangeCarrier, IExchangeCarrierSource, TFrame } from \"../../Carrier.types\";\r\n\r\n/** The HTTP request an action is sent over (the body is supplied by the secure exchange session). */\r\nexport interface IHttpCarrierRequest {\r\n url: string;\r\n headers?: Record<string, string>;\r\n}\r\n\r\n/** The slice of `fetch` the carrier uses — a structural type so callers (and tests) needn't match the\r\n * full platform `fetch` (Bun's adds `preconnect`, etc.). The global `fetch` satisfies it. */\r\nexport type TCarrierFetch = (input: string, init?: RequestInit) => Promise<Response>;\r\n\r\nexport interface IHttpCarrierOptions {\r\n /** Override the reuse key (defaults to `[url]`, so one session is shared per endpoint). */\r\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Override the devtools route info for a specific action. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Override `fetch` (e.g. to route to an in-memory handler in tests). Defaults to global `fetch`. */\r\n fetch?: TCarrierFetch;\r\n}\r\n\r\nfunction shortPath(url: string): string {\r\n try {\r\n return new URL(url).pathname || url;\r\n } catch {\r\n return url;\r\n }\r\n}\r\n\r\n/**\r\n * An HTTP {@link IExchangeCarrierSource}: each `exchange` POSTs one frame body to the action endpoint and\r\n * resolves with the response body as the single correlated reply. Pass it as a `carrier` to\r\n * `connectChannel` — a secure HTTP transport then runs the *same* secure session as a duplex carrier\r\n * (handshake → token → encrypted frames), the request/reply correlation provided for free by the HTTP\r\n * transaction.\r\n *\r\n * `createRequest` derives the URL/headers per action (keep it simple with `() => ({ url })`). The body is\r\n * the session's responsibility, so it is never built here.\r\n */\r\nexport function httpCarrier(\r\n createRequest: (input: ITransportRouteActionParams) => IHttpCarrierRequest,\r\n options: IHttpCarrierOptions = {},\r\n): IExchangeCarrierSource {\r\n const doFetch = options.fetch ?? fetch;\r\n\r\n return {\r\n shape: \"exchange\",\r\n carrierLabel: \"http\",\r\n open: (input): IExchangeCarrier => {\r\n const request = createRequest(input);\r\n return {\r\n label: request.url,\r\n exchange: async (frame: TFrame, opts): Promise<TFrame> => {\r\n const res = await doFetch(request.url, {\r\n method: \"POST\",\r\n headers: { \"Content-Type\": \"application/json\", ...request.headers },\r\n body: typeof frame === \"string\" ? frame : new Uint8Array(frame),\r\n signal: opts?.signal,\r\n });\r\n return await res.text();\r\n },\r\n };\r\n },\r\n getCacheKey: options.getTransportCacheKey ?? ((input) => [createRequest(input).url]),\r\n getRouteInfo:\r\n options.getRouteInfo ??\r\n ((input) => {\r\n const { url } = createRequest(input);\r\n return { carrierLabel: \"http\", method: \"POST\", url, summary: `POST ${shortPath(url)}` };\r\n }),\r\n };\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_httpAcceptorCarrier = require("../../httpAcceptorCarrier-
|
|
2
|
+
const require_httpAcceptorCarrier = require("../../httpAcceptorCarrier-By0Qa__L.cjs");
|
|
3
3
|
let _nice_code_util = require("@nice-code/util");
|
|
4
4
|
//#region src/platform/cloudflare/index.ts
|
|
5
5
|
/**
|
|
@@ -79,10 +79,54 @@ function serveDurableObject(ctx, channel, options) {
|
|
|
79
79
|
secure
|
|
80
80
|
}), serveOptions);
|
|
81
81
|
}
|
|
82
|
+
/** Permissive defaults, matching `serveDurableObject`'s HTTP fallback. */
|
|
83
|
+
const DEFAULT_FORWARD_CORS = {
|
|
84
|
+
"Access-Control-Allow-Origin": "*",
|
|
85
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
86
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
87
|
+
"Access-Control-Max-Age": "86400"
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Fan a Worker's incoming action request out to a *per-id* Durable Object that serves the exchange itself.
|
|
91
|
+
*
|
|
92
|
+
* A secure exchange body is opaque to the Worker (handshake / encrypted frames), so the DO it belongs to
|
|
93
|
+
* has to be chosen from the **URL**, not the body. This picks the stub with your `pickStub` and forwards
|
|
94
|
+
* the request to its `fetch` (where `serveDurableObject` serves the exchange), while answering the CORS
|
|
95
|
+
* `OPTIONS` preflight *at the edge* so a per-id DO is never woken (or billed) just to reply to a preflight:
|
|
96
|
+
* ```ts
|
|
97
|
+
* // wrangler: a Durable Object namespace `BRIDGE`, each instance one bridge serving `bridgeChannel`.
|
|
98
|
+
* export default {
|
|
99
|
+
* fetch(request: Request, env: Env) {
|
|
100
|
+
* return forwardExchangeToDurableObject(request, (req, url) => {
|
|
101
|
+
* const bridgeId = url.pathname.split("/")[2]; // e.g. /bridge/:id/action
|
|
102
|
+
* return env.BRIDGE.get(env.BRIDGE.idFromName(bridgeId));
|
|
103
|
+
* });
|
|
104
|
+
* },
|
|
105
|
+
* };
|
|
106
|
+
*
|
|
107
|
+
* // …and in the Durable Object, serve the secure exchange (+ a WS upgrade) as usual:
|
|
108
|
+
* // fetch(request) { return this.server.fetch(request); }
|
|
109
|
+
* // where this.server = serveDurableObject(this.ctx, bridgeChannel, { runtime, httpFallback: "secure" });
|
|
110
|
+
* ```
|
|
111
|
+
* The matching connector points its `httpCarrier` at `/bridge/:id/action`. `pickStub` may be async (e.g.
|
|
112
|
+
* to look an id up first); it receives the parsed {@link URL} alongside the request.
|
|
113
|
+
*/
|
|
114
|
+
function forwardExchangeToDurableObject(request, pickStub, options = {}) {
|
|
115
|
+
if (request.method === "OPTIONS") {
|
|
116
|
+
const headers = options.cors === false ? void 0 : options.cors ?? DEFAULT_FORWARD_CORS;
|
|
117
|
+
return Promise.resolve(new Response(null, {
|
|
118
|
+
status: 204,
|
|
119
|
+
headers
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
const url = new URL(request.url);
|
|
123
|
+
return Promise.resolve(pickStub(request, url)).then((stub) => stub.fetch(request));
|
|
124
|
+
}
|
|
82
125
|
//#endregion
|
|
83
126
|
exports.cloudflareDurableObjectHost = cloudflareDurableObjectHost;
|
|
84
127
|
exports.durableObjectStorage = durableObjectStorage;
|
|
85
128
|
exports.durableObjectWsCarrier = durableObjectWsCarrier;
|
|
129
|
+
exports.forwardExchangeToDurableObject = forwardExchangeToDurableObject;
|
|
86
130
|
exports.serveDurableObject = serveDurableObject;
|
|
87
131
|
|
|
88
132
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["wsAcceptorCarrier","httpAcceptorCarrier","serveHost"],"sources":["../../../src/platform/cloudflare/index.ts"],"sourcesContent":["import {\r\n createDurableObjectStorageAdapter,\r\n type StorageAdapter,\r\n type TCreateDurableObjectStorageOptions,\r\n} from \"@nice-code/util\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { ActionRuntime } from \"../../ActionRuntime/ActionRuntime\";\r\nimport type { IActionChannel } from \"../../ActionRuntime/Channel/ActionChannel\";\r\nimport type {\r\n IChannelServer,\r\n IServeConnectionStateOptions,\r\n} from \"../../ActionRuntime/Channel/serveChannel\";\r\nimport {\r\n type IChannelHostAdapter,\r\n serveHost,\r\n type TServeHostOptions,\r\n} from \"../../ActionRuntime/Channel/serveHost\";\r\nimport type { ConnectionStateStore } from \"../../ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\r\nimport type {\r\n IDuplexAcceptorCarrier,\r\n TAcceptorCarrier,\r\n} from \"../../ActionRuntime/Transport/Carrier/AcceptorCarrier.types\";\r\nimport { wsAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier\";\r\nimport { httpAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier\";\r\n\r\n/**\r\n * Cloudflare-specific helpers for `@nice-code/action`, imported from `@nice-code/action/platform/cloudflare`.\r\n * They collapse the Durable Object boilerplate (the `WebSocketPair` upgrade, hibernation attachment wiring,\r\n * and DO-storage adapter) into one-liners you hand to `serveChannel`. The core library stays\r\n * platform-agnostic — nothing here is reachable from the main entry.\r\n *\r\n * The Workers runtime surface this module needs is declared *structurally* (and the two globals it\r\n * constructs are declared module-locally below) rather than pulled from `@cloudflare/workers-types`, so the\r\n * library's own DOM-lib build never clashes with that package's global `Response`/`WebSocket` redefinitions.\r\n * A real `DurableObjectState` and its hibernatable `WebSocket`s satisfy these shapes.\r\n */\r\n\r\ntype TDurableObjectStorage = TCreateDurableObjectStorageOptions[\"durableObjectStorage\"];\r\n\r\n/** The slice of a Durable Object's hibernatable WebSocket these helpers touch. */\r\nexport interface IDurableObjectWebSocket {\r\n send(data: string | ArrayBuffer | Uint8Array): void;\r\n serializeAttachment(value: unknown): void;\r\n // Mirrors the Workers runtime's `any` return so a typed connection binding round-trips without a cast.\r\n deserializeAttachment(): any;\r\n}\r\n\r\n/** The slice of a Durable Object's `state` (its `ctx`) these helpers touch. */\r\nexport interface IDurableObjectContext {\r\n storage: TDurableObjectStorage;\r\n getWebSockets(): IDurableObjectWebSocket[];\r\n acceptWebSocket(ws: IDurableObjectWebSocket): void;\r\n /** Register a runtime-answered keepalive so pings never wake the DO. */\r\n setWebSocketAutoResponse(pair: IWebSocketRequestResponsePair): void;\r\n}\r\n\r\n/** An `IChannelServer` whose connections are a Durable Object's hibernatable WebSockets — the type to\r\n * store the result of `serveChannel(...)` in when serving over {@link durableObjectWsCarrier}. `TApp` is the\r\n * per-connection app-state type when `connectionState` is used (defaults to `unknown` otherwise). */\r\nexport type TDurableObjectChannelServer<TApp = unknown> = IChannelServer<\r\n IDurableObjectWebSocket,\r\n TApp\r\n>;\r\n\r\n// Workers runtime globals, declared module-locally (the runtime provides them at deploy time). Declaring\r\n// them here keeps them out of the package's global scope, so the DOM lib's `Response`/`WebSocket` stand\r\n// elsewhere. The `Response` constructor type still returns the DOM `Response` — only its init gains the\r\n// Workers-only `webSocket` field.\r\ndeclare const WebSocketPair: {\r\n new (): { 0: IDurableObjectWebSocket; 1: IDurableObjectWebSocket };\r\n};\r\ndeclare const Response: {\r\n new (\r\n body: BodyInit | null,\r\n init?: {\r\n status?: number;\r\n statusText?: string;\r\n headers?: HeadersInit;\r\n webSocket?: IDurableObjectWebSocket;\r\n },\r\n ): Response;\r\n};\r\n/** The keepalive pair shape — just the fields the Workers runtime's `WebSocketRequestResponsePair` exposes. */\r\ninterface IWebSocketRequestResponsePair {\r\n readonly request: string;\r\n readonly response: string;\r\n}\r\ndeclare const WebSocketRequestResponsePair: {\r\n new (request: string, response: string): IWebSocketRequestResponsePair;\r\n};\r\n\r\nexport interface IDurableObjectWsCarrierOptions {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint —\r\n * then `serveChannel` needs no `storage` for this carrier.\r\n */\r\n secure?: boolean;\r\n}\r\n\r\n/**\r\n * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the\r\n * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it\r\n * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:\r\n * ```ts\r\n * const ws = durableObjectWsCarrier(this.ctx);\r\n * const server = serveChannel(runtime, channel, {\r\n * clientEnv,\r\n * storage: durableObjectStorage(this.ctx, { keyPrefix: \"ws:\" }),\r\n * handlers: [localHandler],\r\n * carriers: [ws, httpAcceptorCarrier()],\r\n * });\r\n * // webSocketMessage(c, m) => ws.receive(c, m);\r\n * // webSocketClose/Error(c) => ws.drop(c);\r\n * ```\r\n *\r\n * The carrier exposes the DO's socket attachment to `serveChannel`, which persists the routing binding\r\n * there and replays it on wake — and, when `connectionState` is requested, co-stores per-connection app\r\n * state in the *same* attachment, so both survive eviction without the DO wiring any of it by hand.\r\n */\r\nexport function durableObjectWsCarrier(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectWsCarrierOptions = {},\r\n): IDuplexAcceptorCarrier<IDurableObjectWebSocket> {\r\n return wsAcceptorCarrier<IDurableObjectWebSocket>({\r\n secure: options.secure,\r\n send: (ws, frame) => ws.send(frame),\r\n upgrade: () => {\r\n const pair = new WebSocketPair();\r\n const client = pair[0];\r\n const server = pair[1];\r\n // Hibernatable WebSocket — the DO can sleep between messages.\r\n ctx.acceptWebSocket(server);\r\n return new Response(null, { status: 101, webSocket: client });\r\n },\r\n // Raw access to each socket's attachment; `serveChannel` owns the composite (binding + app) layout.\r\n attachmentStore: {\r\n getConnections: () => ctx.getWebSockets(),\r\n read: (ws) => ws.deserializeAttachment(),\r\n write: (ws, value) => ws.serializeAttachment(value),\r\n },\r\n });\r\n}\r\n\r\nexport interface IDurableObjectStorageOptions {\r\n /** Namespace prefix for every key (e.g. `\"demo-ws:\"`), so several adapters can share one DO storage. */\r\n keyPrefix?: string;\r\n}\r\n\r\n/**\r\n * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over\r\n * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.\r\n */\r\nexport function durableObjectStorage(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectStorageOptions = {},\r\n): StorageAdapter {\r\n return createDurableObjectStorageAdapter({\r\n durableObjectStorage: ctx.storage,\r\n keyPrefix: options.keyPrefix,\r\n });\r\n}\r\n\r\nexport interface ICloudflareDurableObjectHostOptions {\r\n /** Namespace prefix for the DO-storage crypto identity keys (e.g. `\"lobby-ws:\"`). */\r\n keyPrefix?: string;\r\n /**\r\n * The HTTP fallback that sits beside the WebSocket: `\"plain\"` (default — POSTs the raw action wire, the\r\n * usual fallback for a public client), `\"secure\"` (the full handshake-protected exchange, sharing the WS\r\n * identity), or `false` (WebSocket only).\r\n */\r\n httpFallback?: \"plain\" | \"secure\" | false;\r\n /** Whether the WebSocket runs the secure handshake (default `true`). `false` = a plain WS endpoint. */\r\n secure?: boolean;\r\n}\r\n\r\n/**\r\n * Build the {@link IChannelHostAdapter} for a Durable Object in one call — the entire repeated transport\r\n * stack a DO would otherwise assemble by hand: a hibernatable secure WebSocket carrier, an HTTP fallback,\r\n * the DO-storage-backed crypto identity, and a runtime-answered `ping`/`pong` keepalive (so pings never\r\n * wake the DO). Hand it to {@link serveHost}, or use {@link serveDurableObject} which composes both.\r\n */\r\nexport function cloudflareDurableObjectHost(\r\n ctx: IDurableObjectContext,\r\n options: ICloudflareDurableObjectHostOptions = {},\r\n): IChannelHostAdapter<IDurableObjectWebSocket> {\r\n const httpFallback = options.httpFallback ?? \"plain\";\r\n const carriers: TAcceptorCarrier<IDurableObjectWebSocket>[] = [\r\n durableObjectWsCarrier(ctx, { secure: options.secure }),\r\n ];\r\n if (httpFallback !== false) {\r\n carriers.push(httpAcceptorCarrier({ secure: httpFallback === \"secure\" }));\r\n }\r\n\r\n return {\r\n carriers,\r\n storage: durableObjectStorage(ctx, { keyPrefix: options.keyPrefix }),\r\n onServed: () => {\r\n // Keepalive answered by the runtime itself — pings never wake the DO.\r\n ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair(\"ping\", \"pong\"));\r\n },\r\n };\r\n}\r\n\r\n/** {@link serveDurableObject}'s options: the `serveHost` surface + the DO runtime + the host knobs. */\r\nexport type TServeDurableObjectOptions<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TApp = unknown,\r\n> = TServeHostOptions<TO_ACCEPTOR, IDurableObjectWebSocket, TApp> &\r\n ICloudflareDurableObjectHostOptions & {\r\n /** This DO's runtime (e.g. `new ActionRuntime(coord.withPersistentId(ctx.id.toString()))`). */\r\n runtime: ActionRuntime;\r\n };\r\n\r\n/**\r\n * Serve a secure channel from a Durable Object in one call — the whole transport stack\r\n * ({@link cloudflareDurableObjectHost}: hibernatable secure WebSocket + HTTP fallback + DO-storage crypto\r\n * identity + keepalive) folded in, leaving the DO to forward its four socket lifecycle methods to the\r\n * returned server's `fetch` / `receive` / `drop`:\r\n * ```ts\r\n * const server = serveDurableObject(this.ctx, lobbyChannel, {\r\n * runtime, clientEnv,\r\n * connectionState: { schema: vs_player }, // optional, survives hibernation\r\n * channelCases: { join: (action, conn) => { conn.setState(action.input); conn.broadcast(…); } },\r\n * });\r\n * // fetch(req) => server.fetch(req)\r\n * // webSocketMessage(ws, m) => server.receive(ws, m)\r\n * // webSocketClose/Error(ws)=> server.drop(ws)\r\n * ```\r\n * Passing `connectionState` narrows the return so `server.connections` is non-optional.\r\n */\r\nexport function serveDurableObject<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TApp,\r\n>(\r\n ctx: IDurableObjectContext,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: TServeDurableObjectOptions<TO_ACCEPTOR, TApp> & {\r\n connectionState: IServeConnectionStateOptions<TApp>;\r\n },\r\n): TDurableObjectChannelServer<TApp> & {\r\n connections: ConnectionStateStore<IDurableObjectWebSocket, TApp>;\r\n};\r\nexport function serveDurableObject<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TApp = unknown,\r\n>(\r\n ctx: IDurableObjectContext,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: TServeDurableObjectOptions<TO_ACCEPTOR, TApp>,\r\n): TDurableObjectChannelServer<TApp>;\r\nexport function serveDurableObject(\r\n ctx: IDurableObjectContext,\r\n channel: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\r\n options: TServeDurableObjectOptions<readonly ActionDomain<any>[], any>,\r\n): TDurableObjectChannelServer<any> {\r\n const { runtime, keyPrefix, httpFallback, secure, ...serveOptions } = options;\r\n const host = cloudflareDurableObjectHost(ctx, { keyPrefix, httpFallback, secure });\r\n return serveHost(runtime, channel, host, serveOptions);\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuHA,SAAgB,uBACd,KACA,UAA0C,CAAC,GACM;CACjD,OAAOA,4BAAAA,kBAA2C;EAChD,QAAQ,QAAQ;EAChB,OAAO,IAAI,UAAU,GAAG,KAAK,KAAK;EAClC,eAAe;GACb,MAAM,OAAO,IAAI,cAAc;GAC/B,MAAM,SAAS,KAAK;GACpB,MAAM,SAAS,KAAK;GAEpB,IAAI,gBAAgB,MAAM;GAC1B,OAAO,IAAI,SAAS,MAAM;IAAE,QAAQ;IAAK,WAAW;GAAO,CAAC;EAC9D;EAEA,iBAAiB;GACf,sBAAsB,IAAI,cAAc;GACxC,OAAO,OAAO,GAAG,sBAAsB;GACvC,QAAQ,IAAI,UAAU,GAAG,oBAAoB,KAAK;EACpD;CACF,CAAC;AACH;;;;;AAWA,SAAgB,qBACd,KACA,UAAwC,CAAC,GACzB;CAChB,QAAA,GAAA,gBAAA,kCAAA,CAAyC;EACvC,sBAAsB,IAAI;EAC1B,WAAW,QAAQ;CACrB,CAAC;AACH;;;;;;;AAqBA,SAAgB,4BACd,KACA,UAA+C,CAAC,GACF;CAC9C,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,WAAwD,CAC5D,uBAAuB,KAAK,EAAE,QAAQ,QAAQ,OAAO,CAAC,CACxD;CACA,IAAI,iBAAiB,OACnB,SAAS,KAAKC,4BAAAA,oBAAoB,EAAE,QAAQ,iBAAiB,SAAS,CAAC,CAAC;CAG1E,OAAO;EACL;EACA,SAAS,qBAAqB,KAAK,EAAE,WAAW,QAAQ,UAAU,CAAC;EACnE,gBAAgB;GAEd,IAAI,yBAAyB,IAAI,6BAA6B,QAAQ,MAAM,CAAC;EAC/E;CACF;AACF;AAmDA,SAAgB,mBACd,KACA,SACA,SACkC;CAClC,MAAM,EAAE,SAAS,WAAW,cAAc,QAAQ,GAAG,iBAAiB;CAEtE,OAAOC,4BAAAA,UAAU,SAAS,SADb,4BAA4B,KAAK;EAAE;EAAW;EAAc;CAAO,CAC1C,GAAG,YAAY;AACvD"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["wsAcceptorCarrier","httpAcceptorCarrier","serveHost"],"sources":["../../../src/platform/cloudflare/index.ts"],"sourcesContent":["import {\r\n createDurableObjectStorageAdapter,\r\n type StorageAdapter,\r\n type TCreateDurableObjectStorageOptions,\r\n} from \"@nice-code/util\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { ActionRuntime } from \"../../ActionRuntime/ActionRuntime\";\r\nimport type { IActionChannel } from \"../../ActionRuntime/Channel/ActionChannel\";\r\nimport type {\r\n IChannelServer,\r\n IServeConnectionStateOptions,\r\n} from \"../../ActionRuntime/Channel/serveChannel\";\r\nimport {\r\n type IChannelHostAdapter,\r\n serveHost,\r\n type TServeHostOptions,\r\n} from \"../../ActionRuntime/Channel/serveHost\";\r\nimport type { ConnectionStateStore } from \"../../ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\r\nimport type {\r\n IDuplexAcceptorCarrier,\r\n TAcceptorCarrier,\r\n} from \"../../ActionRuntime/Transport/Carrier/AcceptorCarrier.types\";\r\nimport { wsAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier\";\r\nimport { httpAcceptorCarrier } from \"../../ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier\";\r\n\r\n/**\r\n * Cloudflare-specific helpers for `@nice-code/action`, imported from `@nice-code/action/platform/cloudflare`.\r\n * They collapse the Durable Object boilerplate (the `WebSocketPair` upgrade, hibernation attachment wiring,\r\n * and DO-storage adapter) into one-liners you hand to `serveChannel`. The core library stays\r\n * platform-agnostic — nothing here is reachable from the main entry.\r\n *\r\n * The Workers runtime surface this module needs is declared *structurally* (and the two globals it\r\n * constructs are declared module-locally below) rather than pulled from `@cloudflare/workers-types`, so the\r\n * library's own DOM-lib build never clashes with that package's global `Response`/`WebSocket` redefinitions.\r\n * A real `DurableObjectState` and its hibernatable `WebSocket`s satisfy these shapes.\r\n */\r\n\r\ntype TDurableObjectStorage = TCreateDurableObjectStorageOptions[\"durableObjectStorage\"];\r\n\r\n/** The slice of a Durable Object's hibernatable WebSocket these helpers touch. */\r\nexport interface IDurableObjectWebSocket {\r\n send(data: string | ArrayBuffer | Uint8Array): void;\r\n serializeAttachment(value: unknown): void;\r\n // Mirrors the Workers runtime's `any` return so a typed connection binding round-trips without a cast.\r\n deserializeAttachment(): any;\r\n}\r\n\r\n/** The slice of a Durable Object's `state` (its `ctx`) these helpers touch. */\r\nexport interface IDurableObjectContext {\r\n storage: TDurableObjectStorage;\r\n getWebSockets(): IDurableObjectWebSocket[];\r\n acceptWebSocket(ws: IDurableObjectWebSocket): void;\r\n /** Register a runtime-answered keepalive so pings never wake the DO. */\r\n setWebSocketAutoResponse(pair: IWebSocketRequestResponsePair): void;\r\n}\r\n\r\n/** An `IChannelServer` whose connections are a Durable Object's hibernatable WebSockets — the type to\r\n * store the result of `serveChannel(...)` in when serving over {@link durableObjectWsCarrier}. `TApp` is the\r\n * per-connection app-state type when `connectionState` is used (defaults to `unknown` otherwise). */\r\nexport type TDurableObjectChannelServer<TApp = unknown> = IChannelServer<\r\n IDurableObjectWebSocket,\r\n TApp\r\n>;\r\n\r\n// Workers runtime globals, declared module-locally (the runtime provides them at deploy time). Declaring\r\n// them here keeps them out of the package's global scope, so the DOM lib's `Response`/`WebSocket` stand\r\n// elsewhere. The `Response` constructor type still returns the DOM `Response` — only its init gains the\r\n// Workers-only `webSocket` field.\r\ndeclare const WebSocketPair: {\r\n new (): { 0: IDurableObjectWebSocket; 1: IDurableObjectWebSocket };\r\n};\r\ndeclare const Response: {\r\n new (\r\n body: BodyInit | null,\r\n init?: {\r\n status?: number;\r\n statusText?: string;\r\n headers?: HeadersInit;\r\n webSocket?: IDurableObjectWebSocket;\r\n },\r\n ): Response;\r\n};\r\n/** The keepalive pair shape — just the fields the Workers runtime's `WebSocketRequestResponsePair` exposes. */\r\ninterface IWebSocketRequestResponsePair {\r\n readonly request: string;\r\n readonly response: string;\r\n}\r\ndeclare const WebSocketRequestResponsePair: {\r\n new (request: string, response: string): IWebSocketRequestResponsePair;\r\n};\r\n\r\nexport interface IDurableObjectWsCarrierOptions {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint —\r\n * then `serveChannel` needs no `storage` for this carrier.\r\n */\r\n secure?: boolean;\r\n}\r\n\r\n/**\r\n * Build a hibernatable-WebSocket acceptor carrier for a Durable Object in one call — the `send`, the\r\n * `WebSocketPair` upgrade, and the hibernation attachment hooks all derived from the DO's `ctx`. Hand it\r\n * straight to `serveChannel`'s `carriers`, and forward the DO's socket events to the returned handle:\r\n * ```ts\r\n * const ws = durableObjectWsCarrier(this.ctx);\r\n * const server = serveChannel(runtime, channel, {\r\n * clientEnv,\r\n * storage: durableObjectStorage(this.ctx, { keyPrefix: \"ws:\" }),\r\n * handlers: [localHandler],\r\n * carriers: [ws, httpAcceptorCarrier()],\r\n * });\r\n * // webSocketMessage(c, m) => ws.receive(c, m);\r\n * // webSocketClose/Error(c) => ws.drop(c);\r\n * ```\r\n *\r\n * The carrier exposes the DO's socket attachment to `serveChannel`, which persists the routing binding\r\n * there and replays it on wake — and, when `connectionState` is requested, co-stores per-connection app\r\n * state in the *same* attachment, so both survive eviction without the DO wiring any of it by hand.\r\n */\r\nexport function durableObjectWsCarrier(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectWsCarrierOptions = {},\r\n): IDuplexAcceptorCarrier<IDurableObjectWebSocket> {\r\n return wsAcceptorCarrier<IDurableObjectWebSocket>({\r\n secure: options.secure,\r\n send: (ws, frame) => ws.send(frame),\r\n upgrade: () => {\r\n const pair = new WebSocketPair();\r\n const client = pair[0];\r\n const server = pair[1];\r\n // Hibernatable WebSocket — the DO can sleep between messages.\r\n ctx.acceptWebSocket(server);\r\n return new Response(null, { status: 101, webSocket: client });\r\n },\r\n // Raw access to each socket's attachment; `serveChannel` owns the composite (binding + app) layout.\r\n attachmentStore: {\r\n getConnections: () => ctx.getWebSockets(),\r\n read: (ws) => ws.deserializeAttachment(),\r\n write: (ws, value) => ws.serializeAttachment(value),\r\n },\r\n });\r\n}\r\n\r\nexport interface IDurableObjectStorageOptions {\r\n /** Namespace prefix for every key (e.g. `\"demo-ws:\"`), so several adapters can share one DO storage. */\r\n keyPrefix?: string;\r\n}\r\n\r\n/**\r\n * Wrap a Durable Object's storage as a {@link StorageAdapter} for `serveChannel`'s `storage` — sugar over\r\n * `createDurableObjectStorageAdapter({ durableObjectStorage: ctx.storage, … })` so a DO needs one import.\r\n */\r\nexport function durableObjectStorage(\r\n ctx: IDurableObjectContext,\r\n options: IDurableObjectStorageOptions = {},\r\n): StorageAdapter {\r\n return createDurableObjectStorageAdapter({\r\n durableObjectStorage: ctx.storage,\r\n keyPrefix: options.keyPrefix,\r\n });\r\n}\r\n\r\nexport interface ICloudflareDurableObjectHostOptions {\r\n /** Namespace prefix for the DO-storage crypto identity keys (e.g. `\"lobby-ws:\"`). */\r\n keyPrefix?: string;\r\n /**\r\n * The HTTP fallback that sits beside the WebSocket: `\"plain\"` (default — POSTs the raw action wire, the\r\n * usual fallback for a public client), `\"secure\"` (the full handshake-protected exchange, sharing the WS\r\n * identity), or `false` (WebSocket only).\r\n */\r\n httpFallback?: \"plain\" | \"secure\" | false;\r\n /** Whether the WebSocket runs the secure handshake (default `true`). `false` = a plain WS endpoint. */\r\n secure?: boolean;\r\n}\r\n\r\n/**\r\n * Build the {@link IChannelHostAdapter} for a Durable Object in one call — the entire repeated transport\r\n * stack a DO would otherwise assemble by hand: a hibernatable secure WebSocket carrier, an HTTP fallback,\r\n * the DO-storage-backed crypto identity, and a runtime-answered `ping`/`pong` keepalive (so pings never\r\n * wake the DO). Hand it to {@link serveHost}, or use {@link serveDurableObject} which composes both.\r\n */\r\nexport function cloudflareDurableObjectHost(\r\n ctx: IDurableObjectContext,\r\n options: ICloudflareDurableObjectHostOptions = {},\r\n): IChannelHostAdapter<IDurableObjectWebSocket> {\r\n const httpFallback = options.httpFallback ?? \"plain\";\r\n const carriers: TAcceptorCarrier<IDurableObjectWebSocket>[] = [\r\n durableObjectWsCarrier(ctx, { secure: options.secure }),\r\n ];\r\n if (httpFallback !== false) {\r\n carriers.push(httpAcceptorCarrier({ secure: httpFallback === \"secure\" }));\r\n }\r\n\r\n return {\r\n carriers,\r\n storage: durableObjectStorage(ctx, { keyPrefix: options.keyPrefix }),\r\n onServed: () => {\r\n // Keepalive answered by the runtime itself — pings never wake the DO.\r\n ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair(\"ping\", \"pong\"));\r\n },\r\n };\r\n}\r\n\r\n/** {@link serveDurableObject}'s options: the `serveHost` surface + the DO runtime + the host knobs. */\r\nexport type TServeDurableObjectOptions<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TApp = unknown,\r\n> = TServeHostOptions<TO_ACCEPTOR, IDurableObjectWebSocket, TApp> &\r\n ICloudflareDurableObjectHostOptions & {\r\n /** This DO's runtime (e.g. `new ActionRuntime(coord.withPersistentId(ctx.id.toString()))`). */\r\n runtime: ActionRuntime;\r\n };\r\n\r\n/**\r\n * Serve a secure channel from a Durable Object in one call — the whole transport stack\r\n * ({@link cloudflareDurableObjectHost}: hibernatable secure WebSocket + HTTP fallback + DO-storage crypto\r\n * identity + keepalive) folded in, leaving the DO to forward its four socket lifecycle methods to the\r\n * returned server's `fetch` / `receive` / `drop`:\r\n * ```ts\r\n * const server = serveDurableObject(this.ctx, lobbyChannel, {\r\n * runtime, clientEnv,\r\n * connectionState: { schema: vs_player }, // optional, survives hibernation\r\n * channelCases: { join: (action, conn) => { conn.setState(action.input); conn.broadcast(…); } },\r\n * });\r\n * // fetch(req) => server.fetch(req)\r\n * // webSocketMessage(ws, m) => server.receive(ws, m)\r\n * // webSocketClose/Error(ws)=> server.drop(ws)\r\n * ```\r\n * Passing `connectionState` narrows the return so `server.connections` is non-optional.\r\n */\r\nexport function serveDurableObject<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TApp,\r\n>(\r\n ctx: IDurableObjectContext,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: TServeDurableObjectOptions<TO_ACCEPTOR, TApp> & {\r\n connectionState: IServeConnectionStateOptions<TApp>;\r\n },\r\n): TDurableObjectChannelServer<TApp> & {\r\n connections: ConnectionStateStore<IDurableObjectWebSocket, TApp>;\r\n};\r\nexport function serveDurableObject<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TApp = unknown,\r\n>(\r\n ctx: IDurableObjectContext,\r\n channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: TServeDurableObjectOptions<TO_ACCEPTOR, TApp>,\r\n): TDurableObjectChannelServer<TApp>;\r\nexport function serveDurableObject(\r\n ctx: IDurableObjectContext,\r\n channel: IActionChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\r\n options: TServeDurableObjectOptions<readonly ActionDomain<any>[], any>,\r\n): TDurableObjectChannelServer<any> {\r\n const { runtime, keyPrefix, httpFallback, secure, ...serveOptions } = options;\r\n const host = cloudflareDurableObjectHost(ctx, { keyPrefix, httpFallback, secure });\r\n return serveHost(runtime, channel, host, serveOptions);\r\n}\r\n\r\n/** The slice of a Durable Object stub {@link forwardExchangeToDurableObject} calls — `env.NS.get(id)`. */\r\nexport interface IDurableObjectStub {\r\n fetch(request: Request): Promise<Response>;\r\n}\r\n\r\nexport interface IForwardExchangeToDurableObjectOptions {\r\n /**\r\n * CORS headers for the edge-answered `OPTIONS` preflight (defaults to the permissive `*` set; `false`\r\n * attaches none). Only the preflight is answered here — every other response comes straight from the DO\r\n * (whose `serveDurableObject` applies its own CORS), so the two should agree.\r\n */\r\n cors?: Record<string, string> | false;\r\n}\r\n\r\n/** Permissive defaults, matching `serveDurableObject`'s HTTP fallback. */\r\nconst DEFAULT_FORWARD_CORS: Record<string, string> = {\r\n \"Access-Control-Allow-Origin\": \"*\",\r\n \"Access-Control-Allow-Methods\": \"GET, POST, OPTIONS\",\r\n \"Access-Control-Allow-Headers\": \"Content-Type\",\r\n \"Access-Control-Max-Age\": \"86400\",\r\n};\r\n\r\n/**\r\n * Fan a Worker's incoming action request out to a *per-id* Durable Object that serves the exchange itself.\r\n *\r\n * A secure exchange body is opaque to the Worker (handshake / encrypted frames), so the DO it belongs to\r\n * has to be chosen from the **URL**, not the body. This picks the stub with your `pickStub` and forwards\r\n * the request to its `fetch` (where `serveDurableObject` serves the exchange), while answering the CORS\r\n * `OPTIONS` preflight *at the edge* so a per-id DO is never woken (or billed) just to reply to a preflight:\r\n * ```ts\r\n * // wrangler: a Durable Object namespace `BRIDGE`, each instance one bridge serving `bridgeChannel`.\r\n * export default {\r\n * fetch(request: Request, env: Env) {\r\n * return forwardExchangeToDurableObject(request, (req, url) => {\r\n * const bridgeId = url.pathname.split(\"/\")[2]; // e.g. /bridge/:id/action\r\n * return env.BRIDGE.get(env.BRIDGE.idFromName(bridgeId));\r\n * });\r\n * },\r\n * };\r\n *\r\n * // …and in the Durable Object, serve the secure exchange (+ a WS upgrade) as usual:\r\n * // fetch(request) { return this.server.fetch(request); }\r\n * // where this.server = serveDurableObject(this.ctx, bridgeChannel, { runtime, httpFallback: \"secure\" });\r\n * ```\r\n * The matching connector points its `httpCarrier` at `/bridge/:id/action`. `pickStub` may be async (e.g.\r\n * to look an id up first); it receives the parsed {@link URL} alongside the request.\r\n */\r\nexport function forwardExchangeToDurableObject(\r\n request: Request,\r\n pickStub: (request: Request, url: URL) => IDurableObjectStub | Promise<IDurableObjectStub>,\r\n options: IForwardExchangeToDurableObjectOptions = {},\r\n): Promise<Response> {\r\n if (request.method === \"OPTIONS\") {\r\n const headers = options.cors === false ? undefined : (options.cors ?? DEFAULT_FORWARD_CORS);\r\n return Promise.resolve(new Response(null, { status: 204, headers }));\r\n }\r\n const url = new URL(request.url);\r\n return Promise.resolve(pickStub(request, url)).then((stub) => stub.fetch(request));\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuHA,SAAgB,uBACd,KACA,UAA0C,CAAC,GACM;CACjD,OAAOA,4BAAAA,kBAA2C;EAChD,QAAQ,QAAQ;EAChB,OAAO,IAAI,UAAU,GAAG,KAAK,KAAK;EAClC,eAAe;GACb,MAAM,OAAO,IAAI,cAAc;GAC/B,MAAM,SAAS,KAAK;GACpB,MAAM,SAAS,KAAK;GAEpB,IAAI,gBAAgB,MAAM;GAC1B,OAAO,IAAI,SAAS,MAAM;IAAE,QAAQ;IAAK,WAAW;GAAO,CAAC;EAC9D;EAEA,iBAAiB;GACf,sBAAsB,IAAI,cAAc;GACxC,OAAO,OAAO,GAAG,sBAAsB;GACvC,QAAQ,IAAI,UAAU,GAAG,oBAAoB,KAAK;EACpD;CACF,CAAC;AACH;;;;;AAWA,SAAgB,qBACd,KACA,UAAwC,CAAC,GACzB;CAChB,QAAA,GAAA,gBAAA,kCAAA,CAAyC;EACvC,sBAAsB,IAAI;EAC1B,WAAW,QAAQ;CACrB,CAAC;AACH;;;;;;;AAqBA,SAAgB,4BACd,KACA,UAA+C,CAAC,GACF;CAC9C,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,WAAwD,CAC5D,uBAAuB,KAAK,EAAE,QAAQ,QAAQ,OAAO,CAAC,CACxD;CACA,IAAI,iBAAiB,OACnB,SAAS,KAAKC,4BAAAA,oBAAoB,EAAE,QAAQ,iBAAiB,SAAS,CAAC,CAAC;CAG1E,OAAO;EACL;EACA,SAAS,qBAAqB,KAAK,EAAE,WAAW,QAAQ,UAAU,CAAC;EACnE,gBAAgB;GAEd,IAAI,yBAAyB,IAAI,6BAA6B,QAAQ,MAAM,CAAC;EAC/E;CACF;AACF;AAmDA,SAAgB,mBACd,KACA,SACA,SACkC;CAClC,MAAM,EAAE,SAAS,WAAW,cAAc,QAAQ,GAAG,iBAAiB;CAEtE,OAAOC,4BAAAA,UAAU,SAAS,SADb,4BAA4B,KAAK;EAAE;EAAW;EAAc;CAAO,CAC1C,GAAG,YAAY;AACvD;;AAiBA,MAAM,uBAA+C;CACnD,+BAA+B;CAC/B,gCAAgC;CAChC,gCAAgC;CAChC,0BAA0B;AAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,+BACd,SACA,UACA,UAAkD,CAAC,GAChC;CACnB,IAAI,QAAQ,WAAW,WAAW;EAChC,MAAM,UAAU,QAAQ,SAAS,QAAQ,KAAA,IAAa,QAAQ,QAAQ;EACtE,OAAO,QAAQ,QAAQ,IAAI,SAAS,MAAM;GAAE,QAAQ;GAAK;EAAQ,CAAC,CAAC;CACrE;CACA,MAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;CAC/B,OAAO,QAAQ,QAAQ,SAAS,SAAS,GAAG,CAAC,CAAC,CAAC,MAAM,SAAS,KAAK,MAAM,OAAO,CAAC;AACnF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Lt as IActionChannel, Mt as ConnectionStateStore, St as IServeConnectionStateOptions, Tt as IDuplexAcceptorCarrier, _t as TServeHostOptions, gt as IChannelHostAdapter, l as ActionDomain, u as ActionRuntime, yt as IChannelServer } from "../../AcceptorHandler-Du292dpC.cjs";
|
|
2
2
|
import { StorageAdapter, TCreateDurableObjectStorageOptions } from "@nice-code/util";
|
|
3
3
|
|
|
4
4
|
//#region src/platform/cloudflare/index.d.ts
|
|
@@ -120,6 +120,44 @@ declare function serveDurableObject<TO_ACCEPTOR extends readonly ActionDomain<an
|
|
|
120
120
|
connections: ConnectionStateStore<IDurableObjectWebSocket, TApp>;
|
|
121
121
|
};
|
|
122
122
|
declare function serveDurableObject<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TApp = unknown>(ctx: IDurableObjectContext, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: TServeDurableObjectOptions<TO_ACCEPTOR, TApp>): TDurableObjectChannelServer<TApp>;
|
|
123
|
+
/** The slice of a Durable Object stub {@link forwardExchangeToDurableObject} calls — `env.NS.get(id)`. */
|
|
124
|
+
interface IDurableObjectStub {
|
|
125
|
+
fetch(request: Request): Promise<Response>;
|
|
126
|
+
}
|
|
127
|
+
interface IForwardExchangeToDurableObjectOptions {
|
|
128
|
+
/**
|
|
129
|
+
* CORS headers for the edge-answered `OPTIONS` preflight (defaults to the permissive `*` set; `false`
|
|
130
|
+
* attaches none). Only the preflight is answered here — every other response comes straight from the DO
|
|
131
|
+
* (whose `serveDurableObject` applies its own CORS), so the two should agree.
|
|
132
|
+
*/
|
|
133
|
+
cors?: Record<string, string> | false;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Fan a Worker's incoming action request out to a *per-id* Durable Object that serves the exchange itself.
|
|
137
|
+
*
|
|
138
|
+
* A secure exchange body is opaque to the Worker (handshake / encrypted frames), so the DO it belongs to
|
|
139
|
+
* has to be chosen from the **URL**, not the body. This picks the stub with your `pickStub` and forwards
|
|
140
|
+
* the request to its `fetch` (where `serveDurableObject` serves the exchange), while answering the CORS
|
|
141
|
+
* `OPTIONS` preflight *at the edge* so a per-id DO is never woken (or billed) just to reply to a preflight:
|
|
142
|
+
* ```ts
|
|
143
|
+
* // wrangler: a Durable Object namespace `BRIDGE`, each instance one bridge serving `bridgeChannel`.
|
|
144
|
+
* export default {
|
|
145
|
+
* fetch(request: Request, env: Env) {
|
|
146
|
+
* return forwardExchangeToDurableObject(request, (req, url) => {
|
|
147
|
+
* const bridgeId = url.pathname.split("/")[2]; // e.g. /bridge/:id/action
|
|
148
|
+
* return env.BRIDGE.get(env.BRIDGE.idFromName(bridgeId));
|
|
149
|
+
* });
|
|
150
|
+
* },
|
|
151
|
+
* };
|
|
152
|
+
*
|
|
153
|
+
* // …and in the Durable Object, serve the secure exchange (+ a WS upgrade) as usual:
|
|
154
|
+
* // fetch(request) { return this.server.fetch(request); }
|
|
155
|
+
* // where this.server = serveDurableObject(this.ctx, bridgeChannel, { runtime, httpFallback: "secure" });
|
|
156
|
+
* ```
|
|
157
|
+
* The matching connector points its `httpCarrier` at `/bridge/:id/action`. `pickStub` may be async (e.g.
|
|
158
|
+
* to look an id up first); it receives the parsed {@link URL} alongside the request.
|
|
159
|
+
*/
|
|
160
|
+
declare function forwardExchangeToDurableObject(request: Request, pickStub: (request: Request, url: URL) => IDurableObjectStub | Promise<IDurableObjectStub>, options?: IForwardExchangeToDurableObjectOptions): Promise<Response>;
|
|
123
161
|
//#endregion
|
|
124
|
-
export { ICloudflareDurableObjectHostOptions, IDurableObjectContext, IDurableObjectStorageOptions, IDurableObjectWebSocket, IDurableObjectWsCarrierOptions, TDurableObjectChannelServer, TServeDurableObjectOptions, cloudflareDurableObjectHost, durableObjectStorage, durableObjectWsCarrier, serveDurableObject };
|
|
162
|
+
export { ICloudflareDurableObjectHostOptions, IDurableObjectContext, IDurableObjectStorageOptions, IDurableObjectStub, IDurableObjectWebSocket, IDurableObjectWsCarrierOptions, IForwardExchangeToDurableObjectOptions, TDurableObjectChannelServer, TServeDurableObjectOptions, cloudflareDurableObjectHost, durableObjectStorage, durableObjectWsCarrier, forwardExchangeToDurableObject, serveDurableObject };
|
|
125
163
|
//# sourceMappingURL=index.d.cts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Lt as IActionChannel, Mt as ConnectionStateStore, St as IServeConnectionStateOptions, Tt as IDuplexAcceptorCarrier, _t as TServeHostOptions, gt as IChannelHostAdapter, l as ActionDomain, u as ActionRuntime, yt as IChannelServer } from "../../AcceptorHandler-CLbwu2Pa.mjs";
|
|
2
2
|
import { StorageAdapter, TCreateDurableObjectStorageOptions } from "@nice-code/util";
|
|
3
3
|
|
|
4
4
|
//#region src/platform/cloudflare/index.d.ts
|
|
@@ -120,6 +120,44 @@ declare function serveDurableObject<TO_ACCEPTOR extends readonly ActionDomain<an
|
|
|
120
120
|
connections: ConnectionStateStore<IDurableObjectWebSocket, TApp>;
|
|
121
121
|
};
|
|
122
122
|
declare function serveDurableObject<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TApp = unknown>(ctx: IDurableObjectContext, channel: IActionChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: TServeDurableObjectOptions<TO_ACCEPTOR, TApp>): TDurableObjectChannelServer<TApp>;
|
|
123
|
+
/** The slice of a Durable Object stub {@link forwardExchangeToDurableObject} calls — `env.NS.get(id)`. */
|
|
124
|
+
interface IDurableObjectStub {
|
|
125
|
+
fetch(request: Request): Promise<Response>;
|
|
126
|
+
}
|
|
127
|
+
interface IForwardExchangeToDurableObjectOptions {
|
|
128
|
+
/**
|
|
129
|
+
* CORS headers for the edge-answered `OPTIONS` preflight (defaults to the permissive `*` set; `false`
|
|
130
|
+
* attaches none). Only the preflight is answered here — every other response comes straight from the DO
|
|
131
|
+
* (whose `serveDurableObject` applies its own CORS), so the two should agree.
|
|
132
|
+
*/
|
|
133
|
+
cors?: Record<string, string> | false;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Fan a Worker's incoming action request out to a *per-id* Durable Object that serves the exchange itself.
|
|
137
|
+
*
|
|
138
|
+
* A secure exchange body is opaque to the Worker (handshake / encrypted frames), so the DO it belongs to
|
|
139
|
+
* has to be chosen from the **URL**, not the body. This picks the stub with your `pickStub` and forwards
|
|
140
|
+
* the request to its `fetch` (where `serveDurableObject` serves the exchange), while answering the CORS
|
|
141
|
+
* `OPTIONS` preflight *at the edge* so a per-id DO is never woken (or billed) just to reply to a preflight:
|
|
142
|
+
* ```ts
|
|
143
|
+
* // wrangler: a Durable Object namespace `BRIDGE`, each instance one bridge serving `bridgeChannel`.
|
|
144
|
+
* export default {
|
|
145
|
+
* fetch(request: Request, env: Env) {
|
|
146
|
+
* return forwardExchangeToDurableObject(request, (req, url) => {
|
|
147
|
+
* const bridgeId = url.pathname.split("/")[2]; // e.g. /bridge/:id/action
|
|
148
|
+
* return env.BRIDGE.get(env.BRIDGE.idFromName(bridgeId));
|
|
149
|
+
* });
|
|
150
|
+
* },
|
|
151
|
+
* };
|
|
152
|
+
*
|
|
153
|
+
* // …and in the Durable Object, serve the secure exchange (+ a WS upgrade) as usual:
|
|
154
|
+
* // fetch(request) { return this.server.fetch(request); }
|
|
155
|
+
* // where this.server = serveDurableObject(this.ctx, bridgeChannel, { runtime, httpFallback: "secure" });
|
|
156
|
+
* ```
|
|
157
|
+
* The matching connector points its `httpCarrier` at `/bridge/:id/action`. `pickStub` may be async (e.g.
|
|
158
|
+
* to look an id up first); it receives the parsed {@link URL} alongside the request.
|
|
159
|
+
*/
|
|
160
|
+
declare function forwardExchangeToDurableObject(request: Request, pickStub: (request: Request, url: URL) => IDurableObjectStub | Promise<IDurableObjectStub>, options?: IForwardExchangeToDurableObjectOptions): Promise<Response>;
|
|
123
161
|
//#endregion
|
|
124
|
-
export { ICloudflareDurableObjectHostOptions, IDurableObjectContext, IDurableObjectStorageOptions, IDurableObjectWebSocket, IDurableObjectWsCarrierOptions, TDurableObjectChannelServer, TServeDurableObjectOptions, cloudflareDurableObjectHost, durableObjectStorage, durableObjectWsCarrier, serveDurableObject };
|
|
162
|
+
export { ICloudflareDurableObjectHostOptions, IDurableObjectContext, IDurableObjectStorageOptions, IDurableObjectStub, IDurableObjectWebSocket, IDurableObjectWsCarrierOptions, IForwardExchangeToDurableObjectOptions, TDurableObjectChannelServer, TServeDurableObjectOptions, cloudflareDurableObjectHost, durableObjectStorage, durableObjectWsCarrier, forwardExchangeToDurableObject, serveDurableObject };
|
|
125
163
|
//# sourceMappingURL=index.d.mts.map
|