@nice-code/action 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +698 -666
- package/build/{ActionDevtoolsCore-CCZXQBAo.d.cts → ActionDevtoolsCore-baIHExfj.d.mts} +2 -2
- package/build/{ActionDevtoolsCore-bjYQ8O2_.d.mts → ActionDevtoolsCore-dApyYvTS.d.cts} +2 -2
- package/build/{ActionPayload.types-Bmkzw2df.d.mts → ActionPayload.types-CQM1HRw_.d.cts} +320 -298
- package/build/{ActionPayload.types-CdHOGGZK.d.cts → ActionPayload.types-DXGiw1SF.d.mts} +320 -298
- package/build/devtools/browser/index.d.cts +1 -1
- package/build/devtools/browser/index.d.mts +1 -1
- package/build/devtools/server/index.d.cts +1 -1
- package/build/devtools/server/index.d.mts +1 -1
- package/build/index.cjs +1768 -1718
- package/build/index.cjs.map +1 -1
- package/build/index.d.cts +2 -2
- package/build/index.d.mts +2 -2
- package/build/index.mjs +1769 -1717
- package/build/index.mjs.map +1 -1
- package/build/platform/cloudflare/index.cjs +8 -4
- package/build/platform/cloudflare/index.cjs.map +1 -1
- package/build/platform/cloudflare/index.d.cts +8 -3
- package/build/platform/cloudflare/index.d.mts +8 -3
- package/build/platform/cloudflare/index.mjs +8 -4
- package/build/platform/cloudflare/index.mjs.map +1 -1
- package/build/react-query/index.d.cts +1 -1
- package/build/react-query/index.d.mts +1 -1
- package/build/{wsAcceptorCarrier-DHRbsY1X.cjs → wsAcceptorCarrier-BDJRIPfu.cjs} +2 -2
- package/build/wsAcceptorCarrier-BDJRIPfu.cjs.map +1 -0
- package/build/{wsAcceptorCarrier-CXGlQU_f.mjs → wsAcceptorCarrier-CW2qX25W.mjs} +2 -2
- package/build/wsAcceptorCarrier-CW2qX25W.mjs.map +1 -0
- package/package.json +4 -4
- package/build/wsAcceptorCarrier-CXGlQU_f.mjs.map +0 -1
- package/build/wsAcceptorCarrier-DHRbsY1X.cjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { INiceErrorDomainProps, InferNiceError, NiceError, NiceErrorDomain, err_cast_not_nice } from "@nice-code/error";
|
|
2
|
-
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
2
|
import { RuntimeName } from "std-env";
|
|
4
3
|
import { ClientCryptoKeyLink, StorageAdapter, TSerializedCryptoKeyData_Ed25519_Raw, TSerializedCryptoKeyData_X25519_Raw, TTypeAndId } from "@nice-code/util";
|
|
5
4
|
import * as v from "valibot";
|
|
5
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
6
6
|
|
|
7
7
|
//#region src/ActionDefinition/Schema/ActionSchema.types.d.ts
|
|
8
8
|
type TTransportedValue<RAW_VAL, SERDE_VAL> = [RAW_VAL] | [RAW_VAL, SERDE_VAL];
|
|
@@ -1283,16 +1283,16 @@ declare class ActionRuntime {
|
|
|
1283
1283
|
*/
|
|
1284
1284
|
addHandlers(handlers: TActionRuntimeHandler[]): this;
|
|
1285
1285
|
/**
|
|
1286
|
+
* @internal Low-level primitive — the public way to open a connection is `connectChannel`, which
|
|
1287
|
+
* derives routing from a channel and binds the crypto identity for you. This stays as the raw building
|
|
1288
|
+
* block it sits on (it restates domain lists by hand) and is not part of the supported surface.
|
|
1289
|
+
*
|
|
1286
1290
|
* Declare an external "backend client" in one call: build an
|
|
1287
1291
|
* {@link ConnectorHandler} for `externalCoordinate` carrying the given
|
|
1288
1292
|
* `transports`, route the listed `domains`/`actions` to it, register it (plus any
|
|
1289
1293
|
* `localHandlers` — e.g. server→client push handlers that share the same channel)
|
|
1290
1294
|
* on this runtime, and `apply()`. Returns the external handler so the caller can
|
|
1291
1295
|
* later `clearTransportCache()` it.
|
|
1292
|
-
*
|
|
1293
|
-
* Sugar over `new ConnectorHandler(...).forDomain(...)` + `addHandlers([...])`,
|
|
1294
|
-
* so a single runtime can host one handler per backend target with its transports
|
|
1295
|
-
* declared once and reused across every action routed to that backend.
|
|
1296
1296
|
*/
|
|
1297
1297
|
connectTo(externalCoordinate: RuntimeCoordinate, options: {
|
|
1298
1298
|
transports: Transport[];
|
|
@@ -1679,6 +1679,97 @@ declare class AcceptorHandler<TConn = unknown> extends PeerLinkHandler {
|
|
|
1679
1679
|
}
|
|
1680
1680
|
declare const createAcceptorHandler: <TConn = unknown>(options: IAcceptorHandlerOptions<TConn>) => AcceptorHandler<TConn>;
|
|
1681
1681
|
//#endregion
|
|
1682
|
+
//#region src/ActionRuntime/Transport/Carrier/Carrier.types.d.ts
|
|
1683
|
+
/**
|
|
1684
|
+
* Carrier shapes — the only transport-specific surface a new protocol must implement. The secure
|
|
1685
|
+
* session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;
|
|
1686
|
+
* a carrier just moves frames. Two shapes capture every carrier:
|
|
1687
|
+
*
|
|
1688
|
+
* - {@link IDuplexCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,
|
|
1689
|
+
* Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client
|
|
1690
|
+
* pushes (the return path + broadcast).
|
|
1691
|
+
* - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push
|
|
1692
|
+
* (HTTP, and anything request/response-shaped). The reply rides the response to its own request.
|
|
1693
|
+
*
|
|
1694
|
+
* Frames are `string` (text — handshake control messages and JSON action frames) or binary
|
|
1695
|
+
* (`Uint8Array`/`ArrayBuffer` — the optimized binary wire / encrypted frames).
|
|
1696
|
+
*/
|
|
1697
|
+
type TFrame$1 = string | Uint8Array | ArrayBuffer;
|
|
1698
|
+
/**
|
|
1699
|
+
* A bidirectional, push-capable byte stream. Reduces every duplex carrier to "open, send bytes, receive
|
|
1700
|
+
* bytes, close" — a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe
|
|
1701
|
+
* all satisfy this, so the identical secure session runs over each.
|
|
1702
|
+
*/
|
|
1703
|
+
interface IDuplexCarrier {
|
|
1704
|
+
/** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */
|
|
1705
|
+
readonly ready: Promise<void>;
|
|
1706
|
+
/** Whether the carrier is currently open (a synchronous guard before `send`). */
|
|
1707
|
+
isOpen(): boolean;
|
|
1708
|
+
/** Write one frame to the peer. */
|
|
1709
|
+
send(frame: TFrame$1): void;
|
|
1710
|
+
/**
|
|
1711
|
+
* Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`
|
|
1712
|
+
* receives every inbound frame; `onClose` fires when the carrier goes away.
|
|
1713
|
+
*/
|
|
1714
|
+
attach(handlers: {
|
|
1715
|
+
onMessage: (frame: TFrame$1) => void;
|
|
1716
|
+
onClose: () => void;
|
|
1717
|
+
onError?: (error: unknown) => void;
|
|
1718
|
+
}): void;
|
|
1719
|
+
/** Close the carrier deliberately (a teardown). */
|
|
1720
|
+
close(): void;
|
|
1721
|
+
/** Optional human-readable endpoint for the devtools route chip. */
|
|
1722
|
+
readonly label?: string;
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a
|
|
1726
|
+
* frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the
|
|
1727
|
+
* HTTP transaction), so no correlation id is needed on the wire.
|
|
1728
|
+
*/
|
|
1729
|
+
interface IExchangeCarrier {
|
|
1730
|
+
/** Send one frame, await the single correlated reply frame. */
|
|
1731
|
+
exchange(frame: TFrame$1, opts?: {
|
|
1732
|
+
signal?: AbortSignal;
|
|
1733
|
+
}): Promise<TFrame$1>;
|
|
1734
|
+
/** Optional human-readable endpoint for the devtools route chip. */
|
|
1735
|
+
readonly label?: string;
|
|
1736
|
+
}
|
|
1737
|
+
type TCarrier = IDuplexCarrier | IExchangeCarrier;
|
|
1738
|
+
/**
|
|
1739
|
+
* A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.
|
|
1740
|
+
* Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and handed to
|
|
1741
|
+
* {@link secureTransport} / `LinkTransport` — so adding a new carrier is "write one of these", nothing
|
|
1742
|
+
* else.
|
|
1743
|
+
*/
|
|
1744
|
+
interface IDuplexCarrierSource {
|
|
1745
|
+
/** Open (or reuse) the carrier for an action. */
|
|
1746
|
+
open: (input: ITransportRouteActionParams) => IDuplexCarrier;
|
|
1747
|
+
/** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
|
|
1748
|
+
getCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
1749
|
+
/** Devtools route info for an action routed over this carrier. */
|
|
1750
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
1751
|
+
/** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`, `"memory"`). */
|
|
1752
|
+
readonly carrierLabel: string;
|
|
1753
|
+
}
|
|
1754
|
+
/**
|
|
1755
|
+
* The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an
|
|
1756
|
+
* {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by
|
|
1757
|
+
* `httpCarrier` and handed to {@link secureTransport} — adding a new request/reply protocol is "write
|
|
1758
|
+
* one of these". The `shape` tag lets {@link secureTransport} pick the duplex vs exchange transport.
|
|
1759
|
+
*/
|
|
1760
|
+
interface IExchangeCarrierSource {
|
|
1761
|
+
/** Discriminant so a generic factory can tell an exchange source from a duplex one. */
|
|
1762
|
+
readonly shape: "exchange";
|
|
1763
|
+
/** Open (or reuse) the carrier for an action. */
|
|
1764
|
+
open: (input: ITransportRouteActionParams) => IExchangeCarrier;
|
|
1765
|
+
/** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
|
|
1766
|
+
getCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
1767
|
+
/** Devtools route info for an action routed over this carrier. */
|
|
1768
|
+
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
1769
|
+
/** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
|
|
1770
|
+
readonly carrierLabel: string;
|
|
1771
|
+
}
|
|
1772
|
+
//#endregion
|
|
1682
1773
|
//#region src/ActionRuntime/Transport/codec/createBinaryWireSessionFactory.d.ts
|
|
1683
1774
|
type TFormatMessage = IActionWireFormat;
|
|
1684
1775
|
interface IBinaryWireSessionOptions {
|
|
@@ -1794,32 +1885,79 @@ type TDomainPushHandlers<D> = D extends ActionDomain<infer DEF> ? Partial<TWrapp
|
|
|
1794
1885
|
* the keys and input types follow the channel definition.
|
|
1795
1886
|
*/
|
|
1796
1887
|
type TChannelPushHandlers<TO_CONNECTOR extends readonly ActionDomain<any>[]> = TUnionToIntersection<TDomainPushHandlers<TO_CONNECTOR[number]>>;
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1888
|
+
/**
|
|
1889
|
+
* One transport to the peer, declared by *carrier* — the dial-out dual of `serveChannel`'s acceptor
|
|
1890
|
+
* carriers. {@link connectChannel} binds the shared facts (channel codec/version, runtime, crypto
|
|
1891
|
+
* identity) into each one, so a descriptor only carries what differs between transports: the carrier and
|
|
1892
|
+
* whether it runs the secure handshake.
|
|
1893
|
+
*
|
|
1894
|
+
* A duplex carrier (`wsCarrier(url)`, `rtcCarrier(dc)`) builds a push-capable link; an exchange carrier
|
|
1895
|
+
* (`httpCarrier(...)`) builds a request/reply transport. List them in preference order — the connection
|
|
1896
|
+
* prefers the first that's ready and falls through on failure (e.g. secure WS preferred, HTTP fallback).
|
|
1897
|
+
*/
|
|
1898
|
+
interface IConnectTransport {
|
|
1899
|
+
/** How to reach the peer — a duplex carrier (push-capable) or an exchange carrier (request/reply). */
|
|
1900
|
+
carrier: IDuplexCarrierSource | IExchangeCarrierSource;
|
|
1800
1901
|
/**
|
|
1801
|
-
*
|
|
1802
|
-
*
|
|
1803
|
-
*
|
|
1902
|
+
* Run the authenticated/encrypted handshake over this carrier. Defaults to `true`. A secure transport
|
|
1903
|
+
* draws its identity from the connection's shared `link`/`storage`; set `false` for a plain transport
|
|
1904
|
+
* (e.g. a bare HTTP fallback beside a secure WS), which then needs no `storage`.
|
|
1804
1905
|
*/
|
|
1805
|
-
|
|
1906
|
+
secure?: boolean;
|
|
1907
|
+
/** Security level for this secure transport; defaults to the connection-level `securityLevel`. */
|
|
1908
|
+
securityLevel?: ESecurityLevel;
|
|
1909
|
+
/**
|
|
1910
|
+
* Optional availability gate — when it returns `false` this transport is skipped and the connection
|
|
1911
|
+
* falls through to the next in preference order, re-evaluated per dispatch. Omit = always available.
|
|
1912
|
+
*/
|
|
1913
|
+
available?: (input: ITransportRouteActionParams) => boolean;
|
|
1914
|
+
/** Override the devtools chip label (defaults to the carrier's own label). */
|
|
1915
|
+
label?: string;
|
|
1916
|
+
}
|
|
1917
|
+
interface IConnectChannelOptions<TO_CONNECTOR extends readonly ActionDomain<any>[]> {
|
|
1918
|
+
/** The peer's runtime coordinate — the acceptor this connection dials. */
|
|
1919
|
+
peer: RuntimeCoordinate;
|
|
1920
|
+
/**
|
|
1921
|
+
* The transports to the peer, by carrier, in preference order (e.g. secure WS preferred, HTTP fallback).
|
|
1922
|
+
* They all carry the channel's `toAcceptor` domains; the connection prefers the first that's ready and
|
|
1923
|
+
* falls through on failure. {@link connectChannel} binds the channel + runtime + crypto identity into
|
|
1924
|
+
* each — the dial-out dual of `serveChannel`'s `carriers`.
|
|
1925
|
+
*/
|
|
1926
|
+
transports: readonly IConnectTransport[];
|
|
1927
|
+
/**
|
|
1928
|
+
* One backing store for this connection's crypto identity, fanned across every *secure* transport so
|
|
1929
|
+
* they present the same verify/exchange keys. Required when any transport is secure (the default); a
|
|
1930
|
+
* fully-plain connection (every transport `secure: false`) may omit it. Pass `link` instead to share an
|
|
1931
|
+
* existing identity.
|
|
1932
|
+
*/
|
|
1933
|
+
storage?: StorageAdapter;
|
|
1934
|
+
/** The connection's crypto identity. Defaults to a fresh {@link ClientCryptoKeyLink} over `storage`. */
|
|
1935
|
+
link?: ClientCryptoKeyLink;
|
|
1936
|
+
/** Default security level for secure transports; defaults to `authenticated`. */
|
|
1937
|
+
securityLevel?: ESecurityLevel;
|
|
1806
1938
|
/** Handlers for the channel's acceptor→connector pushes. Optional — omit for a send-only connection. */
|
|
1807
1939
|
onPush?: TChannelPushHandlers<TO_CONNECTOR>;
|
|
1808
1940
|
/** Default per-action timeout for this connection. */
|
|
1809
1941
|
defaultTimeout?: number;
|
|
1810
1942
|
}
|
|
1811
1943
|
/**
|
|
1812
|
-
*
|
|
1813
|
-
* the
|
|
1814
|
-
* `
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
1817
|
-
* fallback)
|
|
1818
|
-
*
|
|
1819
|
-
*
|
|
1820
|
-
*
|
|
1944
|
+
* Open a connection to a peer from a single call — the dial-out dual of `serveChannel`. The channel is
|
|
1945
|
+
* the single source of truth for *what* is routed (`toAcceptor` domains forwarded to the peer,
|
|
1946
|
+
* `toConnector` pushes handled locally from `onPush`); the call binds the shared facts — the channel's
|
|
1947
|
+
* codec/dictionary version, the runtime, and one crypto identity (a {@link ClientCryptoKeyLink} over
|
|
1948
|
+
* `storage`) — into every transport in `transports`, so none of them restate the channel or runtime.
|
|
1949
|
+
* List several transports to make the path transport-agnostic (secure WS preferred, HTTP fallback):
|
|
1950
|
+
* ```ts
|
|
1951
|
+
* const handler = connectChannel(runtime, lobbyChannel, {
|
|
1952
|
+
* peer: runtime_coordinate_lobby_do,
|
|
1953
|
+
* storage,
|
|
1954
|
+
* transports: [{ carrier: wsCarrier(url) }, { carrier: httpCarrier(...), secure: false }],
|
|
1955
|
+
* onPush: { player_joined: (p) => { … } },
|
|
1956
|
+
* });
|
|
1957
|
+
* ```
|
|
1958
|
+
* Returns the {@link ConnectorHandler} so the caller can later `clearTransportCache()` it.
|
|
1821
1959
|
*/
|
|
1822
|
-
declare function connectChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[]>(runtime: ActionRuntime,
|
|
1960
|
+
declare function connectChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[]>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IConnectChannelOptions<TO_CONNECTOR>): ConnectorHandler;
|
|
1823
1961
|
type TDomainAcceptorCases<D, TConn> = D extends ActionDomain<infer DEF> ? { [ID in keyof DEF["actionSchema"] & string]?: TAcceptorConnectionCaseFn<DEF, ID, TConn> } : never;
|
|
1824
1962
|
/**
|
|
1825
1963
|
* The connection-aware case map for a channel's acceptor side: the merged set of every
|
|
@@ -1878,6 +2016,88 @@ interface IAcceptChannelOptions<TConn> {
|
|
|
1878
2016
|
*/
|
|
1879
2017
|
declare function acceptChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn = unknown>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IAcceptChannelOptions<TConn>): AcceptorHandler<TConn>;
|
|
1880
2018
|
//#endregion
|
|
2019
|
+
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore.d.ts
|
|
2020
|
+
/**
|
|
2021
|
+
* The composite value persisted to a connection's attachment: the consumer's own app state plus the
|
|
2022
|
+
* {@link AcceptorHandler} routing binding. Co-storing them in one slot means a transport whose
|
|
2023
|
+
* sockets outlive process eviction (e.g. a Durable Object's hibernatable WebSocket) recovers both the
|
|
2024
|
+
* application identity *and* the action routing from a single attachment after a wake — no storage reads.
|
|
2025
|
+
*/
|
|
2026
|
+
interface IConnectionAttachment<TApp> {
|
|
2027
|
+
app?: TApp;
|
|
2028
|
+
binding?: IAcceptorConnectionBinding;
|
|
2029
|
+
}
|
|
2030
|
+
interface IConnectionStateStoreOptions<TConn, TApp> {
|
|
2031
|
+
/** Read a connection's raw attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
|
|
2032
|
+
read: (connection: TConn) => unknown;
|
|
2033
|
+
/** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
|
|
2034
|
+
write: (connection: TConn, value: IConnectionAttachment<TApp>) => void;
|
|
2035
|
+
/**
|
|
2036
|
+
* All currently-live connections (e.g. `() => ctx.getWebSockets()`). Used to replay routing bindings
|
|
2037
|
+
* after a wake (via {@link createConnectionStateStore}) and to enumerate app state in
|
|
2038
|
+
* {@link ConnectionStateStore.entries}.
|
|
2039
|
+
*/
|
|
2040
|
+
getConnections: () => TConn[];
|
|
2041
|
+
/**
|
|
2042
|
+
* Optional Standard Schema (valibot, zod, …) validating the *app* portion on read. A value that
|
|
2043
|
+
* fails validation reads back as `null` — the same lenient behavior as a hand-written safeParse
|
|
2044
|
+
* helper. The binding is the library's own shape and is never validated.
|
|
2045
|
+
*/
|
|
2046
|
+
schema?: StandardSchemaV1<unknown, TApp>;
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* A typed per-connection state store that co-owns the app state and the acceptor handler's routing
|
|
2050
|
+
* binding in one attachment, so neither the consumer nor the handler has to hand-merge the two. Create
|
|
2051
|
+
* it through {@link createConnectionStateStore} (which also wires binding persistence and replays
|
|
2052
|
+
* surviving connections after a wake), then `get`/`set`/`clearApp` the app state directly.
|
|
2053
|
+
*
|
|
2054
|
+
* The mechanism is carrier-neutral — it only needs read/write/enumerate callbacks for the connection's
|
|
2055
|
+
* attachment — but it pays off on transports whose connections outlive process eviction (e.g. a
|
|
2056
|
+
* Durable Object's hibernatable WebSockets), which is why it lives beside the hibernation adapter.
|
|
2057
|
+
*
|
|
2058
|
+
* ```ts
|
|
2059
|
+
* const players = createConnectionStateStore(serverHandler, {
|
|
2060
|
+
* schema: vs_player,
|
|
2061
|
+
* read: (ws) => ws.deserializeAttachment(),
|
|
2062
|
+
* write: (ws, v) => ws.serializeAttachment(v),
|
|
2063
|
+
* getConnections: () => ctx.getWebSockets(),
|
|
2064
|
+
* });
|
|
2065
|
+
* players.set(ws, player); // binding is preserved automatically
|
|
2066
|
+
* const player = players.get(ws);
|
|
2067
|
+
* ```
|
|
2068
|
+
*/
|
|
2069
|
+
declare class ConnectionStateStore<TConn, TApp> {
|
|
2070
|
+
private readonly options;
|
|
2071
|
+
constructor(options: IConnectionStateStoreOptions<TConn, TApp>);
|
|
2072
|
+
/** The validated app state for a connection, or `null` if unset / invalid. */
|
|
2073
|
+
get(connection: TConn): TApp | null;
|
|
2074
|
+
/** Set the app state, preserving the runtime binding already pinned to the connection. */
|
|
2075
|
+
set(connection: TConn, app: TApp): void;
|
|
2076
|
+
/** Clear the app state but keep the binding (e.g. a spectator that stopped watching). */
|
|
2077
|
+
clearApp(connection: TConn): void;
|
|
2078
|
+
/** Every live connection paired with its (validated) app state — for rebuilding in-memory state after a wake. */
|
|
2079
|
+
entries(): [TConn, TApp | null][];
|
|
2080
|
+
/** @internal Persist a freshly-bound connection's binding, preserving any app state already stored. */
|
|
2081
|
+
_persistBinding(connection: TConn, binding: IAcceptorConnectionBinding): void;
|
|
2082
|
+
/** @internal The persisted binding for a connection, if any (used to replay routing after a wake). */
|
|
2083
|
+
_readBinding(connection: TConn): IAcceptorConnectionBinding | undefined;
|
|
2084
|
+
private _readAttachment;
|
|
2085
|
+
private _validateApp;
|
|
2086
|
+
}
|
|
2087
|
+
/**
|
|
2088
|
+
* Build a per-connection {@link ConnectionStateStore} bound to an {@link AcceptorHandler}: it registers
|
|
2089
|
+
* itself as the handler's connection-bound persistence callback (so bindings are written without
|
|
2090
|
+
* overwriting app state) and immediately replays every live connection's stored binding via
|
|
2091
|
+
* {@link AcceptorHandler.rehydrateConnection} — so on a transport that resumes after eviction (e.g. a
|
|
2092
|
+
* Durable Object waking from hibernation) both the app identity and the action routing come back from a
|
|
2093
|
+
* single attachment, with no storage reads and no hand-rolled merge.
|
|
2094
|
+
*
|
|
2095
|
+
* Lives outside the handler so the generic {@link AcceptorHandler} stays free of any attachment/
|
|
2096
|
+
* hibernation concern — it exposes only the neutral `setOnConnectionBound` + `rehydrateConnection`
|
|
2097
|
+
* hooks this builder drives.
|
|
2098
|
+
*/
|
|
2099
|
+
declare function createConnectionStateStore<TConn, TApp>(handler: AcceptorHandler<TConn>, options: IConnectionStateStoreOptions<TConn, TApp>): ConnectionStateStore<TConn, TApp>;
|
|
2100
|
+
//#endregion
|
|
1881
2101
|
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/createHibernatableWsServerAdapter.d.ts
|
|
1882
2102
|
interface IHibernatableWsServerAdapterOptions<TConn> {
|
|
1883
2103
|
/** The handler to drive (from `createSecureAcceptorHandler` or `createAcceptorHandler`). */
|
|
@@ -1919,97 +2139,6 @@ interface IDuplexConnectionRouter<TConn> {
|
|
|
1919
2139
|
*/
|
|
1920
2140
|
declare function createHibernatableWsServerAdapter<TConn>(options: IHibernatableWsServerAdapterOptions<TConn>): IDuplexConnectionRouter<TConn>;
|
|
1921
2141
|
//#endregion
|
|
1922
|
-
//#region src/ActionRuntime/Transport/Carrier/Carrier.types.d.ts
|
|
1923
|
-
/**
|
|
1924
|
-
* Carrier shapes — the only transport-specific surface a new protocol must implement. The secure
|
|
1925
|
-
* session (handshake + frame crypto + codec) and the action routing on top of it are carrier-agnostic;
|
|
1926
|
-
* a carrier just moves frames. Two shapes capture every carrier:
|
|
1927
|
-
*
|
|
1928
|
-
* - {@link IDuplexCarrier} — a persistent, push-capable byte stream (WebSocket, WebRTC `RTCDataChannel`,
|
|
1929
|
-
* Bluetooth GATT, an in-memory pipe). Either side can send at any time, so it supports server→client
|
|
1930
|
-
* pushes (the return path + broadcast).
|
|
1931
|
-
* - {@link IExchangeCarrier} — a request → single-correlated-reply carrier with no unsolicited push
|
|
1932
|
-
* (HTTP, and anything request/response-shaped). The reply rides the response to its own request.
|
|
1933
|
-
*
|
|
1934
|
-
* Frames are `string` (text — handshake control messages and JSON action frames) or binary
|
|
1935
|
-
* (`Uint8Array`/`ArrayBuffer` — the optimized binary wire / encrypted frames).
|
|
1936
|
-
*/
|
|
1937
|
-
type TFrame$1 = string | Uint8Array | ArrayBuffer;
|
|
1938
|
-
/**
|
|
1939
|
-
* A bidirectional, push-capable byte stream. Reduces every duplex carrier to "open, send bytes, receive
|
|
1940
|
-
* bytes, close" — a WebSocket, a WebRTC data channel, a Bluetooth characteristic, or an in-memory pipe
|
|
1941
|
-
* all satisfy this, so the identical secure session runs over each.
|
|
1942
|
-
*/
|
|
1943
|
-
interface IDuplexCarrier {
|
|
1944
|
-
/** Resolves once the carrier is open and ready to send; rejects if it closes/errors before opening. */
|
|
1945
|
-
readonly ready: Promise<void>;
|
|
1946
|
-
/** Whether the carrier is currently open (a synchronous guard before `send`). */
|
|
1947
|
-
isOpen(): boolean;
|
|
1948
|
-
/** Write one frame to the peer. */
|
|
1949
|
-
send(frame: TFrame$1): void;
|
|
1950
|
-
/**
|
|
1951
|
-
* Register the carrier's handlers. Called exactly once by the session after `ready`. `onMessage`
|
|
1952
|
-
* receives every inbound frame; `onClose` fires when the carrier goes away.
|
|
1953
|
-
*/
|
|
1954
|
-
attach(handlers: {
|
|
1955
|
-
onMessage: (frame: TFrame$1) => void;
|
|
1956
|
-
onClose: () => void;
|
|
1957
|
-
onError?: (error: unknown) => void;
|
|
1958
|
-
}): void;
|
|
1959
|
-
/** Close the carrier deliberately (a teardown). */
|
|
1960
|
-
close(): void;
|
|
1961
|
-
/** Optional human-readable endpoint for the devtools route chip. */
|
|
1962
|
-
readonly label?: string;
|
|
1963
|
-
}
|
|
1964
|
-
/**
|
|
1965
|
-
* A request → single-correlated-reply carrier with no unsolicited push (HTTP). One `exchange` sends a
|
|
1966
|
-
* frame and resolves with exactly the one reply frame for it; the carrier itself correlates them (the
|
|
1967
|
-
* HTTP transaction), so no correlation id is needed on the wire.
|
|
1968
|
-
*/
|
|
1969
|
-
interface IExchangeCarrier {
|
|
1970
|
-
/** Send one frame, await the single correlated reply frame. */
|
|
1971
|
-
exchange(frame: TFrame$1, opts?: {
|
|
1972
|
-
signal?: AbortSignal;
|
|
1973
|
-
}): Promise<TFrame$1>;
|
|
1974
|
-
/** Optional human-readable endpoint for the devtools route chip. */
|
|
1975
|
-
readonly label?: string;
|
|
1976
|
-
}
|
|
1977
|
-
type TCarrier = IDuplexCarrier | IExchangeCarrier;
|
|
1978
|
-
/**
|
|
1979
|
-
* A reusable opener for a {@link IDuplexCarrier} plus the per-action metadata a duplex transport needs.
|
|
1980
|
-
* Built by the small carrier factories (`wsCarrier`, `rtcCarrier`, `inMemoryCarrier`) and handed to
|
|
1981
|
-
* {@link secureTransport} / `LinkTransport` — so adding a new carrier is "write one of these", nothing
|
|
1982
|
-
* else.
|
|
1983
|
-
*/
|
|
1984
|
-
interface IDuplexCarrierSource {
|
|
1985
|
-
/** Open (or reuse) the carrier for an action. */
|
|
1986
|
-
open: (input: ITransportRouteActionParams) => IDuplexCarrier;
|
|
1987
|
-
/** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
|
|
1988
|
-
getCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
1989
|
-
/** Devtools route info for an action routed over this carrier. */
|
|
1990
|
-
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
1991
|
-
/** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`, `"memory"`). */
|
|
1992
|
-
readonly carrierLabel: string;
|
|
1993
|
-
}
|
|
1994
|
-
/**
|
|
1995
|
-
* The exchange-shape counterpart to {@link IDuplexCarrierSource}: a reusable opener for an
|
|
1996
|
-
* {@link IExchangeCarrier} plus the per-action metadata an exchange transport needs. Built by
|
|
1997
|
-
* `httpCarrier` and handed to {@link secureTransport} — adding a new request/reply protocol is "write
|
|
1998
|
-
* one of these". The `shape` tag lets {@link secureTransport} pick the duplex vs exchange transport.
|
|
1999
|
-
*/
|
|
2000
|
-
interface IExchangeCarrierSource {
|
|
2001
|
-
/** Discriminant so a generic factory can tell an exchange source from a duplex one. */
|
|
2002
|
-
readonly shape: "exchange";
|
|
2003
|
-
/** Open (or reuse) the carrier for an action. */
|
|
2004
|
-
open: (input: ITransportRouteActionParams) => IExchangeCarrier;
|
|
2005
|
-
/** Keys identifying a reusable carrier, so one carrier is shared across actions to the same peer. */
|
|
2006
|
-
getCacheKey?: (input: ITransportRouteActionParams) => string[];
|
|
2007
|
-
/** Devtools route info for an action routed over this carrier. */
|
|
2008
|
-
getRouteInfo?: (input: ITransportRouteActionParams) => ITransportRouteInfo;
|
|
2009
|
-
/** Short carrier-kind label for the devtools chip (e.g. `"http"`). */
|
|
2010
|
-
readonly carrierLabel: string;
|
|
2011
|
-
}
|
|
2012
|
-
//#endregion
|
|
2013
2142
|
//#region src/ActionRuntime/Transport/Carrier/AcceptorCarrier.types.d.ts
|
|
2014
2143
|
/**
|
|
2015
2144
|
* Acceptor-side carrier descriptors — the accept-in dual of the connector's {@link IDuplexCarrierSource}
|
|
@@ -2029,17 +2158,21 @@ interface IExchangeCarrierSource {
|
|
|
2029
2158
|
* upgrade.
|
|
2030
2159
|
*/
|
|
2031
2160
|
/**
|
|
2032
|
-
*
|
|
2033
|
-
* Durable Object's hibernatable WebSockets). Optional — omit for a
|
|
2034
|
-
*
|
|
2161
|
+
* Raw read/write access to a connection's persisted attachment, for a duplex carrier whose connections
|
|
2162
|
+
* outlive process eviction (e.g. a Durable Object's hibernatable WebSockets). Optional — omit for a
|
|
2163
|
+
* transport that never hibernates (per-connection state is then in-memory only).
|
|
2164
|
+
*
|
|
2165
|
+
* `serveChannel` owns the attachment *layout*: it co-stores the routing binding and (when
|
|
2166
|
+
* `connectionState` is requested) per-connection app state as one composite in this single slot, so both
|
|
2167
|
+
* survive a wake. The carrier only has to say how to read/write the slot and enumerate live connections.
|
|
2035
2168
|
*/
|
|
2036
|
-
interface
|
|
2037
|
-
/** All currently-live connections —
|
|
2169
|
+
interface IAcceptorAttachmentStore<TConn> {
|
|
2170
|
+
/** All currently-live connections — enumerated on build to replay binding + app state after a wake. */
|
|
2038
2171
|
getConnections: () => TConn[];
|
|
2039
|
-
/** Read a connection's persisted
|
|
2040
|
-
|
|
2041
|
-
/** Persist a connection's
|
|
2042
|
-
|
|
2172
|
+
/** Read a connection's persisted attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
|
|
2173
|
+
read: (connection: TConn) => unknown;
|
|
2174
|
+
/** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
|
|
2175
|
+
write: (connection: TConn, value: unknown) => void;
|
|
2043
2176
|
}
|
|
2044
2177
|
/**
|
|
2045
2178
|
* A duplex carrier is also its own lifecycle handle: once it has been passed to `serveChannel`, feed each
|
|
@@ -2086,8 +2219,12 @@ interface IDuplexAcceptorCarrier<TConn = unknown> extends IDuplexCarrierLifecycl
|
|
|
2086
2219
|
* Only consulted when {@link upgrade} is present.
|
|
2087
2220
|
*/
|
|
2088
2221
|
isUpgrade?: (request: Request, url: URL) => boolean;
|
|
2089
|
-
/**
|
|
2090
|
-
|
|
2222
|
+
/**
|
|
2223
|
+
* Optional attachment read/write for connections that survive eviction (Durable Object hibernation).
|
|
2224
|
+
* Present → `serveChannel` persists the routing binding (and any `connectionState`) here and replays it
|
|
2225
|
+
* on wake. Absent → per-connection state is in-memory only.
|
|
2226
|
+
*/
|
|
2227
|
+
attachmentStore?: IAcceptorAttachmentStore<TConn>;
|
|
2091
2228
|
/** Short carrier-kind label for the devtools chip (e.g. `"ws"`, `"webrtc"`). */
|
|
2092
2229
|
readonly carrierLabel: string;
|
|
2093
2230
|
}
|
|
@@ -2129,7 +2266,15 @@ type TAcceptorCarrier<TConn = unknown> = IDuplexAcceptorCarrier<TConn> | IExchan
|
|
|
2129
2266
|
declare function isExchangeAcceptorCarrier<TConn>(carrier: TAcceptorCarrier<TConn>): carrier is IExchangeAcceptorCarrier;
|
|
2130
2267
|
//#endregion
|
|
2131
2268
|
//#region src/ActionRuntime/Channel/serveChannel.d.ts
|
|
2132
|
-
|
|
2269
|
+
/** Per-connection app-state config for {@link serveChannel}'s `connectionState`. */
|
|
2270
|
+
interface IServeConnectionStateOptions<TApp> {
|
|
2271
|
+
/**
|
|
2272
|
+
* Optional Standard Schema (valibot, zod, …) validating the app state on read — a value that fails
|
|
2273
|
+
* validation reads back as `null`. Omit to store the app state untyped.
|
|
2274
|
+
*/
|
|
2275
|
+
schema?: StandardSchemaV1<unknown, TApp>;
|
|
2276
|
+
}
|
|
2277
|
+
interface IServeChannelOptions<TO_ACCEPTOR extends readonly ActionDomain<any>[], TConn, TApp = unknown> {
|
|
2133
2278
|
/**
|
|
2134
2279
|
* Coordinate of the *connecting clients* (typically env-only, e.g. `RuntimeCoordinate.env("web_app")`),
|
|
2135
2280
|
* used to score return-path dispatch back to the right connection.
|
|
@@ -2166,12 +2311,25 @@ interface IServeChannelOptions<TConn> {
|
|
|
2166
2311
|
verifyKeyResolver?: IClientVerifyKeyResolver;
|
|
2167
2312
|
/** Timeout (ms) applied to server-initiated actions awaiting a client response. */
|
|
2168
2313
|
defaultTimeout?: number;
|
|
2314
|
+
/**
|
|
2315
|
+
* Co-store per-connection app state alongside the routing binding in the sole duplex carrier's connection
|
|
2316
|
+
* attachment, so both survive a wake from eviction. Reach the typed store back on `server.connections`.
|
|
2317
|
+
* Requires the carrier to expose an attachment store (the Cloudflare `durableObjectWsCarrier` does) and
|
|
2318
|
+
* exactly one duplex carrier.
|
|
2319
|
+
*/
|
|
2320
|
+
connectionState?: IServeConnectionStateOptions<TApp>;
|
|
2321
|
+
/**
|
|
2322
|
+
* Connection-aware action cases for the channel's acceptor (`toAcceptor`) domains — each case receives the
|
|
2323
|
+
* primed request *and* the originating connection (the connection-aware dual of `handlers`). Registered on
|
|
2324
|
+
* the runtime for you. Requires exactly one duplex carrier.
|
|
2325
|
+
*/
|
|
2326
|
+
channelCases?: TChannelAcceptorCases<TO_ACCEPTOR, TConn>;
|
|
2169
2327
|
}
|
|
2170
2328
|
/**
|
|
2171
2329
|
* One server serving a secure channel over several carriers — the accept-in dual of `connectChannel`,
|
|
2172
2330
|
* returned by {@link serveChannel}. Wire its surface straight to the host's request/socket events.
|
|
2173
2331
|
*/
|
|
2174
|
-
interface IChannelServer<TConn> {
|
|
2332
|
+
interface IChannelServer<TConn, TApp = unknown> {
|
|
2175
2333
|
/**
|
|
2176
2334
|
* The duplex acceptor handlers — one per duplex carrier, in carrier order (empty if none). For pushing,
|
|
2177
2335
|
* prefer {@link pushToClient} (it resolves the owning handler); reach for these for cross-carrier work
|
|
@@ -2197,6 +2355,23 @@ interface IChannelServer<TConn> {
|
|
|
2197
2355
|
pushToClient: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(target: TConn | RuntimeCoordinate, request: ActionPayload_Request<DOM, ID>, options?: {
|
|
2198
2356
|
timeout?: number;
|
|
2199
2357
|
}) => RunningAction<DOM, ID>;
|
|
2358
|
+
/**
|
|
2359
|
+
* Fan a server-initiated action out to every connection on the sole duplex carrier (skip the origin with
|
|
2360
|
+
* `except`, filter with `where`). The push-to-many counterpart of {@link pushToClient}. Throws if there
|
|
2361
|
+
* isn't exactly one duplex carrier (with several, broadcast over a specific `handlers[i]`).
|
|
2362
|
+
*/
|
|
2363
|
+
broadcast: <DOM extends IActionDomain, ID extends keyof DOM["actionSchema"] & string>(makeRequest: () => ActionPayload_Request<DOM, ID>, options?: {
|
|
2364
|
+
except?: TConn | null;
|
|
2365
|
+
where?: (connection: TConn) => boolean;
|
|
2366
|
+
timeout?: number;
|
|
2367
|
+
onError?: (error: unknown, connection: TConn) => void;
|
|
2368
|
+
}) => void;
|
|
2369
|
+
/**
|
|
2370
|
+
* The per-connection app-state store co-stored with the routing binding in the connection attachment —
|
|
2371
|
+
* present only when `connectionState` was passed. `get`/`set`/`clearApp`/`entries` it directly; it
|
|
2372
|
+
* survives hibernation alongside the binding.
|
|
2373
|
+
*/
|
|
2374
|
+
connections?: ConnectionStateStore<TConn, TApp>;
|
|
2200
2375
|
}
|
|
2201
2376
|
/**
|
|
2202
2377
|
* Serve a secure channel over one or more carriers from a single call — the accept-in dual of
|
|
@@ -2204,24 +2379,33 @@ interface IChannelServer<TConn> {
|
|
|
2204
2379
|
* resolver) and the security block (coordinate, dictionary version, accepted levels) *once* from
|
|
2205
2380
|
* `(runtime, channel)` and fans them across every carrier, so the WebSocket and the secure-HTTP endpoint
|
|
2206
2381
|
* can never drift apart. It registers your handlers (plus the duplex acceptor it builds) on the runtime,
|
|
2207
|
-
* wires hibernation when the duplex carrier
|
|
2208
|
-
* `fetch` / `duplex` / `pushToClient` you forward straight to
|
|
2382
|
+
* wires hibernation when the duplex carrier exposes an attachment store, and returns a single
|
|
2383
|
+
* {@link IChannelServer} whose `fetch` / `duplex` / `pushToClient` / `broadcast` you forward straight to
|
|
2384
|
+
* the host:
|
|
2209
2385
|
* ```ts
|
|
2210
2386
|
* const server = serveChannel(runtime, channel, {
|
|
2211
2387
|
* clientEnv, storage,
|
|
2212
|
-
* carriers: [wsAcceptorCarrier({ send, upgrade,
|
|
2213
|
-
*
|
|
2388
|
+
* carriers: [wsAcceptorCarrier({ send, upgrade, attachmentStore }), httpAcceptorCarrier()],
|
|
2389
|
+
* connectionState: { schema: vs_player }, // optional: co-store per-connection app state (survives hibernation)
|
|
2390
|
+
* channelCases: { join: (action, conn) => { … } }, // optional: connection-aware action cases
|
|
2214
2391
|
* });
|
|
2215
2392
|
* // fetch(req) => server.fetch(req)
|
|
2216
2393
|
* // webSocketMessage(conn, m) => server.duplex?.receive(conn, m)
|
|
2217
2394
|
* // webSocketClose/Error(conn) => server.duplex?.drop(conn)
|
|
2395
|
+
* // server.connections.get(conn) / server.broadcast(() => push.request(…), { except: conn })
|
|
2218
2396
|
* ```
|
|
2219
2397
|
*
|
|
2220
2398
|
* `TConn` (the live-connection token a duplex carrier hands back through `send`/`receive`/`drop`) is
|
|
2221
2399
|
* inferred from the carriers — `WebSocket` for `wsAcceptorCarrier`, the data-channel type for a WebRTC
|
|
2222
|
-
* carrier, and so on — so it stays carrier-agnostic.
|
|
2400
|
+
* carrier, and so on — so it stays carrier-agnostic. Passing `connectionState` narrows the return so
|
|
2401
|
+
* `server.connections` is non-optional.
|
|
2223
2402
|
*/
|
|
2224
|
-
declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[]
|
|
2403
|
+
declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[], TConn, TApp>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp> & {
|
|
2404
|
+
connectionState: IServeConnectionStateOptions<TApp>;
|
|
2405
|
+
}): IChannelServer<TConn, TApp> & {
|
|
2406
|
+
connections: ConnectionStateStore<TConn, TApp>;
|
|
2407
|
+
};
|
|
2408
|
+
declare function serveChannel<TO_ACCEPTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TO_CONNECTOR extends readonly ActionDomain<any>[] = readonly ActionDomain<any>[], TConn = unknown, TApp = unknown>(runtime: ActionRuntime, channel: ISecureChannel<TO_ACCEPTOR, TO_CONNECTOR>, options: IServeChannelOptions<TO_ACCEPTOR, TConn, TApp>): IChannelServer<TConn, TApp>;
|
|
2225
2409
|
//#endregion
|
|
2226
2410
|
//#region src/ActionRuntime/Transport/SecureSession/exchangeAcceptor.d.ts
|
|
2227
2411
|
/** Acceptor secure config for the exchange (HTTP) endpoint — same identity an `AcceptorHandler` uses. */
|
|
@@ -2368,88 +2552,6 @@ interface ISecureAcceptorHandlerOptions<TConn> {
|
|
|
2368
2552
|
*/
|
|
2369
2553
|
declare function createSecureAcceptorHandler<TConn = unknown>(options: ISecureAcceptorHandlerOptions<TConn>): AcceptorHandler<TConn>;
|
|
2370
2554
|
//#endregion
|
|
2371
|
-
//#region src/ActionRuntime/Handler/PeerLink/Acceptor/Hibernation/ConnectionStateStore.d.ts
|
|
2372
|
-
/**
|
|
2373
|
-
* The composite value persisted to a connection's attachment: the consumer's own app state plus the
|
|
2374
|
-
* {@link AcceptorHandler} routing binding. Co-storing them in one slot means a transport whose
|
|
2375
|
-
* sockets outlive process eviction (e.g. a Durable Object's hibernatable WebSocket) recovers both the
|
|
2376
|
-
* application identity *and* the action routing from a single attachment after a wake — no storage reads.
|
|
2377
|
-
*/
|
|
2378
|
-
interface IConnectionAttachment<TApp> {
|
|
2379
|
-
app?: TApp;
|
|
2380
|
-
binding?: IAcceptorConnectionBinding;
|
|
2381
|
-
}
|
|
2382
|
-
interface IConnectionStateStoreOptions<TConn, TApp> {
|
|
2383
|
-
/** Read a connection's raw attachment (e.g. `(ws) => ws.deserializeAttachment()`). */
|
|
2384
|
-
read: (connection: TConn) => unknown;
|
|
2385
|
-
/** Persist a connection's attachment (e.g. `(ws, value) => ws.serializeAttachment(value)`). */
|
|
2386
|
-
write: (connection: TConn, value: IConnectionAttachment<TApp>) => void;
|
|
2387
|
-
/**
|
|
2388
|
-
* All currently-live connections (e.g. `() => ctx.getWebSockets()`). Used to replay routing bindings
|
|
2389
|
-
* after a wake (via {@link createConnectionStateStore}) and to enumerate app state in
|
|
2390
|
-
* {@link ConnectionStateStore.entries}.
|
|
2391
|
-
*/
|
|
2392
|
-
getConnections: () => TConn[];
|
|
2393
|
-
/**
|
|
2394
|
-
* Optional Standard Schema (valibot, zod, …) validating the *app* portion on read. A value that
|
|
2395
|
-
* fails validation reads back as `null` — the same lenient behavior as a hand-written safeParse
|
|
2396
|
-
* helper. The binding is the library's own shape and is never validated.
|
|
2397
|
-
*/
|
|
2398
|
-
schema?: StandardSchemaV1<unknown, TApp>;
|
|
2399
|
-
}
|
|
2400
|
-
/**
|
|
2401
|
-
* A typed per-connection state store that co-owns the app state and the acceptor handler's routing
|
|
2402
|
-
* binding in one attachment, so neither the consumer nor the handler has to hand-merge the two. Create
|
|
2403
|
-
* it through {@link createConnectionStateStore} (which also wires binding persistence and replays
|
|
2404
|
-
* surviving connections after a wake), then `get`/`set`/`clearApp` the app state directly.
|
|
2405
|
-
*
|
|
2406
|
-
* The mechanism is carrier-neutral — it only needs read/write/enumerate callbacks for the connection's
|
|
2407
|
-
* attachment — but it pays off on transports whose connections outlive process eviction (e.g. a
|
|
2408
|
-
* Durable Object's hibernatable WebSockets), which is why it lives beside the hibernation adapter.
|
|
2409
|
-
*
|
|
2410
|
-
* ```ts
|
|
2411
|
-
* const players = createConnectionStateStore(serverHandler, {
|
|
2412
|
-
* schema: vs_player,
|
|
2413
|
-
* read: (ws) => ws.deserializeAttachment(),
|
|
2414
|
-
* write: (ws, v) => ws.serializeAttachment(v),
|
|
2415
|
-
* getConnections: () => ctx.getWebSockets(),
|
|
2416
|
-
* });
|
|
2417
|
-
* players.set(ws, player); // binding is preserved automatically
|
|
2418
|
-
* const player = players.get(ws);
|
|
2419
|
-
* ```
|
|
2420
|
-
*/
|
|
2421
|
-
declare class ConnectionStateStore<TConn, TApp> {
|
|
2422
|
-
private readonly options;
|
|
2423
|
-
constructor(options: IConnectionStateStoreOptions<TConn, TApp>);
|
|
2424
|
-
/** The validated app state for a connection, or `null` if unset / invalid. */
|
|
2425
|
-
get(connection: TConn): TApp | null;
|
|
2426
|
-
/** Set the app state, preserving the runtime binding already pinned to the connection. */
|
|
2427
|
-
set(connection: TConn, app: TApp): void;
|
|
2428
|
-
/** Clear the app state but keep the binding (e.g. a spectator that stopped watching). */
|
|
2429
|
-
clearApp(connection: TConn): void;
|
|
2430
|
-
/** Every live connection paired with its (validated) app state — for rebuilding in-memory state after a wake. */
|
|
2431
|
-
entries(): [TConn, TApp | null][];
|
|
2432
|
-
/** @internal Persist a freshly-bound connection's binding, preserving any app state already stored. */
|
|
2433
|
-
_persistBinding(connection: TConn, binding: IAcceptorConnectionBinding): void;
|
|
2434
|
-
/** @internal The persisted binding for a connection, if any (used to replay routing after a wake). */
|
|
2435
|
-
_readBinding(connection: TConn): IAcceptorConnectionBinding | undefined;
|
|
2436
|
-
private _readAttachment;
|
|
2437
|
-
private _validateApp;
|
|
2438
|
-
}
|
|
2439
|
-
/**
|
|
2440
|
-
* Build a per-connection {@link ConnectionStateStore} bound to an {@link AcceptorHandler}: it registers
|
|
2441
|
-
* itself as the handler's connection-bound persistence callback (so bindings are written without
|
|
2442
|
-
* overwriting app state) and immediately replays every live connection's stored binding via
|
|
2443
|
-
* {@link AcceptorHandler.rehydrateConnection} — so on a transport that resumes after eviction (e.g. a
|
|
2444
|
-
* Durable Object waking from hibernation) both the app identity and the action routing come back from a
|
|
2445
|
-
* single attachment, with no storage reads and no hand-rolled merge.
|
|
2446
|
-
*
|
|
2447
|
-
* Lives outside the handler so the generic {@link AcceptorHandler} stays free of any attachment/
|
|
2448
|
-
* hibernation concern — it exposes only the neutral `setOnConnectionBound` + `rehydrateConnection`
|
|
2449
|
-
* hooks this builder drives.
|
|
2450
|
-
*/
|
|
2451
|
-
declare function createConnectionStateStore<TConn, TApp>(handler: AcceptorHandler<TConn>, options: IConnectionStateStoreOptions<TConn, TApp>): ConnectionStateStore<TConn, TApp>;
|
|
2452
|
-
//#endregion
|
|
2453
2555
|
//#region src/ActionRuntime/Handler/PeerLink/Connector/err_nice_external_client.d.ts
|
|
2454
2556
|
declare const err_nice_external_client: import("@nice-code/error").NiceErrorDomain<{
|
|
2455
2557
|
domain: string;
|
|
@@ -2575,8 +2677,8 @@ interface IWsAcceptorCarrierOptions<TConn> {
|
|
|
2575
2677
|
upgrade?: (request: Request, url: URL) => Response | Promise<Response>;
|
|
2576
2678
|
/** Whether an inbound request is a WS upgrade. Defaults to an `Upgrade: websocket` header. */
|
|
2577
2679
|
isUpgrade?: (request: Request, url: URL) => boolean;
|
|
2578
|
-
/**
|
|
2579
|
-
|
|
2680
|
+
/** Attachment read/write for hibernatable sockets (e.g. a Durable Object); `serveChannel` persists here. */
|
|
2681
|
+
attachmentStore?: IAcceptorAttachmentStore<TConn>;
|
|
2580
2682
|
/** Override the devtools carrier-kind label (defaults to `"ws"`). */
|
|
2581
2683
|
carrierLabel?: string;
|
|
2582
2684
|
}
|
|
@@ -2888,47 +2990,6 @@ declare class LinkTransport extends Transport<ETransportShape.duplex> {
|
|
|
2888
2990
|
getRouteInfo(input: ITransportRouteActionParams): ITransportRouteInfo;
|
|
2889
2991
|
}
|
|
2890
2992
|
//#endregion
|
|
2891
|
-
//#region src/ActionRuntime/Transport/plainTransport.d.ts
|
|
2892
|
-
interface IPlainTransportOptions {
|
|
2893
|
-
/**
|
|
2894
|
-
* How to reach the peer with no security layer. A duplex carrier (`wsCarrier(url)`,
|
|
2895
|
-
* `rtcCarrier(dc)`, `inMemoryCarrier().carrier`) builds a push-capable {@link LinkTransport}; an
|
|
2896
|
-
* exchange carrier (`httpCarrier(...)`) builds a request/reply {@link ExchangeTransport}.
|
|
2897
|
-
*/
|
|
2898
|
-
carrier: IDuplexCarrierSource | IExchangeCarrierSource;
|
|
2899
|
-
/**
|
|
2900
|
-
* Codec for a *duplex* carrier (a duplex link frames the action wire itself). Required for duplex;
|
|
2901
|
-
* ignored for an exchange carrier, which JSON-encodes the action wire in its envelope.
|
|
2902
|
-
*/
|
|
2903
|
-
formatMessage?: TLinkFormatMessage;
|
|
2904
|
-
/** Per-channel codec factory for stateful duplex codecs (e.g. the binary session). */
|
|
2905
|
-
createFormatMessage?: () => TLinkFormatMessage;
|
|
2906
|
-
/**
|
|
2907
|
-
* Optional availability gate. When it returns `false`, this transport reports as `unsupported` for that
|
|
2908
|
-
* action and the manager falls through to the next transport in preference order — without opening the
|
|
2909
|
-
* carrier or computing its cache key. Re-evaluated per dispatch, so a transport can become available
|
|
2910
|
-
* later (e.g. once a session/connection precondition holds) with no reconnect. Omit = always available.
|
|
2911
|
-
*/
|
|
2912
|
-
available?: (input: ITransportRouteActionParams) => boolean;
|
|
2913
|
-
updateRunConfig?: TUpdateActionRunConfig;
|
|
2914
|
-
/** Override the devtools chip label (defaults to the carrier's `carrierLabel`). */
|
|
2915
|
-
label?: string;
|
|
2916
|
-
}
|
|
2917
|
-
/**
|
|
2918
|
-
* The plain (no-handshake, no-crypto) sibling of {@link secureTransport}: swap the carrier to change
|
|
2919
|
-
* protocol, with no security layer. Over an {@link IExchangeCarrierSource} it builds an
|
|
2920
|
-
* {@link ExchangeTransport} that POSTs the bare action wire and completes inline (HTTP is just
|
|
2921
|
-
* `carrier: httpCarrier(...)`); over an {@link IDuplexCarrierSource} it builds a push-capable
|
|
2922
|
-
* {@link LinkTransport} with the given codec. HTTP is therefore no longer a bespoke transport class —
|
|
2923
|
-
* it is "just another carrier", exactly like a WebSocket under {@link secureTransport}.
|
|
2924
|
-
*/
|
|
2925
|
-
declare function plainTransport(options: IPlainTransportOptions & {
|
|
2926
|
-
carrier: IExchangeCarrierSource;
|
|
2927
|
-
}): ExchangeTransport;
|
|
2928
|
-
declare function plainTransport(options: IPlainTransportOptions & {
|
|
2929
|
-
carrier: IDuplexCarrierSource;
|
|
2930
|
-
}): LinkTransport;
|
|
2931
|
-
//#endregion
|
|
2932
2993
|
//#region src/ActionRuntime/Transport/SecureSession/exchangeProtocol.d.ts
|
|
2933
2994
|
/**
|
|
2934
2995
|
* The application-level envelope for secure action traffic over an {@link IExchangeCarrier} (HTTP). An
|
|
@@ -2978,45 +3039,6 @@ declare function encodeExchange(envelope: TExchangeRequest | TExchangeReply): st
|
|
|
2978
3039
|
declare function decodeExchangeRequest(raw: string): TExchangeRequest | undefined;
|
|
2979
3040
|
declare function decodeExchangeReply(raw: string): TExchangeReply | undefined;
|
|
2980
3041
|
//#endregion
|
|
2981
|
-
//#region src/ActionRuntime/Transport/secureTransport.d.ts
|
|
2982
|
-
interface ISecureTransportOptions {
|
|
2983
|
-
/** The shared channel identity (per-connection codec + dictionary version) — same one both ends use. */
|
|
2984
|
-
channel: ISecureChannel;
|
|
2985
|
-
/** This client's runtime — its coordinate is the authenticated identity sent in the handshake. */
|
|
2986
|
-
runtime: ActionRuntime;
|
|
2987
|
-
/** Backing store for this client's crypto identity (a stable verify key across reloads). */
|
|
2988
|
-
storageAdapter: StorageAdapter;
|
|
2989
|
-
/** The level this client requests; the peer must allow it. */
|
|
2990
|
-
securityLevel: ESecurityLevel;
|
|
2991
|
-
/**
|
|
2992
|
-
* Optional availability gate. When it returns `false`, this transport reports as `unsupported` for that
|
|
2993
|
-
* action and the manager falls through to the next transport in preference order — without opening the
|
|
2994
|
-
* carrier or computing its cache key. Re-evaluated per dispatch, so a transport can become available
|
|
2995
|
-
* later (e.g. once a session/connection precondition holds) with no reconnect. Omit = always available.
|
|
2996
|
-
*/
|
|
2997
|
-
available?: (input: ITransportRouteActionParams) => boolean;
|
|
2998
|
-
/**
|
|
2999
|
-
* How to reach the peer. A duplex carrier (`wsCarrier(url)`, `rtcCarrier(dc)`,
|
|
3000
|
-
* `inMemoryCarrier().carrier`) runs the push-capable session; an exchange carrier (`httpCarrier(...)`)
|
|
3001
|
-
* runs the request/reply session over the same handshake + crypto.
|
|
3002
|
-
*/
|
|
3003
|
-
carrier: IDuplexCarrierSource | IExchangeCarrierSource;
|
|
3004
|
-
}
|
|
3005
|
-
/**
|
|
3006
|
-
* The one secure-transport factory — swap the carrier to change protocol. Folds in the boilerplate (the
|
|
3007
|
-
* {@link ClientCryptoKeyLink} from `storageAdapter`, the `security` block from the runtime coordinate +
|
|
3008
|
-
* channel version) and drives it over whatever carrier you pass: a {@link IDuplexCarrierSource} builds a
|
|
3009
|
-
* push-capable {@link LinkTransport} (WS is just `carrier: wsCarrier(url)`), an
|
|
3010
|
-
* {@link IExchangeCarrierSource} builds a request/reply {@link ExchangeTransport} (HTTP, with the same
|
|
3011
|
-
* authentication/encryption). Replaces the old `createSecureWebSocketTransport` / `createSecureLinkTransport`.
|
|
3012
|
-
*/
|
|
3013
|
-
declare function secureTransport(options: ISecureTransportOptions & {
|
|
3014
|
-
carrier: IDuplexCarrierSource;
|
|
3015
|
-
}): LinkTransport;
|
|
3016
|
-
declare function secureTransport(options: ISecureTransportOptions & {
|
|
3017
|
-
carrier: IExchangeCarrierSource;
|
|
3018
|
-
}): ExchangeTransport;
|
|
3019
|
-
//#endregion
|
|
3020
3042
|
//#region src/errors/err_nice_action.d.ts
|
|
3021
3043
|
declare enum EErrId_NiceAction {
|
|
3022
3044
|
not_implemented = "not_implemented",
|
|
@@ -3242,5 +3264,5 @@ interface IActionPayload_Result_JsonObject<DOM extends IActionDomain = IActionDo
|
|
|
3242
3264
|
type TActionPayload_Any_Instance<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = ActionPayload_Request<DOM, ID> | ActionPayload_Result<DOM, ID> | ActionPayload_Progress<DOM, ID>;
|
|
3243
3265
|
type TActionPayload_Any_JsonObject<DOM extends IActionDomain = IActionDomain, ID extends keyof DOM["actionSchema"] & string = keyof DOM["actionSchema"] & string> = IActionPayload_Request_JsonObject<DOM, ID> | IActionPayload_Progress_JsonObject<DOM, ID> | IActionPayload_Result_JsonObject<DOM, ID>;
|
|
3244
3266
|
//#endregion
|
|
3245
|
-
export {
|
|
3246
|
-
//# sourceMappingURL=ActionPayload.types-
|
|
3267
|
+
export { wsAcceptorCarrier as $, ESecurityLevel as $n, actionSchema as $r, TCarrier as $t, encodeExchange as A, ITransportStatusInfo_Base as An, TRunningActionUpdate as Ar, createHibernatableWsServerAdapter as At, IActionFrameCrypto as B, TOnResolveIncomingRequestJson as Bn, IActionDomain as Br, TChannelPushHandlers as Bt, decodeActionFrame as C, IActionTransportResolvers as Cn, ERunningActionFinishedType as Cr, IAcceptorAttachmentStore as Ct, TExchangeRequest as D, ITransportRouteActionParams as Dn, IRunningActionUpdate_Progress as Dr, isExchangeAcceptorCarrier as Dt, TExchangeReply as E, ITransportMethod_SendActionData_Input as En, IRunningActionUpdate_Abort as Er, TAcceptorCarrier as Et, EErrId_NiceTransport as F, IUpdateActionRunConfig_Output as Fn, IRuntimeCoordinateSpecifics as Fr, IAcceptChannelOptions as Ft, IHttpCarrierRequest as G, TTransportCache as Gn, TDomainActionId as Gr, ISecureChannel as Gt, createActionFrameCrypto as H, TOnResolveIncomingResponseJson as Hn, IActionRootDomain as Hr, acceptChannelConnections as Ht, err_nice_transport as I, TGetTransportFn as In, IRuntimeFullCoordinates as Ir, IActionChannel as It, IHttpAcceptorCarrierOptions as J, TTransportStatusInfo_GetTransport_Output as Jn, TPossibleDomainId as Jr, createBinaryWireSessionFactory as Jt, TCarrierFetch as K, TTransportInitializationFinishedInfo as Kn, TInferInputFromSchema as Kr, defineSecureChannel as Kt, ExchangeTransport as L, TOnResolveAnyIncomingActionData as Ln, RuntimeCoordinate as Lr, IConnectChannelOptions as Lt, LinkTransport as M, ITransportStatusInfo_Initializing as Mn, TRunningActionUpdateListener as Mr, IConnectionAttachment as Mt, IActionTransportReadyData_Link as N, ITransportStatusInfo_Ready as Nn, ActionPayload_Result as Nr, IConnectionStateStoreOptions as Nt, decodeExchangeReply as O, ITransportRouteClientParams as On, IRunningActionUpdate_Started as Or, IDuplexConnectionRouter as Ot, TLinkFormatMessage as P, ITransportStatusInfo_Unsupported as Pn, IRuntimeCoordinate as Pr, createConnectionStateStore as Pt, IWsAcceptorCarrierOptions as Q, EHandshakeMessageType as Qn, TInferActionError as Qr, IExchangeCarrierSource as Qt, IExchangeTransportOptions as R, TOnResolveAnyIncomingActionData_Json as Rn, TRuntimeCoordinateEnvId as Rr, IConnectTransport as Rt, IActionFrameDecoder as S, IActionTransportReadyData_Methods as Sn, IActionCore_JsonObject as Sr, serveChannel as St, err_nice_action as T, ITransportDispatchAction as Tn, ERunningActionUpdateType as Tr, IExchangeAcceptorCarrier as Tt, createBinaryWireAdapter as U, TSendActionDataMethod as Un, TActionDomainChildDef as Ur, connectChannel as Ut, IActionFrameCryptoConfig as V, TOnResolveIncomingResponse as Vn, IActionDomainChildOptions as Vr, acceptChannel as Vt, IHttpCarrierOptions as W, TSendReturnDataMethod as Wn, TActionDomainSchema as Wr, defineChannel as Wt, IWsCarrierOptions as X, ITransportConnectionContext as Xn, ActionSchema as Xr, IDuplexCarrierSource as Xt, httpAcceptorCarrier as Y, TUpdateActionRunConfig as Yn, TPossibleDomainIdList as Yr, IDuplexCarrier as Yt, wsCarrier as Z, Transport as Zn, EActionResponseMode as Zr, IExchangeCarrier as Zt, TActionProgress as _, ETransportStatus as _n, MaybePromise as _r, IExchangeAcceptorConfig as _t, IActionPayload_Data_Base as a, TActionChannelFormatMessage as an, IServerHandshakeConfig as ar, rtcDataChannelByteChannel as at, isActionPayload_Request_JsonObject as b, IActionTransportReady as bn, ActionPayload_Request as br, IServeChannelOptions as bt, IActionPayload_Request_JsonObject as c, IActionWireFormat as cn, createInMemoryTofuVerifyKeyResolver as cr, IInMemoryChannelPair as ct, IActionProgress_Custom as d, ActionDomain as dn, decodeHandshakeMessage as dr, err_nice_external_client as dt, TActionSchemaOptions as ei, TFrame$1 as en, IClientHandshakeConfig as er, EErrId_NiceTransport_WebSocket as et, IActionProgress_None as f, ActionRootDomain as fn, encodeHandshakeMessage as fr, ISecureAcceptorHandlerOptions as ft, TActionPayload_Any_JsonObject as g, ETransportShape as gn, createLocalHandler as gr, ExchangeAcceptor as gt, TActionPayload_Any_Instance as h, createConnectorHandler as hn, ActionLocalHandler as hr, createActionFetchHandler as ht, IActionPayload_Base_JsonObject as i, TAcceptorConnectionCaseFn as in, IHandshakeResult as ir, IRtcDataChannelLike as it, ILinkTransportOptions as j, ITransportStatusInfo_Failed as jn, TRunningActionUpdateFinished as jr, ConnectionStateStore as jt, decodeExchangeRequest as k, ITransportRouteInfo as kn, IRunningActionUpdate_Success as kr, IHibernatableWsServerAdapterOptions as kt, IActionPayload_Result as l, createActionRootDomain as ln, createServerHandshake as lr, IInMemoryServerEndpoint as lt, IActionRouteItemHandler as m, ConnectorHandler as mn, PeerLinkHandler as mr, IActionFetchHandlerOptions as mt, EActionProgressType as n, TTransportedValue as ni, IAcceptorConnectionBinding as nn, IClientVerifyKeyResolver as nr, IRtcCarrierOptions as nt, IActionPayload_Progress as o, TActionConnectionEncoding as on, THandshakeMessage as or, IInMemoryCarrier as ot, IActionProgress_Percentage as p, ActionRuntime as pn, runtimeLinkId as pr, createSecureAcceptorHandler as pt, httpCarrier as q, TTransportStatusInfo as qn, TInferOutputFromSchema as qr, IBinaryWireSessionOptions as qt, IActionPayload_Base as r, IAcceptorHandlerOptions as rn, IHandshakeEncryptionKeyMaterial as rr, rtcCarrier as rt, IActionPayload_Progress_JsonObject as s, createAcceptorHandler as sn, createClientHandshake as sr, inMemoryCarrier as st, EActionPayloadType as t, TActionSerializationDefinition as ti, AcceptorHandler as tn, IClientVerifyKeyResolveInput as tr, err_nice_transport_ws as tt, IActionPayload_Result_JsonObject as u, ActionCore as un, createStorageTofuVerifyKeyResolver as ur, createInMemoryChannelPair as ut, TActionResultOutcome as v, IActionTransportDef as vn, RunningAction as vr, IExchangeAcceptorSecurity as vt, EErrId_NiceAction as w, ISecureClientConfig as wn, ERunningActionState as wr, IDuplexAcceptorCarrier as wt, isActionPayload_Any_JsonObject as x, IActionTransportReadyData_Base as xn, IActionCore as xr, IServeConnectionStateOptions as xt, isActionPayload_Result_JsonObject as y, IActionTransportInitialized as yn, ActionPayload_Progress as yr, IChannelServer as yt, IActionTransportReadyData_Exchange as z, TOnResolveIncomingRequest as zn, TRuntimeCoordinateStringId as zr, TChannelAcceptorCases as zt };
|
|
3268
|
+
//# sourceMappingURL=ActionPayload.types-DXGiw1SF.d.mts.map
|