@net-mesh/core 0.19.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 ADDED
@@ -0,0 +1,45 @@
1
+ # @net-mesh/core
2
+
3
+ napi-rs binding to the `libnet` cdylib — the Node-facing surface for the Net mesh, RedEX, CortEX, NetDB, MeshDB, and MeshOS.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @net-mesh/core
9
+ ```
10
+
11
+ The package publishes pre-built `.node` artifacts for every platform listed in `package.json -> napi.targets`. Pulling the package from npm requires no Rust toolchain.
12
+
13
+ ## Build from source
14
+
15
+ ```bash
16
+ npm install
17
+ npm run build # release; full feature set
18
+ npm run build:debug # debug; full feature set
19
+ ```
20
+
21
+ The canonical feature list lives in `package.json -> scripts.build` so CI artifacts and local builds stay aligned.
22
+
23
+ ## Cargo features
24
+
25
+ The five feature flags that gate the storage / query / OS surfaces on this binding. Artifacts published to npm ship with every feature enabled; `napi build` invocations without a feature silently omit its symbols and the build never warns. The TypeScript wrapper destructures the napi exports lazily, so a missing feature surfaces as `undefined` at the import site rather than a load-time error.
26
+
27
+ | Feature | Surface enabled in the napi module |
28
+ |---|---|
29
+ | `cortex` | `Redex`, `RedexFile`, `TasksAdapter`, `MemoriesAdapter`, `NetDb`, `Task`, `Memory`, watch iterators, `RedexError`, `CortexError`, `NetDbError` |
30
+ | `redex-disk` | Disk-backed RedEX persistence — the `persistentDir` ctor option and `persistent: true` on `openFile`. Without it the persistent path rejects with `RedexError`. |
31
+ | `netdb` | `NetDb` composition (requires `cortex`); the `net_netdb_*` FFI entry points ship with this feature. |
32
+ | `meshdb` | `MeshQuery`, `MeshQueryRunner`, `QueryBuilder`, `Predicate`, `InMemoryChainReader`, plus the `libnet_meshdb` cdylib. |
33
+ | `meshos` | `MeshOsDaemonSdk`, `MeshOsDaemonHandle`, plus the `libnet_meshos` cdylib. |
34
+
35
+ Enable at build time:
36
+
37
+ ```bash
38
+ napi build --platform --release --features "cortex netdb redex-disk meshdb meshos"
39
+ ```
40
+
41
+ The repo's `npm run build` script already passes a superset of these flags (`redis,net,cortex,compute,groups,meshos,deck,meshdb`) — see `package.json -> scripts.build` for the exact invocation. Slim the list by editing that script or invoking `napi build` directly with the features you actually need.
42
+
43
+ ## License
44
+
45
+ Apache-2.0
package/errors.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ export declare class CortexError extends Error {
2
+ constructor(detail?: string);
3
+ }
4
+ export declare class NetDbError extends Error {
5
+ constructor(detail?: string);
6
+ }
7
+ export declare class RpcError extends Error {
8
+ constructor(detail?: string);
9
+ }
10
+ export declare class RpcNoRouteError extends RpcError {
11
+ constructor(detail?: string);
12
+ }
13
+ export declare class RpcTimeoutError extends RpcError {
14
+ /** Caller-side elapsed time, parsed out of `elapsed_ms=N` in the message. */
15
+ readonly elapsedMs?: number;
16
+ constructor(detail?: string);
17
+ }
18
+ export declare class RpcServerError extends RpcError {
19
+ /** Wire-level RpcStatus parsed out of `status=0xNNNN` in the message. */
20
+ readonly status?: number;
21
+ constructor(detail?: string);
22
+ }
23
+ export declare class RpcTransportError extends RpcError {
24
+ constructor(detail?: string);
25
+ }
26
+ export declare class RpcCodecError extends RpcError {
27
+ /** Which side of the call surfaced the codec failure. */
28
+ readonly direction?: 'encode' | 'decode';
29
+ constructor(detail?: string, direction?: 'encode' | 'decode');
30
+ }
31
+ /**
32
+ * Caller-driven cancellation. Raised when an in-flight unary
33
+ * call is aborted via `MeshRpc.cancelCall(token)` or via an
34
+ * AbortSignal attached through the typed wrapper's `opts.signal`.
35
+ * CANCEL has been published to the server; the server-side
36
+ * handler observes its `ctx.cancellation` token. Caller-fixable
37
+ * / terminal — NOT retried by the default retry policy.
38
+ */
39
+ export declare class RpcCancelledError extends RpcError {
40
+ constructor(detail?: string);
41
+ }
42
+ /**
43
+ * Inspect an error's message prefix and return a typed error if it
44
+ * matches the napi binding's contract. Non-matching errors are
45
+ * returned unchanged — caller can `throw` the result unconditionally.
46
+ *
47
+ * Duck-typed on `e.message`: napi rejections are real Error
48
+ * instances, but top-level catch handlers may also see arbitrary
49
+ * non-Error values (a thrown string, a plain `{message}` object).
50
+ * We mirror the JS contract — accept anything with a string
51
+ * `message` field — so cross-runtime catch sites don't lose
52
+ * classification just because the throw happened in a context
53
+ * where `instanceof Error` is unreliable (vm boundaries, polyfills).
54
+ */
55
+ export declare function classifyError(e: unknown): unknown;
56
+ /**
57
+ * Pull a `message` field off any value, with the same permissive
58
+ * semantics as `(e && e.message) || ''` from the JS source. Used
59
+ * by `classifyError` and by `defaultRetryable` in
60
+ * `@net-mesh/core/mesh_rpc` to keep the catch-site contract uniform.
61
+ */
62
+ export declare function extractMessage(e: unknown): string;
package/errors.js ADDED
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ // Typed error classes for CortEX / NetDB / nRPC operations.
3
+ //
4
+ // The napi binding throws plain `Error` objects with stable prefixes
5
+ // (`cortex:` / `netdb:` / `nrpc:`) that `classifyError()` inspects to
6
+ // re-throw a typed error. Catch with `instanceof`:
7
+ //
8
+ // import { NetDb } from '@net-mesh/core';
9
+ // import { CortexError, classifyError } from '@net-mesh/core/errors';
10
+ //
11
+ // try {
12
+ // db.tasks.create(1n, 'x', 100n);
13
+ // } catch (e) {
14
+ // throw classifyError(e); // CortexError / NetDbError / original
15
+ // }
16
+ //
17
+ // Prefixes mirror `ERR_*_PREFIX` in `bindings/node/src/cortex.rs`
18
+ // and `bindings/node/src/mesh_rpc.rs`. Keep the strings in lockstep.
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.RpcCancelledError = exports.RpcCodecError = exports.RpcTransportError = exports.RpcServerError = exports.RpcTimeoutError = exports.RpcNoRouteError = exports.RpcError = exports.NetDbError = exports.CortexError = void 0;
21
+ exports.classifyError = classifyError;
22
+ exports.extractMessage = extractMessage;
23
+ const ERR_CORTEX_PREFIX = 'cortex:';
24
+ const ERR_NETDB_PREFIX = 'netdb:';
25
+ const ERR_NRPC_PREFIX = 'nrpc:';
26
+ class CortexError extends Error {
27
+ constructor(detail) {
28
+ super(detail ?? 'cortex adapter error');
29
+ this.name = 'CortexError';
30
+ Object.setPrototypeOf(this, CortexError.prototype);
31
+ }
32
+ }
33
+ exports.CortexError = CortexError;
34
+ class NetDbError extends Error {
35
+ constructor(detail) {
36
+ super(detail ?? 'netdb error');
37
+ this.name = 'NetDbError';
38
+ Object.setPrototypeOf(this, NetDbError.prototype);
39
+ }
40
+ }
41
+ exports.NetDbError = NetDbError;
42
+ // nRPC error hierarchy. Mirrors net::adapter::net::mesh_rpc::RpcError;
43
+ // the napi binding's mesh_rpc.rs::nrpc_err_from_inner emits each
44
+ // variant under a stable kind segment after the `nrpc:` prefix:
45
+ //
46
+ // nrpc:no_route -> RpcNoRouteError
47
+ // nrpc:timeout -> RpcTimeoutError
48
+ // nrpc:server_error -> RpcServerError
49
+ // nrpc:transport -> RpcTransportError
50
+ // nrpc:codec_encode -> RpcCodecError(direction='encode')
51
+ // nrpc:codec_decode -> RpcCodecError(direction='decode')
52
+ // nrpc:cancelled -> RpcCancelledError
53
+ // nrpc:* (anything else) -> RpcError (the base class)
54
+ //
55
+ // Catch with `instanceof RpcError` for "any nRPC failure", or
56
+ // drill down to a concrete subclass for specific handling. The
57
+ // default retry / circuit-breaker policies in @net-mesh/core/mesh_rpc
58
+ // skip RpcCodecError (caller-fixable local bug) by default — same
59
+ // behavior as the Rust SDK's default_retryable predicate.
60
+ class RpcError extends Error {
61
+ constructor(detail) {
62
+ super(detail ?? 'rpc error');
63
+ this.name = 'RpcError';
64
+ Object.setPrototypeOf(this, RpcError.prototype);
65
+ }
66
+ }
67
+ exports.RpcError = RpcError;
68
+ class RpcNoRouteError extends RpcError {
69
+ constructor(detail) {
70
+ super(detail ?? 'no route to target');
71
+ this.name = 'RpcNoRouteError';
72
+ Object.setPrototypeOf(this, RpcNoRouteError.prototype);
73
+ }
74
+ }
75
+ exports.RpcNoRouteError = RpcNoRouteError;
76
+ class RpcTimeoutError extends RpcError {
77
+ constructor(detail) {
78
+ super(detail ?? 'rpc timeout');
79
+ this.name = 'RpcTimeoutError';
80
+ Object.setPrototypeOf(this, RpcTimeoutError.prototype);
81
+ // Best-effort parse of `elapsed_ms=N` so callers can read
82
+ // `err.elapsedMs` without re-parsing the message.
83
+ const m = /elapsed_ms=(\d+)/.exec(detail ?? '');
84
+ if (m)
85
+ this.elapsedMs = Number(m[1]);
86
+ }
87
+ }
88
+ exports.RpcTimeoutError = RpcTimeoutError;
89
+ class RpcServerError extends RpcError {
90
+ constructor(detail) {
91
+ super(detail ?? 'rpc server error');
92
+ this.name = 'RpcServerError';
93
+ Object.setPrototypeOf(this, RpcServerError.prototype);
94
+ // Parse `status=0xNNNN` so callers can pattern-match by
95
+ // status code (e.g. err.status === 0x8001 → typed-handler
96
+ // application error).
97
+ const m = /status=0x([0-9a-fA-F]+)/.exec(detail ?? '');
98
+ if (m)
99
+ this.status = parseInt(m[1], 16);
100
+ }
101
+ }
102
+ exports.RpcServerError = RpcServerError;
103
+ class RpcTransportError extends RpcError {
104
+ constructor(detail) {
105
+ super(detail ?? 'rpc transport error');
106
+ this.name = 'RpcTransportError';
107
+ Object.setPrototypeOf(this, RpcTransportError.prototype);
108
+ }
109
+ }
110
+ exports.RpcTransportError = RpcTransportError;
111
+ class RpcCodecError extends RpcError {
112
+ /** Which side of the call surfaced the codec failure. */
113
+ direction;
114
+ constructor(detail, direction) {
115
+ super(detail ?? 'rpc codec error');
116
+ this.name = 'RpcCodecError';
117
+ // `direction` is always passed through, so it's a regular
118
+ // assignment (no `declare`-dance needed — `'direction' in err`
119
+ // is `true` regardless of whether a specific value was provided).
120
+ this.direction = direction;
121
+ Object.setPrototypeOf(this, RpcCodecError.prototype);
122
+ }
123
+ }
124
+ exports.RpcCodecError = RpcCodecError;
125
+ /**
126
+ * Caller-driven cancellation. Raised when an in-flight unary
127
+ * call is aborted via `MeshRpc.cancelCall(token)` or via an
128
+ * AbortSignal attached through the typed wrapper's `opts.signal`.
129
+ * CANCEL has been published to the server; the server-side
130
+ * handler observes its `ctx.cancellation` token. Caller-fixable
131
+ * / terminal — NOT retried by the default retry policy.
132
+ */
133
+ class RpcCancelledError extends RpcError {
134
+ constructor(detail) {
135
+ super(detail ?? 'rpc cancelled');
136
+ this.name = 'RpcCancelledError';
137
+ Object.setPrototypeOf(this, RpcCancelledError.prototype);
138
+ }
139
+ }
140
+ exports.RpcCancelledError = RpcCancelledError;
141
+ /**
142
+ * Inspect an error's message prefix and return a typed error if it
143
+ * matches the napi binding's contract. Non-matching errors are
144
+ * returned unchanged — caller can `throw` the result unconditionally.
145
+ *
146
+ * Duck-typed on `e.message`: napi rejections are real Error
147
+ * instances, but top-level catch handlers may also see arbitrary
148
+ * non-Error values (a thrown string, a plain `{message}` object).
149
+ * We mirror the JS contract — accept anything with a string
150
+ * `message` field — so cross-runtime catch sites don't lose
151
+ * classification just because the throw happened in a context
152
+ * where `instanceof Error` is unreliable (vm boundaries, polyfills).
153
+ */
154
+ function classifyError(e) {
155
+ const msg = extractMessage(e);
156
+ if (msg.startsWith(ERR_CORTEX_PREFIX)) {
157
+ return new CortexError(msg);
158
+ }
159
+ if (msg.startsWith(ERR_NETDB_PREFIX)) {
160
+ return new NetDbError(msg);
161
+ }
162
+ if (msg.startsWith(ERR_NRPC_PREFIX)) {
163
+ return classifyRpcError(msg);
164
+ }
165
+ return e;
166
+ }
167
+ /**
168
+ * Pull a `message` field off any value, with the same permissive
169
+ * semantics as `(e && e.message) || ''` from the JS source. Used
170
+ * by `classifyError` and by `defaultRetryable` in
171
+ * `@net-mesh/core/mesh_rpc` to keep the catch-site contract uniform.
172
+ */
173
+ function extractMessage(e) {
174
+ if (e === null || e === undefined)
175
+ return '';
176
+ if (typeof e === 'string')
177
+ return e;
178
+ if (typeof e !== 'object')
179
+ return '';
180
+ const msg = e.message;
181
+ return typeof msg === 'string' ? msg : '';
182
+ }
183
+ function classifyRpcError(msg) {
184
+ // Strip the `nrpc:` prefix; the next segment up to the first
185
+ // `:` is the kind. Examples:
186
+ // nrpc:no_route: target=0x... reason=...
187
+ // nrpc:codec_encode: client encode: ...
188
+ const after = msg.slice(ERR_NRPC_PREFIX.length);
189
+ const colonIdx = after.indexOf(':');
190
+ const kind = colonIdx === -1 ? after : after.slice(0, colonIdx);
191
+ switch (kind) {
192
+ case 'no_route':
193
+ return new RpcNoRouteError(msg);
194
+ case 'timeout':
195
+ return new RpcTimeoutError(msg);
196
+ case 'server_error':
197
+ return new RpcServerError(msg);
198
+ case 'transport':
199
+ return new RpcTransportError(msg);
200
+ case 'codec_encode':
201
+ return new RpcCodecError(msg, 'encode');
202
+ case 'codec_decode':
203
+ return new RpcCodecError(msg, 'decode');
204
+ case 'cancelled':
205
+ return new RpcCancelledError(msg);
206
+ default:
207
+ return new RpcError(msg);
208
+ }
209
+ }
package/mesh_rpc.d.ts ADDED
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Per-call options forwarded to the raw napi `MeshRpc`. Mirrors
3
+ * the napi `CallOptions` struct. Routing policy + trace context
4
+ * land in a later phase.
5
+ */
6
+ export interface CallOptions {
7
+ /** Hard deadline, milliseconds from now. */
8
+ deadlineMs?: number;
9
+ /**
10
+ * Streaming-only: initial credit window for per-streaming-
11
+ * response flow control. `undefined` → unbounded.
12
+ */
13
+ streamWindowInitial?: number;
14
+ /**
15
+ * Caller-driven cancellation. Pass an `AbortSignal`; the typed
16
+ * wrapper attaches a one-shot listener that aborts the in-
17
+ * flight call. The call rejects with `RpcCancelledError`.
18
+ * Recognized by `TypedMeshRpc` only — the raw napi `MeshRpc`
19
+ * uses `cancelToken` directly.
20
+ */
21
+ signal?: AbortSignal;
22
+ /**
23
+ * Raw cancel token (advanced; usually set automatically by the
24
+ * typed wrapper from `signal`). Mint via
25
+ * `MeshRpc.reserveCancelToken()` and pair with
26
+ * `MeshRpc.cancelCall(token)`. Most users should use `signal`
27
+ * instead.
28
+ */
29
+ cancelToken?: bigint;
30
+ /**
31
+ * Caller-supplied request headers, appended to the wire
32
+ * `RpcRequestPayload.headers`. Used for application-level metadata
33
+ * the server needs at dispatch-time — most notably the
34
+ * `net-where:` predicate header for Phase 9b
35
+ * predicate-pushdown filtering.
36
+ *
37
+ * Convenience: build entries via `whereHeader(pred)` from
38
+ * `@net-mesh/sdk` for predicate-pushdown.
39
+ */
40
+ requestHeaders?: RpcRequestHeader[];
41
+ }
42
+ /**
43
+ * Single request-header entry. Names follow the lowercase
44
+ * `cyberdeck-*` / `nrpc-*` convention.
45
+ */
46
+ export interface RpcRequestHeader {
47
+ name: string;
48
+ /**
49
+ * Header value bytes. For text-like headers (predicates,
50
+ * trace-context), encode with `Buffer.from(str)`.
51
+ */
52
+ value: Buffer;
53
+ }
54
+ /**
55
+ * Handle returned by {@link TypedMeshRpc.serve}. Calling `close()`
56
+ * unregisters the service; in-flight handlers continue to
57
+ * completion. Always call `close()` explicitly when done — V8 GC
58
+ * eventually finalizes the underlying napi class but timing is
59
+ * non-deterministic.
60
+ */
61
+ export interface ServeHandle {
62
+ close(): void;
63
+ isClosed(): boolean;
64
+ }
65
+ /**
66
+ * Raw napi `MeshRpc` shape. Test stubs and the runtime-loaded
67
+ * `native.MeshRpc` instance both conform to this interface. Used
68
+ * as the `TypedMeshRpc` constructor parameter type.
69
+ */
70
+ export interface RawMeshRpc {
71
+ serve(service: string, handler: (req: Buffer) => Promise<Buffer>): ServeHandle;
72
+ call(targetNodeId: bigint, service: string, request: Buffer, opts?: CallOptions): Promise<Buffer>;
73
+ callService(service: string, request: Buffer, opts?: CallOptions): Promise<Buffer>;
74
+ callStreaming(targetNodeId: bigint, service: string, request: Buffer, opts?: CallOptions): Promise<RawRpcStream>;
75
+ findServiceNodes(service: string): bigint[];
76
+ /** Mint a fresh cancel token (`bigint`). */
77
+ reserveCancelToken(): bigint;
78
+ /** Abort the in-flight call associated with `token`. Idempotent. */
79
+ cancelCall(token: bigint): void;
80
+ }
81
+ /** Raw napi `RpcStream` — minimal shape consumed by `TypedRpcStream`. */
82
+ export interface RawRpcStream {
83
+ next(): Promise<Buffer | null>;
84
+ grant(n: number): Promise<void>;
85
+ flowControlled(): Promise<boolean>;
86
+ close(): Promise<void>;
87
+ }
88
+ /** Handler signature: receives the decoded request and returns a response. */
89
+ export type TypedHandler<Req = unknown, Resp = unknown> = (req: Req) => Resp | Promise<Resp>;
90
+ export declare class TypedMeshRpc {
91
+ private readonly _raw;
92
+ /**
93
+ * Build a TypedMeshRpc against a NetMesh. Cheap; returns a
94
+ * new wrapper around an internal raw MeshRpc.
95
+ */
96
+ static fromMesh(mesh: object): TypedMeshRpc;
97
+ /**
98
+ * Build a TypedMeshRpc from an already-constructed raw MeshRpc.
99
+ * Useful if you need the raw + typed surface side by side.
100
+ */
101
+ constructor(rawMeshRpc: RawMeshRpc);
102
+ /** Underlying raw `MeshRpc` for users who want the Buffer-level surface. */
103
+ get raw(): RawMeshRpc;
104
+ /**
105
+ * Register a typed handler. The handler receives a decoded
106
+ * `Req` and returns a `Resp` (or a Promise of one). JSON
107
+ * encode/decode happens at the binding boundary; encode
108
+ * failure inside the handler surfaces to the caller as
109
+ * `RpcServerError(status=0x0006 Internal)`.
110
+ */
111
+ serve<Req = unknown, Resp = unknown>(service: string, handler: TypedHandler<Req, Resp>): ServeHandle;
112
+ /**
113
+ * Direct-addressed typed call. Encodes `req` as JSON, calls,
114
+ * decodes the response. Throws an `RpcError` subclass on
115
+ * failure (matched by the napi prefix → JS class mapping in
116
+ * `errors.js`).
117
+ *
118
+ * Pass `opts.signal` (AbortSignal) for caller-driven
119
+ * cancellation. The wrapper mints a cancel token via the raw
120
+ * binding's `reserveCancelToken()`, attaches an abort listener,
121
+ * and lets the abort fire `cancelCall(token)` to drop the in-
122
+ * flight call (CANCEL fires on the wire, the call rejects with
123
+ * `nrpc:cancelled:`).
124
+ */
125
+ call<Req = unknown, Resp = unknown>(targetNodeId: bigint, service: string, req: Req, opts?: CallOptions): Promise<Resp>;
126
+ /**
127
+ * Service-discovery typed call. Resolves `service` against the
128
+ * local capability index, picks a target per the routing
129
+ * policy, calls. Throws an `RpcError` subclass on failure.
130
+ */
131
+ callService<Req = unknown, Resp = unknown>(service: string, req: Req, opts?: CallOptions): Promise<Resp>;
132
+ /**
133
+ * Open a typed streaming call. Returns a `TypedRpcStream` that
134
+ * yields decoded `Resp` values per `next()` until EOF.
135
+ */
136
+ callStreaming<Req = unknown, Resp = unknown>(targetNodeId: bigint, service: string, req: Req, opts?: CallOptions): Promise<TypedRpcStream<Resp>>;
137
+ /** Pass-through to `MeshRpc.findServiceNodes`. */
138
+ findServiceNodes(service: string): bigint[];
139
+ /**
140
+ * Direct-addressed typed call with retry. See {@link RetryPolicy}.
141
+ */
142
+ callWithRetry<Req = unknown, Resp = unknown>(targetNodeId: bigint, service: string, req: Req, opts: CallOptions | undefined, policy: RetryPolicy): Promise<Resp>;
143
+ /**
144
+ * Hedge typed call across the listed targets. First reply
145
+ * (Ok or Err) wins; if every target fails, the surfaced error
146
+ * is the primary's (target index 0) for stable diagnostics.
147
+ */
148
+ callWithHedgeTo<Req = unknown, Resp = unknown>(targets: bigint[], service: string, req: Req, opts: CallOptions | undefined, policy: HedgePolicy): Promise<Resp>;
149
+ }
150
+ export declare class TypedRpcStream<Resp = unknown> implements AsyncIterable<Resp> {
151
+ private readonly _raw;
152
+ private _done;
153
+ constructor(rawRpcStream: RawRpcStream);
154
+ /**
155
+ * Pull the next decoded value. Returns `null` on clean EOF.
156
+ * Throws `RpcCodecError(direction='decode')` if a chunk fails
157
+ * to decode (terminates the stream — the underlying CANCEL is
158
+ * fired by the raw RpcStream's drop).
159
+ */
160
+ next(): Promise<Resp | null>;
161
+ /** Async iterator support: `for await (const chunk of stream) { ... }`. */
162
+ [Symbol.asyncIterator](): AsyncIterator<Resp>;
163
+ /** Grant `n` flow-control credits to the server pump. */
164
+ grant(n: number): Promise<void>;
165
+ /** `true` if the call set `streamWindowInitial`. */
166
+ flowControlled(): Promise<boolean>;
167
+ /** Close the stream; emits CANCEL to the server. Idempotent. */
168
+ close(): Promise<void>;
169
+ }
170
+ export declare function defaultRetryable(err: unknown): boolean;
171
+ export interface RetryPolicyOptions {
172
+ /** Total attempts (NOT additional retries). Default 3. Must be >= 1. */
173
+ maxAttempts?: number;
174
+ /** Backoff before the first retry, in ms. Default 50. */
175
+ initialBackoffMs?: number;
176
+ /** Upper bound on per-attempt backoff (true ceiling, after jitter). Default 1000. */
177
+ maxBackoffMs?: number;
178
+ /** Multiplicative growth factor between attempts. Default 2.0. */
179
+ backoffMultiplier?: number;
180
+ /** Full-half jitter on backoffs. Default true. */
181
+ jitter?: boolean;
182
+ /** Predicate: should this error be retried? Default {@link defaultRetryable}. */
183
+ retryable?: (err: unknown) => boolean;
184
+ }
185
+ /**
186
+ * Retry policy. Defaults: 3 attempts, 50ms→1s exponential backoff,
187
+ * full-half jitter on, retryable predicate matches the Rust SDK's
188
+ * `default_retryable` (skips RpcCodecError, RpcNoRouteError, and
189
+ * non-transient ServerError statuses).
190
+ */
191
+ export declare class RetryPolicy {
192
+ readonly maxAttempts: number;
193
+ readonly initialBackoffMs: number;
194
+ readonly maxBackoffMs: number;
195
+ readonly backoffMultiplier: number;
196
+ readonly jitter: boolean;
197
+ readonly retryable: (err: unknown) => boolean;
198
+ constructor(opts?: RetryPolicyOptions);
199
+ /**
200
+ * Compute the backoff for `attempt` (1-indexed). Caps at
201
+ * `maxBackoffMs` AFTER jitter so the cap is a true ceiling.
202
+ */
203
+ computeBackoffMs(attempt: number): number;
204
+ }
205
+ export interface HedgePolicyOptions {
206
+ /** Wait this long after the primary before firing the first hedge. Default 50ms. */
207
+ delayMs?: number;
208
+ /** Number of hedge requests in addition to the primary. Default 1. */
209
+ hedges?: number;
210
+ }
211
+ /**
212
+ * Hedge policy. Fire-then-race: primary at t=0, hedges at
213
+ * t = delayMs * idx. First reply wins; if every hedge fails,
214
+ * the primary's error is surfaced deterministically.
215
+ */
216
+ export declare class HedgePolicy {
217
+ readonly delayMs: number;
218
+ readonly hedges: number;
219
+ constructor(opts?: HedgePolicyOptions);
220
+ }
221
+ export type BreakerState = 'closed' | 'open' | 'half-open';
222
+ export declare function defaultBreakerFailure(err: unknown): boolean;
223
+ export declare class BreakerOpenError extends Error {
224
+ constructor();
225
+ }
226
+ export interface CircuitBreakerOptions {
227
+ /** Consecutive failures before tripping. Default 5. */
228
+ failureThreshold?: number;
229
+ /** Cooldown before transitioning Open → HalfOpen. Default 30000. */
230
+ resetAfterMs?: number;
231
+ /** Successful probes needed to close from HalfOpen. Default 1. */
232
+ successThreshold?: number;
233
+ /** Predicate: does this error count as a failure? Default {@link defaultBreakerFailure}. */
234
+ failurePredicate?: (err: unknown) => boolean;
235
+ }
236
+ /**
237
+ * Three-state circuit breaker. Long-lived; instantiate once per
238
+ * logical downstream and share. `breaker.call(() => ...)`
239
+ * composes around any async op.
240
+ */
241
+ export declare class CircuitBreaker {
242
+ readonly failureThreshold: number;
243
+ readonly resetAfterMs: number;
244
+ readonly successThreshold: number;
245
+ readonly failurePredicate: (err: unknown) => boolean;
246
+ private _state;
247
+ private _consecutiveFailures;
248
+ private _consecutiveSuccesses;
249
+ private _openedAt;
250
+ private _probeInFlight;
251
+ constructor(opts?: CircuitBreakerOptions);
252
+ state(): BreakerState;
253
+ consecutiveFailures(): number;
254
+ reset(): void;
255
+ /**
256
+ * Wrap an async op. Returns its value on success, throws on
257
+ * failure (or on rejection). When the breaker is `Open` within
258
+ * its cooldown, throws `BreakerOpenError` without invoking
259
+ * `op`.
260
+ *
261
+ * Both success and failure paths record the outcome through
262
+ * `_recordOutcome`, which is responsible for clearing
263
+ * `_probeInFlight` on the half-open-probe admission. A
264
+ * synchronous throw inside `op` (rare — `await` is always at
265
+ * `op`'s call site) is still routed through the catch arm.
266
+ */
267
+ call<T>(op: () => Promise<T>): Promise<T>;
268
+ private _tryAdmit;
269
+ private _recordOutcome;
270
+ }
271
+ /**
272
+ * Build an Error a typed serve handler can throw to surface a
273
+ * specific application status code to the caller. The Rust
274
+ * binding parses messages of the form
275
+ * `nrpc:app_error:0x<code>:<body>` and maps them to
276
+ * `RpcStatus::Application(code)` — without this prefix the
277
+ * thrown error becomes a generic `RpcStatus::Internal`. Mirrors
278
+ * the Python binding's `RpcAppError(code, body)`.
279
+ *
280
+ * Use cases: typed handlers that want to return 4xx-style
281
+ * application errors (`NRPC_TYPED_BAD_REQUEST`,
282
+ * `NRPC_TYPED_HANDLER_ERROR`, custom app codes >= 0x8000).
283
+ *
284
+ * @example
285
+ * rpc.serve('echo', (req) => {
286
+ * if (typeof req.text !== 'string') {
287
+ * throw appError(NRPC_TYPED_BAD_REQUEST,
288
+ * JSON.stringify({error: 'missing text'}))
289
+ * }
290
+ * return { echo: req.text }
291
+ * })
292
+ */
293
+ export declare function appError(code: number, body: string | Buffer): Error;
294
+ /** RpcStatus::Application(0x8000): typed handler bad-request body. */
295
+ export declare const NRPC_TYPED_BAD_REQUEST: 32768;
296
+ /** RpcStatus::Application(0x8001): typed handler returned `throw`. */
297
+ export declare const NRPC_TYPED_HANDLER_ERROR: 32769;