@nice-code/action 0.22.0 → 0.24.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.
Files changed (40) hide show
  1. package/README.md +22 -25
  2. package/build/{ActionPayload.types-Dx1JPyfs.d.mts → AcceptorHandler-11-QMdx2.d.mts} +977 -1351
  3. package/build/{ActionPayload.types-L9k0LyBd.d.cts → AcceptorHandler-CxD0c1BE.d.cts} +977 -1351
  4. package/build/{ActionDevtoolsCore-CQ0vrvPD.d.cts → ActionDevtoolsCore-37JP4bOG.d.cts} +2 -2
  5. package/build/{ActionDevtoolsCore-CiLBYC3K.d.mts → ActionDevtoolsCore-Cgq-go1R.d.mts} +2 -2
  6. package/build/advanced/index.cjs +115 -0
  7. package/build/advanced/index.cjs.map +1 -0
  8. package/build/advanced/index.d.cts +344 -0
  9. package/build/advanced/index.d.mts +344 -0
  10. package/build/advanced/index.mjs +88 -0
  11. package/build/advanced/index.mjs.map +1 -0
  12. package/build/{httpAcceptorCarrier-OnJxzsAD.cjs → createHibernatableWsServerAdapter-BNi4k9j3.cjs} +258 -470
  13. package/build/createHibernatableWsServerAdapter-BNi4k9j3.cjs.map +1 -0
  14. package/build/{httpAcceptorCarrier-DL8lf0xB.mjs → createHibernatableWsServerAdapter-C07RfUTH.mjs} +233 -421
  15. package/build/createHibernatableWsServerAdapter-C07RfUTH.mjs.map +1 -0
  16. package/build/devtools/browser/index.d.cts +1 -1
  17. package/build/devtools/browser/index.d.mts +1 -1
  18. package/build/devtools/server/index.d.cts +1 -1
  19. package/build/devtools/server/index.d.mts +1 -1
  20. package/build/httpAcceptorCarrier-C3S_bDkL.cjs +454 -0
  21. package/build/httpAcceptorCarrier-C3S_bDkL.cjs.map +1 -0
  22. package/build/httpAcceptorCarrier-DPBEuewS.mjs +401 -0
  23. package/build/httpAcceptorCarrier-DPBEuewS.mjs.map +1 -0
  24. package/build/index.cjs +84 -458
  25. package/build/index.cjs.map +1 -1
  26. package/build/index.d.cts +2 -2
  27. package/build/index.d.mts +2 -2
  28. package/build/index.mjs +28 -374
  29. package/build/index.mjs.map +1 -1
  30. package/build/platform/cloudflare/index.cjs +1 -1
  31. package/build/platform/cloudflare/index.cjs.map +1 -1
  32. package/build/platform/cloudflare/index.d.cts +3 -3
  33. package/build/platform/cloudflare/index.d.mts +3 -3
  34. package/build/platform/cloudflare/index.mjs +1 -1
  35. package/build/platform/cloudflare/index.mjs.map +1 -1
  36. package/build/react-query/index.d.cts +1 -1
  37. package/build/react-query/index.d.mts +1 -1
  38. package/package.json +15 -4
  39. package/build/httpAcceptorCarrier-DL8lf0xB.mjs.map +0 -1
  40. package/build/httpAcceptorCarrier-OnJxzsAD.cjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"httpAcceptorCarrier-OnJxzsAD.cjs","names":["err_nice","runtime","NiceError","v","DEFAULT_SERVER_SECURITY_LEVELS","ClientCryptoKeyLink","textEncoder","textDecoder","ClientCryptoKeyLink","ClientCryptoKeyLink","ClientCryptoKeyLink"],"sources":["../src/nice_action.static.ts","../src/ActionRuntime/utils/runtimeCoordinateToStringIds.ts","../src/ActionRuntime/RuntimeCoordinate.ts","../src/ActionDefinition/Action/ActionBase.ts","../src/ActionDefinition/Action/Payload/ActionPayload.ts","../src/utils/hashPayloadData.ts","../src/ActionDefinition/Action/Payload/ActionPayload.types.ts","../src/ActionDefinition/Action/Payload/ActionPayload_Progress.ts","../src/ActionDefinition/Action/Payload/ActionPayload_Result.ts","../src/ActionDefinition/Action/Payload/ActionPayload_Request.ts","../src/ActionDefinition/Action/RunningAction.ts","../src/errors/err_nice_action.ts","../src/utils/isAction_Base_JsonObject.ts","../src/utils/isActionPayload_Result_JsonObject.ts","../src/ActionDefinition/Schema/ActionSchema.ts","../src/utils/getAssumedRuntimeEnvironment.ts","../src/utils/isActionPayload_Progress_JsonObject.ts","../src/utils/isActionPayload_Request_JsonObject.ts","../src/utils/isActionPayload_Any_JsonObject.ts","../src/ActionRuntime/HandlerCallStack.ts","../src/ActionRuntime/Handler/PeerLink/Connector/err_nice_external_client.ts","../src/ActionRuntime/Transport/err_nice_transport.ts","../src/ActionRuntime/Transport/Transport.types.ts","../src/ActionRuntime/Transport/ConnectionTransportManager.ts","../src/ActionRuntime/ActionDomainManager.ts","../src/ActionRuntime/Routing/ActionRouter.ts","../src/ActionRuntime/Handler/ActionHandler.ts","../src/ActionRuntime/Handler/PeerLink/PeerLinkHandler.ts","../src/ActionRuntime/Handler/PeerLink/Connector/ConnectorHandler.ts","../src/ActionRuntime/ActionRuntime.ts","../src/ActionRuntime/Handler/Local/ActionLocalHandler.ts","../src/ActionRuntime/Transport/crypto/actionHandshake.ts","../src/utils/decodeActionFrame.ts","../src/ActionRuntime/Transport/crypto/actionFrameCrypto.ts","../src/ActionRuntime/Transport/crypto/frameBytes.ts","../src/ActionRuntime/Transport/SecureSession/frameCryptoPipe.ts","../src/ActionRuntime/Transport/SecureSession/acceptorSecureSession.ts","../src/ActionRuntime/Handler/PeerLink/Acceptor/AcceptorHandler.ts","../src/ActionRuntime/Handler/PeerLink/Acceptor/createSecureActionServer.ts","../src/ActionRuntime/Transport/Carrier/Carrier.types.ts","../src/ActionRuntime/Transport/Transport.ts","../src/ActionRuntime/Transport/SecureSession/exchangeProtocol.ts","../src/ActionRuntime/Transport/SecureSession/establishExchangeSession.ts","../src/ActionRuntime/Transport/helpers/addTransportStatusMetadata.ts","../src/ActionRuntime/Transport/TransportConnection.ts","../src/ActionRuntime/Transport/Exchange/ExchangeConnection.ts","../src/ActionRuntime/Transport/Exchange/ExchangeTransport.ts","../src/ActionRuntime/Transport/helpers/createUnsetTransportResolvers.ts","../src/ActionRuntime/Transport/SecureSession/establishLinkSession.ts","../src/ActionRuntime/Transport/Link/LinkConnection.ts","../src/ActionRuntime/Transport/Link/LinkTransport.ts","../src/ActionRuntime/Transport/plainTransport.ts","../src/ActionRuntime/Transport/secureTransport.ts","../src/ActionRuntime/Channel/ActionChannel.ts","../src/ActionRuntime/Transport/SecureSession/exchangeAcceptor.ts","../src/ActionRuntime/Handler/PeerLink/Acceptor/createActionFetchHandler.ts","../src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore.ts","../src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter.ts","../src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.ts","../src/ActionRuntime/Channel/serveChannel.ts","../src/ActionRuntime/Channel/serveHost.ts","../src/ActionRuntime/Transport/Carrier/duplex/ws/wsAcceptorCarrier.ts","../src/ActionRuntime/Transport/Carrier/exchange/http/httpAcceptorCarrier.ts"],"sourcesContent":["export const DEFAULT_TRANSPORT_TIMEOUT = 10000; // 10 seconds\nexport const UNSET_RUNTIME_ENV_ID = \"_unset_\";\nexport const UNSET_RUNTIME_PER_ID = \"_0_0_\";\n","import type { IRuntimeCoordinate, TRuntimeCoordinateStringId } from \"../RuntimeCoordinate\";\n\nexport function runtimeCoordinateToStringIds(\n coordinate: IRuntimeCoordinate,\n): TRuntimeCoordinateStringId[] {\n return [\n `envId[${coordinate.envId}]perId[${coordinate.perId ?? \"_\"}]:insId[${coordinate.insId ?? \"_\"}]`,\n `envId[${coordinate.envId}]perId[${coordinate.perId ?? \"_\"}]:insId[_]`,\n `envId[${coordinate.envId}]perId[_]:insId[_]`,\n ];\n}\n","import { UNSET_RUNTIME_ENV_ID } from \"../nice_action.static\";\nimport { runtimeCoordinateToStringIds } from \"./utils/runtimeCoordinateToStringIds\";\n\nexport interface IRuntimeCoordinateSpecifics {\n /**\n * A unique and persistent client ID (should stay the same between runtime instance reloads)\n */\n perId?: string;\n /**\n * A unique instance (or \"memory-only\") ID (unique for each instance of an action runtime, which should generally\n * not persist between reloads, depending on the architecture)\n */\n insId?: string;\n}\n\nexport interface IRuntimeCoordinate extends IRuntimeCoordinateSpecifics {\n /**\n * A static runtime environment identifier (e.g. \"web_app_v1\", \"backend_analytics\")\n */\n envId: string;\n}\n\nexport type TRuntimeCoordinateEnvId = `envId[${string}]`;\n\nexport type TRuntimeCoordinateStringId =\n `${TRuntimeCoordinateEnvId}perId[${string | \"_\"}]:insId[${string | \"_\"}]`;\n\nexport interface IRuntimeFullCoordinates extends Required<IRuntimeCoordinate> {}\n\nexport class RuntimeCoordinate implements IRuntimeCoordinate {\n readonly envId: string;\n readonly perId?: string;\n readonly insId?: string;\n\n static get unknown(): RuntimeCoordinate {\n return new RuntimeCoordinate({\n envId: UNSET_RUNTIME_ENV_ID,\n });\n }\n\n static env(envId: string): RuntimeCoordinate {\n return new RuntimeCoordinate({\n envId: envId,\n });\n }\n\n withPersistentId(perId: string): RuntimeCoordinate {\n return this.specify({ perId });\n }\n\n constructor({ envId, perId, insId }: IRuntimeCoordinate) {\n this.envId = envId;\n this.perId = perId;\n this.insId = insId;\n }\n\n specify(specifics: IRuntimeCoordinateSpecifics): RuntimeCoordinate {\n return new RuntimeCoordinate({\n envId: this.envId,\n perId: this.perId,\n insId: this.insId,\n ...specifics,\n });\n }\n\n specifyIfUnset(specifics: IRuntimeCoordinateSpecifics): RuntimeCoordinate {\n return new RuntimeCoordinate({\n envId: this.envId,\n perId: this.perId ?? specifics.perId,\n insId: this.insId ?? specifics.insId,\n });\n }\n\n toJsonObject(): IRuntimeCoordinate {\n return {\n envId: this.envId,\n perId: this.perId,\n insId: this.insId,\n };\n }\n\n isExactlySame(other: IRuntimeCoordinate): boolean {\n return this.envId === other.envId && this.perId === other.perId && this.insId === other.insId;\n }\n\n isSameFor(other: IRuntimeCoordinate): { id: boolean; perId: boolean; insId: boolean } {\n return {\n id: this.envId === other.envId,\n perId: this.envId === other.envId && this.perId === other.perId,\n insId: this.envId === other.envId && this.perId === other.perId && this.insId === other.insId,\n };\n }\n\n similarityLevel(other: IRuntimeCoordinate): number {\n const sameFor = this.isSameFor(other);\n if (sameFor.insId) return 3;\n if (sameFor.perId) return 2;\n if (sameFor.id) return 1;\n return 0;\n }\n\n get stringId(): TRuntimeCoordinateStringId {\n return `envId[${this.envId}]perId[${this.perId ?? \"_\"}]:insId[${this.insId ?? \"_\"}]`;\n }\n\n /**\n * Takes the \"Runtime Coordinate\" and generates a list of full coordinate IDs representing the runtime\n * with decreasing levels of specificity.\n *\n * The first full coordinate ID is the most specific (including instance ID), while the last ID is\n * the least specific (only environment ID).\n *\n * Example output for a RuntimeCoordinate with envId \"web_app\", perId \"user123\", and insId \"instance456\":\n * [\n * \"envId[web_app]perId[user123]:insId[instance456]\",\n * \"envId[web_app]perId[user123]:insId[_]\",\n * \"envId[web_app]perId[_]:insId[_]\"\n * ]\n *\n * @returns a list of \"full\" runtime coordinate IDs with decreasing accuracy for targeting a runtime.\n */\n toStringIds(): TRuntimeCoordinateStringId[] {\n return runtimeCoordinateToStringIds(this);\n }\n}\n","import type { ActionDomain } from \"../Domain/ActionDomain\";\nimport type { IActionDomain } from \"../Domain/ActionDomain.types\";\nimport type { EActionForm, IActionBase, IActionBase_JsonObject } from \"./ActionBase.types\";\n\nexport abstract class ActionBase<\n FORM extends EActionForm,\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n> implements IActionBase<FORM, DOM, ID>\n{\n readonly domain: DOM[\"domain\"];\n readonly allDomains: DOM[\"allDomains\"];\n readonly schema: DOM[\"actionSchema\"][ID];\n\n constructor(\n readonly form: FORM,\n readonly _domain: ActionDomain<DOM>,\n readonly id: ID,\n ) {\n this.domain = _domain.domain;\n this.allDomains = _domain.allDomains;\n this.schema = _domain.actionSchema[id];\n }\n\n protected toJsonObject(): IActionBase_JsonObject<FORM, DOM, ID> {\n return {\n form: this.form,\n domain: this.domain,\n allDomains: this.allDomains,\n id: this.id,\n };\n }\n\n protected toJsonString(): string {\n return JSON.stringify(this.toJsonObject());\n }\n}\n","import type { IActionDomain } from \"../../Domain/ActionDomain.types\";\nimport { ActionBase } from \"../ActionBase\";\nimport { EActionForm } from \"../ActionBase.types\";\nimport type { ActionContext } from \"../Context/ActionContext\";\nimport type {\n EActionPayloadType,\n IActionPayload_Base,\n IActionPayload_Base_JsonObject,\n IActionPayload_Data_Base,\n} from \"./ActionPayload.types\";\n\nexport abstract class ActionPayload<\n DT extends EActionPayloadType,\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n >\n extends ActionBase<EActionForm.data, DOM, ID>\n implements IActionPayload_Base<DT, DOM, ID>\n{\n readonly form: EActionForm.data = EActionForm.data;\n readonly type: DT;\n readonly context: ActionContext<DOM, ID>;\n readonly time: number;\n\n protected constructor(context: ActionContext<DOM, ID>, type: DT, data: IActionPayload_Data_Base) {\n super(EActionForm.data, context._domain, context.id);\n this.context = context;\n this.type = type;\n this.time = data.time;\n }\n\n protected toBaseJsonObject(): IActionPayload_Base_JsonObject<DT, DOM, ID> {\n return {\n ...super.toJsonObject(),\n type: this.type,\n context: this.context.toContextDataJsonObject(),\n time: this.time,\n };\n }\n\n abstract toJsonObject(): IActionPayload_Base_JsonObject<DT, DOM, ID>;\n}\n","function stableStringify(value: unknown): string {\n if (value === null || value === undefined) return String(value);\n if (typeof value !== \"object\") return JSON.stringify(value) ?? \"undefined\";\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const keys = Object.keys(value as object).sort();\n return (\n \"{\" +\n keys\n .map((k) => `${JSON.stringify(k)}:${stableStringify((value as Record<string, unknown>)[k])}`)\n .join(\",\") +\n \"}\"\n );\n}\n\nfunction fnv1a32(str: string): string {\n let hash = 2166136261;\n for (let i = 0; i < str.length; i++) {\n hash = ((hash ^ str.charCodeAt(i)) * 16777619) >>> 0;\n }\n return hash.toString(16).padStart(8, \"0\");\n}\n\n/**\n * Produces a deterministic 8-char hex hash of any JSON-serializable value.\n * Useful for grouping/comparing action inputs, outputs, and progress payloads.\n */\nexport function hashPayloadData(data: unknown): string {\n return fnv1a32(stableStringify(data));\n}\n","import type { TInferActionError } from \"../../..\";\r\nimport type {\r\n IActionHandler_Local_Json,\r\n IActionHandler_Peer_Json,\r\n} from \"../../../ActionRuntime/Handler/ActionHandler.types\";\r\nimport type {\r\n ETransportShape,\r\n ITransportRouteInfo,\r\n} from \"../../../ActionRuntime/Transport/Transport.types\";\r\nimport type {\r\n IActionDomain,\r\n TInferInputFromSchema,\r\n TInferOutputFromSchema,\r\n} from \"../../Domain/ActionDomain.types\";\r\nimport type { EActionForm, IActionBase, IActionBase_JsonObject } from \"../ActionBase.types\";\r\nimport type { ActionContext } from \"../Context/ActionContext\";\r\nimport type { IActionContext_Data_JsonObject } from \"../Context/ActionContext.types\";\r\nimport type { ActionPayload_Progress } from \"./ActionPayload_Progress\";\r\nimport type { ActionPayload_Request } from \"./ActionPayload_Request\";\r\nimport type { ActionPayload_Result } from \"./ActionPayload_Result\";\r\n\r\nexport enum EActionPayloadType {\r\n request = \"request\",\r\n progress = \"progress\",\r\n result = \"result\",\r\n stream = \"stream\",\r\n push = \"push\",\r\n}\r\n\r\nexport interface IActionPayload_Data_Base {\r\n time: number;\r\n}\r\n\r\nexport interface IActionPayload_Base<\r\n DT extends EActionPayloadType,\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n> extends IActionBase<EActionForm.data, DOM, ID>,\r\n IActionPayload_Data_Base {\r\n readonly type: DT;\r\n readonly context: ActionContext<DOM, ID>;\r\n}\r\n\r\nexport type IActionRouteItemHandler =\r\n | IActionHandler_Local_Json\r\n | (IActionHandler_Peer_Json & {\r\n transShape: ETransportShape;\r\n transOrd: number;\r\n transInfo?: ITransportRouteInfo;\r\n });\r\n\r\n/**\r\n * [ ]\r\n * [ ACTION PAYLOAD TYPES ]\r\n * [ ]\r\n */\r\n\r\n/**\r\n *\r\n * [ RESULT ]\r\n *\r\n */\r\n\r\nexport type TActionResultOutcome<OUT, ERR> = { ok: true; output: OUT } | { ok: false; error: ERR };\r\n\r\nexport interface IActionPayload_Result<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n> extends IActionPayload_Base<EActionPayloadType.result, DOM, ID> {\r\n readonly result: TActionResultOutcome<\r\n TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"],\r\n TInferActionError<DOM[\"actionSchema\"][ID]>\r\n >;\r\n}\r\n\r\n/**\r\n *\r\n * [ PROGRESS ]\r\n *\r\n */\r\n\r\nexport enum EActionProgressType {\r\n none = \"none\",\r\n percentage = \"percentage\",\r\n custom = \"custom\",\r\n}\r\n\r\nexport interface IActionProgress_None {\r\n type: EActionProgressType.none;\r\n}\r\n\r\nexport interface IActionProgress_Percentage {\r\n type: EActionProgressType.percentage;\r\n progress: number; // value between 0 and 100\r\n message?: string; // optional message describing the progress\r\n}\r\n\r\nexport interface IActionProgress_Custom {\r\n type: EActionProgressType.custom;\r\n data: any; // custom data for the progress\r\n}\r\n\r\nexport type TActionProgress =\r\n | IActionProgress_None\r\n | IActionProgress_Percentage\r\n | IActionProgress_Custom;\r\n\r\nexport interface IActionPayload_Progress<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n> extends IActionPayload_Base<EActionPayloadType.progress, DOM, ID> {\r\n readonly progress: TActionProgress;\r\n}\r\n\r\n/**\r\n *\r\n * [ ]\r\n * [ Wire JSON types ]\r\n * [ ]\r\n *\r\n */\r\n\r\nexport interface IActionPayload_Base_JsonObject<\r\n DT extends EActionPayloadType,\r\n DOM extends IActionDomain = IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\r\n> extends IActionBase_JsonObject<EActionForm.data, DOM, ID> {\r\n type: DT;\r\n context: IActionContext_Data_JsonObject;\r\n time: number;\r\n}\r\n\r\nexport interface IActionPayload_Request_JsonObject<\r\n DOM extends IActionDomain = IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\r\n> extends IActionPayload_Base_JsonObject<EActionPayloadType.request, DOM, ID> {\r\n type: EActionPayloadType.request;\r\n input: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeInput\"];\r\n inputHash: string;\r\n}\r\n\r\nexport interface IActionPayload_Progress_JsonObject<\r\n DOM extends IActionDomain = IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\r\n> extends IActionPayload_Base_JsonObject<EActionPayloadType.progress, DOM, ID> {\r\n type: EActionPayloadType.progress;\r\n progress: TActionProgress;\r\n}\r\n\r\nexport interface IActionPayload_Result_JsonObject<\r\n DOM extends IActionDomain = IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\r\n> extends IActionPayload_Base_JsonObject<EActionPayloadType.result, DOM, ID> {\r\n type: EActionPayloadType.result;\r\n result: TActionResultOutcome<\r\n TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"SerdeOutput\"],\r\n TInferActionError<DOM[\"actionSchema\"][ID]>\r\n >;\r\n outputHash: string;\r\n}\r\n\r\n/**\r\n *\r\n * [ ]\r\n * [ COMBINED TYPES ]\r\n * [ ]\r\n *\r\n */\r\n\r\nexport type TActionPayload_Any_Instance<\r\n DOM extends IActionDomain = IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\r\n> =\r\n | ActionPayload_Request<DOM, ID>\r\n | ActionPayload_Result<DOM, ID>\r\n | ActionPayload_Progress<DOM, ID>;\r\n\r\nexport type TActionPayload_Any_JsonObject<\r\n DOM extends IActionDomain = IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\r\n> =\r\n | IActionPayload_Request_JsonObject<DOM, ID>\r\n | IActionPayload_Progress_JsonObject<DOM, ID>\r\n | IActionPayload_Result_JsonObject<DOM, ID>;\r\n","import type { IActionDomain } from \"../../Domain/ActionDomain.types\";\nimport type { ActionContext } from \"../Context/ActionContext\";\nimport { ActionPayload } from \"./ActionPayload\";\nimport type {\n IActionPayload_Data_Base,\n IActionPayload_Progress,\n IActionPayload_Progress_JsonObject,\n TActionProgress,\n} from \"./ActionPayload.types\";\nimport { EActionPayloadType } from \"./ActionPayload.types\";\nimport { ActionPayload_Request } from \"./ActionPayload_Request\";\n\nexport class ActionPayload_Progress<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n >\n extends ActionPayload<EActionPayloadType.progress, DOM, ID>\n implements IActionPayload_Progress<DOM, ID>\n{\n readonly progress: TActionProgress;\n\n constructor(\n params: { context: ActionContext<DOM, ID> } | ActionPayload_Request<DOM, ID>,\n progress: TActionProgress,\n data: IActionPayload_Data_Base,\n ) {\n super(params.context, EActionPayloadType.progress, data);\n this.progress = progress;\n }\n\n toJsonObject(): IActionPayload_Progress_JsonObject<DOM, ID> {\n return {\n ...this.toBaseJsonObject(),\n progress: this.progress,\n };\n }\n\n toJsonString(): string {\n return JSON.stringify(this.toJsonObject());\n }\n\n toHttpResponse(): Response {\n return new Response(this.toJsonString(), {\n status: 200,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n}\n","import { hashPayloadData } from \"../../../utils/hashPayloadData\";\nimport type { IActionDomain, TInferOutputFromSchema } from \"../../Domain/ActionDomain.types\";\nimport type { TInferActionError } from \"../../Schema/ActionSchema\";\nimport type { ActionContext } from \"../Context/ActionContext\";\nimport { ActionPayload } from \"./ActionPayload\";\nimport type { IActionPayload_Data_Base, TActionResultOutcome } from \"./ActionPayload.types\";\nimport { EActionPayloadType, type IActionPayload_Result_JsonObject } from \"./ActionPayload.types\";\nimport { ActionPayload_Request } from \"./ActionPayload_Request\";\n\nexport class ActionPayload_Result<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n> extends ActionPayload<EActionPayloadType.result, DOM, ID> {\n readonly result: TActionResultOutcome<\n TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"],\n TInferActionError<DOM[\"actionSchema\"][ID]>\n >;\n readonly outputHash: string;\n\n constructor(\n params: { context: ActionContext<DOM, ID> } | ActionPayload_Request<DOM, ID>,\n result: TActionResultOutcome<\n TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"],\n TInferActionError<DOM[\"actionSchema\"][ID]>\n >,\n data: IActionPayload_Data_Base,\n ) {\n super(params.context, EActionPayloadType.result, data);\n this.result = result;\n this.outputHash = result.ok\n ? hashPayloadData(this.context.schema.serializeOutput(result.output))\n : hashPayloadData(result.error.message);\n }\n\n toJsonObject(): IActionPayload_Result_JsonObject<DOM, ID> {\n const wireResult = this.result.ok\n ? { ok: true as const, output: this.context.schema.serializeOutput(this.result.output) }\n : this.result;\n return {\n ...this.toBaseJsonObject(),\n result: wireResult,\n outputHash: this.outputHash,\n };\n }\n\n toJsonString(): string {\n return JSON.stringify(this.toJsonObject());\n }\n\n toHttpResponse({ useErrorStatus = true }: { useErrorStatus?: boolean } = {}): Response {\n return new Response(this.toJsonString(), {\n status: this.result.ok ? 200 : useErrorStatus ? this.result.error.httpStatusCode : 500,\n headers: { \"Content-Type\": \"application/json\" },\n });\n }\n}\n","import type { IExecuteActionOptions } from \"../../../ActionRuntime/Handler/ActionHandler.types\";\r\nimport { hashPayloadData } from \"../../../utils/hashPayloadData\";\r\nimport type {\r\n IActionDomain,\r\n TInferInputFromSchema,\r\n TInferOutputFromSchema,\r\n} from \"../../Domain/ActionDomain.types\";\r\nimport type { TInferActionError } from \"../../Schema/ActionSchema\";\r\nimport type { ActionContext } from \"../Context/ActionContext\";\r\nimport type { RunningAction } from \"../RunningAction\";\r\nimport { ActionPayload } from \"./ActionPayload\";\r\nimport type { IActionPayload_Data_Base, TActionProgress } from \"./ActionPayload.types\";\r\nimport { EActionPayloadType, type IActionPayload_Request_JsonObject } from \"./ActionPayload.types\";\r\nimport { ActionPayload_Progress } from \"./ActionPayload_Progress\";\r\nimport { ActionPayload_Result } from \"./ActionPayload_Result\";\r\n\r\nexport class ActionPayload_Request<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\r\n> extends ActionPayload<EActionPayloadType.request, DOM, ID> {\r\n readonly input: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"];\r\n readonly inputHash: string;\r\n _callSite?: string;\r\n\r\n constructor(\r\n params: { context: ActionContext<DOM, ID> },\r\n input: TInferInputFromSchema<DOM[\"actionSchema\"][ID]>[\"Input\"],\r\n data: IActionPayload_Data_Base,\r\n ) {\r\n super(params.context, EActionPayloadType.request, data);\r\n this.input = input;\r\n this.inputHash = hashPayloadData(this.context.schema.serializeInput(input));\r\n }\r\n\r\n successResult(\r\n ...args: [TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"]] extends [never]\r\n ? [] | [output: TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"]]\r\n : [output: TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"]]\r\n ): ActionPayload_Result<DOM, ID> {\r\n const output = args[0];\r\n const finalOutput = this.context.schema.validateOutput(output, {\r\n domain: this.domain,\r\n actionId: this.id,\r\n });\r\n return new ActionPayload_Result(this, { ok: true, output: finalOutput }, { time: Date.now() });\r\n }\r\n\r\n errorResult(err: TInferActionError<DOM[\"actionSchema\"][ID]>): ActionPayload_Result<DOM, ID> {\r\n return new ActionPayload_Result(this, { ok: false, error: err }, { time: Date.now() });\r\n }\r\n\r\n progress(progress: TActionProgress): ActionPayload_Progress<DOM, ID> {\r\n return new ActionPayload_Progress(this, progress, { time: Date.now() });\r\n }\r\n\r\n toJsonObject(): IActionPayload_Request_JsonObject<DOM, ID> {\r\n return {\r\n ...super.toBaseJsonObject(),\r\n input: this.context.schema.serializeInput(this.input),\r\n inputHash: this.inputHash,\r\n };\r\n }\r\n\r\n toJsonString(): string {\r\n return JSON.stringify(this.toJsonObject());\r\n }\r\n\r\n async runToOutput(\r\n options?: IExecuteActionOptions<DOM, ID>,\r\n ): Promise<TInferOutputFromSchema<DOM[\"actionSchema\"][ID]>[\"Output\"]> {\r\n const running = await this.run(options);\r\n const result = await running.waitForResultPayload();\r\n if (result.result.ok) return result.result.output;\r\n throw result.result.error;\r\n }\r\n\r\n async runToResultPayload(\r\n options?: IExecuteActionOptions<DOM, ID>,\r\n ): Promise<ActionPayload_Result<DOM, ID>> {\r\n const value = await this.run(options);\r\n return value.waitForResultPayload();\r\n }\r\n\r\n async run(options?: IExecuteActionOptions<DOM, ID>): Promise<RunningAction<DOM, ID>> {\r\n if (this._callSite == null) {\r\n this._callSite = new Error().stack;\r\n }\r\n return this._domain.runAction(this, options);\r\n }\r\n}\r\n","import type { ActionDomain } from \"../..\";\nimport type { IActionDomain } from \"../Domain/ActionDomain.types\";\nimport type { ActionContext } from \"./Context/ActionContext\";\nimport { type IActionPayload_Result_JsonObject } from \"./Payload/ActionPayload.types\";\nimport type { ActionPayload_Progress } from \"./Payload/ActionPayload_Progress\";\nimport type { ActionPayload_Result } from \"./Payload/ActionPayload_Result\";\nimport {\n ERunningActionFinishedType,\n ERunningActionUpdateType,\n type IRunningActionState,\n type IRunningActionState_ConstructorParams,\n type IRunningActionUserMethods,\n type TRunningActionUpdate,\n type TRunningActionUpdateListener,\n} from \"./RunningAction.types\";\n\nexport class RunningAction<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string = keyof DOM[\"actionSchema\"] & string,\n> implements IRunningActionUserMethods<DOM, ID>\n{\n protected _state: IRunningActionState<DOM, ID>;\n\n readonly context: ActionContext<DOM, ID>;\n readonly cuid: string;\n readonly id: ID;\n readonly _domain: ActionDomain<DOM>;\n readonly domain: DOM[\"domain\"];\n readonly allDomains: DOM[\"allDomains\"];\n readonly parentCuid?: string;\n readonly callSite?: string;\n\n private readonly _resultPayloadPromise: Promise<ActionPayload_Result<DOM, ID>>;\n private _resolveResult!: (response: ActionPayload_Result<DOM, ID>) => void;\n private _rejectResult!: (reason?: unknown) => void;\n\n private _isAborted = false;\n\n private readonly _updates: TRunningActionUpdate<DOM, ID>[] = [];\n\n private readonly _updateListeners: TRunningActionUpdateListener<DOM, ID>[] = [];\n\n constructor(initialState: IRunningActionState_ConstructorParams<DOM, ID>) {\n this.context = initialState.context;\n this.cuid = initialState.context.cuid;\n this.id = initialState.context.id;\n this.domain = initialState.context.domain;\n this.allDomains = initialState.context.allDomains;\n this._domain = initialState.context._domain;\n this.parentCuid = initialState.parentCuid;\n this.callSite = initialState.callSite;\n\n this._resultPayloadPromise = new Promise<ActionPayload_Result<DOM, ID>>((resolve, reject) => {\n this._resolveResult = resolve;\n this._rejectResult = reject;\n });\n // Prevent unhandled rejection when this RunningAction is aborted/failed but\n // waitForResultPayload() is never called (e.g. error-path in _runAction).\n this._resultPayloadPromise.catch(() => {});\n\n this._state = {\n request: initialState.request,\n progress: initialState.progress ?? [],\n result: initialState.result,\n };\n\n this._sendUpdate({\n type: ERunningActionUpdateType.started,\n runningAction: this,\n time: Date.now(),\n });\n }\n\n get state(): IRunningActionState<DOM, ID> {\n return this._state;\n }\n\n abort(reason?: unknown): void {\n this._abort(reason);\n }\n\n addUpdateListeners(listeners: TRunningActionUpdateListener<DOM, ID>[]): () => void {\n this._updateListeners.push(...listeners);\n // Emit all past events to the new listeners\n for (const event of this._updates) {\n for (const listener of listeners) {\n listener(event);\n }\n }\n return () => {\n for (const listener of listeners) {\n const i = this._updateListeners.indexOf(listener);\n if (i !== -1) this._updateListeners.splice(i, 1);\n }\n };\n }\n\n async *iterateUpdates(): AsyncIterable<TRunningActionUpdate<DOM, ID>> {\n const queue: TRunningActionUpdate<DOM, ID>[] = [];\n let resolveWaiter: (() => void) | null = null;\n\n const unsubscribe = this.addUpdateListeners([\n (event) => {\n queue.push(event);\n // Wake up the while-loop if it's currently paused waiting for an event\n if (resolveWaiter) {\n resolveWaiter();\n resolveWaiter = null;\n }\n },\n ]);\n\n try {\n while (true) {\n if (queue.length === 0) {\n await new Promise<void>((resolve) => {\n resolveWaiter = resolve;\n });\n }\n\n const event = queue.shift()!;\n yield event;\n\n // Safely terminate the generator when the action concludes\n if (event.type === ERunningActionUpdateType.finished) {\n break;\n }\n }\n } finally {\n unsubscribe();\n }\n }\n\n _sendUpdate(update: TRunningActionUpdate<DOM, ID>): void {\n this._updates.push(update);\n for (const listener of this._updateListeners) listener(update);\n }\n\n _completeWithResult(result: ActionPayload_Result<DOM, ID>): boolean {\n if (this._state.result != null || this._isAborted) return false;\n\n this._state = {\n request: this._state.request,\n progress: this._state.progress,\n result: result,\n };\n\n this._resolveResult(result);\n this._sendUpdate({\n type: ERunningActionUpdateType.finished,\n finishType: ERunningActionFinishedType.success,\n runningAction: this,\n time: Date.now(),\n response: result,\n });\n\n return true;\n }\n\n _abort(reason?: unknown): boolean {\n if (this._state.result != null || this._isAborted) return false;\n this._isAborted = true;\n this._rejectResult(reason);\n\n this._sendUpdate({\n type: ERunningActionUpdateType.finished,\n finishType: ERunningActionFinishedType.aborted,\n runningAction: this,\n time: Date.now(),\n reason,\n });\n\n return true;\n }\n\n _failWithError(error: unknown): boolean {\n if (this._state.result != null || this._isAborted) return false;\n this._isAborted = true;\n this._rejectResult(error);\n\n this._sendUpdate({\n type: ERunningActionUpdateType.finished,\n finishType: ERunningActionFinishedType.failed,\n runningAction: this,\n time: Date.now(),\n error,\n } as any);\n\n return true;\n }\n\n _updateProgress(progress: ActionPayload_Progress<DOM, ID>): void {\n if (this._state.result != null || this._isAborted) return;\n this._state.progress.push(progress);\n\n this._sendUpdate({\n type: ERunningActionUpdateType.progress,\n runningAction: this,\n time: Date.now(),\n progress: progress.progress,\n });\n }\n\n waitForResultPayload(): Promise<ActionPayload_Result<DOM, ID>> {\n return this._resultPayloadPromise;\n }\n\n _resolveFromJson(resultJson: IActionPayload_Result_JsonObject<DOM, ID>): boolean {\n if (this._state.result != null || this._isAborted) return false;\n const result = this._domain.hydrateResultPayload(resultJson);\n return this._completeWithResult(result as unknown as ActionPayload_Result<DOM, ID>);\n }\n}\n","import { err, err_nice } from \"@nice-code/error\";\r\nimport { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\r\nimport { EActionPayloadType } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { IActionRuntimeManagerContext } from \"../ActionRuntime/ActionRuntime.types\";\r\nimport type {\r\n RuntimeCoordinate,\r\n TRuntimeCoordinateStringId,\r\n} from \"../ActionRuntime/RuntimeCoordinate\";\r\n\r\nexport enum EErrId_NiceAction {\r\n not_implemented = \"not_implemented\",\r\n action_id_not_in_domain = \"action_id_not_in_domain\",\r\n domain_already_exists_in_hierarchy = \"domain_already_exists_in_hierarchy\",\r\n domain_no_handler = \"domain_no_handler\",\r\n hydration_domain_mismatch = \"hydration_domain_mismatch\",\r\n hydration_action_state_mismatch = \"hydration_action_state_mismatch\",\r\n hydration_action_id_not_found = \"hydration_action_id_not_found\",\r\n no_action_execution_handler = \"no_action_execution_handler\",\r\n wire_action_not_payload = \"wire_action_not_payload\",\r\n wire_not_action_data = \"wire_not_action_data\",\r\n client_runtime_already_registered = \"client_runtime_already_registered\",\r\n client_runtime_not_registered = \"client_runtime_not_registered\",\r\n runtime_reset = \"runtime_reset\",\r\n no_client_runtimes_registered = \"no_client_runtimes_registered\",\r\n action_input_validation_failed = \"action_input_validation_failed\",\r\n action_input_validation_promise = \"action_input_validation_promise\",\r\n action_output_validation_failed = \"action_output_validation_failed\",\r\n action_output_validation_promise = \"action_output_validation_promise\",\r\n}\r\n\r\nexport const err_nice_action = err_nice.createChildDomain({\r\n domain: \"err_nice_action\",\r\n defaultHttpStatusCode: 500,\r\n schema: {\r\n [EErrId_NiceAction.not_implemented]: err<{ label: string }>({\r\n message: ({ label }) => `The \"${label}\" functionality is not implemented yet.`,\r\n }),\r\n [EErrId_NiceAction.action_id_not_in_domain]: err<{ domain: string; actionId: string }>({\r\n message: ({ actionId, domain }) =>\r\n `Action with id \"${actionId}\" does not exist in domain \"${domain}\".`,\r\n }),\r\n [EErrId_NiceAction.domain_already_exists_in_hierarchy]: err<{\r\n domain: string;\r\n allParentDomains: string[];\r\n parentDomain: string;\r\n }>({\r\n message: ({ domain, allParentDomains, parentDomain }) =>\r\n `Domain \"${domain}\" already exists in the hierarchy under the parent \"${parentDomain}\". All parent domains [\"${allParentDomains.join(\", \")}\"]`,\r\n }),\r\n [EErrId_NiceAction.domain_no_handler]: err<{ domain: string }>({\r\n message: ({ domain }) => `Domain \"${domain}\" has no action handler registered.`,\r\n }),\r\n [EErrId_NiceAction.hydration_domain_mismatch]: err<{\r\n expected: string;\r\n received: string;\r\n }>({\r\n message: ({ expected, received }) =>\r\n `Cannot hydrate action: domain mismatch. Expected \"${expected}\", got \"${received}\".`,\r\n }),\r\n [EErrId_NiceAction.hydration_action_state_mismatch]: err<{\r\n expected: string;\r\n received: string;\r\n }>({\r\n message: ({ expected, received }) =>\r\n `Cannot hydrate action: action state type mismatch. Expected \"${expected}\", got \"${received}\".`,\r\n }),\r\n [EErrId_NiceAction.hydration_action_id_not_found]: err<{\r\n domain: string;\r\n actionId: string;\r\n }>({\r\n message: ({ domain, actionId }) =>\r\n `Cannot hydrate action: id \"${actionId}\" does not exist in domain \"${domain}\".`,\r\n }),\r\n [EErrId_NiceAction.no_action_execution_handler]: err<{\r\n domain: string;\r\n actionId: string;\r\n specifiedClient?: RuntimeCoordinate;\r\n }>({\r\n message: ({ domain, actionId, specifiedClient }) =>\r\n `${specifiedClient ? ` The targeted client runtime [${specifiedClient.stringId}] has no` : \"No\"} action handler registered for \"${actionId}\" in domain \"${domain}\".`,\r\n }),\r\n [EErrId_NiceAction.wire_action_not_payload]: err<{\r\n domain: string;\r\n actionId: string;\r\n actionState: string | undefined;\r\n }>({\r\n message: ({ domain, actionId, actionState }) =>\r\n `Cannot handle wire for action \"${actionId}\" in domain \"${domain}\": expected action form of \"${EActionForm.data}\" and type of \"${EActionPayloadType.request}\", \"${EActionPayloadType.progress}\" or \"${EActionPayloadType.result}\", got \"${actionState}\".`,\r\n }),\r\n [EErrId_NiceAction.wire_not_action_data]: err({\r\n message: () =>\r\n `Cannot handle wire for action: expected an object with a \"domain\" property of type string, a \"form\" property of \"${EActionForm.data}\" and a \"type\" property of \"${EActionPayloadType.request}\", \"${EActionPayloadType.progress}\" or \"${EActionPayloadType.result}\".`,\r\n }),\r\n [EErrId_NiceAction.runtime_reset]: err({\r\n message: () => `Runtime has been reset.`,\r\n }),\r\n [EErrId_NiceAction.client_runtime_already_registered]: err<{\r\n context?: IActionRuntimeManagerContext;\r\n client: RuntimeCoordinate;\r\n }>({\r\n message: ({ context, client }) =>\r\n `Environment is already registered${context?.domain ? ` on domain \"${context.domain}\"` : \"\"} for client [${client.stringId}]. Each client specifier (exact match on all properties) may only be registered once.`,\r\n }),\r\n [EErrId_NiceAction.client_runtime_not_registered]: err<{\r\n context?: IActionRuntimeManagerContext;\r\n clientStringId: TRuntimeCoordinateStringId;\r\n }>({\r\n message: ({ context, clientStringId }) =>\r\n `No runtime registered${context?.domain ? ` on domain \"${context.domain}\"` : \"\"} for client [${clientStringId}].`,\r\n }),\r\n [EErrId_NiceAction.no_client_runtimes_registered]: err<{\r\n context?: IActionRuntimeManagerContext;\r\n }>({\r\n message: ({ context }) =>\r\n `No runtimes registered${context?.domain ? ` on domain \"${context.domain}\"` : \"\"}. Add handlers to a runtime via runtime.addHandlers([handler]) before executing actions.`,\r\n }),\r\n [EErrId_NiceAction.action_input_validation_failed]: err<{\r\n domain: string;\r\n actionId: string;\r\n validationMessage: string;\r\n }>({\r\n message: ({ domain, actionId, validationMessage }) =>\r\n `Input validation failed for action \"${actionId}\" in domain \"${domain}\":\\n${validationMessage}`,\r\n httpStatusCode: 400,\r\n }),\r\n [EErrId_NiceAction.action_input_validation_promise]: err<{\r\n domain: string;\r\n actionId: string;\r\n }>({\r\n message: ({ domain, actionId }) =>\r\n `Input validation for action \"${actionId}\" in domain \"${domain}\" returned a promise, which is not supported.`,\r\n httpStatusCode: 400,\r\n }),\r\n [EErrId_NiceAction.action_output_validation_failed]: err<{\r\n domain: string;\r\n actionId: string;\r\n validationMessage: string;\r\n }>({\r\n message: ({ domain, actionId, validationMessage }) =>\r\n `Output validation failed for action \"${actionId}\" in domain \"${domain}\":\\n${validationMessage}`,\r\n httpStatusCode: 500,\r\n }),\r\n [EErrId_NiceAction.action_output_validation_promise]: err<{\r\n domain: string;\r\n actionId: string;\r\n }>({\r\n message: ({ domain, actionId }) =>\r\n `Output validation for action \"${actionId}\" in domain \"${domain}\" returned a promise, which is not supported.`,\r\n httpStatusCode: 500,\r\n }),\r\n },\r\n});\r\n","import type { IActionBase_JsonObject } from \"../ActionDefinition/Action/ActionBase.types\";\n\nexport const isAction_Base_JsonObject = (obj: unknown): obj is IActionBase_JsonObject => {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n typeof (obj as any).domain === \"string\" &&\n typeof (obj as any).id === \"string\" &&\n typeof (obj as any).form === \"string\"\n );\n};\n","import { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { IActionPayload_Result_JsonObject } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { EActionPayloadType } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { isAction_Base_JsonObject } from \"./isAction_Base_JsonObject\";\n\nexport const isActionPayload_Result_JsonObject = (\n obj: unknown,\n): obj is IActionPayload_Result_JsonObject => {\n return (\n isAction_Base_JsonObject(obj) &&\n (obj as any).result != null &&\n (obj as any).form === EActionForm.data &&\n (obj as any).type === EActionPayloadType.result\n );\n};\n","import { extractMessageFromStandardSchema } from \"@nice-code/common-errors\";\r\nimport {\r\n err_cast_not_nice,\r\n type INiceErrorDomainProps,\r\n type InferNiceError,\r\n type NiceErrorDomain,\r\n} from \"@nice-code/error\";\r\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\r\nimport { EErrId_NiceAction, err_nice_action } from \"../../errors/err_nice_action\";\r\nimport type {\r\n IActionErrorDeclaration,\r\n TActionSchemaOptions,\r\n TInferDeclaredErrors,\r\n TTransportedValue,\r\n} from \"./ActionSchema.types\";\r\n\r\n/**\r\n * What a sender should expect back from an action — declared on its schema so both ends agree without\r\n * any wire flag (each derives the mode from the shared `domain:id`).\r\n *\r\n * - `payload` — a typed output (the action has `.output(...)`); the sender awaits it.\r\n * - `ack` — an empty success confirming receipt (no output); the sender may await it to know the\r\n * receiver handled it (or to surface an error). This is the default for an action with no output.\r\n * - `none` — fire-and-forget: the receiver sends no reply and the sender doesn't wait. The sender's\r\n * running action completes as soon as the frame is on the wire (no pending reply, no timeout).\r\n */\r\nexport enum EActionResponseMode {\r\n payload = \"payload\",\r\n ack = \"ack\",\r\n none = \"none\",\r\n}\r\n\r\nexport class ActionSchema<\r\n INPUT extends TTransportedValue<any, any> = never,\r\n OUTPUT extends TTransportedValue<any, any> = never,\r\n ERRORS extends readonly IActionErrorDeclaration<any, any>[] = readonly [],\r\n> {\r\n private _errorDeclarations: IActionErrorDeclaration[] = [];\r\n private inputOptions: TActionSchemaOptions<any, any> | undefined;\r\n private outputOptions: TActionSchemaOptions<any, any> | undefined;\r\n private _responseMode: EActionResponseMode | undefined;\r\n\r\n get inputSchema(): StandardSchemaV1 | undefined {\r\n return this.inputOptions?.schema;\r\n }\r\n\r\n get outputSchema(): StandardSchemaV1 | undefined {\r\n return this.outputOptions?.schema;\r\n }\r\n\r\n /**\r\n * The response contract for this action. Defaults are inferred — `payload` when an output schema is\r\n * declared, otherwise `ack` — and made explicit by {@link ack} / {@link fireAndForget}.\r\n */\r\n get responseMode(): EActionResponseMode {\r\n if (this._responseMode != null) return this._responseMode;\r\n return this.outputOptions != null ? EActionResponseMode.payload : EActionResponseMode.ack;\r\n }\r\n\r\n /**\r\n * Mark this action as expecting only an acknowledgment (an empty success). Mostly for clarity — an\r\n * output-less action already acks by default — but it documents intent and reads as the deliberate\r\n * counterpart to {@link fireAndForget}.\r\n */\r\n ack(): this {\r\n this._responseMode = EActionResponseMode.ack;\r\n return this;\r\n }\r\n\r\n /**\r\n * Mark this action as fire-and-forget: the receiver sends no reply, and the sender's running action\r\n * completes the moment the frame is sent (no awaited reply, no timeout). Ideal for high-frequency\r\n * server→client pushes (presence, ticks) where an ack would only add wire chatter.\r\n */\r\n fireAndForget(): this {\r\n this._responseMode = EActionResponseMode.none;\r\n return this;\r\n }\r\n\r\n /**\r\n * Declare the input schema (JSON-native or with explicit SERDE type param).\r\n * For non-JSON-native inputs, prefer the 3-argument form below to avoid\r\n * needing explicit type parameters.\r\n */\r\n input<VS extends StandardSchemaV1 = StandardSchemaV1, SERDE_IN = any>(\r\n options: TActionSchemaOptions<VS, SERDE_IN>,\r\n ): ActionSchema<TTransportedValue<StandardSchemaV1.InferInput<VS>, SERDE_IN>, OUTPUT, ERRORS> {\r\n this.inputOptions = options;\r\n return this;\r\n }\r\n\r\n /**\r\n * Declare the output schema (JSON-native or with explicit SERDE type param).\r\n * For non-JSON-native outputs, prefer the 3-argument form below to avoid\r\n * needing explicit type parameters.\r\n */\r\n output<VS extends StandardSchemaV1 = StandardSchemaV1, SERDE_OUT = any>(\r\n options: TActionSchemaOptions<VS, SERDE_OUT>,\r\n ): ActionSchema<INPUT, TTransportedValue<StandardSchemaV1.InferInput<VS>, SERDE_OUT>, ERRORS> {\r\n this.outputOptions = options;\r\n return this;\r\n }\r\n\r\n /**\r\n * Declare that this action may throw any error from `domain`.\r\n * `TInferActionError` will include `NiceError<DEF, keyof schema>` in its union.\r\n */\r\n throws<ERR_DEF extends INiceErrorDomainProps>(\r\n domain: NiceErrorDomain<ERR_DEF>,\r\n ): ActionSchema<\r\n INPUT,\r\n OUTPUT,\r\n readonly [...ERRORS, IActionErrorDeclaration<ERR_DEF, keyof ERR_DEF[\"schema\"] & string>]\r\n >;\r\n\r\n /**\r\n * Declare that this action may throw only the listed `ids` from `domain`.\r\n * `TInferActionError` will include `NiceError<DEF, IDS[number]>` narrowed to those IDs.\r\n */\r\n throws<\r\n ERR_DEF extends INiceErrorDomainProps,\r\n IDS extends ReadonlyArray<keyof ERR_DEF[\"schema\"] & string>,\r\n >(\r\n domain: NiceErrorDomain<ERR_DEF>,\r\n ids: IDS,\r\n ): ActionSchema<\r\n INPUT,\r\n OUTPUT,\r\n readonly [...ERRORS, IActionErrorDeclaration<ERR_DEF, IDS[number] & string>]\r\n >;\r\n\r\n throws(domain: NiceErrorDomain<any>, ids?: ReadonlyArray<string>): ActionSchema<any, any, any> {\r\n this._errorDeclarations.push({ _domain: domain, _ids: ids });\r\n return this;\r\n }\r\n\r\n /**\r\n * Serialize raw input to a JSON-serializable form.\r\n * Uses the schema's serialization.serialize if defined; otherwise the input\r\n * is already JSON-native and is returned as-is.\r\n */\r\n serializeInput(rawInput: INPUT[0]): INPUT[1] {\r\n if (this.inputOptions?.serialization) {\r\n return this.inputOptions.serialization.serialize(rawInput);\r\n }\r\n return rawInput;\r\n }\r\n\r\n /**\r\n * Deserialize a JSON value back into the raw input type.\r\n * Uses serialization.deserialize if defined; otherwise the value is cast\r\n * directly (it's already in the correct shape).\r\n */\r\n deserializeInput(serialized: INPUT[1]): INPUT[0] {\r\n if (this.inputOptions?.serialization) {\r\n return this.inputOptions.serialization.deserialize(serialized);\r\n }\r\n return serialized as INPUT[0];\r\n }\r\n\r\n /**\r\n * Validate raw input against the schema defined via `.input({ schema })`.\r\n * Throws `action_input_validation_failed` if validation fails.\r\n * Returns the validated (and possibly coerced) value on success.\r\n * If no input schema was declared, the value is passed through as-is.\r\n */\r\n validateInput(value: unknown, meta: { domain: string; actionId: string }): INPUT[0] {\r\n if (this.inputOptions?.schema == null) {\r\n return value as INPUT[0];\r\n }\r\n const result = this.inputOptions.schema[\"~standard\"].validate(value);\r\n\r\n if (result instanceof Promise) {\r\n throw err_nice_action.fromId(EErrId_NiceAction.action_input_validation_promise, {\r\n domain: meta.domain,\r\n actionId: meta.actionId,\r\n });\r\n }\r\n\r\n if (result.issues != null) {\r\n throw err_nice_action.fromId(EErrId_NiceAction.action_input_validation_failed, {\r\n domain: meta.domain,\r\n actionId: meta.actionId,\r\n validationMessage: extractMessageFromStandardSchema(result),\r\n });\r\n }\r\n\r\n return result.value as INPUT[0];\r\n }\r\n\r\n validateOutput(value: unknown, meta: { domain: string; actionId: string }): OUTPUT[0] {\r\n if (this.outputOptions?.schema == null) {\r\n return value as OUTPUT[0];\r\n }\r\n const result = this.outputOptions.schema[\"~standard\"].validate(value);\r\n\r\n if (result instanceof Promise) {\r\n throw err_nice_action.fromId(EErrId_NiceAction.action_output_validation_promise, {\r\n domain: meta.domain,\r\n actionId: meta.actionId,\r\n });\r\n }\r\n\r\n if (result.issues != null) {\r\n throw err_nice_action.fromId(EErrId_NiceAction.action_output_validation_failed, {\r\n domain: meta.domain,\r\n actionId: meta.actionId,\r\n validationMessage: extractMessageFromStandardSchema(result),\r\n });\r\n }\r\n\r\n return result.value as OUTPUT[0];\r\n }\r\n\r\n /**\r\n * Serialize raw output to a JSON-serializable form.\r\n */\r\n serializeOutput(rawOutput: OUTPUT[0]): OUTPUT[1] {\r\n if (this.outputOptions?.serialization) {\r\n return this.outputOptions.serialization.serialize(rawOutput);\r\n }\r\n return rawOutput as OUTPUT[1];\r\n }\r\n\r\n /**\r\n * Deserialize a JSON value back into the raw output type.\r\n */\r\n deserializeOutput(serialized: OUTPUT[1]): OUTPUT[0] {\r\n if (this.outputOptions?.serialization) {\r\n return this.outputOptions.serialization.deserialize(serialized);\r\n }\r\n return serialized as OUTPUT[0];\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// TInferActionError — lives here (not in .types) to avoid circular imports\r\n// ---------------------------------------------------------------------------\r\n\r\nexport type TInferActionError<SCH> =\r\n SCH extends ActionSchema<any, any, infer DECLS>\r\n ? DECLS extends readonly IActionErrorDeclaration[]\r\n ? TInferDeclaredErrors<DECLS> | InferNiceError<typeof err_cast_not_nice>\r\n : InferNiceError<typeof err_cast_not_nice>\r\n : never;\r\n\r\nexport const actionSchema = (): ActionSchema => {\r\n return new ActionSchema();\r\n};\r\n","import { runtime } from \"std-env\";\nimport type { IRuntimeMeta } from \"../ActionRuntime/ActionRuntime.types\";\n\nexport const getAssumedRuntimeInfo = (): IRuntimeMeta => {\n return {\n assumed: true,\n runtimeName: runtime,\n };\n};\n","import { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { IActionPayload_Progress_JsonObject } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { EActionPayloadType } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { isAction_Base_JsonObject } from \"./isAction_Base_JsonObject\";\n\nexport const isActionPayload_Progress_JsonObject = (\n obj: unknown,\n): obj is IActionPayload_Progress_JsonObject => {\n return (\n isAction_Base_JsonObject(obj) &&\n \"progress\" in (obj as any) &&\n (obj as any).form === EActionForm.data &&\n (obj as any).type === EActionPayloadType.progress\n );\n};\n","import { EActionForm } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { IActionPayload_Request_JsonObject } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { EActionPayloadType } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { isAction_Base_JsonObject } from \"./isAction_Base_JsonObject\";\n\nexport const isActionPayload_Request_JsonObject = (\n obj: unknown,\n): obj is IActionPayload_Request_JsonObject => {\n return (\n isAction_Base_JsonObject(obj) &&\n \"input\" in (obj as any) &&\n (obj as any).form === EActionForm.data &&\n (obj as any).type === EActionPayloadType.request\n );\n};\n","import type { TActionPayload_Any_JsonObject } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { isActionPayload_Progress_JsonObject } from \"./isActionPayload_Progress_JsonObject\";\nimport { isActionPayload_Request_JsonObject } from \"./isActionPayload_Request_JsonObject\";\nimport { isActionPayload_Result_JsonObject } from \"./isActionPayload_Result_JsonObject\";\n\nexport function isActionPayload_Any_JsonObject(obj: unknown): obj is TActionPayload_Any_JsonObject {\n return (\n isActionPayload_Request_JsonObject(obj) ||\n isActionPayload_Result_JsonObject(obj) ||\n isActionPayload_Progress_JsonObject(obj)\n );\n}\n","// Module-level stack tracking which local handler is currently in its synchronous execution phase.\n// Child actions dispatched synchronously from inside a handler capture the top of this stack\n// as their parentCuid. The stack is push/popped around each handler's synchronous phase only.\nconst _stack: string[] = [];\n\nexport function pushHandlerCuid(cuid: string): void {\n _stack.push(cuid);\n}\n\nexport function popHandlerCuid(): void {\n _stack.pop();\n}\n\nexport function peekHandlerCuid(): string | undefined {\n return _stack[_stack.length - 1];\n}\n","import { err_nice_action } from \"../../../../errors/err_nice_action\";\r\n\r\nexport const err_nice_external_client = err_nice_action.createChildDomain({\r\n domain: \"err_nice_external_client\",\r\n schema: {},\r\n});\r\n","import { err } from \"@nice-code/error\";\r\nimport { err_nice_external_client } from \"../Handler/PeerLink/Connector/err_nice_external_client\";\r\nimport type { ETransportShape } from \"./Transport.types\";\r\n\r\nexport enum EErrId_NiceTransport {\r\n timeout = \"timeout\",\r\n not_found = \"not_found\",\r\n unsupported = \"unsupported\",\r\n initialization_failed = \"initialization_failed\",\r\n send_failed = \"send_failed\",\r\n invalid_action_response = \"invalid_action_response\",\r\n}\r\n\r\nexport const err_nice_transport = err_nice_external_client.createChildDomain({\r\n domain: \"err_nice_transport\",\r\n schema: {\r\n [EErrId_NiceTransport.timeout]: err<{ timeout: number }>({\r\n message: ({ timeout }) => `ActionConnect transport timed out after ${timeout}ms.`,\r\n }),\r\n [EErrId_NiceTransport.not_found]: err<{\r\n actionId: string;\r\n }>({\r\n message: ({ actionId }) => `No connected transport found for action \"${actionId}\".`,\r\n }),\r\n [EErrId_NiceTransport.unsupported]: err<{ transportShapes: ETransportShape[] }>({\r\n message: ({ transportShapes }) =>\r\n `${transportShapes.length} Transport(s) [${transportShapes.join(\", \")}] found but returned \"unsupported\" status.`,\r\n }),\r\n [EErrId_NiceTransport.initialization_failed]: err<{\r\n actionId: string;\r\n }>({\r\n message: ({ actionId }) => `Transports found for action \"${actionId}\", but none are ready.`,\r\n }),\r\n [EErrId_NiceTransport.send_failed]: err<{\r\n actionState: string;\r\n actionId: string;\r\n httpStatusCode?: number;\r\n message?: string;\r\n }>({\r\n message: ({ actionId, httpStatusCode, message }) =>\r\n `Failed to send action \"${actionId}\" [${httpStatusCode ?? \"Unknown status\"}]: ${message ?? \"Unknown error\"}.`,\r\n httpStatusCode: ({ httpStatusCode }) => httpStatusCode ?? 500,\r\n }),\r\n [EErrId_NiceTransport.invalid_action_response]: err<{\r\n actionId: string;\r\n }>({\r\n message: ({ actionId }) => `Invalid action response JSON structure for action \"${actionId}\"`,\r\n }),\r\n },\r\n});\r\n","import type { NiceError } from \"@nice-code/error\";\r\nimport type { ClientCryptoKeyLink } from \"@nice-code/util\";\r\nimport type {\r\n IActionPayload_Request_JsonObject,\r\n IActionPayload_Result_JsonObject,\r\n TActionPayload_Any_Instance,\r\n TActionPayload_Any_JsonObject,\r\n} from \"../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport type { ActionPayload_Result } from \"../../ActionDefinition/Action/Payload/ActionPayload_Result\";\r\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\r\nimport type { IRuntimeCoordinate, RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\n// Carrier-neutral handshake (the file keeps its WS-era name until Phase 3 renames it to crypto/actionHandshake).\r\nimport type { ESecurityLevel } from \"./crypto/actionHandshake\";\r\nimport type { TransportConnection } from \"./TransportConnection\";\r\n\r\n/**\r\n * Carrier-shape class identity: the one carrier-invariant trait the selection layer reasons about —\r\n * whether a carrier can push unsolicited frames (`duplex`: WS/WebRTC/BLE/in-memory) or only answers a\r\n * request with a single correlated reply (`exchange`: HTTP). The concrete carrier kind (\"ws\", \"webrtc\",\r\n * \"http\", …) is a free-form {@link ITransportRouteInfo.carrierLabel} for display, not a class tag.\r\n */\r\nexport enum ETransportShape {\r\n duplex = \"duplex\",\r\n exchange = \"exchange\",\r\n}\r\n\r\n/**\r\n * Serializable, display-only description of how an action was routed through a transport. Stored on\r\n * the action's route items and shown in the devtools external-handler chips. Keep this free of\r\n * sensitive data (e.g. auth headers) — route items travel over the wire.\r\n */\r\nexport interface ITransportRouteInfo {\r\n /** Free-form carrier kind for the devtools chip — e.g. \"ws\", \"webrtc\", \"http\". */\r\n carrierLabel: string;\r\n /** Short label for chips, e.g. \"POST /resolve_action\" or \"ws host/resolve_action/ws\". */\r\n summary?: string;\r\n url?: string;\r\n method?: string;\r\n detail?: Record<string, string | number | boolean>;\r\n}\r\n\r\nexport interface IUpdateActionRunConfig_Output {\r\n timeout?: number;\r\n}\r\n\r\nexport type TUpdateActionRunConfig = (\r\n input: ITransportRouteActionParams & { timeout: number },\r\n) => IUpdateActionRunConfig_Output;\r\n\r\nexport interface IActionTransportReadyData_Base {\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n}\r\n\r\n/**\r\n * Client-side secure-channel config for a connector link — carrier-neutral (the secure session never\r\n * cared about the carrier). When present (and `securityLevel !== none`), the connection runs the\r\n * handshake during initialization and, at the `encrypted` level, encrypts every frame. The acceptor\r\n * side adds a negotiable level set + verify-key resolver on top of this (see Phase 3's `ISecureConfig`).\r\n */\r\nexport interface ISecureClientConfig {\r\n /** The level this client requests; the peer must allow it. */\r\n securityLevel: ESecurityLevel;\r\n /** This client's crypto identity (verify + exchange key pairs, optionally persisted). */\r\n link: ClientCryptoKeyLink;\r\n /** This client's runtime coordinate — its authenticated identity to the peer. */\r\n localCoordinate: IRuntimeCoordinate;\r\n /** Wire dictionary version; the peer rejects the handshake on a mismatch. */\r\n dictionaryVersion: string;\r\n}\r\n\r\n/**\r\n *\r\n * TRANSPORT READINESS RESPONSE\r\n *\r\n */\r\nexport enum ETransportStatus {\r\n uninitialized = \"uninitialized\",\r\n unsupported = \"unsupported\",\r\n initializing = \"initializing\",\r\n ready = \"ready\",\r\n failed = \"failed\",\r\n}\r\n\r\nexport interface ITransportStatusInfo_Base<S extends ETransportStatus> {\r\n status: S;\r\n}\r\n\r\nexport interface ITransportStatusInfo_Failed\r\n extends ITransportStatusInfo_Base<ETransportStatus.failed> {\r\n error: NiceError;\r\n timeFailed: number;\r\n}\r\n\r\nexport interface ITransportStatusInfo_Unsupported\r\n extends ITransportStatusInfo_Base<ETransportStatus.unsupported> {}\r\n\r\nexport interface ITransportStatusInfo_Ready<READY extends IActionTransportReadyData_Base>\r\n extends ITransportStatusInfo_Base<ETransportStatus.ready> {\r\n readyData: READY;\r\n}\r\n\r\nexport type TTransportInitializationFinishedInfo<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Ready<READY>\r\n | ITransportStatusInfo_Failed\r\n | ITransportStatusInfo_Unsupported;\r\n\r\nexport interface ITransportStatusInfo_Initializing<READY extends IActionTransportReadyData_Base>\r\n extends ITransportStatusInfo_Base<ETransportStatus.initializing> {\r\n timeStarted: number;\r\n initializationPromise: Promise<TTransportInitializationFinishedInfo<READY>>;\r\n}\r\n\r\nexport type TTransportStatusInfo<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Base<ETransportStatus.uninitialized>\r\n | ITransportStatusInfo_Unsupported\r\n | ITransportStatusInfo_Initializing<READY>\r\n | ITransportStatusInfo_Ready<READY>\r\n | ITransportStatusInfo_Failed;\r\n\r\nexport type TTransportStatusInfo_GetTransport_Output<READY extends IActionTransportReadyData_Base> =\r\n | ITransportStatusInfo_Base<ETransportStatus.uninitialized>\r\n | ITransportStatusInfo_Unsupported\r\n | Omit<ITransportStatusInfo_Initializing<READY>, \"timeStarted\">\r\n | ITransportStatusInfo_Ready<READY>\r\n | Omit<ITransportStatusInfo_Failed, \"timeFailed\">;\r\n\r\n/**\r\n *\r\n * TRANSPORT ROUTING\r\n *\r\n */\r\n\r\nexport interface ITransportRouteClientParams {\r\n localClient: RuntimeCoordinate;\r\n externalClient: RuntimeCoordinate;\r\n}\r\n\r\nexport interface ITransportRouteActionParams extends ITransportRouteClientParams {\r\n action: TActionPayload_Any_Instance<any, any>;\r\n}\r\n\r\nexport interface ITransportMethod_SendActionData_Input extends ITransportRouteActionParams {\r\n runningAction: RunningAction<any, any>;\r\n timeout: number;\r\n}\r\n\r\nexport interface ITransportDispatchAction<P> extends ITransportMethod_SendActionData_Input {\r\n params: P;\r\n}\r\n\r\nexport type TSendActionDataMethod = (input: ITransportMethod_SendActionData_Input) => void;\r\n\r\nexport type TSendReturnDataMethod = (\r\n payload: TActionPayload_Any_Instance<any, any>,\r\n /**\r\n * The local/external client pair this payload is being returned over. Bidirectional transports use\r\n * it to build the full route params their outgoing formatter expects (e.g. binary packing). Optional\r\n * so existing return-data implementations keep type-checking.\r\n */\r\n clients?: ITransportRouteClientParams,\r\n) => void;\r\n\r\nexport interface IActionTransportReadyData_Methods extends IActionTransportReadyData_Base {\r\n sendActionData: TSendActionDataMethod;\r\n /**\r\n * Optional — implement on bidirectional transports (WebSocket, Custom) to enable return-path\r\n * routing. When present, the runtime uses this to dispatch results and progress payloads directly\r\n * back to `originClient` without going through the original request transport.\r\n */\r\n sendReturnData?: TSendReturnDataMethod;\r\n addOnDisconnectListener?: (callback: () => void) => void;\r\n /**\r\n * Optional — implement on transports holding a long-lived connection (WebSocket, Custom) to close it\r\n * deliberately. Called by `ConnectorHandler.clearTransportCache()` so a teardown actually\r\n * releases the underlying socket instead of leaving it open until GC.\r\n */\r\n disconnect?: () => void;\r\n}\r\n\r\nexport interface IActionTransportReady {\r\n methods: IActionTransportReadyData_Methods;\r\n transport: TransportConnection;\r\n}\r\n\r\nexport type TTransportCache = Map<string, IActionTransportReady | Promise<IActionTransportReady>>;\r\n\r\nexport type TOnResolveIncomingRequest = (request: ActionPayload_Request<any>) => void;\r\nexport type TOnResolveIncomingRequestJson = (\r\n request: IActionPayload_Request_JsonObject<any>,\r\n) => void;\r\n\r\nexport type TOnResolveIncomingResponse = (response: ActionPayload_Result<any>) => void;\r\nexport type TOnResolveIncomingResponseJson = (\r\n response: IActionPayload_Result_JsonObject<any>,\r\n) => void;\r\n\r\nexport type TOnResolveAnyIncomingActionData = (\r\n actionData: ActionPayload_Request<any> | ActionPayload_Result<any>,\r\n) => void;\r\n\r\nexport type TOnResolveAnyIncomingActionData_Json = (\r\n actionData: TActionPayload_Any_JsonObject<any>,\r\n) => void;\r\n\r\nexport interface IActionTransportResolvers {\r\n onIncomingActionDataJson: TOnResolveAnyIncomingActionData_Json;\r\n}\r\n\r\nexport type TGetTransportFn<\r\n IN extends ITransportRouteActionParams,\r\n READY extends IActionTransportReadyData_Base,\r\n> = (input: IN) => TTransportStatusInfo_GetTransport_Output<READY>;\r\n\r\nexport interface IActionTransportInitialized<\r\n IN extends ITransportRouteActionParams,\r\n READY extends IActionTransportReadyData_Base,\r\n> {\r\n getTransportCacheKey?: (input: IN) => string[];\r\n /**\r\n * Optional availability gate, consulted by {@link ConnectionTransportManager} *before* cache-key\r\n * resolution and `getTransport`. When it returns `false`, this transport is treated as `unsupported`\r\n * for that action and the manager falls through to the next transport in preference order — without\r\n * opening the carrier or computing its cache key. Re-evaluated per action dispatch, so a transport can\r\n * become available later (e.g. once a session/connection precondition is met) with no reconnect. Omit =\r\n * always available.\r\n */\r\n isAvailable?: (input: IN) => boolean;\r\n getTransport: TGetTransportFn<IN, READY>;\r\n}\r\n\r\nexport interface IActionTransportDef<\r\n TYPE extends ETransportShape,\r\n INIT extends IActionTransportInitialized<any, any>,\r\n> {\r\n type: TYPE;\r\n initialize: () => INIT;\r\n}\r\n","import { EErrId_NiceTransport, err_nice_transport } from \"./err_nice_transport\";\r\nimport {\r\n ETransportStatus,\r\n type IActionTransportReady,\r\n type ITransportRouteActionParams,\r\n type TTransportCache,\r\n} from \"./Transport.types\";\r\nimport type { TransportConnection } from \"./TransportConnection\";\r\n\r\nexport class ConnectionTransportManager {\r\n private _transports: TransportConnection[] = [];\r\n\r\n constructor(private _cache: TTransportCache) {}\r\n\r\n addTransport(transport: TransportConnection) {\r\n this._transports.push(transport);\r\n }\r\n\r\n /**\r\n * The highest-priority transport (first declared). Used to label an action's route *before* the\r\n * transport has finished connecting — so a still-connecting action shows its (expected) destination\r\n * instead of an \"unknown\" hop. {@link getReadyTransport} still decides the real winner, and the\r\n * caller corrects the hop if a lower-priority transport ends up serving the action.\r\n */\r\n getPreferredTransport(): TransportConnection | undefined {\r\n return this._transports[0];\r\n }\r\n\r\n async getReadyTransport(\r\n routeActionParams: ITransportRouteActionParams,\r\n ): Promise<IActionTransportReady> {\r\n const action = routeActionParams.action;\r\n\r\n // The transports are declared in preference order. We honour that order strictly: a\r\n // higher-priority transport that is still *initializing* (e.g. a WebSocket warming up its\r\n // connection / handshake) takes precedence over a lower-priority transport that happens to be\r\n // synchronously ready right now (e.g. plain HTTP). A lower-priority transport is only used as a\r\n // fallback once every transport ahead of it has been exhausted (unsupported or failed). This is\r\n // what lets a transport \"warm up\" on the first action instead of racing — and quietly losing — to\r\n // an always-ready fallback.\r\n const candidates: Promise<IActionTransportReady>[] = [];\r\n const unavailableTransports: TransportConnection<any>[] = [];\r\n\r\n for (const transport of this._transports) {\r\n // Availability gate — consulted *before* cache-key resolution and `getTransport`. A transport that\r\n // declares itself unavailable for this action (e.g. a socket gated behind a runtime precondition)\r\n // is skipped entirely, so its cache key is never computed and its carrier is never opened.\r\n if (!transport.isAvailable(routeActionParams)) {\r\n unavailableTransports.push(transport);\r\n continue;\r\n }\r\n\r\n const cacheKey = transport.getCacheKey(routeActionParams);\r\n\r\n if (cacheKey != null) {\r\n const cached = this._cache.get(cacheKey);\r\n if (cached != null) {\r\n if (cached instanceof Promise) {\r\n candidates.push(cached);\r\n continue;\r\n }\r\n // A live, ready cached transport: nothing lower priority can outrank it, so stop scanning.\r\n candidates.push(Promise.resolve({ ...cached, transport }));\r\n break;\r\n }\r\n }\r\n\r\n const statusInfo = transport.getTransport(routeActionParams);\r\n\r\n if (statusInfo.status === ETransportStatus.ready) {\r\n const readyData = statusInfo.readyData;\r\n if (cacheKey != null) {\r\n const entry = { methods: readyData, transport };\r\n this._cache.set(cacheKey, entry);\r\n // Only evict if this exact entry is still cached — a socket that closes after the cache was\r\n // re-populated (e.g. a teardown then reconnect under the same key) must not drop the new one.\r\n readyData.addOnDisconnectListener?.(() => {\r\n if (this._cache.get(cacheKey) === entry) this._cache.delete(cacheKey);\r\n });\r\n }\r\n // A synchronously-ready transport is a terminal fallback: anything after it is unreachable.\r\n candidates.push(Promise.resolve({ methods: readyData, transport }));\r\n break;\r\n }\r\n\r\n if (statusInfo.status === ETransportStatus.unsupported) {\r\n unavailableTransports.push(transport);\r\n continue;\r\n }\r\n\r\n if (statusInfo.status === ETransportStatus.initializing) {\r\n const promise = statusInfo.initializationPromise\r\n .then((info): IActionTransportReady => {\r\n if (info.status === ETransportStatus.failed) {\r\n throw info.error;\r\n }\r\n\r\n if (info.status === ETransportStatus.unsupported) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.unsupported, {\r\n transportShapes: [transport.type],\r\n });\r\n }\r\n\r\n const readyData = info.readyData;\r\n const entry = { methods: readyData, transport };\r\n if (cacheKey != null) {\r\n // Adopt the cache slot only if our placeholder promise is still there. A teardown\r\n // (clearTransportCache) or a newer connection on the same key may have replaced it while\r\n // we were connecting — in that case this socket is orphaned, so close it instead of\r\n // clobbering the live entry. The awaiting dispatch then aborts cleanly on the closed socket.\r\n if (this._cache.get(cacheKey) === promise) {\r\n this._cache.set(cacheKey, entry);\r\n readyData.addOnDisconnectListener?.(() => {\r\n if (this._cache.get(cacheKey) === entry) this._cache.delete(cacheKey);\r\n });\r\n } else {\r\n readyData.disconnect?.();\r\n }\r\n }\r\n\r\n return entry;\r\n })\r\n .catch((e) => {\r\n // Only clear our own slot — a newer connection may already own the key.\r\n if (cacheKey != null && this._cache.get(cacheKey) === promise) {\r\n this._cache.delete(cacheKey);\r\n }\r\n throw e;\r\n });\r\n\r\n if (cacheKey != null) {\r\n this._cache.set(cacheKey, promise);\r\n }\r\n\r\n candidates.push(promise);\r\n }\r\n }\r\n\r\n if (candidates.length === 0) {\r\n if (unavailableTransports.length > 0) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.unsupported, {\r\n transportShapes: unavailableTransports.map((t) => t.type),\r\n });\r\n }\r\n\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.not_found, {\r\n actionId: action.id,\r\n });\r\n }\r\n\r\n // Resolve candidates in priority order, falling through to the next only when the preferred\r\n // transport actually fails. Initialization for every candidate was already kicked off above, so\r\n // they all warm up in parallel — we simply *select* the winner by declared preference.\r\n let lastError: unknown;\r\n for (const candidate of candidates) {\r\n try {\r\n return await candidate;\r\n } catch (e) {\r\n lastError = e;\r\n }\r\n }\r\n\r\n throw err_nice_transport\r\n .fromId(EErrId_NiceTransport.initialization_failed, {\r\n actionId: action.id,\r\n })\r\n .withOriginError(lastError);\r\n }\r\n}\r\n","import type {\n TAction_Any_JsonObject,\n TNarrowActionJsonTypeToActionInstanceType,\n} from \"../ActionDefinition/Action/Action.combined.types\";\nimport type { INiceActionIdAndDomain } from \"../ActionDefinition/Action/ActionBase.types\";\nimport type { ActionDomain } from \"../ActionDefinition/Domain/ActionDomain\";\nimport type { IActionDomain } from \"../ActionDefinition/Domain/ActionDomain.types\";\nimport { EErrId_NiceAction, err_nice_action } from \"../errors/err_nice_action\";\n\nexport class ActionDomainManager {\n private _domains: Map<string, ActionDomain<any>> = new Map();\n\n addDomain(domain: ActionDomain<any>): void {\n this._domains.set(domain.domain, domain);\n }\n\n getDomains(): ActionDomain<any>[] {\n return [...this._domains.values()];\n }\n\n verifyIsActionJson(action: INiceActionIdAndDomain<any>): void {\n if (typeof action.domain !== \"string\" || typeof action.id !== \"string\") {\n throw err_nice_action.fromId(EErrId_NiceAction.wire_not_action_data);\n }\n }\n\n getActionDomain<DOM extends IActionDomain, ACT extends INiceActionIdAndDomain<DOM>>(\n action: ACT,\n ): ActionDomain<DOM> | undefined {\n this.verifyIsActionJson(action);\n const domain = this._domains.get(action.domain) as ActionDomain<DOM>;\n\n if (!domain) {\n return undefined;\n }\n\n return domain;\n }\n\n getActionDomainOrThrow<DOM extends IActionDomain, ACT extends INiceActionIdAndDomain<DOM>>(\n action: ACT,\n ): ActionDomain<DOM> {\n this.verifyIsActionJson(action);\n const domain = this._domains.get(action.domain) as ActionDomain<DOM>;\n\n if (!domain) {\n throw err_nice_action.fromId(EErrId_NiceAction.domain_no_handler, {\n domain: action.domain,\n });\n }\n\n return domain;\n }\n\n hydrateActionPayload<\n D extends IActionDomain,\n ID extends keyof D[\"actionSchema\"] & string,\n A extends TAction_Any_JsonObject<D, ID>,\n >(actionJson: A): TNarrowActionJsonTypeToActionInstanceType<D, A, ID> {\n const domain = this.getActionDomainOrThrow(actionJson) as ActionDomain<D>;\n return domain.hydrateAnyAction(actionJson);\n }\n}\n","import type { INiceActionIdAndDomain } from \"../../ActionDefinition/Action/ActionBase.types\";\nimport type { ActionCore } from \"../../ActionDefinition/Action/Core/ActionCore\";\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\nimport type { IActionDomain } from \"../../ActionDefinition/Domain/ActionDomain.types\";\nimport { EErrId_NiceAction, err_nice_action } from \"../../errors/err_nice_action\";\nimport { ActionDomainManager } from \"../ActionDomainManager\";\nimport type { IHandleActionOptions } from \"../Handler/ActionHandler.types\";\nimport {\n EActionRouterContextType,\n type IActionRouterContext,\n type TMatchHandlerKey,\n} from \"./ActionRouter.types\";\n\nexport class ActionRouter<DATA> {\n readonly domainManager = new ActionDomainManager();\n private actionRouteData = new Map<TMatchHandlerKey, DATA[]>();\n private _context: IActionRouterContext;\n\n constructor(context: IActionRouterContext) {\n this._context = context;\n }\n\n // ---------------------------------------------------------------------------\n // Merge / copy\n // ---------------------------------------------------------------------------\n\n /** Copy all routes from another router into this one, replacing any overlapping keys. */\n mergeRouter(actionRouter: ActionRouter<DATA>): void {\n for (const domain of actionRouter.getDomains()) {\n this.domainManager.addDomain(domain);\n }\n for (const [matchKey, routeDataEntries] of actionRouter.actionRouteData.entries()) {\n this.actionRouteData.set(matchKey, [...routeDataEntries]);\n }\n }\n\n addDomainsFromOther(actionRouter: ActionRouter<DATA>): void {\n for (const domain of actionRouter.getDomains()) {\n this.domainManager.addDomain(domain);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Lookup\n // ---------------------------------------------------------------------------\n\n /** All FNs registered for an action, ID-specific entries first then domain wildcard. */\n getRouteDataEntriesForAction(action: { domain: string; id: string }): DATA[] {\n const idKey: TMatchHandlerKey = `dom[${action.domain}]id[${action.id}]`;\n const domKey: TMatchHandlerKey = `dom[${action.domain}]id[_]`;\n return [\n ...(this.actionRouteData.get(idKey) ?? []),\n ...(this.actionRouteData.get(domKey) ?? []),\n ];\n }\n\n /** First FN registered for an action (ID-specific beats domain wildcard). */\n getRouteDataForAction(action: INiceActionIdAndDomain): DATA | undefined {\n return this.getRouteDataEntriesForAction(action)[0];\n }\n\n private throwNoHandlerForAction(\n action: INiceActionIdAndDomain,\n context: IHandleActionOptions,\n ): never {\n if (this._context.contextType === EActionRouterContextType.handler_route) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\n domain: action.domain,\n actionId: action.id,\n specifiedClient: context.targetLocalRuntime?.coordinate,\n });\n }\n\n if (this._context.contextType === EActionRouterContextType.runtime_to_handler) {\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\n domain: action.domain,\n actionId: action.id,\n specifiedClient: this._context.runtime.coordinate,\n });\n }\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\n domain: action.domain,\n actionId: action.id,\n });\n }\n\n getRouteDataEntriesForActionOrThrow(\n action: INiceActionIdAndDomain,\n context: IHandleActionOptions,\n ): DATA[] {\n const entries = this.getRouteDataEntriesForAction(action);\n\n if (entries.length === 0) {\n this.throwNoHandlerForAction(action, context);\n }\n\n return entries;\n }\n\n getRouteDataForActionOrThrow(\n action: INiceActionIdAndDomain,\n context: IHandleActionOptions,\n ): DATA {\n const routeData = this.getRouteDataForAction(action);\n\n if (!routeData) {\n this.throwNoHandlerForAction(action, context);\n }\n\n return routeData;\n }\n\n /** All FNs stored under an exact match key. */\n getForKey(key: TMatchHandlerKey): readonly DATA[] {\n return this.actionRouteData.get(key) ?? [];\n }\n\n /** Every match key that has at least one registered FN. */\n getRegisteredKeys(): TMatchHandlerKey[] {\n return [...this.actionRouteData.keys()];\n }\n\n getDomains(): ActionDomain[] {\n return this.domainManager.getDomains();\n }\n\n // ---------------------------------------------------------------------------\n // Registration — for* (replace) and add* (accumulate)\n // ---------------------------------------------------------------------------\n\n /** Register a handler for all actions in a domain, replacing any existing one. */\n forDomain<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>, routeData: DATA): this {\n this.domainManager.addDomain(domain);\n this.actionRouteData.set(`dom[${domain.domain}]id[_]`, [routeData]);\n return this;\n }\n\n forAction<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM[\"actionSchema\"] & string>(\n action: ActionCore<ACT_DOM, ID>,\n routeData: DATA,\n ): this {\n return this.forActionId(action._domain, action.id, routeData);\n }\n\n /** Register a handler for a specific action, replacing any existing one. */\n forActionId<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM[\"actionSchema\"] & string>(\n domain: ActionDomain<ACT_DOM>,\n id: ID,\n routeData: DATA,\n ): this {\n this.domainManager.addDomain(domain);\n this.actionRouteData.set(`dom[${domain.domain}]id[${id}]`, [routeData]);\n return this;\n }\n\n /** Register one handler for several action IDs, replacing any existing ones. */\n forActionIds<\n ACT_DOM extends IActionDomain,\n IDS extends ReadonlyArray<keyof ACT_DOM[\"actionSchema\"] & string>,\n >(domain: ActionDomain<ACT_DOM>, ids: IDS, routeData: DATA): this {\n this.domainManager.addDomain(domain);\n for (const id of ids) {\n this.forActionId(domain, id, routeData);\n }\n return this;\n }\n\n /** Register per-action handlers from a cases map, replacing any existing ones. */\n forDomainActionCases<FOR_DOM extends IActionDomain>(\n domain: ActionDomain<FOR_DOM>,\n cases: { [ID in keyof FOR_DOM[\"actionSchema\"] & string]?: DATA },\n ): this {\n this.domainManager.addDomain(domain);\n for (const id of Object.keys(cases) as Array<keyof FOR_DOM[\"actionSchema\"] & string>) {\n const routeData = cases[id];\n if (routeData != null) {\n this.actionRouteData.set(`dom[${domain.domain}]id[${id}]`, [routeData]);\n }\n }\n return this;\n }\n\n /** Append a handler for all actions in a domain (accumulates alongside existing). */\n addForDomain<FOR_DOM extends IActionDomain>(\n domain: ActionDomain<FOR_DOM>,\n routeData: DATA,\n ): this {\n this.domainManager.addDomain(domain);\n this._push(`dom[${domain.domain}]id[_]`, routeData);\n return this;\n }\n\n /** Append a handler for a specific action (accumulates alongside existing). */\n addForAction<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM[\"actionSchema\"] & string>(\n domain: ActionDomain<ACT_DOM>,\n id: ID,\n routeData: DATA,\n ): this {\n this.domainManager.addDomain(domain);\n this._push(`dom[${domain.domain}]id[${id}]`, routeData);\n return this;\n }\n\n /** Append one handler for several action IDs (accumulates alongside existing). */\n addForActionIds<\n ACT_DOM extends IActionDomain,\n IDS extends ReadonlyArray<keyof ACT_DOM[\"actionSchema\"] & string>,\n >(domain: ActionDomain<ACT_DOM>, ids: IDS, routeData: DATA): this {\n this.domainManager.addDomain(domain);\n for (const id of ids) {\n this.addForAction(domain, id, routeData);\n }\n return this;\n }\n\n /** Append per-action handlers from a cases map (accumulates alongside existing). */\n addForDomainActionCases<FOR_DOM extends IActionDomain>(\n domain: ActionDomain<FOR_DOM>,\n cases: { [ID in keyof FOR_DOM[\"actionSchema\"] & string]?: DATA },\n ): this {\n this.domainManager.addDomain(domain);\n for (const id of Object.keys(cases) as Array<keyof FOR_DOM[\"actionSchema\"] & string>) {\n const routeData = cases[id];\n if (routeData != null) {\n this._push(`dom[${domain.domain}]id[${id}]`, routeData);\n }\n }\n return this;\n }\n\n /** Append a handler directly by its raw match key (used when the key is known ahead of time). */\n addForKey(key: TMatchHandlerKey, routeData: DATA): this {\n this._push(key, routeData);\n return this;\n }\n\n private _push(key: TMatchHandlerKey, routeData: DATA): void {\n const existing = this.actionRouteData.get(key);\n if (existing != null) {\n existing.push(routeData);\n } else {\n this.actionRouteData.set(key, [routeData]);\n }\n }\n}\n","import { nanoid } from \"nanoid\";\nimport type { IActionRouteItemHandler } from \"../../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\nimport type { IActionDomain } from \"../../ActionDefinition/Domain/ActionDomain.types\";\nimport { ActionRouter } from \"../Routing/ActionRouter\";\nimport type {\n EActionHandlerType,\n IActionHandler_Base,\n IHandleActionOptions,\n TActionHandler_Json,\n} from \"./ActionHandler.types\";\n\nexport abstract class ActionHandler<T extends EActionHandlerType>\n implements IActionHandler_Base<T>\n{\n abstract readonly handlerType: T;\n readonly cuid: string;\n abstract readonly actionRouter: ActionRouter<any>;\n\n constructor() {\n this.cuid = nanoid();\n }\n\n getActionRouter() {\n return this.actionRouter;\n }\n\n abstract handleActionRequest<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string,\n >(\n action: ActionPayload_Request<DOM, ID>,\n config?: IHandleActionOptions,\n ): Promise<RunningAction<DOM, ID>>;\n\n abstract toJsonObject(): TActionHandler_Json;\n\n abstract toHandlerRouteItem(...args: any[]): IActionRouteItemHandler;\n}\n","import type { ActionCore } from \"../../../ActionDefinition/Action/Core/ActionCore\";\r\nimport type {\r\n TActionPayload_Any_Instance,\r\n TActionPayload_Any_JsonObject,\r\n} from \"../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { ActionDomain } from \"../../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { IActionDomain } from \"../../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport type { ActionRuntime } from \"../../ActionRuntime\";\r\nimport { ActionRouter } from \"../../Routing/ActionRouter\";\r\nimport { EActionRouterContextType } from \"../../Routing/ActionRouter.types\";\r\nimport type { RuntimeCoordinate } from \"../../RuntimeCoordinate\";\r\nimport { ActionHandler } from \"../ActionHandler\";\r\nimport { EActionHandlerType, type IActionHandler_Peer } from \"../ActionHandler.types\";\r\n\r\n/**\r\n * Shared base for every handler that routes a domain set to/from *another runtime* (a \"peer\") — the\r\n * unified peer-link concept. Both specializations extend this as siblings, differing only in *who\r\n * establishes the connection*, which is a transport trait, not a routing one:\r\n *\r\n * - {@link ConnectorHandler} — **dial-out**: this runtime opens connection(s) to one peer\r\n * over a transport stack (with caching + fallback). The classic \"client → backend\" link.\r\n * - {@link AcceptorHandler} — **accept-in**: connections are accepted from many peers and fed in\r\n * via `receive()`; it keeps a per-connection registry and can push to any of them.\r\n *\r\n * To the runtime there is no \"client\" vs \"server\" — both are peer-link handlers (`handlerType =\r\n * external`) keyed to a peer coordinate, chosen by the return-path dispatch via {@link sendReturnPayload}.\r\n */\r\nexport abstract class PeerLinkHandler\r\n extends ActionHandler<EActionHandlerType.peer>\r\n implements IActionHandler_Peer\r\n{\r\n /** The peer runtime this handler links to (an env-only coordinate for an accept-in handler). */\r\n readonly peerClient: RuntimeCoordinate;\r\n readonly handlerType = EActionHandlerType.peer;\r\n\r\n /**\r\n * Whether this link can deliver an *unsolicited* frame to the peer (a result/progress pushed back on\r\n * the return path, or a `broadcast`). A duplex carrier (WebSocket/WebRTC/…) can; an exchange-only\r\n * carrier (HTTP) cannot — its reply must ride the response to its own request. The runtime's\r\n * return-path dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) skips handlers that can't\r\n * push, so an exchange-only handler is never asked to deliver one.\r\n */\r\n abstract readonly canPush: boolean;\r\n\r\n readonly actionRouter: ActionRouter<true> = new ActionRouter({\r\n contextType: EActionRouterContextType.handler_route,\r\n handler: this,\r\n });\r\n\r\n /** Listeners installed by the runtime (`resolveIncomingActionPayload`) for inbound peer frames. */\r\n private readonly _incomingActionDataListeners: ((\r\n json: TActionPayload_Any_JsonObject<any, any>,\r\n ) => void)[] = [];\r\n\r\n constructor(peerCoordinate: RuntimeCoordinate) {\r\n super();\r\n this.peerClient = peerCoordinate;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Routing — which domains/actions travel to this peer (shared by both specializations)\r\n // ---------------------------------------------------------------------------\r\n\r\n forDomain<FOR_DOM extends IActionDomain>(domain: ActionDomain<FOR_DOM>): this {\r\n this.actionRouter.forDomain(domain, true);\r\n return this;\r\n }\r\n\r\n forAction<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM[\"actionSchema\"] & string>(\r\n action: ActionCore<ACT_DOM, ID>,\r\n ): this {\r\n this.actionRouter.forAction(action, true);\r\n return this;\r\n }\r\n\r\n forActionIds<\r\n ACT_DOM extends IActionDomain,\r\n IDS extends ReadonlyArray<keyof ACT_DOM[\"actionSchema\"] & string>,\r\n >(domain: ActionDomain<ACT_DOM>, ids: IDS): this {\r\n this.actionRouter.forActionIds(domain, ids, true);\r\n return this;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Runtime binding — inbound peer frames\r\n // ---------------------------------------------------------------------------\r\n\r\n _setIncomingActionDataListener(\r\n listener: (json: TActionPayload_Any_JsonObject<any, any>) => void,\r\n ): void {\r\n this._incomingActionDataListeners.push(listener);\r\n }\r\n\r\n /** Hand a decoded inbound frame to the runtime (called by each specialization's receive path). */\r\n protected _emitIncoming(json: TActionPayload_Any_JsonObject<any, any>): void {\r\n for (const listener of this._incomingActionDataListeners) listener(json);\r\n }\r\n\r\n /**\r\n * Dispatch a result/progress payload back to the action's origin peer over this link. The runtime's\r\n * return-path dispatch calls it on whichever peer-link handler best reaches `originClient`. Returns\r\n * `true` if it was sent, `false` if no channel was available.\r\n */\r\n abstract sendReturnPayload(\r\n payload: TActionPayload_Any_Instance<any, any>,\r\n config: { targetLocalRuntime: ActionRuntime },\r\n ): Promise<boolean>;\r\n\r\n /**\r\n * Whether this handler currently holds a *live* connection bound to `origin`. The runtime's return-path\r\n * dispatch ({@link ActionRuntime.getReturnHandlerForOrigin}) prefers a handler that owns the origin's\r\n * connection over a mere coordinate match, so with several duplex acceptors a result/push routes back\r\n * over the carrier the client connected on. Defaults to `false`; an acceptor overrides it from its\r\n * connection registry.\r\n */\r\n ownsLiveConnectionFor(_origin: RuntimeCoordinate): boolean {\r\n return false;\r\n }\r\n\r\n /** Release any long-lived connections this handler owns (a teardown). No-op by default. */\r\n clearTransportCache(): void {}\r\n}\r\n","import type { IActionRouteItem } from \"../../../../ActionDefinition/Action/Context/ActionContext.types\";\r\nimport type {\r\n IActionRouteItemHandler,\r\n TActionPayload_Any_Instance,\r\n} from \"../../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport { EActionPayloadType } from \"../../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { ActionPayload_Request } from \"../../../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport { RunningAction } from \"../../../../ActionDefinition/Action/RunningAction\";\r\nimport type { IActionDomain } from \"../../../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport { EActionResponseMode } from \"../../../../ActionDefinition/Schema/ActionSchema\";\r\nimport { DEFAULT_TRANSPORT_TIMEOUT } from \"../../../../nice_action.static\";\r\nimport { ActionRuntime } from \"../../../ActionRuntime\";\r\nimport { peekHandlerCuid } from \"../../../HandlerCallStack\";\r\nimport { ConnectionTransportManager } from \"../../../Transport/ConnectionTransportManager\";\r\nimport {\r\n ETransportShape,\r\n type ITransportMethod_SendActionData_Input,\r\n type ITransportRouteActionParams,\r\n type TTransportCache,\r\n} from \"../../../Transport/Transport.types\";\r\nimport type { TransportConnection } from \"../../../Transport/TransportConnection\";\r\nimport type { IActionHandler_Peer_Json, IHandleActionOptions } from \"../../ActionHandler.types\";\r\nimport { PeerLinkHandler } from \"../../PeerLink/PeerLinkHandler\";\r\nimport type { IConnectorHandlerConfig } from \"./ConnectorHandler.types\";\r\n\r\n/**\r\n * Dial-out peer link: this runtime opens connection(s) to one peer over a transport stack (cached, with\r\n * preference-ordered fallback). The classic \"client → backend\" handler — but to the runtime it's just a\r\n * {@link PeerLinkHandler} like the accept-in server one.\r\n */\r\nexport class ConnectorHandler extends PeerLinkHandler {\r\n /**\r\n * Dial-out can receive (and so return) an unsolicited push only over a duplex transport. With every\r\n * transport exchange-only (HTTP), the peer can never push to us — so this link can't deliver one back.\r\n */\r\n readonly canPush: boolean;\r\n\r\n private _defaultTimeout: number;\r\n private _transportCache: TTransportCache = new Map();\r\n private transportManager = new ConnectionTransportManager(this._transportCache);\r\n\r\n constructor({\r\n runtimeCoordinate: peerSpecifier,\r\n transports,\r\n defaultTimeout,\r\n }: IConnectorHandlerConfig) {\r\n super(peerSpecifier);\r\n\r\n this._defaultTimeout = defaultTimeout ?? DEFAULT_TRANSPORT_TIMEOUT;\r\n this.canPush = transports.some((transport) => transport.type === ETransportShape.duplex);\r\n\r\n for (const transport of transports) {\r\n const connection = transport._createConnection({\r\n resolvers: {\r\n onIncomingActionDataJson: (json) => this._emitIncoming(json),\r\n },\r\n });\r\n connection.definition = transport;\r\n this.transportManager.addTransport(connection);\r\n }\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Action handling\r\n // ---------------------------------------------------------------------------\r\n\r\n async handleActionRequest<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n >(\r\n action: ActionPayload_Request<DOM, ID>,\r\n config?: IHandleActionOptions,\r\n ): Promise<RunningAction<DOM, ID>> {\r\n const localRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();\r\n const localClient = localRuntime.coordinate;\r\n\r\n const incomingTimeout = config?.timeout ?? this._defaultTimeout;\r\n\r\n // Capture parent + call site synchronously — once we await the transport the call stack is gone.\r\n const parentCuid = peekHandlerCuid();\r\n const callSite = action._callSite ?? new Error().stack;\r\n\r\n const routeParams: ITransportRouteActionParams = {\r\n action,\r\n localClient,\r\n externalClient: this.peerClient,\r\n };\r\n\r\n // Record the route hop up-front, labelled with the highest-priority transport, so the action shows\r\n // its (expected) destination — the runtime + e.g. \"ws → backend\" — the moment it appears, instead\r\n // of an \"unknown\" hop while the transport is still connecting. `_dispatchWhenTransportReady`\r\n // corrects it once `getReadyTransport` picks the real winner (which can differ if the preferred\r\n // transport is unavailable and a fallback serves the action).\r\n const preferredTransport = this.transportManager.getPreferredTransport();\r\n const routeItem: IActionRouteItem | undefined =\r\n preferredTransport != null\r\n ? {\r\n runtime: localClient,\r\n handler: this.toHandlerRouteItem(preferredTransport, routeParams),\r\n time: Date.now(),\r\n }\r\n : undefined;\r\n if (routeItem != null) action.context.addRouteItem(routeItem);\r\n\r\n // Create + register the RunningAction up-front, before acquiring the transport. Acquiring it can\r\n // take real time (opening a WebSocket + running its handshake on the first action), and that wait\r\n // is part of the action's running lifecycle — not a precondition for the action to exist. Creating\r\n // it now means observers (devtools, the dispatching domain's listeners) see the action the moment\r\n // it's initiated and watch it move through \"connecting → sent → finished\", instead of it only\r\n // popping into existence — already mid-flight or done — once the socket is ready.\r\n const runningAction = new RunningAction<DOM, ID>({\r\n context: action.context,\r\n request: action,\r\n parentCuid,\r\n callSite,\r\n });\r\n localRuntime.registerRunningAction(runningAction);\r\n\r\n // Resolve the transport and dispatch in the background so the RunningAction can be returned (and\r\n // observed) immediately. Any failure along the way aborts the action, surfacing through its result\r\n // and in the devtools just as a synchronous send failure would.\r\n void this._dispatchWhenTransportReady(runningAction, routeParams, routeItem, incomingTimeout);\r\n\r\n return runningAction;\r\n }\r\n\r\n private async _dispatchWhenTransportReady<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n >(\r\n runningAction: RunningAction<DOM, ID>,\r\n routeParams: ITransportRouteActionParams,\r\n routeItem: IActionRouteItem | undefined,\r\n incomingTimeout: number,\r\n ): Promise<void> {\r\n const action = routeParams.action;\r\n try {\r\n const { methods, transport } = await this.transportManager.getReadyTransport(routeParams);\r\n\r\n // Correct the hop to the transport that actually serves the action (the preferred one may have\r\n // been unavailable). Mutated in place so the action wire and the devtools \"finished\" view reflect\r\n // the real route.\r\n const handlerRouteItem = this.toHandlerRouteItem(transport, routeParams);\r\n if (routeItem != null) {\r\n routeItem.handler = handlerRouteItem;\r\n routeItem.time = Date.now();\r\n } else {\r\n action.context.addRouteItem({\r\n runtime: routeParams.localClient,\r\n handler: handlerRouteItem,\r\n time: Date.now(),\r\n });\r\n }\r\n\r\n const sendInput: ITransportMethod_SendActionData_Input = {\r\n ...routeParams,\r\n runningAction,\r\n timeout: incomingTimeout,\r\n };\r\n\r\n if (action.type === EActionPayloadType.request && methods.updateRunConfig != null) {\r\n const runConfig = methods.updateRunConfig(sendInput);\r\n sendInput.timeout = runConfig?.timeout ?? incomingTimeout;\r\n }\r\n\r\n methods.sendActionData(sendInput);\r\n\r\n // Fire-and-forget: no reply correlates back, so resolve the running action on send instead of\r\n // leaving it pending until it times out.\r\n if (\r\n action.type === EActionPayloadType.request &&\r\n action.schema.responseMode === EActionResponseMode.none\r\n ) {\r\n runningAction._completeWithResult(\r\n (action as ActionPayload_Request<any, any>).successResult(undefined),\r\n );\r\n }\r\n } catch (err) {\r\n runningAction._abort(err);\r\n }\r\n }\r\n\r\n /**\r\n * Dispatch a result or progress payload directly back to the external client via the best\r\n * available bidirectional transport (WebSocket / Custom). Used for return-path routing when the\r\n * local runtime recognises that it has a direct channel to the action's originClient.\r\n *\r\n * Returns `true` if the payload was sent, `false` if no suitable transport was available.\r\n */\r\n async sendReturnPayload(\r\n payload: TActionPayload_Any_Instance<any, any>,\r\n config: { targetLocalRuntime: ActionRuntime },\r\n ): Promise<boolean> {\r\n const localClient = config.targetLocalRuntime.coordinate;\r\n try {\r\n const { methods } = await this.transportManager.getReadyTransport({\r\n action: payload,\r\n localClient,\r\n externalClient: this.peerClient,\r\n });\r\n if (methods.sendReturnData == null) return false;\r\n methods.sendReturnData(payload, { localClient, externalClient: this.peerClient });\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n toJsonObject(): IActionHandler_Peer_Json {\r\n return {\r\n type: this.handlerType,\r\n client: this.peerClient,\r\n };\r\n }\r\n\r\n toHandlerRouteItem(\r\n transport: TransportConnection,\r\n input: ITransportRouteActionParams,\r\n ): IActionRouteItemHandler {\r\n return {\r\n type: this.handlerType,\r\n client: this.peerClient,\r\n transOrd: transport.transOrd,\r\n transShape: transport.type,\r\n transInfo: transport.getRouteInfo(input),\r\n };\r\n }\r\n\r\n clearTransportCache() {\r\n // Close any live long-lived connection (e.g. a WebSocket) before dropping it, so a teardown\r\n // actually releases the socket instead of leaving it open until GC. HTTP/stateless transports\r\n // don't implement `disconnect`, so they're simply forgotten.\r\n for (const entry of this._transportCache.values()) {\r\n if (entry instanceof Promise) {\r\n entry.then((ready) => ready.methods.disconnect?.()).catch(() => {});\r\n } else {\r\n entry.methods.disconnect?.();\r\n }\r\n }\r\n this._transportCache.clear();\r\n }\r\n}\r\n\r\nexport const createConnectorHandler = (config: IConnectorHandlerConfig) => {\r\n return new ConnectorHandler(config);\r\n};\r\n","import { castNiceError } from \"@nice-code/error\";\r\nimport { nanoid } from \"nanoid\";\r\nimport type { ActionCore } from \"../ActionDefinition/Action/Core/ActionCore\";\r\nimport type { TActionPayload_Any_Instance } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport {\r\n EActionPayloadType,\r\n type TActionPayload_Any_JsonObject,\r\n} from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport { RunningAction } from \"../ActionDefinition/Action/RunningAction\";\r\nimport {\r\n ERunningActionFinishedType,\r\n ERunningActionUpdateType,\r\n} from \"../ActionDefinition/Action/RunningAction.types\";\r\nimport type { ActionDomain } from \"../ActionDefinition/Domain/ActionDomain\";\r\nimport type { IActionDomain } from \"../ActionDefinition/Domain/ActionDomain.types\";\r\nimport { EActionResponseMode } from \"../ActionDefinition/Schema/ActionSchema\";\r\nimport { EErrId_NiceAction, err_nice_action } from \"../errors/err_nice_action\";\r\nimport { UNSET_RUNTIME_ENV_ID } from \"../nice_action.static\";\r\nimport { getAssumedRuntimeInfo } from \"../utils/getAssumedRuntimeEnvironment\";\r\nimport { isActionPayload_Any_JsonObject } from \"../utils/isActionPayload_Any_JsonObject\";\r\nimport type { IRuntimeMeta, TActionRuntimeHandler } from \"./ActionRuntime.types\";\r\nimport {\r\n EActionHandlerType,\r\n type IHandleActionOptions,\r\n type TActionHandler,\r\n} from \"./Handler/ActionHandler.types\";\r\nimport { ConnectorHandler } from \"./Handler/PeerLink/Connector/ConnectorHandler\";\r\nimport type { PeerLinkHandler } from \"./Handler/PeerLink/PeerLinkHandler\";\r\nimport { ActionRouter } from \"./Routing/ActionRouter\";\r\nimport { EActionRouterContextType } from \"./Routing/ActionRouter.types\";\r\nimport { type IRuntimeCoordinateSpecifics, RuntimeCoordinate } from \"./RuntimeCoordinate\";\r\nimport type { Transport } from \"./Transport/Transport\";\r\n\r\nexport class ActionRuntime {\r\n private _coordinate: RuntimeCoordinate;\r\n readonly timeCreated: number;\r\n readonly runtimeInfo: IRuntimeMeta = getAssumedRuntimeInfo();\r\n private readonly actionRouter: ActionRouter<TActionRuntimeHandler>;\r\n private readonly _pendingRunningActions: Map<string, RunningAction<any, any>> = new Map();\r\n private readonly _registeredPeerHandlers: PeerLinkHandler[] = [];\r\n private _applied = false;\r\n\r\n static getDefault(): ActionRuntime {\r\n return getDefaultActionRuntime();\r\n }\r\n\r\n constructor(coordinate: RuntimeCoordinate) {\r\n this._coordinate = coordinate.specifyIfUnset({\r\n insId: nanoid(14),\r\n });\r\n this.timeCreated = Date.now();\r\n\r\n this.actionRouter = new ActionRouter({\r\n contextType: EActionRouterContextType.runtime_to_handler,\r\n runtime: this,\r\n });\r\n }\r\n\r\n get coordinate(): RuntimeCoordinate {\r\n return this._coordinate;\r\n }\r\n\r\n specifyRuntimeCoordinate(specifics: IRuntimeCoordinateSpecifics & { envId?: string }): void {\r\n if (specifics.envId != null && this._coordinate.envId !== specifics.envId) {\r\n throw err_nice_action.fromId(EErrId_NiceAction.not_implemented, {\r\n label: `updating RuntimeCoordinate with a different \"envId\" (\"${this._coordinate.envId}\" → \"${specifics.envId}\")`,\r\n });\r\n }\r\n\r\n this._coordinate = this._coordinate.specify(specifics);\r\n this.apply();\r\n }\r\n\r\n registerRunningAction(ra: RunningAction<any, any>): void {\r\n this._pendingRunningActions.set(ra.cuid, ra);\r\n ra.addUpdateListeners([\r\n (update) => {\r\n if (update.type === ERunningActionUpdateType.finished) {\r\n this._pendingRunningActions.delete(ra.cuid);\r\n }\r\n },\r\n ]);\r\n }\r\n\r\n resolveIncomingActionPayload(json: TActionPayload_Any_JsonObject<any, any>): void {\r\n if (json.type === EActionPayloadType.request) {\r\n this.handleActionPayloadWire(json).catch((err) => {\r\n console.error(\r\n `[ActionRuntime] Incoming action [${json.domain}:${json.id}:${json.form}:${json.type}] unhandled:`,\r\n err,\r\n );\r\n });\r\n return;\r\n }\r\n this._pendingRunningActions.get(json.context.cuid)?._resolveFromJson(json as any);\r\n }\r\n\r\n /**\r\n * Handle an incoming action wire (e.g. from a transport layer), route it to\r\n * the correct handler, and return the response. The most specific handler\r\n * match is chosen (action-ID-specific beats domain-wildcard).\r\n */\r\n async handleActionPayloadWire<\r\n D extends IActionDomain,\r\n ID extends keyof D[\"actionSchema\"] & string,\r\n >(wire: TActionPayload_Any_JsonObject<D, ID>): Promise<RunningAction<D, ID>>;\r\n async handleActionPayloadWire(wire: unknown): Promise<RunningAction<any, any>>;\r\n async handleActionPayloadWire(wire: unknown): Promise<RunningAction<any, any>> {\r\n let action: TActionPayload_Any_Instance<any, any> | undefined;\r\n\r\n if (isActionPayload_Any_JsonObject(wire)) {\r\n const domain = this.actionRouter.domainManager.getActionDomainOrThrow(wire);\r\n action = domain.hydrateAnyAction(wire) as TActionPayload_Any_Instance<any, any>;\r\n }\r\n\r\n if (action == null) {\r\n throw err_nice_action.fromId(EErrId_NiceAction.wire_not_action_data);\r\n }\r\n\r\n return this.handleActionPayload(action);\r\n }\r\n\r\n async handleActionPayload<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n >(\r\n action: TActionPayload_Any_Instance<DOM, ID>,\r\n options?: Omit<IHandleActionOptions, \"targetLocalRuntime\">,\r\n ): Promise<RunningAction<DOM, ID>> {\r\n if (action.type === EActionPayloadType.request) {\r\n // This is the inbound entrypoint (server receiving a wire, or a bidirectional\r\n // transport pushing an action to this client). Unlike the local-dispatch path\r\n // (`runAction`), the handler doesn't attach the domain's action observers, so\r\n // wire them on here — otherwise inbound actions never surface in devtools.\r\n const observers = action.context._domain._collectActionObservers();\r\n\r\n let handlerForAction: TActionHandler;\r\n try {\r\n handlerForAction = this.getHandlerForActionOrThrow(action, options);\r\n } catch (err) {\r\n const runningAction = new RunningAction<DOM, ID>({\r\n context: action.context,\r\n request: action,\r\n });\r\n runningAction.addUpdateListeners(observers);\r\n runningAction._completeWithResult(action.errorResult(castNiceError(err) as any));\r\n return runningAction;\r\n }\r\n\r\n const runningAction = await handlerForAction.handleActionRequest(action, {\r\n ...options,\r\n targetLocalRuntime: this,\r\n });\r\n runningAction.addUpdateListeners(observers);\r\n this._trySetupReturnDispatch(runningAction);\r\n return runningAction;\r\n }\r\n\r\n throw err_nice_action.fromId(EErrId_NiceAction.not_implemented, {\r\n label: `Handling incoming action payloads of type \"${action.type}\"`,\r\n });\r\n }\r\n\r\n /**\r\n * @internal\r\n *\r\n * Return the first handler registered for the given action, or `undefined`\r\n * if none has been registered (action-ID-specific beats domain-wildcard).\r\n */\r\n _getHandlerForAction<ACT extends TActionPayload_Any_Instance<any, any>>(\r\n action: ACT,\r\n options?: Omit<IHandleActionOptions, \"targetLocalRuntime\">,\r\n ): TActionHandler | undefined {\r\n const handlers = this.actionRouter.getRouteDataEntriesForAction(action);\r\n const targetPeer = options?.targetPeer;\r\n\r\n const possibleHandlers = handlers.filter((handler) => {\r\n if (handler.handlerType === EActionHandlerType.peer) {\r\n if (targetPeer && !targetPeer.isSameFor(handler.peerClient).id) {\r\n return false;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n if (targetPeer != null) {\r\n return false;\r\n }\r\n\r\n if (action.type === EActionPayloadType.request) {\r\n return true;\r\n }\r\n\r\n return false;\r\n });\r\n\r\n if (possibleHandlers.length === 0) {\r\n return undefined;\r\n }\r\n\r\n const scoringPeer = targetPeer ?? RuntimeCoordinate.unknown;\r\n\r\n let handlerScore = -1;\r\n let handler: TActionHandler | undefined;\r\n\r\n for (const possibleHandler of possibleHandlers) {\r\n // A local handler always wins over any external one: registering a local handler for an action\r\n // means \"execute it here\" (the \"handle locally if you can, else forward\" pattern, and the path\r\n // by which an inbound push on a bidirectional domain is handled rather than re-forwarded). This\r\n // is independent of registration order — otherwise an external `forDomain` registered first could\r\n // shadow a local handler for the same action.\r\n if (possibleHandler.handlerType === EActionHandlerType.local) {\r\n return possibleHandler;\r\n }\r\n\r\n if (possibleHandler.handlerType === EActionHandlerType.peer) {\r\n const score = scoringPeer.similarityLevel(possibleHandler.peerClient);\r\n if (score > handlerScore) {\r\n handlerScore = score;\r\n handler = possibleHandler;\r\n }\r\n }\r\n }\r\n\r\n return handler;\r\n }\r\n\r\n getHandlerForActionOrThrow<ACT extends TActionPayload_Any_Instance<any, any>>(\r\n action: ACT,\r\n options?: Omit<IHandleActionOptions, \"localRuntime\">,\r\n ): TActionHandler {\r\n const handler = this._getHandlerForAction(action, options);\r\n\r\n if (handler == null) {\r\n throw err_nice_action.fromId(EErrId_NiceAction.no_action_execution_handler, {\r\n actionId: action.id,\r\n domain: action.domain,\r\n specifiedClient: options?.targetPeer,\r\n });\r\n }\r\n\r\n return handler;\r\n }\r\n\r\n /**\r\n * Register one or more handlers. Each handler's own `actionRouter` defines\r\n * which domains/actions it handles — those routing keys are mirrored into\r\n * this runtime's router so the same action can be served by multiple handlers.\r\n * Duplicate registrations (same handler cuid for the same key) are skipped.\r\n */\r\n addHandlers(handlers: TActionRuntimeHandler[]): this {\r\n for (const handler of handlers) {\r\n if (handler.handlerType === EActionHandlerType.peer) {\r\n handler._setIncomingActionDataListener((json) => this.resolveIncomingActionPayload(json));\r\n this._registeredPeerHandlers.push(handler);\r\n }\r\n\r\n const handlerRouter = handler.getActionRouter();\r\n this.actionRouter.addDomainsFromOther(handlerRouter);\r\n\r\n if (this._applied) {\r\n this.apply();\r\n }\r\n\r\n for (const key of handlerRouter.getRegisteredKeys()) {\r\n const alreadyRegistered = this.actionRouter\r\n .getForKey(key)\r\n .some((h) => h.cuid === handler.cuid);\r\n if (!alreadyRegistered) {\r\n this.actionRouter.addForKey(key, handler);\r\n }\r\n }\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * @internal Low-level primitive — the public way to open a connection is `connectChannel`, which\r\n * derives routing from a channel and binds the crypto identity for you. This stays as the raw building\r\n * block it sits on (it restates domain lists by hand) and is not part of the supported surface.\r\n *\r\n * Declare an external \"backend client\" in one call: build an\r\n * {@link ConnectorHandler} for `externalCoordinate` carrying the given\r\n * `transports`, route the listed `domains`/`actions` to it, register it (plus any\r\n * `localHandlers` — e.g. server→client push handlers that share the same channel)\r\n * on this runtime, and `apply()`. Returns the external handler so the caller can\r\n * later `clearTransportCache()` it.\r\n */\r\n connectTo(\r\n externalCoordinate: RuntimeCoordinate,\r\n options: {\r\n transports: Transport[];\r\n domains?: ActionDomain<any>[];\r\n actions?: ActionCore<any, any>[];\r\n localHandlers?: TActionRuntimeHandler[];\r\n defaultTimeout?: number;\r\n },\r\n ): ConnectorHandler {\r\n const handler = new ConnectorHandler({\r\n runtimeCoordinate: externalCoordinate,\r\n transports: options.transports,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n\r\n for (const domain of options.domains ?? []) {\r\n handler.forDomain(domain);\r\n }\r\n for (const action of options.actions ?? []) {\r\n handler.forAction(action);\r\n }\r\n\r\n this.addHandlers([handler, ...(options.localHandlers ?? [])]);\r\n this.apply();\r\n\r\n return handler;\r\n }\r\n\r\n private applyRuntimeForDomain(domain: ActionDomain<any>): void {\r\n const rootDomain = domain.rootDomain;\r\n if (!rootDomain._hasRuntime(this)) {\r\n rootDomain._registerRuntime(this);\r\n }\r\n }\r\n\r\n /**\r\n * Register this runtime with all root domains covered by its currently-added handlers,\r\n * making it eligible to execute actions dispatched from those domains.\r\n * After apply() is called, any subsequent addHandlers() calls also auto-register.\r\n */\r\n apply(): this {\r\n this._applied = true;\r\n for (const domain of this.actionRouter.getDomains()) {\r\n this.applyRuntimeForDomain(domain);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Find the best registered external handler that can reach `originClient` directly.\r\n * Used to locate the return-path channel for dispatching results back to the action origin.\r\n * Returns `undefined` if no handler matches (score > 0 required, i.e. at least id must match).\r\n *\r\n * A handler that currently holds the origin's *live* connection always wins over a mere coordinate\r\n * match — so with several duplex acceptors (e.g. WS + WebRTC) a result/push routes back over the carrier\r\n * the client actually connected on, never a same-coordinate sibling that lacks the socket. Only when no\r\n * handler owns a live connection do we fall back to the plain best-coordinate-score pick (the\r\n * single-acceptor and connector-only cases, unchanged).\r\n */\r\n getReturnHandlerForOrigin(originClient: RuntimeCoordinate): PeerLinkHandler | undefined {\r\n if (originClient.envId === UNSET_RUNTIME_ENV_ID) return undefined;\r\n\r\n let bestScore = -1;\r\n let bestHandler: PeerLinkHandler | undefined;\r\n let bestOwnedScore = -1;\r\n let bestOwnedHandler: PeerLinkHandler | undefined;\r\n\r\n for (const handler of this._registeredPeerHandlers) {\r\n // Only a push-capable (duplex) link can deliver an unsolicited result back; an exchange-only\r\n // (HTTP) handler returns its reply inline on the request, so skip it as a return-path candidate.\r\n if (!handler.canPush) continue;\r\n const score = originClient.similarityLevel(handler.peerClient);\r\n if (score > bestScore) {\r\n bestScore = score;\r\n bestHandler = handler;\r\n }\r\n if (score > bestOwnedScore && handler.ownsLiveConnectionFor(originClient)) {\r\n bestOwnedScore = score;\r\n bestOwnedHandler = handler;\r\n }\r\n }\r\n\r\n if (bestOwnedHandler != null && bestOwnedScore > 0) return bestOwnedHandler;\r\n return bestScore > 0 ? bestHandler : undefined;\r\n }\r\n\r\n resetRuntime(): void {\r\n for (const ra of this._pendingRunningActions.values()) {\r\n ra._abort(err_nice_action.fromId(EErrId_NiceAction.runtime_reset));\r\n }\r\n\r\n for (const handler of this._registeredPeerHandlers) {\r\n handler.clearTransportCache();\r\n }\r\n }\r\n\r\n private _trySetupReturnDispatch(runningAction: RunningAction<any, any>): void {\r\n // Fire-and-forget actions get no reply — the sender never waits for one. Skip the return path\r\n // entirely (the inbound push was handled locally; that's the whole contract).\r\n if (runningAction.context.schema.responseMode === EActionResponseMode.none) {\r\n return;\r\n }\r\n\r\n const originClient = runningAction.context.originClient;\r\n\r\n if (\r\n originClient.envId === UNSET_RUNTIME_ENV_ID ||\r\n originClient.isSameFor(this._coordinate).id\r\n ) {\r\n return;\r\n }\r\n\r\n runningAction.addUpdateListeners([\r\n (update) => {\r\n if (\r\n update.type === ERunningActionUpdateType.finished &&\r\n update.finishType === ERunningActionFinishedType.success\r\n ) {\r\n const returnHandler = this.getReturnHandlerForOrigin(originClient);\r\n returnHandler\r\n ?.sendReturnPayload(update.response, { targetLocalRuntime: this })\r\n .catch(() => {});\r\n }\r\n },\r\n ]);\r\n }\r\n}\r\n\r\nconst runtimeState: {\r\n defaultLocalRuntime?: ActionRuntime;\r\n assumedRuntimeInfo?: IRuntimeMeta;\r\n} = {\r\n defaultLocalRuntime: undefined,\r\n assumedRuntimeInfo: undefined,\r\n};\r\n\r\nfunction getDefaultActionRuntime(): ActionRuntime {\r\n if (runtimeState.assumedRuntimeInfo == null) {\r\n runtimeState.assumedRuntimeInfo = getAssumedRuntimeInfo();\r\n }\r\n\r\n if (runtimeState.defaultLocalRuntime == null) {\r\n runtimeState.defaultLocalRuntime = new ActionRuntime(\r\n RuntimeCoordinate.unknown.specify({\r\n perId: `${runtimeState.assumedRuntimeInfo?.runtimeName ?? \"unknown\"}-runtime`,\r\n }),\r\n );\r\n }\r\n\r\n return runtimeState.defaultLocalRuntime;\r\n}\r\n","import { castNiceError, isNiceErrorObject, NiceError } from \"@nice-code/error\";\nimport type { ActionCore } from \"../../../ActionDefinition/Action/Core/ActionCore\";\nimport type { IActionRouteItemHandler } from \"../../../ActionDefinition/Action/Payload/ActionPayload.types\";\nimport { ActionPayload_Request } from \"../../../ActionDefinition/Action/Payload/ActionPayload_Request\";\nimport { ActionPayload_Result } from \"../../../ActionDefinition/Action/Payload/ActionPayload_Result\";\nimport { RunningAction } from \"../../../ActionDefinition/Action/RunningAction\";\nimport type { ActionDomain } from \"../../../ActionDefinition/Domain/ActionDomain\";\nimport type { IActionDomain } from \"../../../ActionDefinition/Domain/ActionDomain.types\";\nimport { EErrId_NiceAction, err_nice_action } from \"../../../errors/err_nice_action\";\nimport { isActionPayload_Result_JsonObject } from \"../../../utils/isActionPayload_Result_JsonObject\";\nimport { ActionRuntime } from \"../../ActionRuntime\";\nimport { peekHandlerCuid, popHandlerCuid, pushHandlerCuid } from \"../../HandlerCallStack\";\nimport { ActionRouter } from \"../../Routing/ActionRouter\";\nimport { EActionRouterContextType } from \"../../Routing/ActionRouter.types\";\nimport { ActionHandler } from \"../ActionHandler\";\nimport {\n EActionHandlerType,\n type IActionHandler_Local,\n type IActionHandler_Local_Json,\n type IHandleActionOptions,\n} from \"../ActionHandler.types\";\nimport type { THandleActionExecutionFn } from \"./ActionLocalHandler.types\";\n\nexport class ActionLocalHandler\n extends ActionHandler<EActionHandlerType.local>\n implements IActionHandler_Local\n{\n readonly handlerType = EActionHandlerType.local;\n readonly actionRouter: ActionRouter<THandleActionExecutionFn<any, any>> = new ActionRouter({\n contextType: EActionRouterContextType.handler_route,\n handler: this,\n });\n\n constructor() {\n super();\n }\n\n /**\n * Register a handler for all actions in a domain.\n * Receives the full primed action — use `matchAction()` to narrow to a specific action id.\n * Useful for forwarding all domain actions to a remote endpoint.\n * Lower priority than `forAction`.\n */\n forDomain<FOR_DOM extends IActionDomain>(\n domain: ActionDomain<FOR_DOM>,\n handler: THandleActionExecutionFn<FOR_DOM>,\n ): this {\n this.actionRouter.forDomain(domain, handler);\n return this;\n }\n\n /**\n * Register a handler for a base action instance. Takes priority over domain-wide handlers.\n * Receives the full primed action with narrowed input type.\n * Useful for handling specific actions locally while forwarding the rest of the domain. For example, a local \"ping\" action that checks connectivity without needing a round trip.\n */\n forAction<ACT_DOM extends IActionDomain, ID extends keyof ACT_DOM[\"actionSchema\"] & string>(\n action: ActionCore<ACT_DOM, ID>,\n handler: THandleActionExecutionFn<ACT_DOM, ID>,\n ): this {\n this.actionRouter.forAction(action, handler);\n return this;\n }\n\n /**\n * Register a handler for multiple action IDs (first-match-wins among cases).\n * Receives the full primed action narrowed to the union of those IDs.\n * Use `act.coreAction.id` to branch on which action was dispatched.\n */\n forActionIds<\n ACT_DOM extends IActionDomain,\n IDS extends ReadonlyArray<keyof ACT_DOM[\"actionSchema\"] & string>,\n >(\n domain: ActionDomain<ACT_DOM>,\n ids: IDS,\n handler: THandleActionExecutionFn<ACT_DOM, IDS[number]>,\n ): this {\n this.actionRouter.forActionIds(domain, ids, handler);\n return this;\n }\n\n /**\n * Register per-action handlers for a domain using a single map, without needing\n * separate `forAction` calls. Unregistered action IDs are unaffected.\n *\n * @example\n * ```ts\n * handler.forDomainActionCases(userDomain, {\n * getUser: (primed) => db.getUser(primed.input.userId),\n * deleteUser: (primed) => db.deleteUser(primed.input.userId),\n * });\n * ```\n */\n forDomainActionCases<FOR_DOM extends IActionDomain>(\n domain: ActionDomain<FOR_DOM>,\n cases: {\n [ID in keyof FOR_DOM[\"actionSchema\"] & string]?: THandleActionExecutionFn<FOR_DOM, ID>;\n },\n ): this {\n this.actionRouter.forDomainActionCases(domain, cases);\n return this;\n }\n\n async handleActionRequest<\n DOM extends IActionDomain,\n ID extends keyof DOM[\"actionSchema\"] & string,\n >(\n action: ActionPayload_Request<DOM, ID>,\n config?: IHandleActionOptions,\n ): Promise<RunningAction<DOM, ID>> {\n const targetLocalRuntime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();\n\n const handler = this.actionRouter.getRouteDataForActionOrThrow(action, {\n targetLocalRuntime,\n });\n\n action.context.addRouteItem({\n runtime: targetLocalRuntime.coordinate,\n handler: this.toHandlerRouteItem(),\n time: Date.now(),\n });\n\n const runningAction = new RunningAction<DOM, ID>({\n context: action.context,\n request: action,\n parentCuid: peekHandlerCuid(),\n callSite: action._callSite ?? new Error().stack,\n });\n this._handleRunningAction(handler, runningAction).catch((err) => {\n if (err instanceof NiceError) {\n runningAction._completeWithResult(action.errorResult(err as any));\n } else if (isNiceErrorObject(err)) {\n runningAction._completeWithResult(action.errorResult(castNiceError(err) as any));\n } else {\n runningAction._abort(err);\n }\n });\n return runningAction;\n }\n\n private async _handleRunningAction(\n handler: THandleActionExecutionFn<any, any>,\n runningAction: RunningAction<any, any>,\n ) {\n const state = runningAction.state;\n\n if (state.result != null) {\n return;\n }\n\n // Yield before pushing so that sibling actions dispatched in the same synchronous\n // frame have already read their parentCuid (which they do synchronously in\n // handleActionRequest) before we mutate the stack. Without this, a concurrent\n // sibling's peek would see our cuid and incorrectly treat us as its parent.\n // Truly nested child actions (dispatched from inside the handler body below) still\n // see our cuid correctly because the push happens before we call handler().\n await Promise.resolve();\n\n pushHandlerCuid(runningAction.cuid);\n try {\n const rawResult = await handler(state.request);\n let result: ActionPayload_Result<any, any>;\n\n if (rawResult instanceof ActionPayload_Result) {\n result = rawResult;\n } else if (rawResult != null && isActionPayload_Result_JsonObject(rawResult)) {\n const domain = this.actionRouter.domainManager.getActionDomainOrThrow(state.request);\n result = domain.hydrateResultPayload(rawResult);\n } else {\n result = state.request.successResult(rawResult);\n }\n\n runningAction._completeWithResult(result);\n } finally {\n popHandlerCuid();\n }\n }\n\n async handlePayloadWireOrThrow(\n wire: unknown,\n config?: IHandleActionOptions,\n ): Promise<RunningAction<any, any>> {\n const hydratedAction = this.actionRouter.domainManager.hydrateActionPayload(wire as any);\n\n if (!(hydratedAction instanceof ActionPayload_Request)) {\n throw err_nice_action.fromId(EErrId_NiceAction.wire_action_not_payload, {\n domain: hydratedAction.domain,\n actionId: hydratedAction.id,\n actionState: (hydratedAction as any).type ?? (hydratedAction as any).form,\n });\n }\n\n return await this.handleActionRequest(hydratedAction, config);\n }\n\n toJsonObject(): IActionHandler_Local_Json {\n return {\n type: this.handlerType,\n };\n }\n\n toHandlerRouteItem(): IActionRouteItemHandler {\n return {\n type: this.handlerType,\n };\n }\n}\n\nexport const createLocalHandler = () => {\n return new ActionLocalHandler();\n};\n","import {\r\n type ClientCryptoKeyLink,\r\n createTypedStorage,\r\n type StorageAdapter,\r\n type TSerializedCryptoKeyData_Ed25519_Raw,\r\n type TSerializedCryptoKeyData_X25519_Raw,\r\n type TTypeAndId,\r\n} from \"@nice-code/util\";\r\nimport { nanoid } from \"nanoid\";\r\nimport * as v from \"valibot\";\r\nimport { type IRuntimeCoordinate, RuntimeCoordinate } from \"../../RuntimeCoordinate\";\r\n\r\n/**\r\n * Authenticated handshake for the WebSocket channel. Run once per connection, before any action\r\n * frames flow, it:\r\n * - exchanges each side's {@link RuntimeCoordinate} + public keys,\r\n * - checks both ends share the same wire dictionary version (closes the positional-dictionary footgun),\r\n * - has the client prove control of its verify (Ed25519) key by signing a fresh challenge that binds\r\n * both nonces, both identities, the dictionary version, the level, and every exchanged public key,\r\n * - establishes a `ClientCryptoKeyLink` link on both sides (so the server can verify the signature and,\r\n * for the `encrypted` level, both can derive a shared AES-GCM key with the verify keys folded in).\r\n *\r\n * This module is transport-agnostic: it produces/consumes message objects. The transport + server\r\n * handler drive the I/O and the connection phase (Step 4). Keep `none`-level connections from ever\r\n * reaching here — they skip the handshake entirely.\r\n */\r\n\r\nconst HANDSHAKE_PROTOCOL = \"nice-ws-hs/1\";\r\n\r\n/** How much the channel protects after the handshake — chosen by the consumer (perf vs security). */\r\nexport enum ESecurityLevel {\r\n /** No handshake; identity is self-asserted (fastest, dev / trusted networks). */\r\n none = \"none\",\r\n /** Handshake authenticates identity (sign/verify + key pin); frames stay plaintext over TLS. */\r\n authenticated = \"authenticated\",\r\n /** Authenticated handshake + every frame AES-GCM encrypted with the derived shared key. */\r\n encrypted = \"encrypted\",\r\n}\r\n\r\nexport enum EHandshakeMessageType {\r\n hello = \"hello\",\r\n welcome = \"welcome\",\r\n prove = \"prove\",\r\n accept = \"accept\",\r\n reject = \"reject\",\r\n}\r\n\r\n// --- wire message schemas (these arrive untrusted from the network) --------------------------------\r\n\r\nconst vEd25519Raw = v.custom<TSerializedCryptoKeyData_Ed25519_Raw>(\r\n (val) => typeof val === \"string\" && val.startsWith(\"ed25519::raw_base64::\"),\r\n);\r\nconst vX25519Raw = v.custom<TSerializedCryptoKeyData_X25519_Raw>(\r\n (val) => typeof val === \"string\" && val.startsWith(\"x25519::raw_base64::\"),\r\n);\r\nconst vCoordinate = v.object({\r\n envId: v.string(),\r\n perId: v.optional(v.string()),\r\n insId: v.optional(v.string()),\r\n});\r\nconst vSecurityLevel = v.picklist([\r\n ESecurityLevel.none,\r\n ESecurityLevel.authenticated,\r\n ESecurityLevel.encrypted,\r\n]);\r\n\r\nconst vHsHello = v.object({\r\n t: v.literal(EHandshakeMessageType.hello),\r\n protocol: v.string(),\r\n securityLevel: vSecurityLevel,\r\n dictionaryVersion: v.string(),\r\n client: vCoordinate,\r\n clientNonce: v.string(),\r\n verifyPublicKey: vEd25519Raw,\r\n exchangePublicKey: v.optional(vX25519Raw),\r\n});\r\n\r\nconst vHsWelcome = v.object({\r\n t: v.literal(EHandshakeMessageType.welcome),\r\n securityLevel: vSecurityLevel,\r\n dictionaryVersion: v.string(),\r\n server: vCoordinate,\r\n serverNonce: v.string(),\r\n verifyPublicKey: vEd25519Raw,\r\n exchangePublicKey: v.optional(vX25519Raw),\r\n});\r\n\r\nconst vHsProve = v.object({\r\n t: v.literal(EHandshakeMessageType.prove),\r\n signatureBase64: v.string(),\r\n});\r\n\r\nconst vHsAccept = v.object({\r\n t: v.literal(EHandshakeMessageType.accept),\r\n signatureBase64: v.optional(v.string()),\r\n});\r\n\r\nconst vHsReject = v.object({\r\n t: v.literal(EHandshakeMessageType.reject),\r\n reason: v.string(),\r\n});\r\n\r\nconst vHandshakeMessage = v.variant(\"t\", [vHsHello, vHsWelcome, vHsProve, vHsAccept, vHsReject]);\r\n\r\nexport type THsHello = v.InferOutput<typeof vHsHello>;\r\nexport type THsWelcome = v.InferOutput<typeof vHsWelcome>;\r\nexport type THsProve = v.InferOutput<typeof vHsProve>;\r\nexport type THsAccept = v.InferOutput<typeof vHsAccept>;\r\nexport type THsReject = v.InferOutput<typeof vHsReject>;\r\nexport type THandshakeMessage = v.InferOutput<typeof vHandshakeMessage>;\r\n\r\n/** Serialize a handshake message for the wire (handshake frames are JSON — they aren't the hot path). */\r\nexport function encodeHandshakeMessage(message: THandshakeMessage): string {\r\n return JSON.stringify(message);\r\n}\r\n\r\n/** Parse + structurally validate an incoming handshake frame; `undefined` if it isn't one. */\r\nexport function decodeHandshakeMessage(raw: string): THandshakeMessage | undefined {\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(raw);\r\n } catch {\r\n return undefined;\r\n }\r\n const result = v.safeParse(vHandshakeMessage, parsed);\r\n return result.success ? result.output : undefined;\r\n}\r\n\r\n// --- helpers ---------------------------------------------------------------------------------------\r\n\r\n/** Stable link id for a runtime coordinate — the key both the crypto link and the connection use. */\r\nexport function runtimeLinkId(coordinate: IRuntimeCoordinate): TTypeAndId {\r\n return `runtime::${new RuntimeCoordinate(coordinate).stringId}`;\r\n}\r\n\r\nfunction coordId(coordinate: IRuntimeCoordinate): string {\r\n return new RuntimeCoordinate(coordinate).stringId;\r\n}\r\n\r\nfunction sessionSalt(clientNonce: string, serverNonce: string): string {\r\n return `${clientNonce}::${serverNonce}`;\r\n}\r\n\r\nfunction handshakeInfo(dictionaryVersion: string): string {\r\n return `${HANDSHAKE_PROTOCOL}::${dictionaryVersion}`;\r\n}\r\n\r\n/**\r\n * The exact string both sides sign/verify. JSON-encoded ordered array so field boundaries are\r\n * unambiguous; binds identities, nonces (freshness), version, level, and all exchanged public keys\r\n * (authenticating the keys via the signature, complementing `bindVerifyKeysIntoDerivation`).\r\n */\r\nfunction buildHandshakeChallenge(parts: {\r\n securityLevel: ESecurityLevel;\r\n dictionaryVersion: string;\r\n clientCoordId: string;\r\n serverCoordId: string;\r\n clientNonce: string;\r\n serverNonce: string;\r\n clientVerifyKey: string;\r\n serverVerifyKey: string;\r\n clientExchangeKey?: string;\r\n serverExchangeKey?: string;\r\n}): string {\r\n return JSON.stringify([\r\n HANDSHAKE_PROTOCOL,\r\n parts.securityLevel,\r\n parts.dictionaryVersion,\r\n parts.clientCoordId,\r\n parts.serverCoordId,\r\n parts.clientNonce,\r\n parts.serverNonce,\r\n parts.clientVerifyKey,\r\n parts.serverVerifyKey,\r\n parts.clientExchangeKey ?? \"_\",\r\n parts.serverExchangeKey ?? \"_\",\r\n ]);\r\n}\r\n\r\nfunction reject(reason: string): THsReject {\r\n return { t: EHandshakeMessageType.reject, reason };\r\n}\r\n\r\n/**\r\n * Everything needed to re-derive the shared AES-GCM key for an `encrypted` link after a restart —\r\n * the remote's public keys + the HKDF salt/info used at handshake time. The local (server) key pair is\r\n * recovered from its own persisted `ClientCryptoKeyLink` storage, so re-linking with this material\r\n * yields the identical shared key without a fresh handshake.\r\n */\r\nexport interface IHandshakeEncryptionKeyMaterial {\r\n verifyPublicKey: TSerializedCryptoKeyData_Ed25519_Raw;\r\n exchangePublicKey: TSerializedCryptoKeyData_X25519_Raw;\r\n saltString: string;\r\n infoString: string;\r\n bindVerifyKeysIntoDerivation: boolean;\r\n}\r\n\r\n/** Outcome of a completed handshake — what the transport/handler needs to wire the channel. */\r\nexport interface IHandshakeResult {\r\n /** The crypto-link id (and connection-registry key) for the authenticated remote. */\r\n linkedClientId: TTypeAndId;\r\n /** The remote's authenticated coordinate. */\r\n remote: IRuntimeCoordinate;\r\n securityLevel: ESecurityLevel;\r\n /** For the `encrypted` level: material to restore the shared key after eviction (persist it). */\r\n encryptionKeyMaterial?: IHandshakeEncryptionKeyMaterial;\r\n}\r\n\r\n// --- pluggable trust (TOFU now, pre-provisioned later) ---------------------------------------------\r\n\r\nexport interface IClientVerifyKeyResolveInput {\r\n client: IRuntimeCoordinate;\r\n verifyPublicKey: TSerializedCryptoKeyData_Ed25519_Raw;\r\n}\r\n\r\nexport interface IClientVerifyKeyResolver {\r\n /**\r\n * Decide whether a presented verify key is trusted for a client identity. The signature is already\r\n * verified by the time this runs, so this is purely the identity-pinning decision. Swap in a\r\n * persistent / pre-provisioned implementation without touching the protocol.\r\n */\r\n resolve(input: IClientVerifyKeyResolveInput): Promise<{ trusted: boolean; reason?: string }>;\r\n}\r\n\r\nfunction tofuPinKey(client: IRuntimeCoordinate): string {\r\n // Pin to the persistent identity (perId) so a client may reconnect with new instances (insId),\r\n // but a changed verify key for the same persistent identity is rejected.\r\n return `${client.envId}::${client.perId ?? client.insId ?? \"_\"}`;\r\n}\r\n\r\n/**\r\n * In-memory trust-on-first-use resolver: trusts (and pins) the first verify key seen for a client\r\n * identity, then rejects a different key for that identity. The default; replace with a storage-backed\r\n * resolver for cross-restart pinning (see Step 5).\r\n */\r\nexport function createInMemoryTofuVerifyKeyResolver(): IClientVerifyKeyResolver {\r\n const pinned = new Map<string, string>();\r\n return {\r\n async resolve({ client, verifyPublicKey }) {\r\n const key = tofuPinKey(client);\r\n const existing = pinned.get(key);\r\n if (existing == null) {\r\n pinned.set(key, verifyPublicKey);\r\n return { trusted: true };\r\n }\r\n if (existing === verifyPublicKey) return { trusted: true };\r\n return { trusted: false, reason: \"verify key changed for client identity (pin mismatch)\" };\r\n },\r\n };\r\n}\r\n\r\ninterface ITofuPinStorage {\r\n pins: Record<string, TSerializedCryptoKeyData_Ed25519_Raw>;\r\n}\r\n\r\n/**\r\n * Storage-backed trust-on-first-use resolver: pins survive process restarts / Durable Object eviction\r\n * (e.g. back it with `createDurableObjectStorageAdapter`). Same policy as the in-memory variant — trust\r\n * + pin the first verify key per client identity, reject a different one thereafter.\r\n */\r\nexport function createStorageTofuVerifyKeyResolver(\r\n storageAdapter: StorageAdapter,\r\n): IClientVerifyKeyResolver {\r\n const storage = createTypedStorage<ITofuPinStorage>({ storageAdapter });\r\n return {\r\n async resolve({ client, verifyPublicKey }) {\r\n const key = tofuPinKey(client);\r\n const existing = (await storage.getJson(\"pins\"))?.[key];\r\n if (existing == null) {\r\n await storage.updateJsonWithDef(\"pins\", {}, (current) => ({\r\n ...current,\r\n [key]: verifyPublicKey,\r\n }));\r\n return { trusted: true };\r\n }\r\n if (existing === verifyPublicKey) return { trusted: true };\r\n return { trusted: false, reason: \"verify key changed for client identity (pin mismatch)\" };\r\n },\r\n };\r\n}\r\n\r\n// --- client side -----------------------------------------------------------------------------------\r\n\r\nexport interface IClientHandshakeConfig {\r\n link: ClientCryptoKeyLink;\r\n localCoordinate: IRuntimeCoordinate;\r\n dictionaryVersion: string;\r\n securityLevel: ESecurityLevel;\r\n}\r\n\r\nexport function createClientHandshake(config: IClientHandshakeConfig) {\r\n const { link, localCoordinate, dictionaryVersion, securityLevel } = config;\r\n const wantsEncryption = securityLevel === ESecurityLevel.encrypted;\r\n const clientNonce = nanoid();\r\n\r\n let pending:\r\n | { linkedServerId: TTypeAndId; server: IRuntimeCoordinate; challenge: string }\r\n | undefined;\r\n\r\n return {\r\n async createHello(): Promise<THsHello> {\r\n return {\r\n t: EHandshakeMessageType.hello,\r\n protocol: HANDSHAKE_PROTOCOL,\r\n securityLevel,\r\n dictionaryVersion,\r\n client: localCoordinate,\r\n clientNonce,\r\n verifyPublicKey: await link.getLocalVerifyPublicKey(),\r\n exchangePublicKey: wantsEncryption ? await link.getLocalExchangePublicKey() : undefined,\r\n };\r\n },\r\n\r\n async onWelcome(welcome: THsWelcome): Promise<THsProve> {\r\n if (welcome.dictionaryVersion !== dictionaryVersion) {\r\n throw new Error(\"[ws-handshake] server dictionary version mismatch\");\r\n }\r\n if (welcome.securityLevel !== securityLevel) {\r\n throw new Error(\"[ws-handshake] server security level mismatch\");\r\n }\r\n if (wantsEncryption && welcome.exchangePublicKey == null) {\r\n throw new Error(\"[ws-handshake] server did not provide an exchange key for encryption\");\r\n }\r\n\r\n const linkedServerId = runtimeLinkId(welcome.server);\r\n await link.linkClient({\r\n linkedClientId: linkedServerId,\r\n verifyPublicKey: welcome.verifyPublicKey,\r\n // Salt/info/exchange only matter for the encrypted level's shared-key derivation; the\r\n // authenticated level links the verify key alone (passing salt without an exchange key throws).\r\n ...(wantsEncryption\r\n ? {\r\n exchangePublicKey: welcome.exchangePublicKey,\r\n saltString: sessionSalt(clientNonce, welcome.serverNonce),\r\n infoString: handshakeInfo(dictionaryVersion),\r\n bindVerifyKeysIntoDerivation: true,\r\n }\r\n : {}),\r\n });\r\n\r\n const challenge = buildHandshakeChallenge({\r\n securityLevel,\r\n dictionaryVersion,\r\n clientCoordId: coordId(localCoordinate),\r\n serverCoordId: coordId(welcome.server),\r\n clientNonce,\r\n serverNonce: welcome.serverNonce,\r\n clientVerifyKey: await link.getLocalVerifyPublicKey(),\r\n serverVerifyKey: welcome.verifyPublicKey,\r\n clientExchangeKey: wantsEncryption ? await link.getLocalExchangePublicKey() : undefined,\r\n serverExchangeKey: welcome.exchangePublicKey,\r\n });\r\n\r\n pending = { linkedServerId, server: welcome.server, challenge };\r\n return {\r\n t: EHandshakeMessageType.prove,\r\n signatureBase64: (await link.signChallenge([challenge])).signatureBase64,\r\n };\r\n },\r\n\r\n async onAccept(accept: THsAccept): Promise<IHandshakeResult> {\r\n if (pending == null) throw new Error(\"[ws-handshake] accept before welcome\");\r\n\r\n // Optional mutual auth: confirm the server signed the same challenge (liveness over TLS).\r\n if (accept.signatureBase64 != null) {\r\n const valid = await link.verifyChallengeFromLinkedClient({\r\n linkedClientId: pending.linkedServerId,\r\n challenge: pending.challenge,\r\n signatureBase64: accept.signatureBase64,\r\n });\r\n if (!valid) throw new Error(\"[ws-handshake] server signature invalid\");\r\n }\r\n\r\n return { linkedClientId: pending.linkedServerId, remote: pending.server, securityLevel };\r\n },\r\n };\r\n}\r\n\r\n// --- server side -----------------------------------------------------------------------------------\r\n\r\nexport interface IServerHandshakeConfig {\r\n link: ClientCryptoKeyLink;\r\n localCoordinate: IRuntimeCoordinate;\r\n dictionaryVersion: string;\r\n /**\r\n * The level(s) this server accepts. A single level is strict (the client must match). An array is a\r\n * negotiable allowed set — the server adopts whichever level the client requests, as long as it's in\r\n * the set (lets one backend serve `authenticated` and `encrypted` clients at once). `none` in the set\r\n * is handled by the transport/handler (a `none` client never reaches the handshake).\r\n */\r\n securityLevel: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key. Defaults to in-memory TOFU. */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n}\r\n\r\nexport function createServerHandshake(config: IServerHandshakeConfig) {\r\n const { link, localCoordinate, dictionaryVersion } = config;\r\n const allowedLevels = Array.isArray(config.securityLevel)\r\n ? config.securityLevel\r\n : [config.securityLevel];\r\n const verifyKeyResolver = config.verifyKeyResolver ?? createInMemoryTofuVerifyKeyResolver();\r\n const serverNonce = nanoid();\r\n\r\n let pending:\r\n | {\r\n client: IRuntimeCoordinate;\r\n linkedClientId: TTypeAndId;\r\n challenge: string;\r\n clientVerifyKey: TSerializedCryptoKeyData_Ed25519_Raw;\r\n negotiatedLevel: ESecurityLevel;\r\n keyMaterial?: IHandshakeEncryptionKeyMaterial;\r\n }\r\n | undefined;\r\n let result: IHandshakeResult | undefined;\r\n\r\n return {\r\n async onHello(hello: THsHello): Promise<THsWelcome | THsReject> {\r\n if (hello.protocol !== HANDSHAKE_PROTOCOL) return reject(\"unsupported handshake protocol\");\r\n if (hello.dictionaryVersion !== dictionaryVersion)\r\n return reject(\"dictionary version mismatch\");\r\n\r\n // Negotiate: adopt the client's requested level if this server allows it. `none` never\r\n // handshakes, so it's not a valid negotiated level here.\r\n const negotiatedLevel = hello.securityLevel;\r\n if (negotiatedLevel === ESecurityLevel.none || !allowedLevels.includes(negotiatedLevel)) {\r\n return reject(\"security level not allowed\");\r\n }\r\n const wantsEncryption = negotiatedLevel === ESecurityLevel.encrypted;\r\n if (wantsEncryption && hello.exchangePublicKey == null) {\r\n return reject(\"missing exchange key for encryption\");\r\n }\r\n\r\n const linkedClientId = runtimeLinkId(hello.client);\r\n await link.linkClient({\r\n linkedClientId,\r\n verifyPublicKey: hello.verifyPublicKey,\r\n // See client side: only the encrypted level needs the exchange key + HKDF salt/info.\r\n ...(wantsEncryption\r\n ? {\r\n exchangePublicKey: hello.exchangePublicKey,\r\n saltString: sessionSalt(hello.clientNonce, serverNonce),\r\n infoString: handshakeInfo(dictionaryVersion),\r\n bindVerifyKeysIntoDerivation: true,\r\n }\r\n : {}),\r\n });\r\n\r\n const serverVerifyKey = await link.getLocalVerifyPublicKey();\r\n const serverExchangeKey = wantsEncryption\r\n ? await link.getLocalExchangePublicKey()\r\n : undefined;\r\n\r\n const keyMaterial: IHandshakeEncryptionKeyMaterial | undefined =\r\n wantsEncryption && hello.exchangePublicKey != null\r\n ? {\r\n verifyPublicKey: hello.verifyPublicKey,\r\n exchangePublicKey: hello.exchangePublicKey,\r\n saltString: sessionSalt(hello.clientNonce, serverNonce),\r\n infoString: handshakeInfo(dictionaryVersion),\r\n bindVerifyKeysIntoDerivation: true,\r\n }\r\n : undefined;\r\n\r\n pending = {\r\n client: hello.client,\r\n linkedClientId,\r\n clientVerifyKey: hello.verifyPublicKey,\r\n negotiatedLevel,\r\n keyMaterial,\r\n challenge: buildHandshakeChallenge({\r\n securityLevel: negotiatedLevel,\r\n dictionaryVersion,\r\n clientCoordId: coordId(hello.client),\r\n serverCoordId: coordId(localCoordinate),\r\n clientNonce: hello.clientNonce,\r\n serverNonce,\r\n clientVerifyKey: hello.verifyPublicKey,\r\n serverVerifyKey,\r\n clientExchangeKey: hello.exchangePublicKey,\r\n serverExchangeKey,\r\n }),\r\n };\r\n\r\n return {\r\n t: EHandshakeMessageType.welcome,\r\n securityLevel: negotiatedLevel,\r\n dictionaryVersion,\r\n server: localCoordinate,\r\n serverNonce,\r\n verifyPublicKey: serverVerifyKey,\r\n exchangePublicKey: serverExchangeKey,\r\n };\r\n },\r\n\r\n async onProve(prove: THsProve): Promise<THsAccept | THsReject> {\r\n if (pending == null) return reject(\"prove before hello\");\r\n\r\n const signatureValid = await link.verifyChallengeFromLinkedClient({\r\n linkedClientId: pending.linkedClientId,\r\n challenge: pending.challenge,\r\n signatureBase64: prove.signatureBase64,\r\n });\r\n if (!signatureValid) return reject(\"invalid client signature\");\r\n\r\n const trust = await verifyKeyResolver.resolve({\r\n client: pending.client,\r\n verifyPublicKey: pending.clientVerifyKey,\r\n });\r\n if (!trust.trusted) return reject(trust.reason ?? \"client verify key not trusted\");\r\n\r\n result = {\r\n linkedClientId: pending.linkedClientId,\r\n remote: pending.client,\r\n securityLevel: pending.negotiatedLevel,\r\n encryptionKeyMaterial: pending.keyMaterial,\r\n };\r\n\r\n // Sign the same challenge back so the client can confirm server liveness (mutual auth).\r\n return {\r\n t: EHandshakeMessageType.accept,\r\n signatureBase64: (await link.signChallenge([pending.challenge])).signatureBase64,\r\n };\r\n },\r\n\r\n /** The completed handshake result once `onProve` has accepted, else `undefined`. */\r\n getResult(): IHandshakeResult | undefined {\r\n return result;\r\n },\r\n };\r\n}\r\n","import type { TActionPayload_Any_JsonObject } from \"../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport { isActionPayload_Any_JsonObject } from \"./isActionPayload_Any_JsonObject\";\r\n\r\n/**\r\n * Minimal codec shape needed to turn an incoming channel frame back into action wire JSON. Matches\r\n * the `formatMessage` object the WebSocket transport (and `createBinaryWireAdapter`) provide.\r\n */\r\nexport interface IActionFrameDecoder {\r\n incoming?: (\r\n frame: string | ArrayBuffer | Uint8Array | Blob,\r\n ) => TActionPayload_Any_JsonObject<any, any> | undefined;\r\n}\r\n\r\n/**\r\n * Decode a single inbound channel frame (text or binary) into validated action wire JSON, or\r\n * `undefined` if it isn't a recognisable action payload.\r\n *\r\n * Shared by the WebSocket transport's message listener and the server-side `AcceptorHandler` so\r\n * both decode identically: a binary `decoder.incoming` (e.g. msgpackr) takes precedence, and plain\r\n * text frames fall back to JSON — keeping binary and JSON clients interoperable on one channel.\r\n */\r\nexport function decodeActionFrame(\r\n frame: string | ArrayBuffer | Uint8Array,\r\n decoder?: IActionFrameDecoder,\r\n): TActionPayload_Any_JsonObject<any, any> | undefined {\r\n const decoded =\r\n decoder?.incoming?.(frame) ??\r\n (typeof frame === \"string\" ? parseJsonActionFrame(frame) : undefined);\r\n\r\n return decoded != null && isActionPayload_Any_JsonObject(decoded) ? decoded : undefined;\r\n}\r\n\r\nfunction parseJsonActionFrame(\r\n message: string,\r\n): TActionPayload_Any_JsonObject<any, any> | undefined {\r\n try {\r\n const json = JSON.parse(message);\r\n return isActionPayload_Any_JsonObject(json) ? json : undefined;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n","import type { ClientCryptoKeyLink, TTypeAndId } from \"@nice-code/util\";\r\nimport { pack, unpack } from \"msgpackr\";\r\n\r\n/**\r\n * Async AES-GCM transform for the `encrypted` security level. It wraps the opaque binary frame a\r\n * session codec produces (it does NOT look inside it), encrypting on the way out and decrypting on the\r\n * way in with the shared key established by the handshake.\r\n *\r\n * It is deliberately separate from the (synchronous) session `formatMessage`: WebCrypto is always\r\n * Promise-based, so encryption has to happen at the transport's async I/O boundary — the connection\r\n * encrypts after `session.outgoing()` and decrypts before `session.incoming()`. The `authenticated`\r\n * and `none` levels use no crypto transform at all (frames go out as the session produced them).\r\n *\r\n * Wire shape of an encrypted frame: `pack([nonceBytes, ciphertextBytes])` — msgpack carries the two\r\n * binary fields with a couple of bytes of overhead, no base64 inflation.\r\n */\r\nexport interface IActionFrameCrypto {\r\n /** Encrypt one session frame for sending. */\r\n encryptFrame(frame: Uint8Array): Promise<Uint8Array>;\r\n /** Decrypt one received frame back to the session frame. Throws on a non-binary / malformed /\r\n * tampered frame — the caller (transport) decides how to react (drop / close). */\r\n decryptFrame(frame: string | ArrayBuffer | Uint8Array): Promise<Uint8Array>;\r\n}\r\n\r\nexport interface IActionFrameCryptoConfig {\r\n link: ClientCryptoKeyLink;\r\n /** The handshake-established link id for the remote (key + connection-registry id). */\r\n linkedClientId: TTypeAndId;\r\n}\r\n\r\nconst ENCRYPTED_ENVELOPE_LENGTH = 2;\r\n\r\n/**\r\n * Build the encrypt/decrypt transform for a connection whose handshake settled on the `encrypted`\r\n * level. Keyed by the link + `linkedClientId`, so it reuses the cached shared AES-GCM key.\r\n */\r\nexport function createActionFrameCrypto({\r\n link,\r\n linkedClientId,\r\n}: IActionFrameCryptoConfig): IActionFrameCrypto {\r\n return {\r\n async encryptFrame(frame: Uint8Array): Promise<Uint8Array> {\r\n const { nonce, ciphertext } = await link.encryptBytesForLinkedClient({\r\n linkedClientId,\r\n dataToEncrypt: frame,\r\n });\r\n return pack([nonce, ciphertext]);\r\n },\r\n\r\n async decryptFrame(frame: string | ArrayBuffer | Uint8Array): Promise<Uint8Array> {\r\n if (typeof frame === \"string\") {\r\n throw new Error(\"[ws-crypto] expected an encrypted binary frame, received text\");\r\n }\r\n\r\n const buffer = frame instanceof ArrayBuffer ? new Uint8Array(frame) : frame;\r\n const envelope = unpack(buffer);\r\n if (!Array.isArray(envelope) || envelope.length !== ENCRYPTED_ENVELOPE_LENGTH) {\r\n throw new Error(\"[ws-crypto] malformed encrypted frame envelope\");\r\n }\r\n\r\n const [nonce, ciphertext] = envelope;\r\n if (!(nonce instanceof Uint8Array) || !(ciphertext instanceof Uint8Array)) {\r\n throw new Error(\"[ws-crypto] malformed encrypted frame fields\");\r\n }\r\n\r\n // AES-GCM verifies integrity — a tampered ciphertext throws here.\r\n return await link.decryptBytesFromLinkedClient({\r\n linkedClientId,\r\n dataToDecrypt: { nonce, ciphertext },\r\n });\r\n },\r\n };\r\n}\r\n","import type { TFrame } from \"../Carrier/Carrier.types\";\r\n\r\n/** Normalize any frame form to bytes (for the AES-GCM layer, which works on `Uint8Array`). */\r\nexport function toFrameBytes(frame: TFrame): Uint8Array {\r\n if (typeof frame === \"string\") return new TextEncoder().encode(frame);\r\n if (frame instanceof ArrayBuffer) return new Uint8Array(frame);\r\n return frame;\r\n}\r\n","import type { TFrame } from \"../Carrier/Carrier.types\";\r\nimport type { IActionFrameCrypto } from \"../crypto/actionFrameCrypto\";\r\nimport { toFrameBytes } from \"../crypto/frameBytes\";\r\n\r\n/**\r\n * Ordered frame crypto over a single channel/connection — the one place the encrypt-on-send /\r\n * decrypt-on-receive logic lives, shared by both link roles:\r\n *\r\n * - the **connector** (one carrier, one crypto) — see `establishLinkSession`;\r\n * - the **acceptor** (one per accepted connection, crypto re-derived asynchronously after a hibernation\r\n * wake) — see `AcceptorHandler`.\r\n *\r\n * Outbound frames go on an ordered chain so async AES-GCM never reorders them, and the chain gates once\r\n * on `crypto` — which, when `crypto` is a promise, also gates the first send on the post-wake key\r\n * re-derivation. With no `crypto` it's a plaintext passthrough.\r\n */\r\nexport interface IFrameCryptoPipe {\r\n /** Encrypt (if secured) and write `frame`, preserving dispatch order. Dropped if `isOpen` is false. */\r\n send(frame: TFrame): void;\r\n /** Decrypt an inbound frame; resolves `undefined` (frame dropped) if decryption fails. */\r\n decryptIncoming(frame: TFrame): Promise<TFrame | undefined>;\r\n}\r\n\r\nexport interface IFrameCryptoPipeConfig {\r\n /** Raw frame writer — the carrier's `send`, or the acceptor's per-connection send. */\r\n write: (frame: TFrame) => void;\r\n /** Optional synchronous open-guard; when it returns `false`, sends are dropped (a closed carrier). */\r\n isOpen?: () => boolean;\r\n /**\r\n * The AES-GCM transform, or a promise of it. `undefined` ⇒ plaintext passthrough. A promise lets the\r\n * acceptor gate the connection's first frame on an async key re-derivation after a hibernation wake.\r\n */\r\n crypto?: IActionFrameCrypto | Promise<IActionFrameCrypto>;\r\n /** Short label for the encrypt/decrypt failure logs (e.g. `\"link\"`, `\"ws-server\"`). */\r\n label?: string;\r\n}\r\n\r\nexport function createFrameCryptoPipe(config: IFrameCryptoPipeConfig): IFrameCryptoPipe {\r\n const { write, isOpen, crypto, label = \"link\" } = config;\r\n\r\n // Ordered send chain: async encryption must keep frames in dispatch order.\r\n let sendChain: Promise<void> = Promise.resolve();\r\n\r\n const send = (frame: TFrame): void => {\r\n if (isOpen != null && !isOpen()) return;\r\n if (crypto == null) {\r\n write(frame);\r\n return;\r\n }\r\n const bytes = toFrameBytes(frame);\r\n sendChain = sendChain\r\n .then(() => crypto)\r\n .then((c) => c.encryptFrame(bytes))\r\n .then((encrypted) => {\r\n if (isOpen == null || isOpen()) write(encrypted);\r\n })\r\n .catch((err) => console.error(`[${label}] failed to encrypt/send frame`, err));\r\n };\r\n\r\n const decryptIncoming = async (frame: TFrame): Promise<TFrame | undefined> => {\r\n if (crypto == null) return frame;\r\n try {\r\n return await (await crypto).decryptFrame(frame);\r\n } catch (err) {\r\n console.error(`[${label}] failed to decrypt incoming frame`, err);\r\n return undefined;\r\n }\r\n };\r\n\r\n return { send, decryptIncoming };\r\n}\r\n","import type { ClientCryptoKeyLink, TTypeAndId } from \"@nice-code/util\";\r\nimport { type IRuntimeCoordinate, RuntimeCoordinate } from \"../../RuntimeCoordinate\";\r\nimport type { TFrame } from \"../Carrier/Carrier.types\";\r\nimport { createActionFrameCrypto } from \"../crypto/actionFrameCrypto\";\r\nimport {\r\n createServerHandshake,\r\n decodeHandshakeMessage,\r\n EHandshakeMessageType,\r\n ESecurityLevel,\r\n encodeHandshakeMessage,\r\n type IClientVerifyKeyResolver,\r\n type IHandshakeEncryptionKeyMaterial,\r\n type IHandshakeResult,\r\n} from \"../crypto/actionHandshake\";\r\nimport { createFrameCryptoPipe, type IFrameCryptoPipe } from \"./frameCryptoPipe\";\r\n\r\n/** What the handshake authenticated — the handler binds this identity + persists it for resumption. */\r\nexport interface IAcceptorAuthResult {\r\n /** The peer's *authenticated* coordinate — the only identity inbound frames are bound to. */\r\n client: RuntimeCoordinate;\r\n securityLevel: ESecurityLevel;\r\n linkedClientId: TTypeAndId;\r\n /** Present at the `encrypted` level — persist it so an evicted connection re-derives its key. */\r\n keyMaterial?: IHandshakeEncryptionKeyMaterial;\r\n}\r\n\r\n/** The persisted secure state replayed to resume an authenticated connection after eviction. */\r\nexport interface IAcceptorSecureResumeState {\r\n securityLevel: ESecurityLevel;\r\n linkedClientId: TTypeAndId;\r\n keyMaterial?: IHandshakeEncryptionKeyMaterial;\r\n}\r\n\r\nexport interface IAcceptorSecureSessionConfig {\r\n /** This acceptor's crypto identity (verify + exchange key pairs, optionally persisted). */\r\n link: ClientCryptoKeyLink;\r\n /** This acceptor's coordinate — its identity to clients during the handshake. */\r\n localCoordinate: IRuntimeCoordinate;\r\n /** Wire dictionary version; the handshake rejects a client on a mismatch. */\r\n dictionaryVersion: string;\r\n /** Accepted level(s) — a single level is strict, an array is a negotiable allowed set. */\r\n securityLevel: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n /** Whether a plain (`none`) client may be served on this endpoint (i.e. `none` is in the set). */\r\n noneAllowed: boolean;\r\n /** Raw frame writer to this one connection. */\r\n send: (frame: TFrame) => void;\r\n /** Called once the handshake authenticates the peer — the handler binds identity + persists it. */\r\n onAuthenticated: (auth: IAcceptorAuthResult) => void;\r\n /** Route a frame from a `none`-negotiated connection through the handler's plain (self-asserted) path. */\r\n routePlain: (frame: TFrame) => void;\r\n /** Route a decrypted, ready-to-decode action frame from an authenticated connection. */\r\n routeAction: (bytes: TFrame) => void;\r\n}\r\n\r\n/**\r\n * The acceptor (accept-in) counterpart to the connector's `establishLinkSession`: one connection's\r\n * secure session — the server-side handshake driving, the ordered frame crypto, and the per-connection\r\n * phase (undecided → plain | authenticated). The crypto itself (ordered encrypt-send / decrypt) lives in\r\n * the shared {@link createFrameCryptoPipe}, the same primitive the connector uses — so the secure logic\r\n * lives once for both link roles.\r\n *\r\n * The handler owns one of these per accepted connection and feeds it inbound frames via {@link receive};\r\n * identity binding, persistence, codec, and return routing stay in the handler (driven through the\r\n * config callbacks). Inbound processing is serialized per connection because the handshake and decryption\r\n * are async — this keeps handshake ordering and frame order intact.\r\n */\r\nexport class AcceptorSecureSession {\r\n private _handshake?: ReturnType<typeof createServerHandshake>;\r\n /** The ordered encrypt-send / decrypt pipe — present only for an `encrypted` connection. */\r\n private _pipe?: IFrameCryptoPipe;\r\n private _authed = false;\r\n private _plain = false;\r\n /** Serializes inbound processing (handshake + decryption are async). */\r\n private _inboundChain: Promise<void> = Promise.resolve();\r\n\r\n constructor(private readonly config: IAcceptorSecureSessionConfig) {}\r\n\r\n /** Feed one inbound frame. Serialized per connection; routes back through the config callbacks. */\r\n receive(frame: TFrame): void {\r\n this._inboundChain = this._inboundChain\r\n .then(() => this._receive(frame))\r\n .catch((err) => console.error(\"[ws-server] failed to process inbound frame\", err));\r\n }\r\n\r\n private async _receive(frame: TFrame): Promise<void> {\r\n // A connection that already negotiated `none` is routed plainly (self-asserted identity).\r\n if (this._plain) {\r\n this.config.routePlain(frame);\r\n return;\r\n }\r\n\r\n if (!this._authed) {\r\n // Undecided first frame: a handshake control frame → drive the secure handshake; anything else,\r\n // when `none` is allowed, means a plain client → route it plainly from here on.\r\n const message = typeof frame === \"string\" ? decodeHandshakeMessage(frame) : undefined;\r\n if (message == null) {\r\n if (this.config.noneAllowed) {\r\n this._plain = true;\r\n this.config.routePlain(frame);\r\n }\r\n return;\r\n }\r\n\r\n await this.config.link.initialize();\r\n if (this._handshake == null) {\r\n this._handshake = createServerHandshake({\r\n link: this.config.link,\r\n localCoordinate: this.config.localCoordinate,\r\n dictionaryVersion: this.config.dictionaryVersion,\r\n securityLevel: this.config.securityLevel,\r\n verifyKeyResolver: this.config.verifyKeyResolver,\r\n });\r\n }\r\n\r\n if (message.t === EHandshakeMessageType.hello) {\r\n this.config.send(encodeHandshakeMessage(await this._handshake.onHello(message)));\r\n } else if (message.t === EHandshakeMessageType.prove) {\r\n const reply = await this._handshake.onProve(message);\r\n this.config.send(encodeHandshakeMessage(reply));\r\n const result = this._handshake.getResult();\r\n if (reply.t === EHandshakeMessageType.accept && result != null) this._complete(result);\r\n }\r\n return;\r\n }\r\n\r\n // Authenticated phase: decrypt (if encrypted), then hand the bytes to the handler to decode + route.\r\n const bytes = this._pipe != null ? await this._pipe.decryptIncoming(frame) : frame;\r\n if (bytes === undefined) return; // decryption failed (logged inside the pipe) — drop the frame\r\n this.config.routeAction(bytes);\r\n }\r\n\r\n private _complete(result: IHandshakeResult): void {\r\n this._authed = true;\r\n this._handshake = undefined;\r\n\r\n if (result.securityLevel === ESecurityLevel.encrypted) {\r\n this._pipe = this._buildPipe(\r\n createActionFrameCrypto({ link: this.config.link, linkedClientId: result.linkedClientId }),\r\n );\r\n }\r\n\r\n this.config.onAuthenticated({\r\n client: new RuntimeCoordinate(result.remote),\r\n securityLevel: result.securityLevel,\r\n linkedClientId: result.linkedClientId,\r\n keyMaterial: result.encryptionKeyMaterial,\r\n });\r\n }\r\n\r\n /**\r\n * Restore an already-authenticated session after eviction — no handshake. For an `encrypted`\r\n * connection the shared key is re-derived asynchronously (the acceptor re-links the client off its own\r\n * persisted identity); the pipe's crypto IS that promise, so the connection's first in/out frame\r\n * naturally waits for the key before encrypt/decrypt — no separate gate.\r\n */\r\n rehydrate(state: IAcceptorSecureResumeState): void {\r\n this._authed = true;\r\n\r\n if (state.securityLevel !== ESecurityLevel.encrypted || state.keyMaterial == null) return;\r\n\r\n const { link } = this.config;\r\n const { linkedClientId, keyMaterial } = state;\r\n const cryptoReady = link\r\n .initialize()\r\n .then(() =>\r\n link.linkClient({\r\n linkedClientId,\r\n verifyPublicKey: keyMaterial.verifyPublicKey,\r\n exchangePublicKey: keyMaterial.exchangePublicKey,\r\n saltString: keyMaterial.saltString,\r\n infoString: keyMaterial.infoString,\r\n bindVerifyKeysIntoDerivation: keyMaterial.bindVerifyKeysIntoDerivation,\r\n }),\r\n )\r\n .then(() => createActionFrameCrypto({ link, linkedClientId }));\r\n\r\n // Surface a re-link failure clearly (the pipe also logs per-frame, but this names the cause).\r\n cryptoReady.catch((err) =>\r\n console.error(\"[ws-server] failed to restore encrypted session\", err),\r\n );\r\n this._pipe = this._buildPipe(cryptoReady);\r\n }\r\n\r\n /** Send an outbound frame: through the encrypt pipe when encrypted, otherwise raw. */\r\n send(frame: TFrame): void {\r\n if (this._pipe != null) {\r\n this._pipe.send(frame);\r\n return;\r\n }\r\n this.config.send(frame);\r\n }\r\n\r\n private _buildPipe(\r\n crypto: Parameters<typeof createFrameCryptoPipe>[0][\"crypto\"],\r\n ): IFrameCryptoPipe {\r\n return createFrameCryptoPipe({ write: this.config.send, crypto, label: \"ws-server\" });\r\n }\r\n}\r\n","import type { ClientCryptoKeyLink, TTypeAndId } from \"@nice-code/util\";\r\nimport type { TDistributeActionPayload_Request } from \"../../../../ActionDefinition/Action/Action.combined.types\";\r\nimport type { IActionRouteItemHandler } from \"../../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport {\r\n EActionPayloadType,\r\n type TActionPayload_Any_Instance,\r\n type TActionPayload_Any_JsonObject,\r\n} from \"../../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { ActionPayload_Request } from \"../../../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport { RunningAction } from \"../../../../ActionDefinition/Action/RunningAction\";\r\nimport { ERunningActionUpdateType } from \"../../../../ActionDefinition/Action/RunningAction.types\";\r\nimport type { ActionDomain } from \"../../../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { IActionDomain } from \"../../../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport { EActionResponseMode } from \"../../../../ActionDefinition/Schema/ActionSchema\";\r\nimport { DEFAULT_TRANSPORT_TIMEOUT, UNSET_RUNTIME_ENV_ID } from \"../../../../nice_action.static\";\r\nimport { decodeActionFrame } from \"../../../../utils/decodeActionFrame\";\r\nimport { ActionRuntime } from \"../../../ActionRuntime\";\r\nimport { peekHandlerCuid } from \"../../../HandlerCallStack\";\r\nimport { type IRuntimeCoordinate, RuntimeCoordinate } from \"../../../RuntimeCoordinate\";\r\nimport type { IActionWireFormat } from \"../../../Transport/codec/actionWireCodec\";\r\nimport {\r\n ESecurityLevel,\r\n type IClientVerifyKeyResolver,\r\n type IHandshakeEncryptionKeyMaterial,\r\n} from \"../../../Transport/crypto/actionHandshake\";\r\nimport { EErrId_NiceTransport, err_nice_transport } from \"../../../Transport/err_nice_transport\";\r\nimport {\r\n AcceptorSecureSession,\r\n type IAcceptorAuthResult,\r\n} from \"../../../Transport/SecureSession/acceptorSecureSession\";\r\nimport { ETransportShape } from \"../../../Transport/Transport.types\";\r\nimport type { IActionHandler_Peer_Json, IHandleActionOptions } from \"../../ActionHandler.types\";\r\nimport { ActionLocalHandler } from \"../../Local/ActionLocalHandler\";\r\nimport type { THandleActionExecutionFn } from \"../../Local/ActionLocalHandler.types\";\r\nimport { PeerLinkHandler } from \"../../PeerLink/PeerLinkHandler\";\r\n\r\n/** The codec shape `AcceptorHandler` uses to pack/unpack frames — same as the Link transport's. */\r\nexport type TActionChannelFormatMessage = IActionWireFormat;\r\n\r\n/** How a connection encodes its frames, remembered so we answer each client in its own dialect. */\r\nexport type TActionConnectionEncoding = \"json\" | \"binary\";\r\n\r\n/** A connection's restorable identity — what to persist so a binding survives transport eviction. */\r\nexport interface IAcceptorConnectionBinding {\r\n /** Full client coordinate, so `originClient` can be re-injected into frames that omit it. */\r\n client: IRuntimeCoordinate;\r\n encoding: TActionConnectionEncoding;\r\n /**\r\n * Secure-session state (set once a connection's handshake completes). Persist it alongside the\r\n * binding so an authenticated/encrypted connection resumes after eviction without re-handshaking —\r\n * the `keyMaterial` lets the server re-derive the shared key from its own persisted identity.\r\n */\r\n secure?: {\r\n securityLevel: ESecurityLevel;\r\n linkedClientId: TTypeAndId;\r\n keyMaterial?: IHandshakeEncryptionKeyMaterial;\r\n };\r\n}\r\n\r\n/**\r\n * Server-side secure-channel config. When set, each connection negotiates a level from\r\n * {@link securityLevel}: an `authenticated`/`encrypted` client must complete the handshake (and is then\r\n * bound to its *authenticated* coordinate) before any action frame is accepted. A `none` client (only\r\n * when `none` is in the allowed set) is accepted as-is with a self-asserted identity. For the\r\n * `encrypted` level the codec source should be a session factory (`createFormatMessage`).\r\n */\r\nexport interface IAcceptorSecurity {\r\n /**\r\n * Accepted level(s). A single level is strict; an array is a negotiable allowed set — the server\r\n * adopts whichever level each client requests (e.g. `[none, authenticated, encrypted]` serves all\r\n * three over one endpoint).\r\n */\r\n securityLevel: ESecurityLevel | readonly ESecurityLevel[];\r\n /** This server's crypto identity (verify + exchange key pairs, optionally persisted). */\r\n link: ClientCryptoKeyLink;\r\n /** This server's coordinate — its identity to clients during the handshake. */\r\n localCoordinate: IRuntimeCoordinate;\r\n /** Wire dictionary version; the handshake rejects a client on a mismatch. */\r\n dictionaryVersion: string;\r\n /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n}\r\n\r\ninterface IAcceptorHandlerBaseOptions<TConn> {\r\n /**\r\n * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env(\"web_app\")`).\r\n * The runtime's return-path dispatch scores incoming actions' `originClient` against this to pick\r\n * this handler for sending results/pushes back over the right channel.\r\n */\r\n clientEnv: RuntimeCoordinate;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\r\n /**\r\n * The runtime this handler belongs to. When set, {@link AcceptorHandler.broadcast} can be called\r\n * without threading a runtime through each call. Optional — `pushToClient` still takes one explicitly.\r\n */\r\n runtime?: ActionRuntime;\r\n /** Timeout (ms) applied to server-initiated actions awaiting a client response. */\r\n defaultTimeout?: number;\r\n /**\r\n * Called once when a connection is first bound to a client identity. Use it to persist the binding\r\n * for transports that can resume after eviction — e.g. a Durable Object's hibernatable WebSocket:\r\n * `(ws, binding) => ws.serializeAttachment(binding)` — then replay it via {@link AcceptorHandler.rehydrateConnection}\r\n * when the channel comes back.\r\n */\r\n onConnectionBound?: (connection: TConn, binding: IAcceptorConnectionBinding) => void;\r\n /**\r\n * Enable the authenticated (optionally encrypted) handshake. When omitted, connections are trusted\r\n * as-is (identity self-asserted) — fine for dev / trusted networks.\r\n */\r\n security?: IAcceptorSecurity;\r\n}\r\n\r\n/**\r\n * Provide exactly one codec source:\r\n * - `formatMessage` — a single shared codec for every connection (stateless, e.g. `createBinaryWireAdapter`).\r\n * - `createFormatMessage` — a per-connection factory for stateful codecs (e.g.\r\n * `createBinaryWireSessionFactory`, whose sessions hold correlation + identity state). Required for the\r\n * leanest binary wire; the handler creates and caches one codec per connection.\r\n */\r\nexport type IAcceptorHandlerOptions<TConn> = IAcceptorHandlerBaseOptions<TConn> &\r\n (\r\n | { formatMessage: TActionChannelFormatMessage; createFormatMessage?: never }\r\n | { createFormatMessage: () => TActionChannelFormatMessage; formatMessage?: never }\r\n );\r\n\r\n/**\r\n * A connection-aware execution case (see {@link AcceptorHandler.forConnectionDomainCases}). It receives\r\n * the primed request plus a per-invocation `context` — whatever the wiring's context mapper produces from\r\n * the originating connection. The low-level handler passes the raw connection (`TConn | undefined`); the\r\n * higher-level `serveChannel` enriches it into an `IConnectionContext` (state + broadcast + pushBack). A\r\n * case may return the action's raw output, a result payload, or nothing (auto-wrapped as an empty\r\n * success) — exactly like a local handler case.\r\n */\r\nexport type TAcceptorCaseFn<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n TCtx,\r\n> = (\r\n action: TDistributeActionPayload_Request<DOM, ID>,\r\n context: TCtx,\r\n) => ReturnType<THandleActionExecutionFn<DOM, ID>> | void;\r\n\r\n/**\r\n * The connection-aware case the bare {@link AcceptorHandler} serves: its `context` is the originating\r\n * client's live connection (resolved from the request's `originClient`, `undefined` if the socket is\r\n * gone). It's {@link TAcceptorCaseFn} fixed to `TConn | undefined` — the un-enriched shape used by\r\n * {@link AcceptorHandler.forConnectionDomainCases} and `acceptChannelConnections`.\r\n */\r\nexport type TAcceptorConnectionCaseFn<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n TConn,\r\n> = TAcceptorCaseFn<DOM, ID, TConn | undefined>;\r\n\r\n/**\r\n * Server-side handler for backends that accept many client connections over a single open channel\r\n * (WebSockets, Durable Objects, …). It is transport-agnostic: you feed it inbound frames with\r\n * {@link receive} and tell it how to write outbound frames via the `send` option.\r\n *\r\n * Add it alongside your local execution handler:\r\n * ```ts\r\n * const serverHandler = createAcceptorHandler({ clientEnv, formatMessage, send: (ws, f) => ws.send(f) });\r\n * runtime.addHandlers([localHandler, serverHandler]);\r\n * // per inbound message (e.g. a Durable Object's webSocketMessage):\r\n * serverHandler.receive(ws, message);\r\n * ```\r\n *\r\n * Inbound requests route to your local handler; the runtime's return dispatch then calls this\r\n * handler back (it is an external handler keyed to `clientEnv`) to send the result to the originating\r\n * connection. The handler keeps a per-connection identity registry so each result lands on the right\r\n * socket, and remembers each connection's encoding so binary and JSON clients can share the channel.\r\n *\r\n * It registers an empty action router, so it is never chosen to *execute* an inbound request — only\r\n * to ferry results/pushes back out.\r\n */\r\nexport class AcceptorHandler<TConn = unknown> extends PeerLinkHandler {\r\n /** Accept-in over a live (duplex) connection registry — it pushes results/broadcasts to bound sockets. */\r\n readonly canPush = true;\r\n\r\n private readonly _formatMessage?: TActionChannelFormatMessage;\r\n private readonly _createFormatMessage?: () => TActionChannelFormatMessage;\r\n private readonly _send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\r\n private readonly _runtime?: ActionRuntime;\r\n private readonly _serverTimeout: number;\r\n private _onConnectionBound?: (connection: TConn, binding: IAcceptorConnectionBinding) => void;\r\n\r\n private readonly _security?: IAcceptorSecurity;\r\n /** Normalized accepted levels; whether `none` (plain) is allowed; whether any level needs a handshake. */\r\n private readonly _allowedLevels: readonly ESecurityLevel[];\r\n private readonly _noneAllowed: boolean;\r\n private readonly _handshakeMode: boolean;\r\n\r\n // Per-connection identity + encoding + codec registries.\r\n private readonly _connByClient = new Map<string, TConn>();\r\n private readonly _clientByConn = new Map<TConn, RuntimeCoordinate>();\r\n private readonly _connEncoding = new Map<TConn, TActionConnectionEncoding>();\r\n private readonly _codecByConn = new Map<TConn, TActionChannelFormatMessage>();\r\n\r\n // Per-connection secure session (only built when `security` is set): owns the handshake driving, the\r\n // ordered encrypt-send / decrypt pipe, and the connection's phase. The handler keeps only the identity\r\n // registry + encoding/codec; all crypto/handshake/chain bookkeeping lives in the session core.\r\n private readonly _sessionByConn = new Map<TConn, AcceptorSecureSession>();\r\n\r\n constructor(options: IAcceptorHandlerOptions<TConn>) {\r\n super(options.clientEnv);\r\n this._formatMessage = options.formatMessage;\r\n this._createFormatMessage = options.createFormatMessage;\r\n this._send = options.send;\r\n this._runtime = options.runtime;\r\n this._serverTimeout = options.defaultTimeout ?? DEFAULT_TRANSPORT_TIMEOUT;\r\n this._onConnectionBound = options.onConnectionBound;\r\n this._security = options.security;\r\n this._allowedLevels =\r\n options.security == null\r\n ? []\r\n : Array.isArray(options.security.securityLevel)\r\n ? options.security.securityLevel\r\n : [options.security.securityLevel];\r\n this._noneAllowed = this._allowedLevels.includes(ESecurityLevel.none);\r\n this._handshakeMode = this._allowedLevels.some((level) => level !== ESecurityLevel.none);\r\n }\r\n\r\n /**\r\n * The codec for a connection: a per-connection session (cached) when a factory was provided, else\r\n * the single shared `formatMessage`.\r\n */\r\n private _codecFor(connection: TConn): TActionChannelFormatMessage {\r\n if (this._createFormatMessage != null) {\r\n let codec = this._codecByConn.get(connection);\r\n if (codec == null) {\r\n codec = this._createFormatMessage();\r\n this._codecByConn.set(connection, codec);\r\n }\r\n return codec;\r\n }\r\n if (this._formatMessage != null) return this._formatMessage;\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.not_found, {\r\n actionId: \"server-handler-codec (provide formatMessage or createFormatMessage)\",\r\n });\r\n }\r\n\r\n /**\r\n * Register (or replace) the connection-bound persistence callback after construction. Used by\r\n * lifecycle helpers like {@link createHibernatableWsServerAdapter} so persistence and replay are\r\n * owned by one place instead of being split across the constructor options.\r\n */\r\n setOnConnectionBound(\r\n onConnectionBound: (connection: TConn, binding: IAcceptorConnectionBinding) => void,\r\n ): void {\r\n this._onConnectionBound = onConnectionBound;\r\n }\r\n\r\n /**\r\n * Feed one inbound frame from a connection into the runtime. Decodes text or binary, binds the\r\n * connection to the requesting client's identity, then routes it (requests execute locally;\r\n * results/progress resolve pending server-initiated actions).\r\n */\r\n receive(connection: TConn, frame: string | ArrayBuffer | Uint8Array): void {\r\n const security = this._security;\r\n // No security configured, or only `none` accepted → always plain (synchronous, self-asserted).\r\n if (security == null || !this._handshakeMode) {\r\n this._receivePlain(connection, frame);\r\n return;\r\n }\r\n\r\n // Secure mode: the per-connection session drives the handshake + decryption (serialized internally)\r\n // and routes back through the handler via the callbacks wired in `_sessionFor`.\r\n this._sessionFor(connection, security).receive(frame);\r\n }\r\n\r\n private _receivePlain(connection: TConn, frame: string | ArrayBuffer | Uint8Array): void {\r\n const wire = decodeActionFrame(frame, this._codecFor(connection));\r\n if (wire == null) return;\r\n\r\n const encoding: TActionConnectionEncoding = typeof frame === \"string\" ? \"json\" : \"binary\";\r\n this._connEncoding.set(connection, encoding);\r\n\r\n if (wire.type === EActionPayloadType.request) {\r\n this._resolveRequestIdentity(connection, wire, encoding);\r\n }\r\n\r\n this._emitIncoming(wire);\r\n }\r\n\r\n /**\r\n * The secure session for a connection (built lazily on its first secure-mode frame), with the\r\n * handler-owned effects — raw send, identity binding + persistence, and inbound routing — wired in as\r\n * callbacks. The session owns all crypto/handshake/chain state; the handler keeps only the registry.\r\n */\r\n private _sessionFor(connection: TConn, security: IAcceptorSecurity): AcceptorSecureSession {\r\n let session = this._sessionByConn.get(connection);\r\n if (session == null) {\r\n session = new AcceptorSecureSession({\r\n link: security.link,\r\n localCoordinate: security.localCoordinate,\r\n dictionaryVersion: security.dictionaryVersion,\r\n securityLevel: security.securityLevel,\r\n verifyKeyResolver: security.verifyKeyResolver,\r\n noneAllowed: this._noneAllowed,\r\n send: (frame) => this._send(connection, frame),\r\n onAuthenticated: (auth) => this._onConnectionAuthenticated(connection, auth),\r\n routePlain: (frame) => this._receivePlain(connection, frame),\r\n routeAction: (bytes) => this._routeAuthedActionBytes(connection, bytes),\r\n });\r\n this._sessionByConn.set(connection, session);\r\n }\r\n return session;\r\n }\r\n\r\n /** Bind + persist a connection's authenticated identity once its handshake completes. */\r\n private _onConnectionAuthenticated(connection: TConn, auth: IAcceptorAuthResult): void {\r\n this._bindConnection(connection, auth.client);\r\n this._connEncoding.set(connection, \"binary\");\r\n\r\n // Persist the secure session so an evicted connection can resume without re-handshaking.\r\n this._onConnectionBound?.(connection, {\r\n client: auth.client.toJsonObject(),\r\n encoding: \"binary\",\r\n secure: {\r\n securityLevel: auth.securityLevel,\r\n linkedClientId: auth.linkedClientId,\r\n keyMaterial: auth.keyMaterial,\r\n },\r\n });\r\n }\r\n\r\n /** Decode a decrypted authenticated frame, inject the *authenticated* identity, and route it. */\r\n private _routeAuthedActionBytes(\r\n connection: TConn,\r\n bytes: string | ArrayBuffer | Uint8Array,\r\n ): void {\r\n const wire = decodeActionFrame(bytes, this._codecFor(connection));\r\n if (wire == null) return;\r\n\r\n // The connection is bound to an *authenticated* coordinate — always use it, never the wire's\r\n // self-asserted originClient.\r\n if (wire.type === EActionPayloadType.request) {\r\n const bound = this._clientByConn.get(connection);\r\n if (bound != null) wire.context.originClient = bound.toJsonObject();\r\n }\r\n\r\n this._emitIncoming(wire);\r\n }\r\n\r\n /**\r\n * Ensure an inbound request carries the client's identity and that this connection is bound to it,\r\n * so its result can be routed back. A session codec omits `originClient` after the first request, so\r\n * when it's missing we restore it from the (possibly rehydrated) binding instead. (Plain mode only;\r\n * secure mode binds the authenticated coordinate at handshake time.)\r\n */\r\n private _resolveRequestIdentity(\r\n connection: TConn,\r\n wire: TActionPayload_Any_JsonObject<any>,\r\n encoding: TActionConnectionEncoding,\r\n ): void {\r\n const wireOrigin = wire.context.originClient;\r\n\r\n if (wireOrigin != null && wireOrigin.envId !== UNSET_RUNTIME_ENV_ID) {\r\n const clientCoord = new RuntimeCoordinate(wireOrigin);\r\n const isNewBinding = this._clientByConn.get(connection)?.stringId !== clientCoord.stringId;\r\n this._bindConnection(connection, clientCoord);\r\n if (isNewBinding) {\r\n this._onConnectionBound?.(connection, { client: clientCoord.toJsonObject(), encoding });\r\n }\r\n return;\r\n }\r\n\r\n // Identity dropped by the session — restore it from the binding so return routing still works.\r\n const bound = this._clientByConn.get(connection);\r\n if (bound != null) wire.context.originClient = bound.toJsonObject();\r\n }\r\n\r\n /**\r\n * Restore a connection→client binding without an inbound frame — for transports that resume after\r\n * eviction. Pair it with the {@link IAcceptorHandlerOptions.onConnectionBound} hook: persist\r\n * the binding there, then replay each live connection here when the channel comes back (e.g. a\r\n * Durable Object iterating `ctx.getWebSockets()` as it wakes from hibernation).\r\n */\r\n rehydrateConnection(connection: TConn, binding: IAcceptorConnectionBinding): void {\r\n this._bindConnection(connection, new RuntimeCoordinate(binding.client));\r\n this._connEncoding.set(connection, binding.encoding);\r\n\r\n const secure = binding.secure;\r\n const security = this._security;\r\n if (secure == null || security == null) return;\r\n\r\n // The connection had already authenticated before eviction — resume its secure session (no\r\n // handshake; an encrypted session re-derives its shared key off the acceptor's persisted identity).\r\n this._sessionFor(connection, security).rehydrate(secure);\r\n }\r\n\r\n toJsonObject(): IActionHandler_Peer_Json {\r\n return {\r\n type: this.handlerType,\r\n client: this.peerClient,\r\n };\r\n }\r\n\r\n override toHandlerRouteItem(): IActionRouteItemHandler {\r\n return {\r\n type: this.handlerType,\r\n client: this.peerClient,\r\n transShape: ETransportShape.duplex,\r\n transOrd: 0,\r\n };\r\n }\r\n\r\n /** Forget a connection (call on socket close) so stale entries don't misroute later results. */\r\n dropConnection(connection: TConn): void {\r\n const coord = this._clientByConn.get(connection);\r\n if (coord != null && this._connByClient.get(coord.stringId) === connection) {\r\n this._connByClient.delete(coord.stringId);\r\n }\r\n this._clientByConn.delete(connection);\r\n this._connEncoding.delete(connection);\r\n this._codecByConn.delete(connection);\r\n this._sessionByConn.delete(connection);\r\n }\r\n\r\n /** Live connection for a client coordinate, if currently registered. */\r\n getConnectionForClient(client: RuntimeCoordinate): TConn | undefined {\r\n return this._connByClient.get(client.stringId);\r\n }\r\n\r\n /** This acceptor owns the origin's return path when it currently holds a live connection bound to it. */\r\n override ownsLiveConnectionFor(origin: RuntimeCoordinate): boolean {\r\n return this._connByClient.has(origin.stringId);\r\n }\r\n\r\n /** Whether this acceptor currently tracks `connection` — used to pick the owning handler among several. */\r\n hasConnection(connection: TConn): boolean {\r\n return this._clientByConn.has(connection);\r\n }\r\n\r\n /**\r\n * Send (and optionally await) a server-initiated action to a specific connected client. Pass the\r\n * connection token directly (e.g. the `ws`) or a client `RuntimeCoordinate` to look one up.\r\n */\r\n pushToClient<DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n runtime: ActionRuntime,\r\n target: TConn | RuntimeCoordinate,\r\n request: ActionPayload_Request<DOM, ID>,\r\n options?: { timeout?: number },\r\n ): RunningAction<DOM, ID> {\r\n const connection = this._resolveConnection(target);\r\n return this._dispatch(runtime, connection, request, options?.timeout);\r\n }\r\n\r\n /**\r\n * Build a local handler whose cases are connection-aware: each case receives the primed request and\r\n * the originating client's live connection (resolved from `originClient`), so handlers don't repeat\r\n * the `getConnectionForClient(action.context.originClient)` lookup. Cases may return raw output or\r\n * nothing, just like {@link ActionLocalHandler.forDomainActionCases}. Add the returned handler to the\r\n * runtime alongside this server handler:\r\n * ```ts\r\n * runtime.addHandlers([serverHandler.forConnectionDomainCases(domain, { … }), serverHandler]);\r\n * ```\r\n */\r\n forConnectionDomainCases<FOR_DOM extends IActionDomain>(\r\n domain: ActionDomain<FOR_DOM>,\r\n cases: {\r\n [ID in keyof FOR_DOM[\"actionSchema\"] & string]?: TAcceptorConnectionCaseFn<\r\n FOR_DOM,\r\n ID,\r\n TConn\r\n >;\r\n },\r\n ): ActionLocalHandler {\r\n // Default context = the raw connection (or undefined when the socket is gone). The cast bridges the\r\n // per-id mapped case map to the merged `any`-keyed map the multi form iterates (a known TS variance\r\n // limitation on the contravariant action param — same bridge `acceptChannelConnections` uses).\r\n return this.forConnectionDomainCasesMulti(\r\n [domain],\r\n cases as Record<string, TAcceptorConnectionCaseFn<any, any, TConn> | undefined>,\r\n (connection) => connection,\r\n );\r\n }\r\n\r\n /**\r\n * Like {@link forConnectionDomainCases} but spanning several domains with one merged case map — used\r\n * by channel-derived wiring (`acceptChannelConnections` / `serveChannel`) where the channel's\r\n * `toAcceptor` domains are served together. Each domain takes only the cases whose ids it owns, so a\r\n * single map can cover several domains and unrelated ids are ignored.\r\n *\r\n * `mapContext` turns the resolved connection into whatever the case's second argument should be: the\r\n * raw connection for the low-level helper, or an enriched `IConnectionContext` for `serveChannel`. It's\r\n * called once per inbound action, after the originating connection is resolved.\r\n */\r\n forConnectionDomainCasesMulti<TCtx>(\r\n domains: readonly ActionDomain<any>[],\r\n cases: Record<string, TAcceptorCaseFn<any, any, TCtx> | undefined>,\r\n mapContext: (\r\n connection: TConn | undefined,\r\n request: ActionPayload_Request<any, any>,\r\n ) => TCtx,\r\n ): ActionLocalHandler {\r\n const handler = new ActionLocalHandler();\r\n\r\n for (const domain of domains) {\r\n const ownedIds = new Set(Object.keys(domain.actionsMap()));\r\n const wrapped: Record<string, THandleActionExecutionFn<any, any>> = {};\r\n\r\n for (const id in cases) {\r\n if (!ownedIds.has(id)) continue;\r\n const caseFn = cases[id];\r\n if (caseFn == null) continue;\r\n wrapped[id] = (request) => {\r\n const connection = this.getConnectionForClient(request.context.originClient);\r\n return caseFn(request, mapContext(connection, request));\r\n };\r\n }\r\n\r\n handler.forDomainActionCases(domain, wrapped);\r\n }\r\n\r\n return handler;\r\n }\r\n\r\n /**\r\n * Fan a server-initiated request out to every currently-bound connection. A fresh request is built\r\n * per connection (each push mutates its own action context) and dispatched fire-and-forget. Pass\r\n * `except` to skip the originating socket and `where` to filter by connection (e.g. read its\r\n * attachment for a role). Iterating bound connections (rather than every accepted socket) skips\r\n * sockets that are still mid-handshake and so can't yet receive a frame.\r\n */\r\n broadcast<DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n options?: {\r\n runtime?: ActionRuntime;\r\n except?: TConn | null;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ): void {\r\n const runtime = options?.runtime ?? this._runtime;\r\n if (runtime == null) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.not_found, {\r\n actionId: \"server-handler-runtime (construct with `runtime` or pass `options.runtime`)\",\r\n });\r\n }\r\n\r\n for (const connection of this._clientByConn.keys()) {\r\n if (options?.except != null && connection === options.except) continue;\r\n if (options?.where != null && !options.where(connection)) continue;\r\n try {\r\n this.pushToClient(runtime, connection, makeRequest(), { timeout: options?.timeout });\r\n } catch (error) {\r\n if (options?.onError != null) options.onError(error, connection);\r\n else console.error(\"[ws-server] broadcast push failed\", error);\r\n }\r\n }\r\n }\r\n\r\n override async sendReturnPayload(\r\n payload: TActionPayload_Any_Instance<any, any>,\r\n config: { targetLocalRuntime: ActionRuntime },\r\n ): Promise<boolean> {\r\n const connection = this._connByClient.get(payload.context.originClient.stringId);\r\n if (connection == null) return false;\r\n this._sendPayload(connection, payload, config.targetLocalRuntime.coordinate);\r\n return true;\r\n }\r\n\r\n override async handleActionRequest<\r\n DOM extends IActionDomain,\r\n ID extends keyof DOM[\"actionSchema\"] & string,\r\n >(\r\n action: ActionPayload_Request<DOM, ID>,\r\n config?: IHandleActionOptions,\r\n ): Promise<RunningAction<DOM, ID>> {\r\n const runtime = config?.targetLocalRuntime ?? ActionRuntime.getDefault();\r\n const connection = this._resolveSingleConnection();\r\n return this._dispatch(runtime, connection, action, config?.timeout);\r\n }\r\n\r\n private _dispatch<DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n runtime: ActionRuntime,\r\n connection: TConn,\r\n action: ActionPayload_Request<DOM, ID>,\r\n timeout?: number,\r\n ): RunningAction<DOM, ID> {\r\n const timeoutMs = timeout ?? this._serverTimeout;\r\n\r\n // The client must be able to route its result back to *us*, so the origin is this backend.\r\n action.context._setOriginClient(runtime.coordinate);\r\n action.context.addRouteItem({\r\n runtime: runtime.coordinate,\r\n handler: this.toHandlerRouteItem(),\r\n time: Date.now(),\r\n });\r\n\r\n const runningAction = new RunningAction<DOM, ID>({\r\n context: action.context,\r\n request: action,\r\n parentCuid: peekHandlerCuid(),\r\n callSite: action._callSite,\r\n });\r\n runtime.registerRunningAction(runningAction);\r\n\r\n // Fire-and-forget: no reply will come, so don't park a pending action behind a timeout. Send the\r\n // frame and complete immediately as success — the running action still surfaces in devtools, it just\r\n // resolves on send rather than on a (never-arriving) reply.\r\n if (action.schema.responseMode === EActionResponseMode.none) {\r\n try {\r\n this._sendPayload(connection, action, runtime.coordinate);\r\n runningAction._completeWithResult(\r\n (action as ActionPayload_Request<any, any>).successResult(undefined),\r\n );\r\n } catch (err) {\r\n runningAction._abort(err);\r\n }\r\n return runningAction;\r\n }\r\n\r\n const timeoutId = setTimeout(() => {\r\n runningAction._abort(\r\n err_nice_transport.fromId(EErrId_NiceTransport.timeout, { timeout: timeoutMs }),\r\n );\r\n }, timeoutMs);\r\n runningAction.addUpdateListeners([\r\n (update) => {\r\n if (update.type === ERunningActionUpdateType.finished) clearTimeout(timeoutId);\r\n },\r\n ]);\r\n\r\n try {\r\n this._sendPayload(connection, action, runtime.coordinate);\r\n } catch (err) {\r\n runningAction._abort(err);\r\n }\r\n\r\n return runningAction;\r\n }\r\n\r\n private _sendPayload(\r\n connection: TConn,\r\n payload: TActionPayload_Any_Instance<any, any>,\r\n localClient: RuntimeCoordinate,\r\n ): void {\r\n const encoding = this._connEncoding.get(connection) ?? \"binary\";\r\n const frame =\r\n encoding === \"json\"\r\n ? JSON.stringify(payload.toJsonObject())\r\n : this._codecFor(connection).outgoing({\r\n action: payload,\r\n localClient,\r\n externalClient: this.peerClient,\r\n });\r\n\r\n // A secure connection's session encrypts (and orders) the frame; plain/authenticated connections (and\r\n // connections with no secure session at all) send as-is.\r\n const session = this._sessionByConn.get(connection);\r\n if (session == null) {\r\n this._send(connection, frame);\r\n return;\r\n }\r\n session.send(frame);\r\n }\r\n\r\n private _bindConnection(connection: TConn, client: RuntimeCoordinate): void {\r\n this._connByClient.set(client.stringId, connection);\r\n this._clientByConn.set(connection, client);\r\n }\r\n\r\n private _resolveConnection(target: TConn | RuntimeCoordinate): TConn {\r\n if (target instanceof RuntimeCoordinate) {\r\n const connection = this._connByClient.get(target.stringId);\r\n if (connection == null) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.not_found, {\r\n actionId: target.stringId,\r\n });\r\n }\r\n return connection;\r\n }\r\n return target;\r\n }\r\n\r\n private _resolveSingleConnection(): TConn {\r\n if (this._clientByConn.size !== 1) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.not_found, {\r\n actionId:\r\n \"server-handler-target (use pushToClient with an explicit connection or client coordinate)\",\r\n });\r\n }\r\n return this._clientByConn.keys().next().value as TConn;\r\n }\r\n}\r\n\r\nexport const createAcceptorHandler = <TConn = unknown>(\r\n options: IAcceptorHandlerOptions<TConn>,\r\n): AcceptorHandler<TConn> => {\r\n return new AcceptorHandler<TConn>(options);\r\n};\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { ActionRuntime } from \"../../../ActionRuntime\";\r\nimport type { RuntimeCoordinate } from \"../../../RuntimeCoordinate\";\r\nimport {\r\n createStorageTofuVerifyKeyResolver,\r\n ESecurityLevel,\r\n type IClientVerifyKeyResolver,\r\n} from \"../../../Transport/crypto/actionHandshake\";\r\nimport type { ISecureChannel } from \"../../../Channel/secureChannel\";\r\nimport { AcceptorHandler } from \"./AcceptorHandler\";\r\n\r\n/** Default accepted set: negotiate per connection to whatever the client picks. */\r\nconst DEFAULT_SERVER_SECURITY_LEVELS = [\r\n ESecurityLevel.none,\r\n ESecurityLevel.authenticated,\r\n ESecurityLevel.encrypted,\r\n] as const;\r\n\r\nexport interface ISecureAcceptorHandlerOptions<TConn> {\r\n /** The shared channel identity (codec + dictionary version) — same one the clients use. */\r\n channel: ISecureChannel;\r\n /**\r\n * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env(\"web_app\")`),\r\n * used to route results/pushes back over this handler.\r\n */\r\n clientEnv: RuntimeCoordinate;\r\n /** This server's runtime — its coordinate is the server identity presented in the handshake. */\r\n runtime: ActionRuntime;\r\n /**\r\n * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins.\r\n * Their keys don't collide, so a single adapter is enough; back it with persistent storage (e.g. a\r\n * Durable Object's storage) so identity and pins survive eviction.\r\n */\r\n storageAdapter: StorageAdapter;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\r\n /**\r\n * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storageAdapter`.\r\n * Pass an existing link to share one identity across several acceptors on the same server (e.g. a\r\n * WebSocket acceptor and a secure-HTTP {@link createActionFetchHandler}), so they present the same\r\n * verify/exchange keys — avoiding a divergent-key race when two fresh links initialize concurrently.\r\n */\r\n link?: ClientCryptoKeyLink;\r\n /** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */\r\n securityLevel?: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storageAdapter`. */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n /** Timeout (ms) applied to server-initiated actions awaiting a client response. */\r\n defaultTimeout?: number;\r\n}\r\n\r\n/**\r\n * Build an {@link AcceptorHandler} for the secure binary channel with the boilerplate folded in:\r\n * it creates the {@link ClientCryptoKeyLink} and the storage-backed TOFU resolver from a single\r\n * `storageAdapter`, installs the channel's per-connection codec, and assembles the `security` block\r\n * from the runtime coordinate + channel version (accepting all three levels by default).\r\n *\r\n * For a hibernatable transport (e.g. a Durable Object), pair it with\r\n * {@link createHibernatableWsServerAdapter} to wire persistence + replay.\r\n */\r\nexport function createSecureAcceptorHandler<TConn = unknown>(\r\n options: ISecureAcceptorHandlerOptions<TConn>,\r\n): AcceptorHandler<TConn> {\r\n const link = options.link ?? new ClientCryptoKeyLink({ storageAdapter: options.storageAdapter });\r\n\r\n return new AcceptorHandler<TConn>({\r\n clientEnv: options.clientEnv,\r\n createFormatMessage: options.channel.createCodec,\r\n send: options.send,\r\n runtime: options.runtime,\r\n defaultTimeout: options.defaultTimeout,\r\n security: {\r\n securityLevel: options.securityLevel ?? DEFAULT_SERVER_SECURITY_LEVELS,\r\n link,\r\n localCoordinate: options.runtime.coordinate.toJsonObject(),\r\n dictionaryVersion: options.channel.dictionaryVersion,\r\n verifyKeyResolver:\r\n options.verifyKeyResolver ?? createStorageTofuVerifyKeyResolver(options.storageAdapter),\r\n },\r\n });\r\n}\r\n","import type { ITransportRouteActionParams, ITransportRouteInfo } from \"../Transport.types\";\r\n\r\n/**\r\n * Carrier shapes — the only transport-specific surface a new protocol must implement. The secure\r\n * session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;\r\n * a carrier just moves frames. Two shapes capture every carrier:\r\n *\r\n * - {@link IDuplexCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,\r\n * Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client\r\n * pushes (the return path + broadcast).\r\n * - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP, and anything request/response-shaped). The reply rides the response to its own request.\r\n *\r\n * Frames are `string` (text — handshake control messages and JSON action frames) or binary\r\n * (`Uint8Array`/`ArrayBuffer` — the optimized binary wire / encrypted frames).\r\n */\r\nexport type TFrame = string | Uint8Array | ArrayBuffer;\r\n\r\n/**\r\n * A bidirectional, push-capable byte stream. Reduces every duplex carrier to \"open, send bytes, receive\r\n * bytes, close\" — a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe\r\n * all satisfy this, so the identical secure session runs over each.\r\n */\r\nexport interface IDuplexCarrier {\r\n /** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */\r\n readonly ready: Promise<void>;\r\n /** Whether the carrier is currently open (a synchronous guard before `send`). */\r\n isOpen(): boolean;\r\n /** Write one frame to the peer. */\r\n send(frame: TFrame): void;\r\n /**\r\n * Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`\r\n * receives every inbound frame; `onClose` fires when the carrier goes away.\r\n */\r\n attach(handlers: {\r\n onMessage: (frame: TFrame) => void;\r\n onClose: () => void;\r\n onError?: (error: unknown) => void;\r\n }): void;\r\n /** Close the carrier deliberately (a teardown). */\r\n close(): void;\r\n /** Optional human-readable endpoint for the devtools route chip. */\r\n readonly label?: string;\r\n}\r\n\r\n/**\r\n * A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a\r\n * frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the\r\n * HTTP transaction), so no correlation id is needed on the wire.\r\n */\r\nexport interface IExchangeCarrier {\r\n /** Send one frame, await the single correlated reply frame. */\r\n exchange(frame: TFrame, opts?: { signal?: AbortSignal }): Promise<TFrame>;\r\n /** Optional human-readable endpoint for the devtools route chip. */\r\n readonly label?: string;\r\n}\r\n\r\nexport type TCarrier = IDuplexCarrier | IExchangeCarrier;\r\n\r\n/**\r\n * A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.\r\n * Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and handed to\r\n * {@link secureTransport} / `LinkTransport` — so adding a new carrier is \"write one of these\", nothing\r\n * else.\r\n */\r\nexport interface IDuplexCarrierSource {\r\n /** Open (or reuse) the carrier for an action. */\r\n open: (input: ITransportRouteActionParams) => IDuplexCarrier;\r\n /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\r\n getCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Devtools route info for an action routed over this carrier. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`, `\"memory\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an\r\n * {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by\r\n * `httpCarrier` and handed to {@link secureTransport} — adding a new request/reply protocol is \"write\r\n * one of these\". The `shape` tag lets {@link secureTransport} pick the duplex vs exchange transport.\r\n */\r\nexport interface IExchangeCarrierSource {\r\n /** Discriminant so a generic factory can tell an exchange source from a duplex one. */\r\n readonly shape: \"exchange\";\r\n /** Open (or reuse) the carrier for an action. */\r\n open: (input: ITransportRouteActionParams) => IExchangeCarrier;\r\n /** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */\r\n getCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /** Devtools route info for an action routed over this carrier. */\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * Narrow a carrier source to the exchange shape via its `shape` discriminant — the one branch the\r\n * transport factories ({@link secureTransport}, {@link plainTransport}) use to pick the duplex vs\r\n * exchange transport. A duplex source carries no `shape`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeCarrierSource(\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource,\r\n): carrier is IExchangeCarrierSource {\r\n return \"shape\" in carrier && carrier.shape === \"exchange\";\r\n}\r\n","import type { IActionTransportResolvers } from \"./Transport.types\";\r\nimport {\r\n type ETransportShape,\r\n type ITransportRouteActionParams,\r\n type ITransportRouteInfo,\r\n} from \"./Transport.types\";\r\nimport type { TransportConnection } from \"./TransportConnection\";\r\n\r\n/**\r\n * Context handed to a {@link Transport} definition when a handler builds a live connection from it.\r\n * Only bidirectional transports (WebSocket / Custom) make use of `resolvers`.\r\n */\r\nexport interface ITransportConnectionContext {\r\n resolvers?: IActionTransportResolvers;\r\n}\r\n\r\n/**\r\n * Reusable transport definition. Devs construct these (`secureTransport({ carrier: wsCarrier(url) })`,\r\n * `plainTransport({ carrier: httpCarrier(...) })`, …) and pass them to a\r\n * `ConnectorHandler`. A single\r\n * definition can be shared across multiple handlers — each handler builds its own live\r\n * {@link TransportConnection} via {@link TransportConnection._createConnection}.\r\n */\r\nexport abstract class Transport<T extends ETransportShape = ETransportShape> {\r\n abstract readonly type: T;\r\n\r\n /** Internal: build a fresh, per-handler live connection from this definition. */\r\n abstract _createConnection(ctx: ITransportConnectionContext): TransportConnection<T>;\r\n\r\n /**\r\n * Resolve human-readable info about how a specific action would be routed through this transport\r\n * (e.g. the request URL/method, or the WebSocket endpoint). Surfaced in the action devtools.\r\n */\r\n abstract getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;\r\n}\r\n","import type { TActionPayload_Any_JsonObject } from \"../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\n\r\n/**\r\n * The application-level envelope for secure action traffic over an {@link IExchangeCarrier} (HTTP). An\r\n * exchange carrier only moves one request frame → one reply frame with no unsolicited push, so the\r\n * handshake and the per-action token + crypto all ride in this envelope (a JSON string body) rather than\r\n * on a persistent channel. The three security levels share it:\r\n *\r\n * - `none` — no handshake, no token: an `act` envelope carries the plaintext wire both ways.\r\n * - `authenticated` — a one-time handshake yields a session `token`; each later `act` carries it +\r\n * the plaintext wire.\r\n * - `encrypted` — same, but the wire is AES-GCM ciphertext, base64 in the `c` field.\r\n *\r\n * The handshake runs as two `hs` exchanges (hello→welcome, prove→accept) correlated by a client-chosen\r\n * `hsid`, since stateless requests can't rely on channel ordering. The `accept` reply carries the token.\r\n */\r\n\r\ntype TWireJson = TActionPayload_Any_JsonObject<any, any>;\r\n\r\n/** Connector → acceptor request envelope. */\r\nexport type TExchangeRequest =\r\n | { k: \"hs\"; hsid: string; m: string }\r\n | { k: \"act\"; t?: string; w: TWireJson }\r\n | { k: \"act\"; t?: string; c: string };\r\n\r\n/** Acceptor → connector reply envelope. */\r\nexport type TExchangeReply =\r\n | { k: \"hs\"; m: string; t?: string }\r\n | { k: \"act\"; w: TWireJson }\r\n | { k: \"act\"; c: string }\r\n | { k: \"err\"; message: string };\r\n\r\nexport function encodeExchange(envelope: TExchangeRequest | TExchangeReply): string {\r\n return JSON.stringify(envelope);\r\n}\r\n\r\nexport function decodeExchangeRequest(raw: string): TExchangeRequest | undefined {\r\n return parse<TExchangeRequest>(raw);\r\n}\r\n\r\nexport function decodeExchangeReply(raw: string): TExchangeReply | undefined {\r\n return parse<TExchangeReply>(raw);\r\n}\r\n\r\nfunction parse<T>(raw: string): T | undefined {\r\n try {\r\n return JSON.parse(raw) as T;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n// --- base64 for the encrypted ciphertext bytes (carried in a JSON string field) -----------------\r\n// `btoa`/`atob` over a binary string — available in browsers, web/Cloudflare Workers, and Node 16+.\r\n\r\nexport function bytesToBase64(bytes: Uint8Array): string {\r\n let binary = \"\";\r\n for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);\r\n return btoa(binary);\r\n}\r\n\r\nexport function base64ToBytes(base64: string): Uint8Array {\r\n const binary = atob(base64);\r\n const bytes = new Uint8Array(binary.length);\r\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);\r\n return bytes;\r\n}\r\n","import { nanoid } from \"nanoid\";\r\nimport { EActionPayloadType } from \"../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport { ERunningActionUpdateType } from \"../../../ActionDefinition/Action/RunningAction.types\";\r\nimport { isActionPayload_Result_JsonObject } from \"../../../utils/isActionPayload_Result_JsonObject\";\r\nimport type { IExchangeCarrier, TFrame } from \"../Carrier/Carrier.types\";\r\nimport { createActionFrameCrypto, type IActionFrameCrypto } from \"../crypto/actionFrameCrypto\";\r\nimport {\r\n createClientHandshake,\r\n decodeHandshakeMessage,\r\n EHandshakeMessageType,\r\n ESecurityLevel,\r\n encodeHandshakeMessage,\r\n} from \"../crypto/actionHandshake\";\r\nimport { EErrId_NiceTransport, err_nice_transport } from \"../err_nice_transport\";\r\nimport type {\r\n IActionTransportReadyData_Methods,\r\n ISecureClientConfig,\r\n TSendActionDataMethod,\r\n TUpdateActionRunConfig,\r\n} from \"../Transport.types\";\r\nimport {\r\n base64ToBytes,\r\n bytesToBase64,\r\n decodeExchangeReply,\r\n encodeExchange,\r\n type TExchangeReply,\r\n type TExchangeRequest,\r\n} from \"./exchangeProtocol\";\r\n\r\n/**\r\n * Connector-side (dial-out) secure session over an {@link IExchangeCarrier} (HTTP): the request → single\r\n * correlated reply counterpart to the duplex `establishLinkSession`. The secure session is identical in\r\n * spirit — handshake, then per-frame crypto — but carried by independent exchanges rather than a push\r\n * channel, so it can deliver no unsolicited frame (`canPush = false`); each action's result rides the\r\n * reply to its own request, completing the {@link RunningAction} inline (there is no return path).\r\n *\r\n * At `none` there is no handshake or token: an `act` envelope carries the plaintext wire both ways. At\r\n * `authenticated`/`encrypted` a one-time handshake (two `hs` exchanges) yields a session token replayed\r\n * on every later `act`; at `encrypted` the wire is AES-GCM ciphertext.\r\n */\r\nexport interface IExchangeSessionContext {\r\n carrier: IExchangeCarrier;\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n /** When present (and not `none`) the handshake runs at bring-up; absent ⇒ plain exchange. */\r\n secure?: ISecureClientConfig;\r\n}\r\n\r\ninterface IExchangeSecureState {\r\n token?: string;\r\n crypto?: IActionFrameCrypto;\r\n}\r\n\r\nconst textEncoder = new TextEncoder();\r\nconst textDecoder = new TextDecoder();\r\n\r\n/** Plain path (no handshake/token): every action rides a bare `act` envelope, plaintext both ways. */\r\nexport function finalizePlainExchangeMethods(\r\n ctx: IExchangeSessionContext,\r\n): IActionTransportReadyData_Methods {\r\n return buildExchangeMethods(ctx, {});\r\n}\r\n\r\n/** Secure path: run the handshake (two exchanges) once at bring-up, then reuse the token + crypto. */\r\nexport async function finalizeSecureExchangeMethods(\r\n ctx: IExchangeSessionContext & { secure: ISecureClientConfig },\r\n): Promise<IActionTransportReadyData_Methods> {\r\n const state = await runConnectorExchangeHandshake(ctx.carrier, ctx.secure);\r\n return buildExchangeMethods(ctx, state);\r\n}\r\n\r\nfunction buildExchangeMethods(\r\n ctx: IExchangeSessionContext,\r\n state: IExchangeSecureState,\r\n): IActionTransportReadyData_Methods {\r\n const sendActionData: TSendActionDataMethod = (inputs) => {\r\n void runExchange(ctx.carrier, state, inputs).catch((err) => inputs.runningAction._abort(err));\r\n };\r\n\r\n return { sendActionData, updateRunConfig: ctx.updateRunConfig };\r\n}\r\n\r\nasync function runExchange(\r\n carrier: IExchangeCarrier,\r\n state: IExchangeSecureState,\r\n inputs: Parameters<TSendActionDataMethod>[0],\r\n): Promise<void> {\r\n const { action, runningAction, timeout } = inputs;\r\n\r\n const ac = new AbortController();\r\n let timedOut = false;\r\n const timeoutId = setTimeout(() => {\r\n timedOut = true;\r\n ac.abort();\r\n }, timeout);\r\n const unsubscribe = runningAction.addUpdateListeners([\r\n (update) => {\r\n if (update.type === ERunningActionUpdateType.finished) {\r\n clearTimeout(timeoutId);\r\n ac.abort();\r\n }\r\n },\r\n ]);\r\n\r\n try {\r\n const request = await buildRequestEnvelope(state, action);\r\n const replyRaw = await carrier.exchange(encodeExchange(request), { signal: ac.signal });\r\n\r\n // Only a request awaits a reply (progress/result payloads aren't sent by a connector over exchange).\r\n if (action.type !== EActionPayloadType.request) return;\r\n\r\n const reply = decodeExchangeReply(asString(replyRaw));\r\n if (reply == null) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.invalid_action_response, {\r\n actionId: action.id,\r\n });\r\n }\r\n if (reply.k === \"err\") {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.send_failed, {\r\n actionState: action.type,\r\n actionId: action.id,\r\n message: reply.message,\r\n });\r\n }\r\n\r\n const wire = await extractReplyWire(state, reply);\r\n if (wire == null || !isActionPayload_Result_JsonObject(wire)) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.invalid_action_response, {\r\n actionId: action.id,\r\n });\r\n }\r\n\r\n runningAction._completeWithResult(action._domain.hydrateResultPayload(wire));\r\n } catch (err) {\r\n if (timedOut) {\r\n throw err_nice_transport.fromId(EErrId_NiceTransport.timeout, { timeout });\r\n }\r\n throw err;\r\n } finally {\r\n clearTimeout(timeoutId);\r\n unsubscribe();\r\n }\r\n}\r\n\r\nasync function buildRequestEnvelope(\r\n state: IExchangeSecureState,\r\n action: Parameters<TSendActionDataMethod>[0][\"action\"],\r\n): Promise<TExchangeRequest> {\r\n const wire = action.toJsonObject();\r\n if (state.crypto != null) {\r\n const ciphertext = await state.crypto.encryptFrame(textEncoder.encode(JSON.stringify(wire)));\r\n return { k: \"act\", t: state.token, c: bytesToBase64(ciphertext) };\r\n }\r\n return { k: \"act\", t: state.token, w: wire };\r\n}\r\n\r\nasync function extractReplyWire(\r\n state: IExchangeSecureState,\r\n reply: TExchangeReply,\r\n): Promise<ReturnType<Parameters<TSendActionDataMethod>[0][\"action\"][\"toJsonObject\"]> | undefined> {\r\n if (reply.k !== \"act\") return undefined;\r\n if (\"c\" in reply) {\r\n if (state.crypto == null) return undefined;\r\n const plain = await state.crypto.decryptFrame(base64ToBytes(reply.c));\r\n return JSON.parse(textDecoder.decode(plain));\r\n }\r\n return reply.w;\r\n}\r\n\r\nasync function runConnectorExchangeHandshake(\r\n carrier: IExchangeCarrier,\r\n secure: ISecureClientConfig,\r\n): Promise<IExchangeSecureState> {\r\n await secure.link.initialize();\r\n\r\n const handshake = createClientHandshake({\r\n link: secure.link,\r\n localCoordinate: secure.localCoordinate,\r\n dictionaryVersion: secure.dictionaryVersion,\r\n securityLevel: secure.securityLevel,\r\n });\r\n const hsid = nanoid();\r\n\r\n // Exchange 1: hello → welcome.\r\n const hello = await handshake.createHello();\r\n const welcomeReply = decodeExchangeReply(\r\n asString(\r\n await carrier.exchange(encodeExchange({ k: \"hs\", hsid, m: encodeHandshakeMessage(hello) })),\r\n ),\r\n );\r\n if (welcomeReply?.k !== \"hs\") throw new Error(\"[exchange-handshake] expected a welcome reply\");\r\n const welcome = decodeHandshakeMessage(welcomeReply.m);\r\n if (welcome == null) throw new Error(\"[exchange-handshake] malformed welcome\");\r\n if (welcome.t === EHandshakeMessageType.reject) {\r\n throw new Error(`[exchange-handshake] rejected by peer: ${welcome.reason}`);\r\n }\r\n if (welcome.t !== EHandshakeMessageType.welcome) {\r\n throw new Error(`[exchange-handshake] expected welcome, got ${welcome.t}`);\r\n }\r\n\r\n // Exchange 2: prove → accept (+ session token).\r\n const prove = await handshake.onWelcome(welcome);\r\n const acceptReply = decodeExchangeReply(\r\n asString(\r\n await carrier.exchange(encodeExchange({ k: \"hs\", hsid, m: encodeHandshakeMessage(prove) })),\r\n ),\r\n );\r\n if (acceptReply?.k !== \"hs\") throw new Error(\"[exchange-handshake] expected an accept reply\");\r\n const accept = decodeHandshakeMessage(acceptReply.m);\r\n if (accept == null) throw new Error(\"[exchange-handshake] malformed accept\");\r\n if (accept.t === EHandshakeMessageType.reject) {\r\n throw new Error(`[exchange-handshake] rejected by peer: ${accept.reason}`);\r\n }\r\n if (accept.t !== EHandshakeMessageType.accept) {\r\n throw new Error(`[exchange-handshake] expected accept, got ${accept.t}`);\r\n }\r\n if (acceptReply.t == null) throw new Error(\"[exchange-handshake] accept missing session token\");\r\n\r\n const result = await handshake.onAccept(accept);\r\n const crypto =\r\n result.securityLevel === ESecurityLevel.encrypted\r\n ? createActionFrameCrypto({ link: secure.link, linkedClientId: result.linkedClientId })\r\n : undefined;\r\n\r\n return { token: acceptReply.t, crypto };\r\n}\r\n\r\nfunction asString(frame: TFrame): string {\r\n if (typeof frame === \"string\") return frame;\r\n return textDecoder.decode(frame instanceof ArrayBuffer ? new Uint8Array(frame) : frame);\r\n}\r\n","import {\r\n ETransportStatus,\r\n type IActionTransportReadyData_Base,\r\n type TTransportStatusInfo,\r\n type TTransportStatusInfo_GetTransport_Output,\r\n} from \"../Transport.types\";\r\n\r\nexport function addTransportStatusMetadata<READY extends IActionTransportReadyData_Base>(\r\n transportStatus: TTransportStatusInfo_GetTransport_Output<READY>,\r\n): TTransportStatusInfo<READY> {\r\n if (transportStatus.status === ETransportStatus.ready) {\r\n return {\r\n status: ETransportStatus.ready,\r\n readyData: transportStatus.readyData,\r\n };\r\n }\r\n\r\n if (transportStatus.status === ETransportStatus.initializing) {\r\n return {\r\n status: ETransportStatus.initializing,\r\n initializationPromise: transportStatus.initializationPromise,\r\n timeStarted: Date.now(),\r\n };\r\n }\r\n\r\n if (transportStatus.status === ETransportStatus.failed) {\r\n return {\r\n status: ETransportStatus.failed,\r\n error: transportStatus.error,\r\n timeFailed: Date.now(),\r\n };\r\n }\r\n\r\n if (transportStatus.status === ETransportStatus.unsupported) {\r\n return {\r\n status: ETransportStatus.unsupported,\r\n };\r\n }\r\n\r\n return {\r\n status: ETransportStatus.uninitialized,\r\n };\r\n}\r\n","import { addTransportStatusMetadata } from \"./helpers/addTransportStatusMetadata\";\r\nimport type { Transport } from \"./Transport\";\r\nimport {\r\n type ETransportShape,\r\n ETransportStatus,\r\n type IActionTransportDef,\r\n type IActionTransportInitialized,\r\n type IActionTransportReadyData_Base,\r\n type IActionTransportReadyData_Methods,\r\n type ITransportRouteActionParams,\r\n type ITransportRouteInfo,\r\n type TTransportInitializationFinishedInfo,\r\n type TTransportStatusInfo,\r\n} from \"./Transport.types\";\r\n\r\nlet transportOrd = 0;\r\n\r\n/**\r\n * Live, per-handler transport runtime built from a reusable {@link Transport} definition. Holds the\r\n * connection-scoped state (ordinal, initialized config, sockets / abort sets) that must not be shared\r\n * across handlers. Construct these via `definition._createConnection(...)`, never directly.\r\n */\r\nexport abstract class TransportConnection<\r\n T extends ETransportShape = ETransportShape,\r\n RP extends ITransportRouteActionParams = ITransportRouteActionParams,\r\n RD extends IActionTransportReadyData_Base = IActionTransportReadyData_Base,\r\n I extends IActionTransportInitialized<RP, RD> = IActionTransportInitialized<RP, RD>,\r\n DEF extends IActionTransportDef<T, I> = IActionTransportDef<T, I>,\r\n> {\r\n readonly transOrd = transportOrd++;\r\n readonly type: T;\r\n readonly initialized: I;\r\n\r\n /** Backref to the public definition that created this connection (used for devtools route info). */\r\n definition?: Transport<T>;\r\n\r\n constructor(readonly def: DEF) {\r\n this.type = def.type;\r\n this.initialized = def.initialize();\r\n }\r\n\r\n /**\r\n * Devtools route info for an action routed through this live connection. Defaults to the stateless\r\n * {@link definition}'s info; connections override to enrich it from live state (e.g. the actual\r\n * resolved socket URL) when the definition couldn't resolve it on its own.\r\n */\r\n getRouteInfo(input: RP): ITransportRouteInfo | undefined {\r\n return this.definition?.getRouteInfo(input);\r\n }\r\n\r\n /**\r\n * Build the live send/receive methods from a `ready` readyData, synchronously. This is the plain\r\n * (no-handshake) path and the only required per-transport hook. Stream carriers that must await the\r\n * carrier opening and/or run a handshake additionally override {@link _needsAsyncBringUp},\r\n * {@link _awaitCarrierReady}, and {@link _finalizeReady}.\r\n */\r\n protected abstract _finalizeTransportMethods(inputs: RD): IActionTransportReadyData_Methods;\r\n\r\n /**\r\n * Whether a `ready`-status transport still needs asynchronous bring-up before its methods exist —\r\n * awaiting the carrier to open and/or running a handshake. Default `false`: a stateless transport\r\n * (HTTP) is usable the instant `getTransport` reports `ready`, so it stays a terminal *synchronous*\r\n * fallback in {@link ConnectionTransportManager}. Stream carriers (Link/WS) override to `true`.\r\n */\r\n protected _needsAsyncBringUp(_readyData: RD): boolean {\r\n return false;\r\n }\r\n\r\n /** Await the carrier becoming ready to send (e.g. a socket `open`). Default: nothing to await. */\r\n protected _awaitCarrierReady(_readyData: RD): Promise<void> {\r\n return Promise.resolve();\r\n }\r\n\r\n /**\r\n * Finalize during async bring-up — may run a handshake, so it can be async. Defaults to the\r\n * synchronous {@link _finalizeTransportMethods}; secure stream carriers override to branch plain/secure.\r\n */\r\n protected _finalizeReady(\r\n readyData: RD,\r\n ): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods> {\r\n return this._finalizeTransportMethods(readyData);\r\n }\r\n\r\n protected _getCacheKey(input: RP): string | null {\r\n const parts = this.initialized.getTransportCacheKey?.(input);\r\n if (parts == null) return null;\r\n return parts.join(\"\\x00\");\r\n }\r\n\r\n getCacheKey(input: RP): string | null {\r\n const inner = this._getCacheKey(input);\r\n if (inner == null) return null;\r\n return `${this.transOrd}:${inner}`;\r\n }\r\n\r\n /**\r\n * Whether this transport can serve the given action right now. Consulted by the manager before\r\n * cache-key resolution and `getTransport`; a `false` result skips this transport (treated as\r\n * `unsupported`) and the manager falls through to the next in preference order. Defaults to `true`\r\n * when the transport declares no gate.\r\n */\r\n isAvailable(input: RP): boolean {\r\n return this.initialized.isAvailable?.(input) ?? true;\r\n }\r\n\r\n getTransport(input: RP): TTransportStatusInfo<IActionTransportReadyData_Methods> {\r\n return this._processTransportStatus(input);\r\n }\r\n\r\n protected _processTransportStatus(\r\n input: RP,\r\n ): TTransportStatusInfo<IActionTransportReadyData_Methods> {\r\n const statusInfo = addTransportStatusMetadata(this.initialized.getTransport(input));\r\n\r\n if (statusInfo.status === ETransportStatus.ready) {\r\n // Already-usable + stateless → finalize synchronously (terminal fallback). Otherwise report\r\n // `initializing` and bring the carrier up (await open, run handshake) off the dispatch path.\r\n if (!this._needsAsyncBringUp(statusInfo.readyData)) {\r\n return {\r\n status: ETransportStatus.ready,\r\n readyData: this._finalizeTransportMethods(statusInfo.readyData),\r\n };\r\n }\r\n return {\r\n status: ETransportStatus.initializing,\r\n timeStarted: Date.now(),\r\n initializationPromise: this._bringUp(statusInfo.readyData),\r\n };\r\n }\r\n\r\n if (statusInfo.status === ETransportStatus.initializing) {\r\n const initializationPromise: Promise<\r\n TTransportInitializationFinishedInfo<IActionTransportReadyData_Methods>\r\n > = statusInfo.initializationPromise.then((result) =>\r\n result.status === ETransportStatus.ready ? this._bringUp(result.readyData) : result,\r\n );\r\n return {\r\n status: ETransportStatus.initializing,\r\n timeStarted: statusInfo.timeStarted,\r\n initializationPromise,\r\n };\r\n }\r\n\r\n // unsupported / failed / uninitialized — pass through.\r\n return statusInfo;\r\n }\r\n\r\n /** Await carrier readiness, then finalize (possibly running a handshake) into the live methods. */\r\n private async _bringUp(\r\n readyData: RD,\r\n ): Promise<TTransportInitializationFinishedInfo<IActionTransportReadyData_Methods>> {\r\n await this._awaitCarrierReady(readyData);\r\n return {\r\n status: ETransportStatus.ready,\r\n readyData: await this._finalizeReady(readyData),\r\n };\r\n }\r\n}\r\n","import { ESecurityLevel } from \"../crypto/actionHandshake\";\r\nimport {\r\n finalizePlainExchangeMethods,\r\n finalizeSecureExchangeMethods,\r\n type IExchangeSessionContext,\r\n} from \"../SecureSession/establishExchangeSession\";\r\nimport {\r\n ETransportShape,\r\n type IActionTransportReadyData_Methods,\r\n type ITransportRouteActionParams,\r\n} from \"../Transport.types\";\r\nimport { TransportConnection } from \"../TransportConnection\";\r\nimport type {\r\n IActionTransportDef_Exchange,\r\n IActionTransportInitialized_Exchange,\r\n IActionTransportReadyData_Exchange,\r\n} from \"./TransportExchange.types\";\r\n\r\n/**\r\n * Carrier-agnostic live connection for the exchange (request → single reply) shape — the HTTP\r\n * counterpart to {@link LinkConnection}. It owns only the bring-up (run the secure handshake on first\r\n * use); the request/reply lifecycle + crypto live in the shared `establishExchangeSession`.\r\n */\r\nexport class ExchangeConnection extends TransportConnection<\r\n ETransportShape.exchange,\r\n ITransportRouteActionParams,\r\n IActionTransportReadyData_Exchange,\r\n IActionTransportInitialized_Exchange,\r\n IActionTransportDef_Exchange\r\n> {\r\n constructor(def: Omit<IActionTransportDef_Exchange, \"type\">) {\r\n super({ ...def, type: ETransportShape.exchange });\r\n }\r\n\r\n protected override _getCacheKey(input: ITransportRouteActionParams): string {\r\n return this.initialized.getTransportCacheKey?.(input).join(\"\\x00\") ?? \"\";\r\n }\r\n\r\n // Only a secure exchange needs async bring-up (the handshake). A plain (`none`) exchange is usable the\r\n // instant the carrier opens — finalized synchronously like HTTP today.\r\n protected override _needsAsyncBringUp(data: IActionTransportReadyData_Exchange): boolean {\r\n return data.secureChannel != null && data.secureChannel.securityLevel !== ESecurityLevel.none;\r\n }\r\n\r\n protected override _finalizeReady(\r\n data: IActionTransportReadyData_Exchange,\r\n ): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods> {\r\n const secure = data.secureChannel;\r\n if (secure != null && secure.securityLevel !== ESecurityLevel.none) {\r\n return finalizeSecureExchangeMethods({ ...this._sessionContext(data), secure });\r\n }\r\n return this._finalizeTransportMethods(data);\r\n }\r\n\r\n _finalizeTransportMethods(\r\n data: IActionTransportReadyData_Exchange,\r\n ): IActionTransportReadyData_Methods {\r\n return finalizePlainExchangeMethods(this._sessionContext(data));\r\n }\r\n\r\n private _sessionContext(data: IActionTransportReadyData_Exchange): IExchangeSessionContext {\r\n return {\r\n carrier: data.carrier,\r\n updateRunConfig: data.updateRunConfig,\r\n secure: data.secureChannel,\r\n };\r\n }\r\n}\r\n","import type { IExchangeCarrier } from \"../Carrier/Carrier.types\";\r\nimport { type ITransportConnectionContext, Transport } from \"../Transport\";\r\nimport {\r\n ETransportShape,\r\n ETransportStatus,\r\n type ISecureClientConfig,\r\n type ITransportRouteActionParams,\r\n type ITransportRouteInfo,\r\n type TUpdateActionRunConfig,\r\n} from \"../Transport.types\";\r\nimport { ExchangeConnection } from \"./ExchangeConnection\";\r\n\r\nexport interface IExchangeTransportOptions {\r\n /** Open (or reuse) the exchange carrier for an action — e.g. `httpCarrier(...).open`. */\r\n openCarrier: (input: ITransportRouteActionParams) => IExchangeCarrier;\r\n /** Secure config; when set (and `securityLevel !== none`) the handshake runs once at bring-up. */\r\n security?: ISecureClientConfig;\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n /** Keys identifying a reusable session, so one carrier is shared across actions to the same peer. */\r\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /**\r\n * Optional availability gate. When it returns `false`, the manager skips this transport for that action\r\n * (reporting `unsupported`) and falls through to the next — without opening the carrier or computing its\r\n * cache key. Re-evaluated per dispatch, so the transport can become available later with no reconnect.\r\n */\r\n available?: (input: ITransportRouteActionParams) => boolean;\r\n /** Short label for the devtools chip (defaults to \"exchange\"). */\r\n label?: string;\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n}\r\n\r\n/**\r\n * A carrier-agnostic exchange (request → single reply) transport: it drives nice-action's secure session\r\n * over any {@link IExchangeCarrier} (HTTP being the one built-in). The duplex counterpart is\r\n * {@link LinkTransport}; this is the no-push half — its reply rides the response to its own request, so it\r\n * can't deliver an unsolicited frame (the runtime never picks it for the return path).\r\n */\r\nexport class ExchangeTransport extends Transport<ETransportShape.exchange> {\r\n readonly type = ETransportShape.exchange;\r\n\r\n constructor(private readonly options: IExchangeTransportOptions) {\r\n super();\r\n }\r\n\r\n static create(options: IExchangeTransportOptions): ExchangeTransport {\r\n return new ExchangeTransport(options);\r\n }\r\n\r\n _createConnection(_ctx: ITransportConnectionContext): ExchangeConnection {\r\n const options = this.options;\r\n\r\n return new ExchangeConnection({\r\n initialize: () => ({\r\n getTransportCacheKey: options.getTransportCacheKey,\r\n isAvailable: options.available,\r\n getTransport: (input) => ({\r\n status: ETransportStatus.ready,\r\n readyData: {\r\n carrier: options.openCarrier(input),\r\n secureChannel: options.security,\r\n updateRunConfig: options.updateRunConfig,\r\n },\r\n }),\r\n }),\r\n });\r\n }\r\n\r\n getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo {\r\n if (this.options.getRouteInfo != null) return this.options.getRouteInfo(input);\r\n return {\r\n carrierLabel: this.options.label ?? \"exchange\",\r\n summary: this.options.label ?? \"exchange\",\r\n };\r\n }\r\n}\r\n","import type { IActionTransportResolvers } from \"../Transport.types\";\r\n\r\nexport const createUnsetTransportResolvers = (\r\n transportLabel: string,\r\n): IActionTransportResolvers => ({\r\n onIncomingActionDataJson: (json) => {\r\n console.warn(\r\n `Received incoming action JSON [${json.domain}:${json.id}] on Transport [${transportLabel}] but no incoming data listener has been set.`,\r\n );\r\n },\r\n});\r\n","import type { NiceError } from \"@nice-code/error\";\r\nimport { EActionPayloadType } from \"../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport type { RunningAction } from \"../../../ActionDefinition/Action/RunningAction\";\r\nimport { ERunningActionUpdateType } from \"../../../ActionDefinition/Action/RunningAction.types\";\r\nimport { decodeActionFrame } from \"../../../utils/decodeActionFrame\";\r\nimport type { IDuplexCarrier } from \"../Carrier/Carrier.types\";\r\nimport { createActionFrameCrypto, type IActionFrameCrypto } from \"../crypto/actionFrameCrypto\";\r\nimport {\r\n createClientHandshake,\r\n decodeHandshakeMessage,\r\n EHandshakeMessageType,\r\n ESecurityLevel,\r\n encodeHandshakeMessage,\r\n type THandshakeMessage,\r\n} from \"../crypto/actionHandshake\";\r\nimport { EErrId_NiceTransport, err_nice_transport } from \"../err_nice_transport\";\r\nimport type { TLinkFormatMessage } from \"../Link/TransportLink.types\";\r\nimport type {\r\n IActionTransportReadyData_Methods,\r\n IActionTransportResolvers,\r\n ISecureClientConfig,\r\n TSendActionDataMethod,\r\n TUpdateActionRunConfig,\r\n} from \"../Transport.types\";\r\nimport { createFrameCryptoPipe, type IFrameCryptoPipe } from \"./frameCryptoPipe\";\r\n\r\nconst HANDSHAKE_TIMEOUT_MS = 15_000;\r\n\r\n/**\r\n * Connector-side (dial-out) secure session over an {@link IDuplexCarrier}: the handshake → frame crypto\r\n * → codec → action send/receive loop. The crypto itself (ordered encrypt-send / decrypt) lives in the\r\n * shared {@link createFrameCryptoPipe}, which the acceptor reuses too — so the secure logic lives once.\r\n * `LinkConnection` (WebRTC, Bluetooth, in-memory, …) and `WebSocketConnection` (socket wrapped as a\r\n * carrier) both delegate here.\r\n */\r\nexport interface ILinkSessionContext {\r\n channel: IDuplexCarrier;\r\n resolvers: IActionTransportResolvers;\r\n formatMessage?: TLinkFormatMessage;\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n /** The error used to abort in-flight actions when the channel is closed/gone (carrier-specific). */\r\n makeDisconnectError: (actionId: string) => NiceError;\r\n}\r\n\r\n/** Plain path (no handshake): route every inbound frame to the runtime; send without crypto. */\r\nexport function finalizePlainLinkMethods(\r\n ctx: ILinkSessionContext,\r\n): IActionTransportReadyData_Methods {\r\n const disconnectListeners: (() => void)[] = [];\r\n const abortSet = new Set<RunningAction<any, any>>();\r\n const pipe = makePipe(ctx, undefined);\r\n\r\n ctx.channel.attach({\r\n onMessage: (frame) => void handleIncomingActionFrame(ctx, pipe, frame),\r\n onClose: () => onChannelClosed(ctx, disconnectListeners, abortSet),\r\n onError: () => onChannelClosed(ctx, disconnectListeners, abortSet),\r\n });\r\n\r\n return buildSendMethods(ctx, pipe, disconnectListeners, abortSet);\r\n}\r\n\r\n/**\r\n * Secure path: a single message handler feeds the handshake until it completes, then routes action\r\n * frames (decrypting at the `encrypted` level). Frames that race ahead of activation are buffered and\r\n * flushed once the handshake lands, so nothing is lost.\r\n */\r\nexport async function finalizeSecureLinkMethods(\r\n ctx: ILinkSessionContext & { secure: ISecureClientConfig },\r\n): Promise<IActionTransportReadyData_Methods> {\r\n const disconnectListeners: (() => void)[] = [];\r\n const abortSet = new Set<RunningAction<any, any>>();\r\n\r\n // Holder so the (synchronously-attached) message handler can reach the pipe once the handshake builds\r\n // it, without a single-assignment `let`.\r\n const session: { pipe?: IFrameCryptoPipe } = {};\r\n let active = false;\r\n const handshakeQueue: THandshakeMessage[] = [];\r\n const handshakeWaiters: ((message: THandshakeMessage) => void)[] = [];\r\n const pendingActionFrames: (string | ArrayBuffer | Uint8Array)[] = [];\r\n\r\n ctx.channel.attach({\r\n onMessage: (frame) => {\r\n if (active && session.pipe != null) {\r\n void handleIncomingActionFrame(ctx, session.pipe, frame);\r\n return;\r\n }\r\n // Handshake phase: text frames are control messages; buffer anything else for after activation.\r\n if (typeof frame === \"string\") {\r\n const message = decodeHandshakeMessage(frame);\r\n if (message != null) {\r\n const waiter = handshakeWaiters.shift();\r\n if (waiter != null) waiter(message);\r\n else handshakeQueue.push(message);\r\n return;\r\n }\r\n }\r\n pendingActionFrames.push(frame);\r\n },\r\n onClose: () => onChannelClosed(ctx, disconnectListeners, abortSet),\r\n onError: () => onChannelClosed(ctx, disconnectListeners, abortSet),\r\n });\r\n\r\n const nextHandshakeMessage = (): Promise<THandshakeMessage> => {\r\n const queued = handshakeQueue.shift();\r\n if (queued != null) return Promise.resolve(queued);\r\n return new Promise<THandshakeMessage>((resolve, reject) => {\r\n const timeout = setTimeout(\r\n () => reject(new Error(\"[link-handshake] timed out waiting for peer reply\")),\r\n HANDSHAKE_TIMEOUT_MS,\r\n );\r\n handshakeWaiters.push((message) => {\r\n clearTimeout(timeout);\r\n resolve(message);\r\n });\r\n });\r\n };\r\n\r\n const crypto = await runClientHandshake(ctx.channel, ctx.secure, nextHandshakeMessage);\r\n const pipe = makePipe(ctx, crypto);\r\n session.pipe = pipe;\r\n\r\n active = true;\r\n for (const frame of pendingActionFrames) await handleIncomingActionFrame(ctx, pipe, frame);\r\n pendingActionFrames.length = 0;\r\n\r\n return buildSendMethods(ctx, pipe, disconnectListeners, abortSet);\r\n}\r\n\r\nfunction makePipe(\r\n ctx: ILinkSessionContext,\r\n crypto: IActionFrameCrypto | undefined,\r\n): IFrameCryptoPipe {\r\n return createFrameCryptoPipe({\r\n write: (frame) => ctx.channel.send(frame),\r\n isOpen: () => ctx.channel.isOpen(),\r\n crypto,\r\n });\r\n}\r\n\r\nasync function runClientHandshake(\r\n channel: IDuplexCarrier,\r\n secure: ISecureClientConfig,\r\n nextHandshakeMessage: () => Promise<THandshakeMessage>,\r\n): Promise<IActionFrameCrypto | undefined> {\r\n await secure.link.initialize();\r\n\r\n const handshake = createClientHandshake({\r\n link: secure.link,\r\n localCoordinate: secure.localCoordinate,\r\n dictionaryVersion: secure.dictionaryVersion,\r\n securityLevel: secure.securityLevel,\r\n });\r\n\r\n channel.send(encodeHandshakeMessage(await handshake.createHello()));\r\n\r\n const welcome = await nextHandshakeMessage();\r\n if (welcome.t === EHandshakeMessageType.reject) {\r\n throw new Error(`[link-handshake] rejected by peer: ${welcome.reason}`);\r\n }\r\n if (welcome.t !== EHandshakeMessageType.welcome) {\r\n throw new Error(`[link-handshake] expected welcome, got ${welcome.t}`);\r\n }\r\n\r\n channel.send(encodeHandshakeMessage(await handshake.onWelcome(welcome)));\r\n\r\n const accept = await nextHandshakeMessage();\r\n if (accept.t === EHandshakeMessageType.reject) {\r\n throw new Error(`[link-handshake] rejected by peer: ${accept.reason}`);\r\n }\r\n if (accept.t !== EHandshakeMessageType.accept) {\r\n throw new Error(`[link-handshake] expected accept, got ${accept.t}`);\r\n }\r\n\r\n const result = await handshake.onAccept(accept);\r\n return result.securityLevel === ESecurityLevel.encrypted\r\n ? createActionFrameCrypto({ link: secure.link, linkedClientId: result.linkedClientId })\r\n : undefined;\r\n}\r\n\r\nfunction buildSendMethods(\r\n ctx: ILinkSessionContext,\r\n pipe: IFrameCryptoPipe,\r\n disconnectListeners: (() => void)[],\r\n abortSet: Set<RunningAction<any, any>>,\r\n): IActionTransportReadyData_Methods {\r\n const channel = ctx.channel;\r\n\r\n const sendActionData: TSendActionDataMethod = (inputs) => {\r\n const { action, runningAction, timeout } = inputs;\r\n\r\n if (!channel.isOpen()) {\r\n if (action.type === EActionPayloadType.request) {\r\n runningAction._abort(ctx.makeDisconnectError(action.id));\r\n }\r\n return;\r\n }\r\n\r\n if (action.type === EActionPayloadType.request) {\r\n abortSet.add(runningAction);\r\n const timeoutId = setTimeout(() => {\r\n runningAction._abort(err_nice_transport.fromId(EErrId_NiceTransport.timeout, { timeout }));\r\n }, timeout);\r\n runningAction.addUpdateListeners([\r\n (update) => {\r\n if (update.type === ERunningActionUpdateType.finished) {\r\n clearTimeout(timeoutId);\r\n abortSet.delete(runningAction);\r\n }\r\n },\r\n ]);\r\n }\r\n\r\n pipe.send(ctx.formatMessage?.outgoing(inputs) ?? JSON.stringify(inputs.action.toJsonObject()));\r\n };\r\n\r\n return {\r\n sendActionData,\r\n updateRunConfig: ctx.updateRunConfig,\r\n addOnDisconnectListener: (cb) => {\r\n disconnectListeners.push(cb);\r\n },\r\n disconnect: () => {\r\n try {\r\n channel.close();\r\n } catch {\r\n // already closing/closed — the close handler still runs cleanup\r\n }\r\n },\r\n sendReturnData: (payload, clients) => {\r\n const formatted =\r\n clients != null ? ctx.formatMessage?.outgoing({ action: payload, ...clients }) : undefined;\r\n pipe.send(formatted ?? JSON.stringify(payload.toJsonObject()));\r\n },\r\n };\r\n}\r\n\r\nasync function handleIncomingActionFrame(\r\n ctx: ILinkSessionContext,\r\n pipe: IFrameCryptoPipe,\r\n frame: string | ArrayBuffer | Uint8Array,\r\n): Promise<void> {\r\n const decoded = await pipe.decryptIncoming(frame);\r\n if (decoded === undefined) return;\r\n\r\n const rawJson = decodeActionFrame(decoded, ctx.formatMessage);\r\n if (rawJson != null) ctx.resolvers.onIncomingActionDataJson(rawJson);\r\n}\r\n\r\nfunction onChannelClosed(\r\n ctx: ILinkSessionContext,\r\n disconnectListeners: (() => void)[],\r\n abortSet: Set<RunningAction<any, any>>,\r\n): void {\r\n for (const cb of disconnectListeners) cb();\r\n const error = ctx.makeDisconnectError(\"—\");\r\n for (const ra of [...abortSet]) ra._abort(error);\r\n}\r\n","import type { NiceError } from \"@nice-code/error\";\r\nimport { ESecurityLevel } from \"../crypto/actionHandshake\";\r\nimport { EErrId_NiceTransport, err_nice_transport } from \"../err_nice_transport\";\r\nimport { createUnsetTransportResolvers } from \"../helpers/createUnsetTransportResolvers\";\r\nimport {\r\n finalizePlainLinkMethods,\r\n finalizeSecureLinkMethods,\r\n type ILinkSessionContext,\r\n} from \"../SecureSession/establishLinkSession\";\r\nimport {\r\n ETransportShape,\r\n type IActionTransportReadyData_Methods,\r\n type IActionTransportResolvers,\r\n type ITransportRouteActionParams,\r\n} from \"../Transport.types\";\r\nimport { TransportConnection } from \"../TransportConnection\";\r\nimport type {\r\n IActionTransportDef_Link,\r\n IActionTransportInitialized_Link,\r\n IActionTransportReadyData_Link,\r\n} from \"./TransportLink.types\";\r\n\r\n/** Abort error for a closed link channel (carrier-neutral — the carrier itself isn't named). */\r\nfunction linkDisconnectError(actionId: string): NiceError {\r\n return err_nice_transport.fromId(EErrId_NiceTransport.send_failed, {\r\n actionId,\r\n actionState: \"request\",\r\n message: \"link channel disconnected\",\r\n });\r\n}\r\n\r\n/**\r\n * Carrier-agnostic live connection. It owns only the *bring-up* (open the carrier, then run the secure\r\n * session); the session itself — handshake, frame crypto, codec, send/receive — lives in the shared\r\n * {@link finalizeSecureLinkMethods}/{@link finalizePlainLinkMethods}, so a WebSocket, a WebRTC data\r\n * channel, a Bluetooth characteristic, and an in-memory pipe all run the identical secure layer.\r\n */\r\nexport class LinkConnection extends TransportConnection<\r\n ETransportShape.duplex,\r\n ITransportRouteActionParams,\r\n IActionTransportReadyData_Link,\r\n IActionTransportInitialized_Link,\r\n IActionTransportDef_Link\r\n> {\r\n private resolvers: IActionTransportResolvers;\r\n\r\n constructor(def: Omit<IActionTransportDef_Link, \"type\">, resolvers?: IActionTransportResolvers) {\r\n super({ ...def, type: ETransportShape.duplex });\r\n this.resolvers = resolvers ?? createUnsetTransportResolvers(\"link\");\r\n }\r\n\r\n protected override _getCacheKey(input: ITransportRouteActionParams): string {\r\n return this.initialized.getTransportCacheKey?.(input).join(\"\\x00\") ?? \"\";\r\n }\r\n\r\n // A link is always brought up asynchronously: open the carrier (`channel.ready`), then run the\r\n // handshake when secure. The base reports `initializing` and drives these hooks.\r\n protected override _needsAsyncBringUp(): boolean {\r\n return true;\r\n }\r\n\r\n protected override _awaitCarrierReady(data: IActionTransportReadyData_Link): Promise<void> {\r\n return data.channel.ready;\r\n }\r\n\r\n protected override _finalizeReady(\r\n data: IActionTransportReadyData_Link,\r\n ): IActionTransportReadyData_Methods | Promise<IActionTransportReadyData_Methods> {\r\n const secure = data.secureChannel;\r\n if (secure != null && secure.securityLevel !== ESecurityLevel.none) {\r\n return finalizeSecureLinkMethods({ ...this._sessionContext(data), secure });\r\n }\r\n return this._finalizeTransportMethods(data);\r\n }\r\n\r\n private _sessionContext(data: IActionTransportReadyData_Link): ILinkSessionContext {\r\n return {\r\n channel: data.channel,\r\n resolvers: this.resolvers,\r\n formatMessage: data.formatMessage,\r\n updateRunConfig: data.updateRunConfig,\r\n makeDisconnectError: linkDisconnectError,\r\n };\r\n }\r\n\r\n // Public (not `protected`) so the binary-frame test can finalize a connection's methods directly.\r\n _finalizeTransportMethods(\r\n data: IActionTransportReadyData_Link,\r\n ): IActionTransportReadyData_Methods {\r\n return finalizePlainLinkMethods(this._sessionContext(data));\r\n }\r\n}\r\n","import type { IDuplexCarrier } from \"../Carrier/Carrier.types\";\r\nimport { type ITransportConnectionContext, Transport } from \"../Transport\";\r\nimport {\r\n ETransportShape,\r\n ETransportStatus,\r\n type ISecureClientConfig,\r\n type ITransportRouteActionParams,\r\n type ITransportRouteInfo,\r\n type TUpdateActionRunConfig,\r\n} from \"../Transport.types\";\r\nimport { LinkConnection } from \"./LinkConnection\";\r\nimport type { TLinkFormatMessage } from \"./TransportLink.types\";\r\n\r\nexport interface ILinkTransportOptions {\r\n /**\r\n * Open (or reuse) the carrier for an action — a WebSocket adapter, a WebRTC data channel, a Bluetooth\r\n * characteristic, an in-memory pipe, anything that satisfies {@link IDuplexCarrier}.\r\n */\r\n openChannel: (input: ITransportRouteActionParams) => IDuplexCarrier;\r\n /** Shared codec for every channel (stateless). */\r\n formatMessage?: TLinkFormatMessage;\r\n /**\r\n * Per-channel codec factory — called once per opened channel so stateful codecs (e.g. the binary\r\n * session) get their own instance. Takes precedence over `formatMessage`.\r\n */\r\n createFormatMessage?: () => TLinkFormatMessage;\r\n /** Secure-channel config; when set (and `securityLevel !== none`) the handshake runs on init. */\r\n security?: ISecureClientConfig;\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n /** Keys identifying a reusable channel, so one carrier is shared across actions to the same peer. */\r\n getTransportCacheKey?: (input: ITransportRouteActionParams) => string[];\r\n /**\r\n * Optional availability gate. When it returns `false`, the manager skips this transport for that action\r\n * (reporting `unsupported`) and falls through to the next — without opening the carrier or computing its\r\n * cache key. Re-evaluated per dispatch, so the transport can become available later with no reconnect.\r\n */\r\n available?: (input: ITransportRouteActionParams) => boolean;\r\n /** Short label for the devtools chip (defaults to \"link\"). */\r\n label?: string;\r\n getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;\r\n}\r\n\r\n/**\r\n * A carrier-agnostic transport: it drives nice-action's secure session + action routing over any\r\n * {@link IDuplexCarrier}. The WebSocket transport is the special case that opens a `WebSocket`;\r\n * this opens whatever `openChannel` returns, so the identical secure layer works over WebRTC, Bluetooth,\r\n * or an in-memory pipe. Reported with an overridable carrier label in the devtools (defaults to \"link\").\r\n */\r\nexport class LinkTransport extends Transport<ETransportShape.duplex> {\r\n readonly type = ETransportShape.duplex;\r\n\r\n constructor(private readonly options: ILinkTransportOptions) {\r\n super();\r\n }\r\n\r\n static create(options: ILinkTransportOptions): LinkTransport {\r\n return new LinkTransport(options);\r\n }\r\n\r\n _createConnection(ctx: ITransportConnectionContext): LinkConnection {\r\n const options = this.options;\r\n\r\n return new LinkConnection(\r\n {\r\n initialize: () => ({\r\n getTransportCacheKey: options.getTransportCacheKey,\r\n isAvailable: options.available,\r\n getTransport: (input) => ({\r\n status: ETransportStatus.ready,\r\n readyData: {\r\n channel: options.openChannel(input),\r\n formatMessage: options.createFormatMessage?.() ?? options.formatMessage,\r\n updateRunConfig: options.updateRunConfig,\r\n secureChannel: options.security,\r\n },\r\n }),\r\n }),\r\n },\r\n ctx.resolvers,\r\n );\r\n }\r\n\r\n getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo {\r\n if (this.options.getRouteInfo != null) return this.options.getRouteInfo(input);\r\n return {\r\n carrierLabel: this.options.label ?? \"link\",\r\n summary: this.options.label ?? \"link\",\r\n };\r\n }\r\n}\r\n","import {\r\n type IDuplexCarrierSource,\r\n type IExchangeCarrierSource,\r\n isExchangeCarrierSource,\r\n} from \"./Carrier/Carrier.types\";\r\nimport { ExchangeTransport } from \"./Exchange/ExchangeTransport\";\r\nimport { LinkTransport } from \"./Link/LinkTransport\";\r\nimport type { TLinkFormatMessage } from \"./Link/TransportLink.types\";\r\nimport type { ITransportRouteActionParams, TUpdateActionRunConfig } from \"./Transport.types\";\r\n\r\nexport interface IPlainTransportOptions {\r\n /**\r\n * How to reach the peer with no security layer. A duplex carrier (`wsCarrier(url)`,\r\n * `rtcCarrier(dc)`, `inMemoryCarrier().carrier`) builds a push-capable {@link LinkTransport}; an\r\n * exchange carrier (`httpCarrier(...)`) builds a request/reply {@link ExchangeTransport}.\r\n */\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource;\r\n /**\r\n * Codec for a *duplex* carrier (a duplex link frames the action wire itself). Required for duplex;\r\n * ignored for an exchange carrier, which JSON-encodes the action wire in its envelope.\r\n */\r\n formatMessage?: TLinkFormatMessage;\r\n /** Per-channel codec factory for stateful duplex codecs (e.g. the binary session). */\r\n createFormatMessage?: () => TLinkFormatMessage;\r\n /**\r\n * Optional availability gate. When it returns `false`, this transport reports as `unsupported` for that\r\n * action and the manager falls through to the next transport in preference order — without opening the\r\n * carrier or computing its cache key. Re-evaluated per dispatch, so a transport can become available\r\n * later (e.g. once a session/connection precondition holds) with no reconnect. Omit = always available.\r\n */\r\n available?: (input: ITransportRouteActionParams) => boolean;\r\n updateRunConfig?: TUpdateActionRunConfig;\r\n /** Override the devtools chip label (defaults to the carrier's `carrierLabel`). */\r\n label?: string;\r\n}\r\n\r\n/**\r\n * The plain (no-handshake, no-crypto) sibling of {@link secureTransport}: swap the carrier to change\r\n * protocol, with no security layer. Over an {@link IExchangeCarrierSource} it builds an\r\n * {@link ExchangeTransport} that POSTs the bare action wire and completes inline (HTTP is just\r\n * `carrier: httpCarrier(...)`); over an {@link IDuplexCarrierSource} it builds a push-capable\r\n * {@link LinkTransport} with the given codec. HTTP is therefore no longer a bespoke transport class —\r\n * it is \"just another carrier\", exactly like a WebSocket under {@link secureTransport}.\r\n */\r\nexport function plainTransport(\r\n options: IPlainTransportOptions & { carrier: IExchangeCarrierSource },\r\n): ExchangeTransport;\r\nexport function plainTransport(\r\n options: IPlainTransportOptions & { carrier: IDuplexCarrierSource },\r\n): LinkTransport;\r\nexport function plainTransport(\r\n options: IPlainTransportOptions,\r\n): LinkTransport | ExchangeTransport {\r\n const carrier = options.carrier;\r\n\r\n if (isExchangeCarrierSource(carrier)) {\r\n return ExchangeTransport.create({\r\n openCarrier: carrier.open,\r\n getTransportCacheKey: carrier.getCacheKey,\r\n available: options.available,\r\n getRouteInfo: carrier.getRouteInfo,\r\n label: options.label ?? carrier.carrierLabel,\r\n updateRunConfig: options.updateRunConfig,\r\n });\r\n }\r\n\r\n return LinkTransport.create({\r\n openChannel: carrier.open,\r\n formatMessage: options.formatMessage,\r\n createFormatMessage: options.createFormatMessage,\r\n getTransportCacheKey: carrier.getCacheKey,\r\n available: options.available,\r\n getRouteInfo: carrier.getRouteInfo,\r\n label: options.label ?? carrier.carrierLabel,\r\n updateRunConfig: options.updateRunConfig,\r\n });\r\n}\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { ActionRuntime } from \"../ActionRuntime\";\r\nimport {\r\n type IDuplexCarrierSource,\r\n type IExchangeCarrierSource,\r\n isExchangeCarrierSource,\r\n} from \"./Carrier/Carrier.types\";\r\nimport type { ISecureChannel } from \"../Channel/secureChannel\";\r\nimport type { ESecurityLevel } from \"./crypto/actionHandshake\";\r\nimport { ExchangeTransport } from \"./Exchange/ExchangeTransport\";\r\nimport { LinkTransport } from \"./Link/LinkTransport\";\r\nimport type { ITransportRouteActionParams } from \"./Transport.types\";\r\n\r\nexport interface ISecureTransportOptions {\r\n /** The shared channel identity (per-connection codec + dictionary version) — same one both ends use. */\r\n channel: ISecureChannel;\r\n /** This client's runtime — its coordinate is the authenticated identity sent in the handshake. */\r\n runtime: ActionRuntime;\r\n /**\r\n * Backing store for this client's crypto identity (a stable verify key across reloads). Required unless\r\n * you pass a ready-made `link`, which already carries the identity.\r\n */\r\n storageAdapter?: StorageAdapter;\r\n /**\r\n * The client's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storageAdapter`.\r\n * Pass an existing link to share one identity across several secure transports to the same peer (e.g. a\r\n * secure WS preferred + secure HTTP fallback), so they present the same verify/exchange keys — exactly\r\n * as `connectChannel` does when it fans one identity across a connection's secure transports.\r\n */\r\n link?: ClientCryptoKeyLink;\r\n /** The level this client requests; the peer must allow it. */\r\n securityLevel: ESecurityLevel;\r\n /**\r\n * Optional availability gate. When it returns `false`, this transport reports as `unsupported` for that\r\n * action and the manager falls through to the next transport in preference order — without opening the\r\n * carrier or computing its cache key. Re-evaluated per dispatch, so a transport can become available\r\n * later (e.g. once a session/connection precondition holds) with no reconnect. Omit = always available.\r\n */\r\n available?: (input: ITransportRouteActionParams) => boolean;\r\n /**\r\n * How to reach the peer. A duplex carrier (`wsCarrier(url)`, `rtcCarrier(dc)`,\r\n * `inMemoryCarrier().carrier`) runs the push-capable session; an exchange carrier (`httpCarrier(...)`)\r\n * runs the request/reply session over the same handshake + crypto.\r\n */\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource;\r\n}\r\n\r\n/**\r\n * The one secure-transport factory — swap the carrier to change protocol. Folds in the boilerplate (the\r\n * {@link ClientCryptoKeyLink} from `storageAdapter`, the `security` block from the runtime coordinate +\r\n * channel version) and drives it over whatever carrier you pass: a {@link IDuplexCarrierSource} builds a\r\n * push-capable {@link LinkTransport} (WS is just `carrier: wsCarrier(url)`), an\r\n * {@link IExchangeCarrierSource} builds a request/reply {@link ExchangeTransport} (HTTP, with the same\r\n * authentication/encryption). Replaces the old `createSecureWebSocketTransport` / `createSecureLinkTransport`.\r\n */\r\nexport function secureTransport(\r\n options: ISecureTransportOptions & { carrier: IDuplexCarrierSource },\r\n): LinkTransport;\r\nexport function secureTransport(\r\n options: ISecureTransportOptions & { carrier: IExchangeCarrierSource },\r\n): ExchangeTransport;\r\nexport function secureTransport(\r\n options: ISecureTransportOptions,\r\n): LinkTransport | ExchangeTransport {\r\n const link =\r\n options.link ??\r\n (options.storageAdapter != null\r\n ? new ClientCryptoKeyLink({ storageAdapter: options.storageAdapter })\r\n : undefined);\r\n if (link == null) {\r\n throw new Error(\"secureTransport: provide `link` or `storageAdapter` for the crypto identity.\");\r\n }\r\n const security = {\r\n securityLevel: options.securityLevel,\r\n link,\r\n localCoordinate: options.runtime.coordinate.toJsonObject(),\r\n dictionaryVersion: options.channel.dictionaryVersion,\r\n };\r\n const carrier = options.carrier;\r\n\r\n if (isExchangeCarrierSource(carrier)) {\r\n return ExchangeTransport.create({\r\n openCarrier: carrier.open,\r\n getTransportCacheKey: carrier.getCacheKey,\r\n available: options.available,\r\n getRouteInfo: carrier.getRouteInfo,\r\n label: carrier.carrierLabel,\r\n security,\r\n });\r\n }\r\n\r\n return LinkTransport.create({\r\n openChannel: carrier.open,\r\n createFormatMessage: options.channel.createCodec,\r\n getTransportCacheKey: carrier.getCacheKey,\r\n available: options.available,\r\n getRouteInfo: carrier.getRouteInfo,\r\n label: carrier.carrierLabel,\r\n security,\r\n });\r\n}\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { TWrappableDomainActionHandler } from \"../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport type { ActionRuntime } from \"../ActionRuntime\";\r\nimport type { ActionLocalHandler } from \"../Handler/Local/ActionLocalHandler\";\r\nimport type {\r\n AcceptorHandler,\r\n TAcceptorCaseFn,\r\n} from \"../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport { createSecureAcceptorHandler } from \"../Handler/PeerLink/Acceptor/createSecureActionServer\";\r\nimport type { ConnectorHandler } from \"../Handler/PeerLink/Connector/ConnectorHandler\";\r\nimport type { RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\nimport {\r\n type IDuplexCarrierSource,\r\n type IExchangeCarrierSource,\r\n isExchangeCarrierSource,\r\n} from \"../Transport/Carrier/Carrier.types\";\r\nimport {\r\n ESecurityLevel,\r\n type IClientVerifyKeyResolver,\r\n} from \"../Transport/crypto/actionHandshake\";\r\nimport { plainTransport } from \"../Transport/plainTransport\";\r\nimport { secureTransport } from \"../Transport/secureTransport\";\r\nimport type { Transport } from \"../Transport/Transport\";\r\nimport type { ITransportRouteActionParams } from \"../Transport/Transport.types\";\r\nimport type { ISecureChannel } from \"./secureChannel\";\r\n\r\n/**\r\n * A transport-agnostic routing contract between two runtimes, declared *by role* rather than by\r\n * \"client\"/\"server\". The two ends are named for the only asymmetry that survives every carrier (WS,\r\n * WebRTC, BLE, raw TCP): which side dials and which side accepts.\r\n *\r\n * - The **connector** dials out and opens the link ({@link connectChannel}).\r\n * - The **acceptor** accepts incoming links and can push back ({@link acceptChannelConnections}).\r\n *\r\n * `toAcceptor` domains flow connector→acceptor (the classic \"request\"); `toConnector` domains flow\r\n * acceptor→connector (the classic \"push\"). Both ends derive their routing from the same channel instead\r\n * of restating domain lists — and because the contract is independent of how bytes move, the very same\r\n * channel can be carried over HTTP, secure WebSockets, or a mix (WS preferred, HTTP fallback).\r\n *\r\n * Build a plain one with {@link defineChannel}; layer wire-specific identity on top with\r\n * `defineSecureChannel` (which returns an `ISecureChannel` — still an `IActionChannel`, so it works\r\n * everywhere a channel is expected).\r\n */\r\nexport interface IActionChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n> {\r\n /**\r\n * Domains the connector *sends to the acceptor* (connector→acceptor requests). The connector forwards\r\n * them over its transport(s); the acceptor executes them.\r\n */\r\n toAcceptorDomains: TO_ACCEPTOR;\r\n /**\r\n * Domains the acceptor *pushes to the connector* (acceptor→connector). The connector registers local\r\n * handlers for them ({@link connectChannel}'s `onPush`); the acceptor broadcasts them. Pushes need a\r\n * bidirectional transport (e.g. a WebSocket) — over a request-only transport like HTTP they simply\r\n * never flow.\r\n */\r\n toConnectorDomains: TO_CONNECTOR;\r\n}\r\n\r\n/**\r\n * Declare a transport-agnostic channel by role. Use it for HTTP, custom transports, or as the routing\r\n * half of a richer channel. The order of each list is part of the contract for wire formats that pack\r\n * positionally (see `defineSecureChannel`) — add new domains to the end of their list. (`domains` is\r\n * accepted as a legacy alias for `toAcceptor`.)\r\n */\r\nexport function defineChannel<\r\n const TO_ACCEPTOR extends readonly ActionDomain<any>[] = [],\r\n const TO_CONNECTOR extends readonly ActionDomain<any>[] = [],\r\n>(options: {\r\n toAcceptor: TO_ACCEPTOR;\r\n toConnector: TO_CONNECTOR;\r\n}): IActionChannel<TO_ACCEPTOR, TO_CONNECTOR> {\r\n return {\r\n toAcceptorDomains: options.toAcceptor as TO_ACCEPTOR,\r\n toConnectorDomains: options.toConnector as TO_CONNECTOR,\r\n };\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Channel-derived wiring (connector + acceptor)\r\n// ---------------------------------------------------------------------------\r\n\r\ntype TUnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void\r\n ? I\r\n : never;\r\n\r\ntype TDomainPushHandlers<D> =\r\n D extends ActionDomain<infer DEF> ? Partial<TWrappableDomainActionHandler<DEF>> : never;\r\n\r\n/**\r\n * The `onPush` map for a channel: the merged set of every acceptor→connector (`toConnector`) action\r\n * handler, each receiving the pushed action's input. Derived from the channel's `toConnectorDomains`, so\r\n * the keys and input types follow the channel definition.\r\n */\r\nexport type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> =\r\n TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;\r\n\r\n/**\r\n * One transport to the peer, declared by *carrier* — the dial-out dual of `serveChannel`'s acceptor\r\n * carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto\r\n * identity) into each one, so a descriptor only carries what differs between transports: the carrier and\r\n * whether it runs the secure handshake.\r\n *\r\n * A duplex carrier (`wsCarrier(url)`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier\r\n * (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection\r\n * prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback).\r\n */\r\nexport interface IConnectTransport {\r\n /** How to reach the peer — a duplex carrier (push-capable) or an exchange carrier (request/reply). */\r\n carrier: IDuplexCarrierSource | IExchangeCarrierSource;\r\n /**\r\n * Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport\r\n * draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport\r\n * (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`.\r\n */\r\n secure?: boolean;\r\n /** Security level for this secure transport; defaults to the connection-level `securityLevel`. */\r\n securityLevel?: ESecurityLevel;\r\n /**\r\n * Optional availability gate — when it returns `false` this transport is skipped and the connection\r\n * falls through to the next in preference order, re-evaluated per dispatch. Omit = always available.\r\n */\r\n available?: (input: ITransportRouteActionParams) => boolean;\r\n /** Override the devtools chip label (defaults to the carrier's own label). */\r\n label?: string;\r\n}\r\n\r\nexport interface IConnectChannelOptions<TO_CONNECTOR extends readonly ActionDomain<any>[]> {\r\n /** The peer's runtime coordinate — the acceptor this connection dials. */\r\n peer: RuntimeCoordinate;\r\n /**\r\n * The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback).\r\n * They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and\r\n * falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into\r\n * each — the dial-out dual of `serveChannel`'s `carriers`.\r\n */\r\n transports: readonly IConnectTransport[];\r\n /**\r\n * One backing store for this connection's crypto identity, fanned across every *secure* transport so\r\n * they present the same verify/exchange keys. Required when any transport is secure (the default); a\r\n * fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an\r\n * existing identity.\r\n */\r\n storage?: StorageAdapter;\r\n /** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */\r\n link?: ClientCryptoKeyLink;\r\n /** Default security level for secure transports; defaults to `authenticated`. */\r\n securityLevel?: ESecurityLevel;\r\n /** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */\r\n onPush?: TChannelPushHandlers<TO_CONNECTOR>;\r\n /** Default per-action timeout for this connection. */\r\n defaultTimeout?: number;\r\n}\r\n\r\n/**\r\n * Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is\r\n * the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer,\r\n * `toConnector` pushes handled locally from `onPush`); the call binds the shared facts — the channel's\r\n * codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over\r\n * `storage`) — into every transport in `transports`, so none of them restate the channel or runtime.\r\n * List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback):\r\n * ```ts\r\n * const handler = connectChannel(runtime, lobbyChannel, {\r\n * peer: runtime_coordinate_lobby_do,\r\n * storage,\r\n * transports: [{ carrier: wsCarrier(url) }, { carrier: httpCarrier(...), secure: false }],\r\n * onPush: { player_joined: (p) => { … } },\r\n * });\r\n * ```\r\n * Returns the {@link ConnectorHandler} so the caller can later `clearTransportCache()` it.\r\n */\r\nexport function connectChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n>(\r\n runtime: ActionRuntime,\r\n channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IConnectChannelOptions<TO_CONNECTOR>,\r\n): ConnectorHandler {\r\n const securityLevel = options.securityLevel ?? ESecurityLevel.authenticated;\r\n\r\n // One crypto identity for the whole connection, fanned across every secure transport so they present\r\n // the same keys — the dial-out mirror of `serveChannel` sharing one link across its carriers. Built\r\n // only when something needs it; a fully-plain connection uses no storage.\r\n const anySecure = options.transports.some((transport) => transport.secure ?? true);\r\n let link: ClientCryptoKeyLink | undefined = options.link;\r\n if (anySecure && link == null) {\r\n if (options.storage == null) {\r\n throw new Error(\r\n \"connectChannel: a secure transport requires `storage` (or `link`). Pass it, or set `secure: false` on the transport for a plain connection.\",\r\n );\r\n }\r\n link = new ClientCryptoKeyLink({ storageAdapter: options.storage });\r\n }\r\n\r\n const transports: Transport[] = options.transports.map((transport) => {\r\n const carrier = transport.carrier;\r\n const secure = transport.secure ?? true;\r\n\r\n // Branch on carrier shape so the secure/plain overloads narrow cleanly. A duplex carrier frames the\r\n // action wire itself (so a plain duplex link needs the channel codec); an exchange carrier JSON-encodes\r\n // the wire in its envelope and ignores the codec.\r\n if (isExchangeCarrierSource(carrier)) {\r\n return secure\r\n ? secureTransport({\r\n channel,\r\n runtime,\r\n link,\r\n securityLevel: transport.securityLevel ?? securityLevel,\r\n available: transport.available,\r\n carrier,\r\n })\r\n : plainTransport({ carrier, available: transport.available, label: transport.label });\r\n }\r\n\r\n return secure\r\n ? secureTransport({\r\n channel,\r\n runtime,\r\n link,\r\n securityLevel: transport.securityLevel ?? securityLevel,\r\n available: transport.available,\r\n carrier,\r\n })\r\n : plainTransport({\r\n carrier,\r\n createFormatMessage: channel.createCodec,\r\n available: transport.available,\r\n label: transport.label,\r\n });\r\n });\r\n\r\n // Each push domain self-filters `onPush` to its own action ids, so a channel with several push\r\n // domains splits one merged map across the right handlers.\r\n const pushHandlers =\r\n options.onPush != null\r\n ? channel.toConnectorDomains.map((domain) =>\r\n domain.wrapAsPartialLocalHandler(\r\n options.onPush as Parameters<typeof domain.wrapAsPartialLocalHandler>[0],\r\n ),\r\n )\r\n : [];\r\n\r\n return runtime.connectTo(options.peer, {\r\n transports,\r\n domains: [...channel.toAcceptorDomains],\r\n localHandlers: pushHandlers,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n}\r\n\r\ntype TDomainAcceptorCases<D, TCtx> =\r\n D extends ActionDomain<infer DEF>\r\n ? { [ID in keyof DEF[\"actionSchema\"] & string]?: TAcceptorCaseFn<DEF, ID, TCtx> }\r\n : never;\r\n\r\n/**\r\n * The connection-aware case map for a channel's acceptor side: the merged set of every\r\n * connector→acceptor (`toAcceptor`) action handler, each receiving the primed request plus a per-action\r\n * `context`. `TCtx` is whatever the wiring supplies as that second argument — the raw connection\r\n * (`TConn | undefined`) for the low-level `acceptChannelConnections`, or an enriched `IConnectionContext`\r\n * for `serveChannel`'s `channelCases`. Derived from the channel's `toAcceptorDomains`, so the keys and\r\n * input/output types follow the channel.\r\n */\r\nexport type TChannelAcceptorCases<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TCtx,\r\n> = TUnionToIntersection<TDomainAcceptorCases<TO_ACCEPTOR[number], TCtx>>;\r\n\r\n/**\r\n * Register an acceptor handler's execution for a channel straight from its definition: the channel's\r\n * `toAcceptor` domains are served together with one merged, connection-aware case map (each case gets\r\n * the primed request + the originating connection, as with\r\n * {@link AcceptorHandler.forConnectionDomainCases}). The domain list is taken from the channel,\r\n * never restated. Add the returned handler to the runtime alongside the acceptor handler:\r\n * ```ts\r\n * runtime.addHandlers([acceptChannelConnections(serverHandler, channel, { … }), serverHandler]);\r\n * ```\r\n *\r\n * The case's second argument is the raw connection (`TConn | undefined`). For the richer state +\r\n * broadcast + pushBack context, serve the channel through `serveChannel`'s `channelCases` instead.\r\n */\r\nexport function acceptChannelConnections<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn>(\r\n serverHandler: AcceptorHandler<TConn>,\r\n channel: IActionChannel<TO_ACCEPTOR, any>,\r\n cases: TChannelAcceptorCases<TO_ACCEPTOR, TConn | undefined>,\r\n): ActionLocalHandler {\r\n return serverHandler.forConnectionDomainCasesMulti(\r\n channel.toAcceptorDomains,\r\n cases as Record<string, TAcceptorCaseFn<any, any, TConn | undefined> | undefined>,\r\n (connection) => connection,\r\n );\r\n}\r\n\r\nexport interface IAcceptChannelOptions<TConn> {\r\n /**\r\n * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env(\"web_app\")`),\r\n * used to score return-path dispatch back to the right connection.\r\n */\r\n clientEnv: RuntimeCoordinate;\r\n /**\r\n * One backing store for the acceptor's crypto identity *and* its trust-on-first-use verify-key pins\r\n * (their keys don't collide). Back it with persistent storage so identity + pins survive eviction.\r\n */\r\n storageAdapter: StorageAdapter;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\r\n /**\r\n * The acceptor's crypto identity. Defaults to a fresh `ClientCryptoKeyLink` over `storageAdapter`.\r\n * Pass an existing link to share one identity across several acceptors on the same server (e.g. this\r\n * WebSocket acceptor and a secure-HTTP `createActionFetchHandler`), so they present the same keys.\r\n */\r\n link?: ClientCryptoKeyLink;\r\n /** Accepted level(s); defaults to negotiating any of none/authenticated/encrypted. */\r\n securityLevel?: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storageAdapter`. */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n /** Timeout (ms) applied to acceptor-initiated actions awaiting a client response. */\r\n defaultTimeout?: number;\r\n}\r\n\r\n/**\r\n * Build the secure {@link AcceptorHandler} for a channel — the accept-in counterpart to\r\n * {@link connectChannel}. It folds in the same boilerplate as {@link createSecureAcceptorHandler} (the\r\n * `ClientCryptoKeyLink` + storage-backed TOFU resolver from one `storageAdapter`, the channel's codec +\r\n * dictionary version, the `security` block from the runtime coordinate) but takes the `(runtime, channel,\r\n * options)` shape of the channel family. Pair it with {@link acceptChannelConnections} for execution:\r\n * ```ts\r\n * const acceptor = acceptChannel(runtime, gameChannel, { clientEnv, storageAdapter, send });\r\n * runtime.addHandlers([acceptChannelConnections(acceptor, gameChannel, { … }), acceptor]);\r\n * ```\r\n */\r\nexport function acceptChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TConn = unknown,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IAcceptChannelOptions<TConn>,\r\n): AcceptorHandler<TConn> {\r\n return createSecureAcceptorHandler<TConn>({\r\n channel,\r\n runtime,\r\n clientEnv: options.clientEnv,\r\n storageAdapter: options.storageAdapter,\r\n link: options.link,\r\n send: options.send,\r\n securityLevel: options.securityLevel,\r\n verifyKeyResolver: options.verifyKeyResolver,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n}\r\n","import type { ClientCryptoKeyLink } from \"@nice-code/util\";\r\nimport { nanoid } from \"nanoid\";\r\nimport { EActionPayloadType } from \"../../../ActionDefinition/Action/Payload/ActionPayload.types\";\r\nimport { isActionPayload_Any_JsonObject } from \"../../../utils/isActionPayload_Any_JsonObject\";\r\nimport type { ActionRuntime } from \"../../ActionRuntime\";\r\nimport { type IRuntimeCoordinate, RuntimeCoordinate } from \"../../RuntimeCoordinate\";\r\nimport { createActionFrameCrypto, type IActionFrameCrypto } from \"../crypto/actionFrameCrypto\";\r\nimport {\r\n createServerHandshake,\r\n decodeHandshakeMessage,\r\n EHandshakeMessageType,\r\n ESecurityLevel,\r\n encodeHandshakeMessage,\r\n type IClientVerifyKeyResolver,\r\n} from \"../crypto/actionHandshake\";\r\nimport {\r\n base64ToBytes,\r\n bytesToBase64,\r\n decodeExchangeRequest,\r\n encodeExchange,\r\n type TExchangeReply,\r\n} from \"./exchangeProtocol\";\r\n\r\n/** Acceptor secure config for the exchange (HTTP) endpoint — same identity an `AcceptorHandler` uses. */\r\nexport interface IExchangeAcceptorSecurity {\r\n /** This acceptor's crypto identity (verify + exchange key pairs, optionally persisted). */\r\n link: ClientCryptoKeyLink;\r\n /** This acceptor's coordinate — its identity to clients during the handshake. */\r\n localCoordinate: IRuntimeCoordinate;\r\n /** Wire dictionary version; the handshake rejects a client on a mismatch. */\r\n dictionaryVersion: string;\r\n /** Accepted level(s) — a single level is strict, an array is a negotiable allowed set. */\r\n securityLevel: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key (defaults to in-memory TOFU inside the handshake). */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n}\r\n\r\nexport interface IExchangeAcceptorConfig {\r\n security: IExchangeAcceptorSecurity;\r\n /** The runtime that executes an inbound action wire and produces its result. */\r\n runtime: ActionRuntime;\r\n}\r\n\r\n/** A live secure session, keyed by the token the connector replays on every action POST. */\r\ninterface IExchangeSession {\r\n client: RuntimeCoordinate;\r\n securityLevel: ESecurityLevel;\r\n crypto?: IActionFrameCrypto;\r\n}\r\n\r\nconst textEncoder = new TextEncoder();\r\nconst textDecoder = new TextDecoder();\r\n\r\n/**\r\n * Acceptor (accept-in) side of the secure exchange protocol — the HTTP counterpart to\r\n * {@link AcceptorSecureSession}. Each POST body is one {@link decodeExchangeRequest} envelope; the\r\n * acceptor drives the server handshake over the two `hs` POSTs (correlated by `hsid`, since stateless\r\n * requests can't rely on channel ordering), mints a session **token** on accept, and on every later `act`\r\n * POST resolves the session by token, decrypts the body (at `encrypted`), routes it through the runtime,\r\n * and returns the (encrypted) result inline as the reply.\r\n *\r\n * Sessions and in-flight handshakes are held in memory — fine for a single-instance server. (Surviving a\r\n * Durable-Object eviction would persist each token's `keyMaterial` and re-derive the key on a miss, the\r\n * same primitive `AcceptorSecureSession.rehydrate` uses; left as a follow-up.)\r\n */\r\nexport class ExchangeAcceptor {\r\n private readonly _security: IExchangeAcceptorSecurity;\r\n private readonly _runtime: ActionRuntime;\r\n private readonly _allowedLevels: readonly ESecurityLevel[];\r\n private readonly _noneAllowed: boolean;\r\n\r\n private readonly _pendingHandshakes = new Map<string, ReturnType<typeof createServerHandshake>>();\r\n private readonly _sessions = new Map<string, IExchangeSession>();\r\n\r\n constructor(config: IExchangeAcceptorConfig) {\r\n this._security = config.security;\r\n this._runtime = config.runtime;\r\n this._allowedLevels = Array.isArray(config.security.securityLevel)\r\n ? config.security.securityLevel\r\n : [config.security.securityLevel];\r\n this._noneAllowed = this._allowedLevels.includes(ESecurityLevel.none);\r\n }\r\n\r\n /** Process one POST body (an exchange envelope), returning the reply body to send back. */\r\n async handlePost(body: string): Promise<string> {\r\n const request = decodeExchangeRequest(body);\r\n if (request == null) return this._err(\"malformed exchange request\");\r\n\r\n if (request.k === \"hs\") return encodeExchange(await this._handleHandshake(request));\r\n return encodeExchange(await this._handleAction(request));\r\n }\r\n\r\n private async _handleHandshake(request: { hsid: string; m: string }): Promise<TExchangeReply> {\r\n const message = decodeHandshakeMessage(request.m);\r\n if (message == null) return { k: \"err\", message: \"malformed handshake message\" };\r\n\r\n const security = this._security;\r\n await security.link.initialize();\r\n\r\n let handshake = this._pendingHandshakes.get(request.hsid);\r\n if (handshake == null) {\r\n handshake = createServerHandshake({\r\n link: security.link,\r\n localCoordinate: security.localCoordinate,\r\n dictionaryVersion: security.dictionaryVersion,\r\n securityLevel: security.securityLevel,\r\n verifyKeyResolver: security.verifyKeyResolver,\r\n });\r\n this._pendingHandshakes.set(request.hsid, handshake);\r\n }\r\n\r\n if (message.t === EHandshakeMessageType.hello) {\r\n return { k: \"hs\", m: encodeHandshakeMessage(await handshake.onHello(message)) };\r\n }\r\n if (message.t === EHandshakeMessageType.prove) {\r\n const reply = await handshake.onProve(message);\r\n this._pendingHandshakes.delete(request.hsid);\r\n const result = handshake.getResult();\r\n if (reply.t === EHandshakeMessageType.accept && result != null) {\r\n const token = nanoid();\r\n this._sessions.set(token, {\r\n client: new RuntimeCoordinate(result.remote),\r\n securityLevel: result.securityLevel,\r\n crypto:\r\n result.securityLevel === ESecurityLevel.encrypted\r\n ? createActionFrameCrypto({\r\n link: security.link,\r\n linkedClientId: result.linkedClientId,\r\n })\r\n : undefined,\r\n });\r\n return { k: \"hs\", m: encodeHandshakeMessage(reply), t: token };\r\n }\r\n return { k: \"hs\", m: encodeHandshakeMessage(reply) };\r\n }\r\n\r\n return { k: \"err\", message: `unexpected handshake message ${message.t}` };\r\n }\r\n\r\n private async _handleAction(\r\n request: { t?: string } & ({ w: unknown } | { c: string }),\r\n ): Promise<TExchangeReply> {\r\n let session: IExchangeSession | undefined;\r\n let candidate: unknown;\r\n\r\n if (request.t != null) {\r\n session = this._sessions.get(request.t);\r\n if (session == null) return { k: \"err\", message: \"unknown or expired session token\" };\r\n\r\n if (\"c\" in request) {\r\n if (session.crypto == null) return { k: \"err\", message: \"session is not encrypted\" };\r\n const plain = await session.crypto.decryptFrame(base64ToBytes(request.c));\r\n candidate = JSON.parse(textDecoder.decode(plain));\r\n } else {\r\n candidate = request.w;\r\n }\r\n } else {\r\n // No token → a plain (`none`) client; only honoured when `none` is in the allowed set.\r\n if (!this._noneAllowed || \"c\" in request) {\r\n return { k: \"err\", message: \"missing session token\" };\r\n }\r\n candidate = request.w;\r\n }\r\n\r\n if (!isActionPayload_Any_JsonObject(candidate)) {\r\n return { k: \"err\", message: \"malformed action wire\" };\r\n }\r\n const wire = candidate;\r\n\r\n // Bind the *authenticated* identity (never the wire's self-asserted one) so the action runs as the\r\n // handshake-verified client.\r\n if (session != null && wire.type === EActionPayloadType.request) {\r\n wire.context.originClient = session.client.toJsonObject();\r\n }\r\n\r\n const running = await this._runtime.handleActionPayloadWire(wire);\r\n const result = await running.waitForResultPayload();\r\n const resultWire = result.toJsonObject();\r\n\r\n if (session?.crypto != null) {\r\n const ciphertext = await session.crypto.encryptFrame(\r\n textEncoder.encode(JSON.stringify(resultWire)),\r\n );\r\n return { k: \"act\", c: bytesToBase64(ciphertext) };\r\n }\r\n return { k: \"act\", w: resultWire };\r\n }\r\n\r\n private _err(message: string): string {\r\n return encodeExchange({ k: \"err\", message });\r\n }\r\n}\r\n","import type { ActionRuntime } from \"../../../ActionRuntime\";\r\nimport {\r\n ExchangeAcceptor,\r\n type IExchangeAcceptorSecurity,\r\n} from \"../../../Transport/SecureSession/exchangeAcceptor\";\r\n\r\n/** Permissive defaults — fine for a public action endpoint; override (or disable) via `cors`. */\r\nconst DEFAULT_CORS_HEADERS: 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\nexport interface IActionFetchHandlerOptions {\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204` with them).\r\n * Defaults to permissive `*`; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Which requests carry an action wire on `POST`. Default: pathname ends with `/action`. */\r\n isActionPath?: (url: URL) => boolean;\r\n /** Which requests are WebSocket upgrades. Default: pathname ends with `/ws`. */\r\n isWebSocketPath?: (url: URL) => boolean;\r\n /**\r\n * Whether a request is a WebSocket upgrade for this endpoint, given the whole request (not just the\r\n * URL). When set it *replaces* the default gate (an `Upgrade: websocket` header on an\r\n * {@link isWebSocketPath} match) — use it when the discriminant needs a header or method, not only the\r\n * path. Only consulted when {@link onWebSocketUpgrade} is present.\r\n */\r\n isWebSocketUpgrade?: (request: Request, url: URL) => boolean;\r\n /**\r\n * Perform the transport-specific WebSocket upgrade (e.g. a Durable Object's\r\n * `new WebSocketPair()` + `ctx.acceptWebSocket()` returning a `101`). Omit for HTTP-only endpoints.\r\n * Its response is returned as-is — a `101` upgrade carries no CORS headers.\r\n */\r\n onWebSocketUpgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /** Forwarded to `ActionPayload_Result.toHttpResponse` — use the error's HTTP status (default true). */\r\n useErrorStatus?: boolean;\r\n /**\r\n * Enable the secure exchange protocol (handshake + token sessions + body encryption) on the `/action`\r\n * endpoint, mirroring an `AcceptorHandler`'s `security`. The matching connector is\r\n * `secureTransport({ carrier: httpCarrier(...), securityLevel })`. When omitted, the endpoint speaks\r\n * today's plain protocol (the raw action wire is POSTed and the result is the response body).\r\n */\r\n security?: IExchangeAcceptorSecurity;\r\n}\r\n\r\n/**\r\n * Build the `fetch` handler a server/Durable-Object exposes for action traffic, folding in the\r\n * boilerplate every endpoint repeats: CORS (incl. the `OPTIONS` preflight), routing the `/action`\r\n * `POST` body through the runtime (`handleActionPayloadWire` → `waitForResultPayload` →\r\n * `toHttpResponse`), an optional WebSocket-upgrade hook, and a `404` fallback.\r\n *\r\n * It only touches web-standard `Request`/`Response`, so it stays transport-agnostic — the one\r\n * environment-specific bit (the WS upgrade) is injected via {@link IActionFetchHandlerOptions.onWebSocketUpgrade}:\r\n * ```ts\r\n * this.fetchHandler = createActionFetchHandler(this.runtime, {\r\n * onWebSocketUpgrade: () => {\r\n * const pair = new WebSocketPair();\r\n * this.ctx.acceptWebSocket(pair[1]);\r\n * return new Response(null, { status: 101, webSocket: pair[0] });\r\n * },\r\n * });\r\n * // async fetch(request) { return this.fetchHandler(request); }\r\n * ```\r\n */\r\nexport function createActionFetchHandler(\r\n runtime: ActionRuntime,\r\n options: IActionFetchHandlerOptions = {},\r\n): (request: Request) => Promise<Response> {\r\n const corsHeaders = options.cors === false ? {} : (options.cors ?? DEFAULT_CORS_HEADERS);\r\n const isActionPath = options.isActionPath ?? ((url) => url.pathname.endsWith(\"/action\"));\r\n const isWebSocketPath = options.isWebSocketPath ?? ((url) => url.pathname.endsWith(\"/ws\"));\r\n const exchangeAcceptor =\r\n options.security != null\r\n ? new ExchangeAcceptor({ runtime, security: options.security })\r\n : undefined;\r\n\r\n const withCors = (response: Response): Response => {\r\n if (options.cors === false) return response;\r\n const headers = new Headers(response.headers);\r\n for (const [key, value] of Object.entries(corsHeaders)) headers.set(key, value);\r\n return new Response(response.body, { status: response.status, headers });\r\n };\r\n\r\n return async (request: Request): Promise<Response> => {\r\n if (request.method === \"OPTIONS\") {\r\n return withCors(new Response(null, { status: 204 }));\r\n }\r\n\r\n const url = new URL(request.url);\r\n\r\n const isWebSocketUpgrade =\r\n options.isWebSocketUpgrade ??\r\n ((req: Request, u: URL) => req.headers.get(\"Upgrade\") === \"websocket\" && isWebSocketPath(u));\r\n\r\n if (options.onWebSocketUpgrade != null && isWebSocketUpgrade(request, url)) {\r\n return options.onWebSocketUpgrade(request, url);\r\n }\r\n\r\n if (request.method === \"POST\" && isActionPath(url)) {\r\n // Secure exchange: the body is a protocol envelope (handshake or tokened/encrypted action); the\r\n // acceptor drives the handshake + token session and returns the reply envelope inline.\r\n if (exchangeAcceptor != null) {\r\n const reply = await exchangeAcceptor.handlePost(await request.text());\r\n return withCors(\r\n new Response(reply, { status: 200, headers: { \"Content-Type\": \"application/json\" } }),\r\n );\r\n }\r\n\r\n const running = await runtime.handleActionPayloadWire(await request.json());\r\n const result = await running.waitForResultPayload();\r\n return withCors(result.toHttpResponse({ useErrorStatus: options.useErrorStatus }));\r\n }\r\n\r\n return withCors(new Response(\"Not found\", { status: 404 }));\r\n };\r\n}\r\n","import type { StandardSchemaV1 } from \"@standard-schema/spec\";\r\nimport type { AcceptorHandler, IAcceptorConnectionBinding } from \"../AcceptorHandler\";\r\n\r\n/**\r\n * The composite value persisted to a connection's attachment: the consumer's own app state plus the\r\n * {@link AcceptorHandler} routing binding. Co-storing them in one slot means a transport whose\r\n * sockets outlive process eviction (e.g. a Durable Object's hibernatable WebSocket) recovers both the\r\n * application identity *and* the action routing from a single attachment after a wake — no storage reads.\r\n */\r\nexport interface IConnectionAttachment<TApp> {\r\n app?: TApp;\r\n binding?: IAcceptorConnectionBinding;\r\n}\r\n\r\nexport interface IConnectionStateStoreOptions<TConn, TApp> {\r\n /** Read a connection's raw attachment (e.g. `(ws) => ws.deserializeAttachment()`). */\r\n read: (connection: TConn) => unknown;\r\n /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */\r\n write: (connection: TConn, value: IConnectionAttachment<TApp>) => void;\r\n /**\r\n * All currently-live connections (e.g. `() => ctx.getWebSockets()`). Used to replay routing bindings\r\n * after a wake (via {@link createConnectionStateStore}) and to enumerate app state in\r\n * {@link ConnectionStateStore.entries}.\r\n */\r\n getConnections: () => TConn[];\r\n /**\r\n * Optional Standard Schema (valibot, zod, …) validating the *app* portion on read. A value that\r\n * fails validation reads back as `null` — the same lenient behavior as a hand-written safeParse\r\n * helper. The binding is the library's own shape and is never validated.\r\n */\r\n schema?: StandardSchemaV1<unknown, TApp>;\r\n}\r\n\r\n/**\r\n * A typed per-connection state store that co-owns the app state and the acceptor handler's routing\r\n * binding in one attachment, so neither the consumer nor the handler has to hand-merge the two. Create\r\n * it through {@link createConnectionStateStore} (which also wires binding persistence and replays\r\n * surviving connections after a wake), then `get`/`set`/`clearApp` the app state directly.\r\n *\r\n * The mechanism is carrier-neutral — it only needs read/write/enumerate callbacks for the connection's\r\n * attachment — but it pays off on transports whose connections outlive process eviction (e.g. a\r\n * Durable Object's hibernatable WebSockets), which is why it lives beside the hibernation adapter.\r\n *\r\n * ```ts\r\n * const players = createConnectionStateStore(serverHandler, {\r\n * schema: vs_player,\r\n * read: (ws) => ws.deserializeAttachment(),\r\n * write: (ws, v) => ws.serializeAttachment(v),\r\n * getConnections: () => ctx.getWebSockets(),\r\n * });\r\n * players.set(ws, player); // binding is preserved automatically\r\n * const player = players.get(ws);\r\n * ```\r\n */\r\nexport class ConnectionStateStore<TConn, TApp> {\r\n constructor(private readonly options: IConnectionStateStoreOptions<TConn, TApp>) {}\r\n\r\n /** The validated app state for a connection, or `null` if unset / invalid. */\r\n get(connection: TConn): TApp | null {\r\n return this._readAttachment(connection).app ?? null;\r\n }\r\n\r\n /** Set the app state, preserving the runtime binding already pinned to the connection. */\r\n set(connection: TConn, app: TApp): void {\r\n const existing = this._readAttachment(connection);\r\n this.options.write(connection, { app, binding: existing.binding });\r\n }\r\n\r\n /** Clear the app state but keep the binding (e.g. a spectator that stopped watching). */\r\n clearApp(connection: TConn): void {\r\n const existing = this._readAttachment(connection);\r\n this.options.write(connection, { binding: existing.binding });\r\n }\r\n\r\n /** Every live connection paired with its (validated) app state — for rebuilding in-memory state after a wake. */\r\n entries(): [TConn, TApp | null][] {\r\n return this.options\r\n .getConnections()\r\n .map((connection) => [connection, this._readAttachment(connection).app ?? null]);\r\n }\r\n\r\n /** @internal Persist a freshly-bound connection's binding, preserving any app state already stored. */\r\n _persistBinding(connection: TConn, binding: IAcceptorConnectionBinding): void {\r\n const existing = this._readAttachment(connection);\r\n this.options.write(connection, { app: existing.app, binding });\r\n }\r\n\r\n /** @internal The persisted binding for a connection, if any (used to replay routing after a wake). */\r\n _readBinding(connection: TConn): IAcceptorConnectionBinding | undefined {\r\n return this._readAttachment(connection).binding;\r\n }\r\n\r\n private _readAttachment(connection: TConn): IConnectionAttachment<TApp> {\r\n try {\r\n const raw = this.options.read(connection);\r\n if (typeof raw !== \"object\" || raw === null) return {};\r\n\r\n const attachment = raw as IConnectionAttachment<TApp>;\r\n const result: IConnectionAttachment<TApp> = {};\r\n // The binding is our own serialized shape — trust it as written.\r\n if (attachment.binding != null) result.binding = attachment.binding;\r\n if (attachment.app !== undefined) {\r\n const app = this._validateApp(attachment.app);\r\n if (app !== undefined) result.app = app;\r\n }\r\n return result;\r\n } catch {\r\n return {};\r\n }\r\n }\r\n\r\n private _validateApp(value: unknown): TApp | undefined {\r\n const schema = this.options.schema;\r\n if (schema == null) return value as TApp;\r\n const result = schema[\"~standard\"].validate(value);\r\n // App state is validated on a synchronous read path; an async schema can't be honored here.\r\n if (result instanceof Promise) return undefined;\r\n if (result.issues != null) return undefined;\r\n return result.value;\r\n }\r\n}\r\n\r\n/**\r\n * Build a per-connection {@link ConnectionStateStore} bound to an {@link AcceptorHandler}: it registers\r\n * itself as the handler's connection-bound persistence callback (so bindings are written without\r\n * overwriting app state) and immediately replays every live connection's stored binding via\r\n * {@link AcceptorHandler.rehydrateConnection} — so on a transport that resumes after eviction (e.g. a\r\n * Durable Object waking from hibernation) both the app identity and the action routing come back from a\r\n * single attachment, with no storage reads and no hand-rolled merge.\r\n *\r\n * Lives outside the handler so the generic {@link AcceptorHandler} stays free of any attachment/\r\n * hibernation concern — it exposes only the neutral `setOnConnectionBound` + `rehydrateConnection`\r\n * hooks this builder drives.\r\n */\r\nexport function createConnectionStateStore<TConn, TApp>(\r\n handler: AcceptorHandler<TConn>,\r\n options: IConnectionStateStoreOptions<TConn, TApp>,\r\n): ConnectionStateStore<TConn, TApp> {\r\n const store = new ConnectionStateStore<TConn, TApp>(options);\r\n handler.setOnConnectionBound((connection, binding) => store._persistBinding(connection, binding));\r\n\r\n // Rebuild routing for sockets that survived an eviction.\r\n for (const connection of options.getConnections()) {\r\n const binding = store._readBinding(connection);\r\n if (binding != null) handler.rehydrateConnection(connection, binding);\r\n }\r\n\r\n return store;\r\n}\r\n","import type { AcceptorHandler, IAcceptorConnectionBinding } from \"../AcceptorHandler\";\r\n\r\nexport interface IHibernatableWsServerAdapterOptions<TConn> {\r\n /** The handler to drive (from `createSecureAcceptorHandler` or `createAcceptorHandler`). */\r\n handler: AcceptorHandler<TConn>;\r\n /** All currently-live connections — replayed on construction to rebuild bindings after a wake. */\r\n getConnections: () => TConn[];\r\n /** Read a connection's persisted binding (e.g. `(ws) => ws.deserializeAttachment()`). */\r\n getAttachment: (connection: TConn) => IAcceptorConnectionBinding | undefined;\r\n /** Persist a connection's binding when it is bound (e.g. `(ws, b) => ws.serializeAttachment(b)`). */\r\n setAttachment: (connection: TConn, binding: IAcceptorConnectionBinding) => void;\r\n}\r\n\r\n/**\r\n * The neutral lifecycle surface for a duplex (push-capable) acceptor: feed it each inbound frame and tell\r\n * it when a connection goes away. Carrier-agnostic — a WebSocket, a WebRTC data channel, or any other\r\n * duplex connection drives the same two methods.\r\n */\r\nexport interface IDuplexConnectionRouter<TConn> {\r\n /** Feed one inbound frame from a connection into the handler. */\r\n receive: (connection: TConn, frame: string | ArrayBuffer | Uint8Array) => void;\r\n /** Forget a connection (call on socket close/error). */\r\n drop: (connection: TConn) => void;\r\n}\r\n\r\n/**\r\n * Wire the hibernation lifecycle for an acceptor handler on a transport whose connections outlive process\r\n * eviction (e.g. a Durable Object's hibernatable WebSockets). It owns persistence end to end:\r\n * registers `setAttachment` as the handler's connection-bound callback and immediately replays every\r\n * live connection's stored binding via `getAttachment`, so results/pushes still route after a wake.\r\n *\r\n * Layered on top of the generic {@link AcceptorHandler} — it touches only the handler's neutral\r\n * `setOnConnectionBound` / `rehydrateConnection` / `receive` / `dropConnection` surface, so no\r\n * hibernation concern leaks into the handler itself.\r\n *\r\n * Construct it once when the handler is built, then forward connection events:\r\n * ```ts\r\n * const duplex = createHibernatableWsServerAdapter({ handler, getConnections, getAttachment, setAttachment });\r\n * // webSocketMessage(ws, msg) => duplex.receive(ws, msg);\r\n * // webSocketClose/Error(ws) => duplex.drop(ws);\r\n * ```\r\n */\r\nexport function createHibernatableWsServerAdapter<TConn>(\r\n options: IHibernatableWsServerAdapterOptions<TConn>,\r\n): IDuplexConnectionRouter<TConn> {\r\n const { handler, getConnections, getAttachment, setAttachment } = options;\r\n\r\n handler.setOnConnectionBound(setAttachment);\r\n\r\n // Rebuild bindings for connections that survived an eviction.\r\n for (const connection of getConnections()) {\r\n const binding = getAttachment(connection);\r\n if (binding != null) handler.rehydrateConnection(connection, binding);\r\n }\r\n\r\n return {\r\n receive: (connection, frame) => handler.receive(connection, frame),\r\n drop: (connection) => handler.dropConnection(connection),\r\n };\r\n}\r\n","import type { IDuplexConnectionRouter } from \"../../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { ETransportShape } from \"../Transport.types\";\r\nimport type { TFrame } from \"./Carrier.types\";\r\n\r\n/**\r\n * Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}\r\n * / {@link IExchangeCarrierSource}. Where a connector source knows how to *open* a carrier to a peer, an\r\n * acceptor carrier knows how to *serve* one peer's traffic on this server. Both shapes are carrier-neutral\r\n * about security: `serveChannel` builds the crypto identity (link + TOFU resolver) and the security block\r\n * once from `(runtime, channel)` and fans it across every carrier, so a carrier descriptor never restates\r\n * it.\r\n *\r\n * Two shapes mirror the connector side:\r\n *\r\n * - {@link IDuplexAcceptorCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC, …). It can\r\n * push acceptor→connector, so it carries the return path and broadcasts, and it may need an upgrade step\r\n * (e.g. a Durable Object's `WebSocketPair`) and optional hibernation persistence.\r\n * - {@link IExchangeAcceptorCarrier} — a request → single-correlated-reply carrier with no unsolicited push\r\n * (HTTP). The reply rides the response to its own request; there is nothing to push and nothing to\r\n * upgrade.\r\n */\r\n\r\n/**\r\n * Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections\r\n * outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional — omit for a\r\n * transport that never hibernates (per-connection state is then in-memory only).\r\n *\r\n * `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when\r\n * `connectionState` is requested) per-connection app state as one composite in this single slot, so both\r\n * survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections.\r\n */\r\nexport interface IAcceptorAttachmentStore<TConn> {\r\n /** All currently-live connections — enumerated on build to replay binding + app state after a wake. */\r\n getConnections: () => TConn[];\r\n /** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */\r\n read: (connection: TConn) => unknown;\r\n /** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */\r\n write: (connection: TConn, value: unknown) => void;\r\n}\r\n\r\n/**\r\n * A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each\r\n * inbound frame to {@link receive} and forget a connection on close/error with {@link drop}. This is how a\r\n * server with *several* duplex carriers routes each connection's traffic to the right one — you hold the\r\n * carrier you created and feed it directly, so no per-carrier router lookup is needed. The methods throw if\r\n * called before the carrier is served. (`serveChannel` binds the live router via {@link _activate}.)\r\n */\r\nexport interface IDuplexCarrierLifecycle<TConn> {\r\n /** Feed one inbound frame from a live connection into the server. Throws until the carrier is served. */\r\n receive(connection: TConn, frame: TFrame): void;\r\n /** Forget a connection on close/error. No-op until the carrier is served. */\r\n drop(connection: TConn): void;\r\n /** @internal `serveChannel` binds this carrier's live connection router here. */\r\n _activate(router: IDuplexConnectionRouter<TConn>): void;\r\n}\r\n\r\n/**\r\n * Build the inert lifecycle slot a duplex carrier factory spreads into its handle: `receive`/`drop` throw\r\n * (or no-op) until `serveChannel` calls `_activate` with the carrier's live router. Shared by every duplex\r\n * carrier factory (`wsAcceptorCarrier`, a WebRTC carrier, the Cloudflare DO helper, …) so the\r\n * stateful-handle wiring lives in exactly one place.\r\n */\r\nexport function createDuplexCarrierLifecycle<TConn>(): IDuplexCarrierLifecycle<TConn> {\r\n let router: IDuplexConnectionRouter<TConn> | undefined;\r\n return {\r\n receive(connection, frame) {\r\n if (router == null) {\r\n throw new Error(\r\n \"acceptor carrier not served yet — pass it to serveChannel() before feeding frames\",\r\n );\r\n }\r\n router.receive(connection, frame);\r\n },\r\n drop(connection) {\r\n router?.drop(connection);\r\n },\r\n _activate(liveRouter) {\r\n router = liveRouter;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * A duplex (push-capable) carrier on the acceptor side. Describes how to write a frame back to a live\r\n * connection, how to perform the transport-specific upgrade that admits one, which requests are such\r\n * upgrades, and (optionally) how to persist bindings across hibernation. Built by `wsAcceptorCarrier` and\r\n * handed to `serveChannel`'s `carriers` list — the returned carrier is also its own lifecycle handle (see\r\n * {@link IDuplexCarrierLifecycle}).\r\n */\r\nexport interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycle<TConn> {\r\n /** Discriminant so `serveChannel` can tell a duplex carrier from an exchange one. */\r\n readonly shape: ETransportShape.duplex;\r\n /**\r\n * Whether each connection runs the secure handshake (default `true`). `false` makes it a plain duplex\r\n * carrier: connections speak the channel's wire codec directly with a self-asserted identity — no\r\n * handshake, pins, or encryption (the duplex dual of `httpAcceptorCarrier({ secure: false })`). A plain\r\n * carrier ignores the central crypto identity, so it needs no `storage` on `serveChannel`.\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific upgrade for an inbound request, returning its raw response (e.g. a\r\n * Durable Object's `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit for a carrier that\r\n * is fed connections out of band (the server then only routes frames via {@link receive}/{@link drop}).\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /**\r\n * Whether an inbound request is an upgrade for this carrier. Defaults to an `Upgrade: websocket` header.\r\n * Only consulted when {@link upgrade} is present.\r\n */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /**\r\n * Optional attachment read/write for connections that survive eviction (Durable Object hibernation).\r\n * Present → `serveChannel` persists the routing binding (and any `connectionState`) here and replays it\r\n * on wake. Absent → per-connection state is in-memory only.\r\n */\r\n attachmentStore?: IAcceptorAttachmentStore<TConn>;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"ws\"`, `\"webrtc\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\n/**\r\n * An exchange (request/reply) carrier on the acceptor side, over web-standard `Request`/`Response`. By\r\n * default it speaks the *secure* exchange protocol (handshake → token session → encrypted frames), whose\r\n * identity is supplied centrally by `serveChannel`. Set {@link secure} to `false` for a plain endpoint\r\n * that POSTs the raw action wire and returns the result inline — the request/reply dual of the connector's\r\n * `plainTransport({ carrier: httpCarrier(...) })`. So a server can pair a secure duplex (WebSocket) with a\r\n * plain HTTP fallback on the same runtime. Built by `httpAcceptorCarrier`.\r\n */\r\nexport interface IExchangeAcceptorCarrier {\r\n /** Discriminant so `serveChannel` can tell an exchange carrier from a duplex one. */\r\n readonly shape: ETransportShape.exchange;\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). `false` makes it a plain\r\n * endpoint: the body is the raw action wire and the result is the response body — no handshake, token,\r\n * or encryption. A plain endpoint ignores the central crypto identity entirely.\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Short carrier-kind label for the devtools chip (e.g. `\"http\"`). */\r\n readonly carrierLabel: string;\r\n}\r\n\r\nexport type TAcceptorCarrier<TConn = unknown> =\r\n | IDuplexAcceptorCarrier<TConn>\r\n | IExchangeAcceptorCarrier;\r\n\r\n/**\r\n * Narrow an acceptor carrier to the exchange shape via its `shape` discriminant — the one branch\r\n * `serveChannel` uses to pick the duplex (push-capable) vs exchange (request/reply) wiring. A duplex\r\n * carrier carries `shape: ETransportShape.duplex`, so the `else` branch is the duplex one.\r\n */\r\nexport function isExchangeAcceptorCarrier<TConn>(\r\n carrier: TAcceptorCarrier<TConn>,\r\n): carrier is IExchangeAcceptorCarrier {\r\n return carrier.shape === ETransportShape.exchange;\r\n}\r\n","import { ClientCryptoKeyLink, type StorageAdapter } from \"@nice-code/util\";\r\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\r\nimport type { ActionPayload_Request } from \"../../ActionDefinition/Action/Payload/ActionPayload_Request\";\r\nimport type { RunningAction } from \"../../ActionDefinition/Action/RunningAction\";\r\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\r\nimport type { IActionDomain } from \"../../ActionDefinition/Domain/ActionDomain.types\";\r\nimport type { ActionRuntime } from \"../ActionRuntime\";\r\nimport type { TActionRuntimeHandler } from \"../ActionRuntime.types\";\r\nimport {\r\n type AcceptorHandler,\r\n createAcceptorHandler,\r\n type TAcceptorCaseFn,\r\n} from \"../Handler/PeerLink/Acceptor/AcceptorHandler\";\r\nimport { createActionFetchHandler } from \"../Handler/PeerLink/Acceptor/createActionFetchHandler\";\r\nimport {\r\n type ConnectionStateStore,\r\n createConnectionStateStore,\r\n type IConnectionAttachment,\r\n} from \"../Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\r\nimport {\r\n createHibernatableWsServerAdapter,\r\n type IDuplexConnectionRouter,\r\n} from \"../Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter\";\r\nimport { RuntimeCoordinate } from \"../RuntimeCoordinate\";\r\nimport {\r\n type IDuplexAcceptorCarrier,\r\n type IExchangeAcceptorCarrier,\r\n isExchangeAcceptorCarrier,\r\n type TAcceptorCarrier,\r\n} from \"../Transport/Carrier/AcceptorCarrier.types\";\r\nimport {\r\n createStorageTofuVerifyKeyResolver,\r\n ESecurityLevel,\r\n type IClientVerifyKeyResolver,\r\n} from \"../Transport/crypto/actionHandshake\";\r\nimport type { IExchangeAcceptorSecurity } from \"../Transport/SecureSession/exchangeAcceptor\";\r\nimport { acceptChannel, type TChannelAcceptorCases } from \"./ActionChannel\";\r\nimport type { ISecureChannel } from \"./secureChannel\";\r\n\r\n/** Default accepted set, shared by every carrier: negotiate per connection to whatever the client picks. */\r\nconst DEFAULT_SERVER_SECURITY_LEVELS = [\r\n ESecurityLevel.none,\r\n ESecurityLevel.authenticated,\r\n ESecurityLevel.encrypted,\r\n] as const;\r\n\r\n/** Per-connection app-state config for {@link serveChannel}'s `connectionState`. */\r\nexport interface IServeConnectionStateOptions<TApp> {\r\n /**\r\n * Optional Standard Schema (valibot, zod, …) validating the app state on read — a value that fails\r\n * validation reads back as `null`. Omit to store the app state untyped.\r\n */\r\n schema?: StandardSchemaV1<unknown, TApp>;\r\n}\r\n\r\n/**\r\n * The per-action handle a `serveChannel` `channelCases` case receives as its second argument — the\r\n * originating connection enriched with everything a case typically reaches for, so it never threads\r\n * `ws` through `this.connections` / `this.server` by hand:\r\n *\r\n * - {@link state} / {@link setState} / {@link clearState} — the connection's typed app state (when\r\n * `connectionState` is configured), co-stored with the routing binding so it survives hibernation.\r\n * - {@link broadcast} — fan a server push to every other connection (skip self with `exceptSelf`).\r\n * - {@link pushBack} — push a server-initiated action down *this* same connection.\r\n *\r\n * Over the HTTP-exchange path there is no live socket: {@link connection} is `null`, {@link state} reads\r\n * `null`, {@link setState}/{@link clearState} are no-ops, and {@link pushBack} throws (an exchange reply\r\n * rides its own request — it can't carry an unsolicited push).\r\n */\r\nexport interface IConnectionContext<TConn, TApp = unknown> {\r\n /** The originating live connection, or `null` on the HTTP-exchange path (escape hatch). */\r\n connection: TConn | null;\r\n /** The originating client's coordinate (`= action.context.originClient`). */\r\n origin: RuntimeCoordinate;\r\n /** This connection's app state, or `null` if unset / no live socket. */\r\n state: TApp | null;\r\n /** Set this connection's app state (no-op without a live socket). Preserves the routing binding. */\r\n setState: (value: TApp) => void;\r\n /** Clear this connection's app state but keep the routing binding (no-op without a live socket). */\r\n clearState: () => void;\r\n /** Fan a server-initiated action out to every connection; `exceptSelf` skips this one. */\r\n broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n options?: {\r\n exceptSelf?: boolean;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ) => void;\r\n /** Push a server-initiated action down this same connection. Throws if there is no live socket. */\r\n pushBack: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n request: ActionPayload_Request<DOM, ID>,\r\n options?: { timeout?: number },\r\n ) => RunningAction<DOM, ID>;\r\n}\r\n\r\nexport interface IServeChannelOptions<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TConn,\r\n TApp = unknown,\r\n> {\r\n /**\r\n * Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env(\"web_app\")`),\r\n * used to score return-path dispatch back to the right connection.\r\n */\r\n clientEnv: RuntimeCoordinate;\r\n /**\r\n * One backing store for the server's crypto identity *and* its trust-on-first-use verify-key pins\r\n * (their keys don't collide). It is built once and shared across every carrier, so the WebSocket and the\r\n * secure-HTTP endpoint present the exact same verify/exchange keys and trust the same pinned clients.\r\n * Back it with persistent storage (e.g. a Durable Object's storage) so identity + pins survive eviction.\r\n *\r\n * Required only when at least one carrier is secure (the default). A fully-plain server (every carrier\r\n * `secure: false`) needs no storage and may omit it.\r\n */\r\n storage?: StorageAdapter;\r\n /**\r\n * The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`.\r\n * Build them with `wsAcceptorCarrier` / `httpAcceptorCarrier`. Any number of duplex (push-capable)\r\n * carriers are supported (e.g. WebSocket + WebRTC), plus at most one exchange (request/reply) carrier;\r\n * all share one crypto identity and one runtime, and each result/push routes back over the carrier its\r\n * client connected on.\r\n */\r\n carriers: readonly TAcceptorCarrier<TConn>[];\r\n /** Your execution handlers (e.g. the local handler holding the action cases). Registered for you. */\r\n handlers?: TActionRuntimeHandler[];\r\n /**\r\n * The server's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. Pass an\r\n * existing link only to share identity with acceptors built outside this call.\r\n */\r\n link?: ClientCryptoKeyLink;\r\n /** Accepted level(s) for every carrier; defaults to negotiating any of none/authenticated/encrypted. */\r\n securityLevel?: ESecurityLevel | readonly ESecurityLevel[];\r\n /** Trust decision for a client's verify key; defaults to storage-backed TOFU over `storage`. */\r\n verifyKeyResolver?: IClientVerifyKeyResolver;\r\n /** Timeout (ms) applied to server-initiated actions awaiting a client response. */\r\n defaultTimeout?: number;\r\n /**\r\n * Co-store per-connection app state alongside the routing binding in the sole duplex carrier's connection\r\n * attachment, so both survive a wake from eviction. Reach the typed store back on `server.connections`.\r\n * Requires the carrier to expose an attachment store (the Cloudflare `durableObjectWsCarrier` does) and\r\n * exactly one duplex carrier.\r\n */\r\n connectionState?: IServeConnectionStateOptions<TApp>;\r\n /**\r\n * Connection-aware action cases for the channel's acceptor (`toAcceptor`) domains — each case receives the\r\n * primed request *and* an {@link IConnectionContext} (the connection plus its typed `state` and\r\n * `broadcast`/`pushBack`). The connection-aware dual of `handlers`, registered on the runtime for you.\r\n * Requires exactly one duplex carrier.\r\n */\r\n channelCases?: TChannelAcceptorCases<TO_ACCEPTOR, IConnectionContext<TConn, TApp>>;\r\n}\r\n\r\n/**\r\n * One server serving a secure channel over several carriers — the accept-in dual of `connectChannel`,\r\n * returned by {@link serveChannel}. Wire its surface straight to the host's request/socket events.\r\n */\r\nexport interface IChannelServer<TConn, TApp = unknown> {\r\n /**\r\n * The duplex acceptor handlers — one per duplex carrier, in carrier order (empty if none). For pushing,\r\n * prefer {@link pushToClient} (it resolves the owning handler); reach for these for cross-carrier work\r\n * like a per-handler `broadcast`.\r\n */\r\n handlers: AcceptorHandler<TConn>[];\r\n /**\r\n * Unified request handler: answers the CORS preflight, performs the duplex upgrade for an upgrade\r\n * request, serves a secure-exchange action `POST`, else `404`. Forward the host's `fetch` straight to it.\r\n */\r\n fetch: (request: Request) => Promise<Response>;\r\n /**\r\n * Feed one inbound frame from a live connection into the server — forward your host's \"message\" event\r\n * here (a Durable Object's `webSocketMessage`, a Bun `websocket.message`, a Node `ws.on(\"message\")`).\r\n * Routes to the sole duplex carrier; throws with a clear message when there are zero or several duplex\r\n * carriers (for the multi-carrier case feed each `handlers[i]` / carrier handle directly).\r\n */\r\n receive: (connection: TConn, frame: string | Uint8Array | ArrayBuffer) => void;\r\n /**\r\n * Forget a connection on close/error — forward your host's \"close\"/\"error\" event here. Routes to the\r\n * sole duplex carrier; a no-op when there are none (an HTTP-only server has no sockets to drop).\r\n */\r\n drop: (connection: TConn) => void;\r\n /**\r\n * Push a server-initiated action to a connected client (the runtime is bound in, so unlike\r\n * {@link AcceptorHandler.pushToClient} you pass only the target + request). It routes through the duplex\r\n * carrier the target connected on. Throws if no duplex carrier currently holds the target.\r\n */\r\n pushToClient: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n target: TConn | RuntimeCoordinate,\r\n request: ActionPayload_Request<DOM, ID>,\r\n options?: { timeout?: number },\r\n ) => RunningAction<DOM, ID>;\r\n /**\r\n * Fan a server-initiated action out to every connection on the sole duplex carrier (skip the origin with\r\n * `except`, filter with `where`). The push-to-many counterpart of {@link pushToClient}. Throws if there\r\n * isn't exactly one duplex carrier (with several, broadcast over a specific `handlers[i]`).\r\n */\r\n broadcast: <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n options?: {\r\n except?: TConn | null;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ) => void;\r\n /**\r\n * The per-connection app-state store co-stored with the routing binding in the connection attachment —\r\n * present only when `connectionState` was passed. `get`/`set`/`clearApp`/`entries` it directly; it\r\n * survives hibernation alongside the binding.\r\n */\r\n connections?: ConnectionStateStore<TConn, TApp>;\r\n}\r\n\r\n/**\r\n * Serve a secure channel over one or more carriers from a single call — the accept-in dual of\r\n * `connectChannel`. It builds the crypto identity (a {@link ClientCryptoKeyLink} + a storage-backed TOFU\r\n * resolver) and the security block (coordinate, dictionary version, accepted levels) *once* from\r\n * `(runtime, channel)` and fans them across every carrier, so the WebSocket and the secure-HTTP endpoint\r\n * can never drift apart. It registers your handlers (plus the duplex acceptor it builds) on the runtime,\r\n * wires hibernation when the duplex carrier exposes an attachment store, and returns a single\r\n * {@link IChannelServer} whose `fetch` / `receive` / `drop` / `pushToClient` / `broadcast` you forward\r\n * straight to the host:\r\n * ```ts\r\n * const server = serveChannel(runtime, channel, {\r\n * clientEnv, storage,\r\n * carriers: [wsAcceptorCarrier({ send, upgrade, attachmentStore }), httpAcceptorCarrier()],\r\n * connectionState: { schema: vs_player }, // optional: co-store per-connection app state (survives hibernation)\r\n * channelCases: { join: (action, conn) => { conn.setState(action.input); … } }, // connection-aware cases\r\n * });\r\n * // fetch(req) => server.fetch(req)\r\n * // webSocketMessage(conn, m) => server.receive(conn, m)\r\n * // webSocketClose/Error(conn) => server.drop(conn)\r\n * // server.connections.get(conn) / server.broadcast(() => push.request(…), { except: conn })\r\n * ```\r\n *\r\n * On Cloudflare, `serveDurableObject` folds the whole DO transport stack (carriers + storage + keepalive)\r\n * into this — reach for it instead of assembling the carriers by hand.\r\n *\r\n * `TConn` (the live-connection token a duplex carrier hands back through `send`/`receive`/`drop`) is\r\n * inferred from the carriers — `WebSocket` for `wsAcceptorCarrier`, the data-channel type for a WebRTC\r\n * carrier, and so on — so it stays carrier-agnostic. Passing `connectionState` narrows the return so\r\n * `server.connections` is non-optional.\r\n */\r\nexport function serveChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[],\r\n TConn,\r\n TApp,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp> & {\r\n connectionState: IServeConnectionStateOptions<TApp>;\r\n },\r\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\r\nexport function serveChannel<\r\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\r\n TConn = unknown,\r\n TApp = unknown,\r\n>(\r\n runtime: ActionRuntime,\r\n channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>,\r\n options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>,\r\n): IChannelServer<TConn, TApp>;\r\nexport function serveChannel(\r\n runtime: ActionRuntime,\r\n channel: ISecureChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\r\n options: IServeChannelOptions<readonly ActionDomain<any>[], any, any>,\r\n): IChannelServer<any, any> {\r\n type TConn = any;\r\n const duplexCarriers = options.carriers.filter(\r\n (carrier): carrier is IDuplexAcceptorCarrier<TConn> => !isExchangeAcceptorCarrier(carrier),\r\n );\r\n const exchangeCarriers = options.carriers.filter(isExchangeAcceptorCarrier);\r\n\r\n if (exchangeCarriers.length > 1) {\r\n throw new Error(\"serveChannel: at most one exchange carrier is supported\");\r\n }\r\n const exchangeCarrier: IExchangeAcceptorCarrier | undefined = exchangeCarriers[0];\r\n\r\n const singleDuplex = duplexCarriers.length === 1;\r\n if (options.connectionState != null && !singleDuplex) {\r\n throw new Error(\"serveChannel: `connectionState` requires exactly one duplex carrier\");\r\n }\r\n if (options.channelCases != null && !singleDuplex) {\r\n throw new Error(\"serveChannel: `channelCases` requires exactly one duplex carrier\");\r\n }\r\n\r\n const exchangeSecure = exchangeCarrier != null && (exchangeCarrier.secure ?? true);\r\n const anyDuplexSecure = duplexCarriers.some((carrier) => carrier.secure ?? true);\r\n const securityLevel = options.securityLevel ?? DEFAULT_SERVER_SECURITY_LEVELS;\r\n\r\n // The shared crypto identity (link + TOFU resolver) — built once and fanned across every *secure*\r\n // carrier. Only constructed when something needs it; a fully-plain server uses no storage.\r\n let secure:\r\n | {\r\n storage: StorageAdapter;\r\n link: ClientCryptoKeyLink;\r\n verifyKeyResolver: IClientVerifyKeyResolver;\r\n }\r\n | undefined;\r\n if (anyDuplexSecure || exchangeSecure) {\r\n const storage = options.storage;\r\n if (storage == null) {\r\n throw new Error(\r\n \"serveChannel: a secure carrier requires `storage`. Pass it, or set `secure: false` on the carrier for a plain endpoint.\",\r\n );\r\n }\r\n secure = {\r\n storage,\r\n link: options.link ?? new ClientCryptoKeyLink({ storageAdapter: storage }),\r\n verifyKeyResolver: options.verifyKeyResolver ?? createStorageTofuVerifyKeyResolver(storage),\r\n };\r\n }\r\n\r\n // One acceptor handler per duplex carrier, each activated as its own lifecycle handle. Secure carriers →\r\n // `acceptChannel` (handshake + the shared identity, so they present the same keys as the exchange\r\n // endpoint); plain carriers → the base `createAcceptorHandler` over the channel's wire codec, no crypto.\r\n // Phase-A connection-aware return routing then sends each result/push back over the carrier its client\r\n // connected on.\r\n // The plain (in-memory only) router for a connection — no persistence across eviction.\r\n const plainRouter = (handler: AcceptorHandler<TConn>): IDuplexConnectionRouter<TConn> => ({\r\n receive: (connection, frame) => handler.receive(connection, frame),\r\n drop: (connection) => handler.dropConnection(connection),\r\n });\r\n const asObject = (value: unknown): object =>\r\n typeof value === \"object\" && value != null ? value : {};\r\n\r\n const handlers: AcceptorHandler<TConn>[] = [];\r\n // The per-connection app-state store, built once over the sole duplex carrier when `connectionState` is set.\r\n let connections: ConnectionStateStore<TConn, any> | undefined;\r\n for (const carrier of duplexCarriers) {\r\n const handler =\r\n (carrier.secure ?? true) && secure != null\r\n ? acceptChannel<any, any, TConn>(runtime, channel, {\r\n clientEnv: options.clientEnv,\r\n storageAdapter: secure.storage,\r\n link: secure.link,\r\n verifyKeyResolver: secure.verifyKeyResolver,\r\n securityLevel,\r\n send: carrier.send,\r\n defaultTimeout: options.defaultTimeout,\r\n })\r\n : createAcceptorHandler<TConn>({\r\n clientEnv: options.clientEnv,\r\n createFormatMessage: channel.createCodec,\r\n send: carrier.send,\r\n runtime,\r\n defaultTimeout: options.defaultTimeout,\r\n });\r\n\r\n // The attachment is one composite slot `{ app?, binding? }` per connection: serveChannel owns its\r\n // layout so binding *and* app state survive a wake from a single slot.\r\n // - no attachment store → in-memory only (no replay).\r\n // - `connectionState` requested → the connection store owns binding + app persistence and wake-replay.\r\n // - otherwise → the bare hibernation adapter persists/replays just the `.binding` sub-field.\r\n const attach = carrier.attachmentStore;\r\n let router: IDuplexConnectionRouter<TConn>;\r\n if (attach == null) {\r\n router = plainRouter(handler);\r\n } else if (options.connectionState != null) {\r\n connections = createConnectionStateStore<TConn, any>(handler, {\r\n schema: options.connectionState.schema,\r\n getConnections: attach.getConnections,\r\n read: attach.read,\r\n write: attach.write,\r\n });\r\n router = plainRouter(handler); // the store already replayed surviving bindings\r\n } else {\r\n router = createHibernatableWsServerAdapter<TConn>({\r\n handler,\r\n getConnections: attach.getConnections,\r\n getAttachment: (connection) =>\r\n (attach.read(connection) as IConnectionAttachment<unknown> | null | undefined)?.binding,\r\n setAttachment: (connection, binding) =>\r\n attach.write(connection, { ...asObject(attach.read(connection)), binding }),\r\n });\r\n }\r\n carrier._activate(router);\r\n handlers.push(handler);\r\n }\r\n\r\n runtime.addHandlers([...(options.handlers ?? []), ...handlers]);\r\n\r\n // --- Connection lifecycle + server-push surface ---\r\n // `receive`/`drop` are the universal forwarding seam every host environment feeds (a Durable Object's\r\n // `webSocketMessage`, a Bun `websocket.message`, …). They route to the sole duplex carrier (the common\r\n // case); with several, each carrier is its own handle, so callers feed those directly.\r\n const soleDuplex: IDuplexConnectionRouter<TConn> | undefined = singleDuplex\r\n ? duplexCarriers[0]\r\n : undefined;\r\n const receive = (connection: TConn, frame: string | Uint8Array | ArrayBuffer): void => {\r\n if (soleDuplex == null) {\r\n throw new Error(\r\n duplexCarriers.length === 0\r\n ? \"serveChannel: no duplex carrier to receive on (this server has no socket transport)\"\r\n : \"serveChannel: several duplex carriers — feed each carrier handle's receive() directly\",\r\n );\r\n }\r\n soleDuplex.receive(connection, frame);\r\n };\r\n const drop = (connection: TConn): void => {\r\n soleDuplex?.drop(connection);\r\n };\r\n\r\n const pushToClient = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n target: TConn | RuntimeCoordinate,\r\n request: ActionPayload_Request<DOM, ID>,\r\n pushOptions?: { timeout?: number },\r\n ): RunningAction<DOM, ID> => {\r\n // Route the push through the handler that actually holds the target's live connection.\r\n const owner =\r\n target instanceof RuntimeCoordinate\r\n ? handlers.find((handler) => handler.ownsLiveConnectionFor(target))\r\n : handlers.find((handler) => handler.hasConnection(target));\r\n if (owner == null) {\r\n throw new Error(\"serveChannel: no duplex carrier holds a connection for the push target\");\r\n }\r\n return owner.pushToClient(runtime, target, request, pushOptions);\r\n };\r\n\r\n const broadcast = <DOM extends IActionDomain, ID extends keyof DOM[\"actionSchema\"] & string>(\r\n makeRequest: () => ActionPayload_Request<DOM, ID>,\r\n broadcastOptions?: {\r\n except?: TConn | null;\r\n where?: (connection: TConn) => boolean;\r\n timeout?: number;\r\n onError?: (error: unknown, connection: TConn) => void;\r\n },\r\n ): void => {\r\n if (!singleDuplex) {\r\n throw new Error(\r\n \"serveChannel: broadcast requires exactly one duplex carrier — broadcast over a specific handlers[i] instead\",\r\n );\r\n }\r\n handlers[0].broadcast(makeRequest, { runtime, ...broadcastOptions });\r\n };\r\n\r\n // The per-action context handed to `channelCases`: the resolved connection enriched with typed `state`\r\n // plus `broadcast`/`pushBack`, so a case never reaches back into `connections`/the server by hand.\r\n const makeConnectionContext = (\r\n connection: TConn | undefined,\r\n request: ActionPayload_Request<any, any>,\r\n ): IConnectionContext<TConn, any> => ({\r\n connection: connection ?? null,\r\n origin: request.context.originClient,\r\n get state() {\r\n return connection != null && connections != null ? connections.get(connection) : null;\r\n },\r\n setState(value) {\r\n if (connection != null && connections != null) connections.set(connection, value);\r\n },\r\n clearState() {\r\n if (connection != null && connections != null) connections.clearApp(connection);\r\n },\r\n broadcast(makeRequest, contextOptions) {\r\n broadcast(makeRequest, {\r\n except: contextOptions?.exceptSelf ? (connection ?? null) : null,\r\n where: contextOptions?.where,\r\n timeout: contextOptions?.timeout,\r\n onError: contextOptions?.onError,\r\n });\r\n },\r\n pushBack(pushRequest, pushOptions) {\r\n if (connection == null) {\r\n throw new Error(\r\n \"serveChannel: connection context has no live socket to push back to (HTTP-exchange path)\",\r\n );\r\n }\r\n return pushToClient(connection, pushRequest, pushOptions);\r\n },\r\n });\r\n\r\n // Connection-aware action cases for the channel's acceptor domains, registered on the runtime alongside\r\n // the (sole) duplex acceptor — the connection-aware dual of `handlers`. Each case gets the enriched\r\n // `IConnectionContext`, built per inbound action.\r\n if (options.channelCases != null) {\r\n runtime.addHandlers([\r\n handlers[0].forConnectionDomainCasesMulti(\r\n channel.toAcceptorDomains,\r\n options.channelCases as Record<\r\n string,\r\n TAcceptorCaseFn<any, any, IConnectionContext<TConn, any>> | undefined\r\n >,\r\n makeConnectionContext,\r\n ),\r\n ]);\r\n }\r\n\r\n // The exchange (request/reply) security block — derived from the same identity + the channel/runtime\r\n // facts, so it never restates what the duplex side already knows. Omitted for a plain exchange carrier\r\n // (`secure: false`), which then POSTs the raw wire — letting a plain HTTP fallback sit beside a secure WS.\r\n const exchangeSecurity: IExchangeAcceptorSecurity | undefined =\r\n exchangeSecure && secure != null\r\n ? {\r\n link: secure.link,\r\n verifyKeyResolver: secure.verifyKeyResolver,\r\n localCoordinate: runtime.coordinate.toJsonObject(),\r\n dictionaryVersion: channel.dictionaryVersion,\r\n securityLevel,\r\n }\r\n : undefined;\r\n\r\n // Compose the upgrade across the duplex carriers that can be reached via an HTTP upgrade (typically just\r\n // one WebSocket carrier; a WebRTC carrier is signalled out of band and has no `upgrade`). The first\r\n // carrier whose `isUpgrade` matches handles the request.\r\n const defaultIsUpgrade = (request: Request) => request.headers.get(\"Upgrade\") === \"websocket\";\r\n const upgraders: {\r\n isUpgrade: (request: Request, url: URL) => boolean;\r\n upgrade: (request: Request, url: URL) => Response | Promise<Response>;\r\n }[] = [];\r\n for (const carrier of duplexCarriers) {\r\n if (carrier.upgrade == null) continue;\r\n upgraders.push({ isUpgrade: carrier.isUpgrade ?? defaultIsUpgrade, upgrade: carrier.upgrade });\r\n }\r\n\r\n const fetch = createActionFetchHandler(runtime, {\r\n cors: exchangeCarrier?.cors,\r\n onWebSocketUpgrade:\r\n upgraders.length === 0\r\n ? undefined\r\n : (request, url) =>\r\n (upgraders.find((u) => u.isUpgrade(request, url)) ?? upgraders[0]).upgrade(\r\n request,\r\n url,\r\n ),\r\n isWebSocketUpgrade:\r\n upgraders.length === 0\r\n ? undefined\r\n : (request, url) => upgraders.some((u) => u.isUpgrade(request, url)),\r\n // A single forwarding route means the path is already matched: any POST that isn't an upgrade is an\r\n // action exchange. With no exchange carrier there is no HTTP action endpoint, so POSTs fall through.\r\n isActionPath:\r\n exchangeCarrier != null ? (exchangeCarrier.isActionPath ?? (() => true)) : () => false,\r\n security: exchangeSecurity,\r\n useErrorStatus: exchangeCarrier?.useErrorStatus,\r\n });\r\n\r\n return { handlers, fetch, receive, drop, pushToClient, broadcast, connections };\r\n}\r\n","import type { StorageAdapter } from \"@nice-code/util\";\nimport type { ActionDomain } from \"../../ActionDefinition/Domain/ActionDomain\";\nimport type { ActionRuntime } from \"../ActionRuntime\";\nimport type { ConnectionStateStore } from \"../Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore\";\nimport type { TAcceptorCarrier } from \"../Transport/Carrier/AcceptorCarrier.types\";\nimport {\n type IChannelServer,\n type IServeChannelOptions,\n type IServeConnectionStateOptions,\n serveChannel,\n} from \"./serveChannel\";\nimport type { ISecureChannel } from \"./secureChannel\";\n\n/**\n * An environment-neutral description of *where* a channel is served — the accept-in dual of a connector's\n * transport stack, factored out so a platform adapter (a Cloudflare Durable Object, a Bun/Node WebSocket\n * server, …) supplies only what differs per environment while the channel + case wiring stays identical.\n * A host bundles:\n *\n * - the {@link carriers} the channel is served over (e.g. a WebSocket + an HTTP fallback),\n * - the {@link storage} backing the server's crypto identity, and\n * - an {@link onServed} hook run once the server exists (e.g. registering a keepalive auto-response).\n *\n * Build one with a platform helper (`cloudflareDurableObjectHost`) and hand it to {@link serveHost}.\n */\nexport interface IChannelHostAdapter<TConn> {\n /** The carriers this channel is served over — the accept-in dual of `connectChannel`'s `transports`. */\n carriers: readonly TAcceptorCarrier<TConn>[];\n /** Backing store for the server's crypto identity + TOFU pins. Required when any carrier is secure. */\n storage?: StorageAdapter;\n /** Run once after the server is built — e.g. register a transport keepalive. */\n onServed?: (server: IChannelServer<TConn, unknown>) => void;\n}\n\n/** {@link serveChannel}'s options minus what the host adapter supplies (`carriers`, `storage`). */\nexport type TServeHostOptions<\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\n TConn,\n TApp = unknown,\n> = Omit<IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>, \"carriers\" | \"storage\">;\n\n/**\n * Serve a channel over a {@link IChannelHostAdapter} — the environment-neutral core every platform helper\n * (e.g. `serveDurableObject`) composes. It folds the host's carriers + storage into `serveChannel`, then\n * runs the host's `onServed` hook. Everything else (`clientEnv`, `channelCases`, `connectionState`,\n * `handlers`, …) is the same `serveChannel` surface, so moving a server between environments is swapping\n * the host adapter and nothing else. Passing `connectionState` narrows the return so `server.connections`\n * is non-optional, exactly as with `serveChannel`.\n */\nexport function serveHost<\n TO_ACCEPTOR extends readonly ActionDomain<any>[],\n TO_CONNECTOR extends readonly ActionDomain<any>[],\n TConn,\n TApp,\n>(\n runtime: ActionRuntime,\n channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n host: IChannelHostAdapter<TConn>,\n options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp> & {\n connectionState: IServeConnectionStateOptions<TApp>;\n },\n): IChannelServer<TConn, TApp> & { connections: ConnectionStateStore<TConn, TApp> };\nexport function serveHost<\n TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[],\n TConn = unknown,\n TApp = unknown,\n>(\n runtime: ActionRuntime,\n channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>,\n host: IChannelHostAdapter<TConn>,\n options: TServeHostOptions<TO_ACCEPTOR, TConn, TApp>,\n): IChannelServer<TConn, TApp>;\nexport function serveHost(\n runtime: ActionRuntime,\n channel: ISecureChannel<readonly ActionDomain<any>[], readonly ActionDomain<any>[]>,\n host: IChannelHostAdapter<any>,\n options: TServeHostOptions<readonly ActionDomain<any>[], any, any>,\n): IChannelServer<any, any> {\n const server = serveChannel(runtime, channel, {\n ...options,\n carriers: host.carriers,\n storage: host.storage,\n });\n host.onServed?.(server);\n return server;\n}\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport {\r\n createDuplexCarrierLifecycle,\r\n type IAcceptorAttachmentStore,\r\n type IDuplexAcceptorCarrier,\r\n} from \"../../AcceptorCarrier.types\";\r\nimport type { TFrame } from \"../../Carrier.types\";\r\n\r\nexport interface IWsAcceptorCarrierOptions<TConn> {\r\n /**\r\n * Whether each socket runs the secure handshake (default `true`). Pass `false` for a plain WS endpoint\r\n * — connections speak the channel's wire codec with a self-asserted identity, no handshake/pins/encryption\r\n * (and `serveChannel` then needs no `storage` for this carrier).\r\n */\r\n secure?: boolean;\r\n /** Write an encoded frame to a specific live connection (e.g. `(ws, frame) => ws.send(frame)`). */\r\n send: (connection: TConn, frame: TFrame) => void;\r\n /**\r\n * Perform the transport-specific WebSocket upgrade, returning its raw response (e.g. a Durable Object's\r\n * `new WebSocketPair()` + `ctx.acceptWebSocket()` → a `101`). Omit if sockets are fed in out of band.\r\n */\r\n upgrade?: (request: Request, url: URL) => Response | Promise<Response>;\r\n /** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */\r\n isUpgrade?: (request: Request, url: URL) => boolean;\r\n /** Attachment read/write for hibernatable sockets (e.g. a Durable Object); `serveChannel` persists here. */\r\n attachmentStore?: IAcceptorAttachmentStore<TConn>;\r\n /** Override the devtools carrier-kind label (defaults to `\"ws\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * A WebSocket {@link IDuplexAcceptorCarrier}: the accept-in dual of {@link wsCarrier}. It describes how to\r\n * write frames back to a live socket, how to upgrade an inbound request into one, and (optionally) how to\r\n * persist bindings across hibernation. Hand it to `serveChannel`'s `carriers` list — the secure session,\r\n * codec, and crypto identity are supplied centrally there, so this only carries the WS-specific surface.\r\n */\r\nexport function wsAcceptorCarrier<TConn = WebSocket>(\r\n options: IWsAcceptorCarrierOptions<TConn>,\r\n): IDuplexAcceptorCarrier<TConn> {\r\n return {\r\n ...createDuplexCarrierLifecycle<TConn>(),\r\n shape: ETransportShape.duplex,\r\n carrierLabel: options.carrierLabel ?? \"ws\",\r\n secure: options.secure,\r\n send: options.send,\r\n upgrade: options.upgrade,\r\n isUpgrade: options.isUpgrade ?? ((request) => request.headers.get(\"Upgrade\") === \"websocket\"),\r\n attachmentStore: options.attachmentStore,\r\n };\r\n}\r\n","import { ETransportShape } from \"../../../Transport.types\";\r\nimport type { IExchangeAcceptorCarrier } from \"../../AcceptorCarrier.types\";\r\n\r\nexport interface IHttpAcceptorCarrierOptions {\r\n /**\r\n * Whether this endpoint runs the secure exchange protocol (default `true`). Pass `false` for a plain\r\n * endpoint — the body is the raw action wire and the result is the response body, the request/reply dual\r\n * of `plainTransport({ carrier: httpCarrier(...) })`. A plain endpoint ignores the crypto identity, so it\r\n * can sit alongside a secure WebSocket on the same server (e.g. a secure WS preferred, plain HTTP fallback).\r\n */\r\n secure?: boolean;\r\n /** Which requests carry an action exchange envelope on `POST`. Defaults to `serveChannel`'s path match. */\r\n isActionPath?: (url: URL) => boolean;\r\n /**\r\n * CORS headers merged onto every response (a preflight `OPTIONS` is answered `204`). Defaults to the\r\n * permissive `*` set; pass `false` to attach no CORS headers at all.\r\n */\r\n cors?: Record<string, string> | false;\r\n /** Plain mode only: use the error's HTTP status for failures (default `true`). Ignored when secure. */\r\n useErrorStatus?: boolean;\r\n /** Override the devtools carrier-kind label (defaults to `\"http\"`). */\r\n carrierLabel?: string;\r\n}\r\n\r\n/**\r\n * An HTTP {@link IExchangeAcceptorCarrier}: the accept-in dual of {@link httpCarrier}. It serves the\r\n * secure exchange protocol (handshake → token session → encrypted frames) over web-standard\r\n * `Request`/`Response`. The crypto identity, runtime coordinate, dictionary version, and accepted security\r\n * levels are all supplied centrally by `serveChannel`, so this only needs to say which requests carry an\r\n * action envelope and how to answer CORS.\r\n */\r\nexport function httpAcceptorCarrier(\r\n options: IHttpAcceptorCarrierOptions = {},\r\n): IExchangeAcceptorCarrier {\r\n return {\r\n shape: ETransportShape.exchange,\r\n carrierLabel: options.carrierLabel ?? \"http\",\r\n secure: options.secure,\r\n isActionPath: options.isActionPath,\r\n cors: options.cors,\r\n useErrorStatus: options.useErrorStatus,\r\n };\r\n}\r\n"],"mappings":"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"}