@macula-io/ts 0.13.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/LICENSE +201 -0
- package/README.md +343 -0
- package/dist/binding.d.ts +47 -0
- package/dist/binding.js +133 -0
- package/dist/binding.js.map +1 -0
- package/dist/content.d.ts +14 -0
- package/dist/content.js +45 -0
- package/dist/content.js.map +1 -0
- package/dist/dht.d.ts +54 -0
- package/dist/dht.js +25 -0
- package/dist/dht.js.map +1 -0
- package/dist/directdial.d.ts +79 -0
- package/dist/directdial.js +76 -0
- package/dist/directdial.js.map +1 -0
- package/dist/identity.d.ts +43 -0
- package/dist/identity.js +83 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/pubsub.d.ts +55 -0
- package/dist/pubsub.js +8 -0
- package/dist/pubsub.js.map +1 -0
- package/dist/rpc.d.ts +69 -0
- package/dist/rpc.js +47 -0
- package/dist/rpc.js.map +1 -0
- package/dist/session.d.ts +453 -0
- package/dist/session.js +807 -0
- package/dist/session.js.map +1 -0
- package/dist/ucan.d.ts +108 -0
- package/dist/ucan.js +142 -0
- package/dist/ucan.js.map +1 -0
- package/package.json +57 -0
- package/prebuilds/darwin-arm64/@macula-io+ts.node +0 -0
- package/prebuilds/darwin-x64/@macula-io+ts.node +0 -0
- package/prebuilds/linux-arm64/@macula-io+ts.node +0 -0
- package/prebuilds/linux-x64/@macula-io+ts.node +0 -0
- package/prebuilds/win32-x64/@macula-io+ts.node +0 -0
package/dist/pubsub.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { JsonValue } from "./rpc.js";
|
|
2
|
+
/** Options for Session.publish(). */
|
|
3
|
+
export interface PublishOptions {
|
|
4
|
+
/** How many milliseconds from now this event should live -- a
|
|
5
|
+
* DURATION, not a timestamp (matches every other ttlMs in this SDK,
|
|
6
|
+
* e.g. dht.ts's DHT_DEFAULT_TTL_MS) -- PublishSpec's own `ttl_ms`
|
|
7
|
+
* (frame/pubsub.go) is computed from this. Omitted means no TTL, not
|
|
8
|
+
* zero or an invented default -- macula-go's Publish has no fallback
|
|
9
|
+
* of its own for this field, unlike the DHT puts' TTL. */
|
|
10
|
+
ttlMs?: number;
|
|
11
|
+
/** The realm this PUBLISH is scoped to, as a 64-character hex string
|
|
12
|
+
* (32 bytes) -- see session.ts's CallOptions.realm for the full
|
|
13
|
+
* convention this shares. Omitted means the all-zero realm. Realm is
|
|
14
|
+
* an exact-match routing key: a subscribe() only ever receives this
|
|
15
|
+
* event if its own realm matches exactly, not a prefix or default
|
|
16
|
+
* fallback. */
|
|
17
|
+
realm?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Options for Session.subscribe(). */
|
|
20
|
+
export interface SubscribeOptions {
|
|
21
|
+
/** The realm this SUBSCRIBE listens on -- see PublishOptions.realm's
|
|
22
|
+
* own doc for the format and exact-match semantics. Omitted means the
|
|
23
|
+
* all-zero realm. Must match the realm a publisher actually used, or
|
|
24
|
+
* nothing published under a different realm is ever delivered here. */
|
|
25
|
+
realm?: string;
|
|
26
|
+
/** Called at most once, only if this subscription's background
|
|
27
|
+
* reader exits on its own -- the underlying session/connection died,
|
|
28
|
+
* or some other transport error ended the read loop -- rather than
|
|
29
|
+
* via the stop() subscribe() returned being called. See
|
|
30
|
+
* Session.subscribe()'s own doc for the full story (a real bug this
|
|
31
|
+
* SDK had and fixed: without this signal, such a subscription went
|
|
32
|
+
* silent forever and left the Session unable to close cleanly).
|
|
33
|
+
* Optional: even without a handler here, the subscription still
|
|
34
|
+
* tears itself down automatically and correctly the moment this
|
|
35
|
+
* happens -- this is purely a notification hook for a caller who
|
|
36
|
+
* wants to know why events stopped arriving. */
|
|
37
|
+
onClosed?: (error: Error) => void;
|
|
38
|
+
}
|
|
39
|
+
/** One delivered EVENT -- macula-go's frame.EventInfo, minus `Realm`
|
|
40
|
+
* (the delivering EVENT's own realm is not surfaced back per-event here
|
|
41
|
+
* -- a caller already knows it, since subscribe()'s own `realm` option
|
|
42
|
+
* is what selected which realm's events reach this handler at all) and
|
|
43
|
+
* `DeliveredVia` (a routing/telemetry detail, not part of this SDK's
|
|
44
|
+
* scope yet). `payload`
|
|
45
|
+
* follows rpc.ts's JsonValue rules exactly like a CALL payload does --
|
|
46
|
+
* no boolean, embedded bytes as "0x"-prefixed hex (see cabi/wirevalue.go).
|
|
47
|
+
* `publisher` is the raw 32-byte Ed25519 public key of whoever published
|
|
48
|
+
* this event -- NOT verified against the frame's own signature by this
|
|
49
|
+
* SDK, matching findRecord/findRecords/findRecordsByType's identical
|
|
50
|
+
* "does not verify" posture (dht.ts). */
|
|
51
|
+
export interface PubsubEvent {
|
|
52
|
+
readonly payload: JsonValue;
|
|
53
|
+
readonly publisher: Uint8Array;
|
|
54
|
+
readonly seq: number;
|
|
55
|
+
}
|
package/dist/pubsub.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Pubsub shapes -- macula-go's connection.Session.Publish/Subscribe/
|
|
2
|
+
// Unsubscribe and frame.EventInfo (frame/pubsub.go, connection/
|
|
3
|
+
// connection.go, connection/subscriber.go). Session (session.ts) is the
|
|
4
|
+
// actual FFI entry point (publish()/subscribe()), matching call()/
|
|
5
|
+
// serve()/the DHT methods' own shape -- this file holds the shapes that
|
|
6
|
+
// side needs, the same split rpc.ts/dht.ts already have.
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=pubsub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pubsub.js","sourceRoot":"","sources":["../src/pubsub.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gEAAgE;AAChE,wEAAwE;AACxE,mEAAmE;AACnE,wEAAwE;AACxE,yDAAyD"}
|
package/dist/rpc.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** A CALL/RESULT payload, restricted to what macula's wire CBOR can
|
|
2
|
+
* actually represent (cbor.Value's Kind enum: UInt/NegInt/Bytes/Text/
|
|
3
|
+
* List/Map/Null/Float -- no bool, no undefined). There is deliberately
|
|
4
|
+
* no `boolean` in this union: encode true/false as 1/0 yourself, the
|
|
5
|
+
* same rule this project's macula MCP server and macula-cli's own
|
|
6
|
+
* wirevalue package both enforce -- a JS boolean silently reaching the
|
|
7
|
+
* wire is exactly the mistake this type exists to make impossible at
|
|
8
|
+
* compile time.
|
|
9
|
+
*
|
|
10
|
+
* Bytes have no native JSON shape either: represent them as a
|
|
11
|
+
* "0x"-prefixed hex string, cabi's own convention (ported from
|
|
12
|
+
* macula-cli's wirevalue package, see cabi/wirevalue.go) -- a payload
|
|
13
|
+
* or reply containing raw bytes round-trips through call()/serve()
|
|
14
|
+
* unchanged as long as both sides agree on that convention. */
|
|
15
|
+
export type JsonValue = string | number | null | JsonValue[] | {
|
|
16
|
+
[key: string]: JsonValue;
|
|
17
|
+
};
|
|
18
|
+
/** The BOLT#4 fields a failed CALL carries -- see bolt4/bolt4.go's own
|
|
19
|
+
* 17-code table (UnknownNextPeer, TemporaryRelayFailure, Unauthorized,
|
|
20
|
+
* ...). `retryable` is bolt4.Code.IsRetryable()'s verdict, computed
|
|
21
|
+
* Go-side from `code` (cabi/rpc.go), not re-derived here. */
|
|
22
|
+
export interface Bolt4ErrorInfo {
|
|
23
|
+
readonly code: number;
|
|
24
|
+
readonly name: string;
|
|
25
|
+
readonly retryable: boolean;
|
|
26
|
+
readonly detail: string | null;
|
|
27
|
+
}
|
|
28
|
+
/** Thrown by Session.call() when the provider (or a relay in between)
|
|
29
|
+
* answered with a real BOLT#4 ERROR frame instead of a RESULT -- e.g.
|
|
30
|
+
* calling a procedure nobody has advertised comes back
|
|
31
|
+
* unknown_next_peer; a provider handler that threw comes back
|
|
32
|
+
* unknown_error (macula-go's connection/serve.go maps every handler
|
|
33
|
+
* error to that one code, matching macula_station_link.erl's own
|
|
34
|
+
* handle_inbound_call/2); a provider handler that panicked (recovered)
|
|
35
|
+
* comes back temporary_relay_failure. Distinct from a plain
|
|
36
|
+
* Error/rejection out of call() itself, which means this CALL never
|
|
37
|
+
* got a wire-level answer at all (a local timeout, a dead connection,
|
|
38
|
+
* a payload macula's CBOR can't represent). */
|
|
39
|
+
export declare class MaculaCallError extends Error {
|
|
40
|
+
readonly code: number;
|
|
41
|
+
readonly bolt4Name: string;
|
|
42
|
+
readonly retryable: boolean;
|
|
43
|
+
readonly detail: string | null;
|
|
44
|
+
constructor(info: Bolt4ErrorInfo);
|
|
45
|
+
}
|
|
46
|
+
/** The JSON envelope cabi/rpc.go's macula_session_call returns --
|
|
47
|
+
* internal to the FFI boundary, not part of the public API. Kept in
|
|
48
|
+
* sync BY HAND with cabi/rpc.go's callEnvelope/callEnvelopeError Go
|
|
49
|
+
* structs; there is no shared schema generating either side. */
|
|
50
|
+
export type CallEnvelope = {
|
|
51
|
+
ok: true;
|
|
52
|
+
payload: JsonValue;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
bolt4: Bolt4ErrorInfo;
|
|
56
|
+
};
|
|
57
|
+
/** How long Session.call() waits for a RESULT/ERROR before giving up,
|
|
58
|
+
* in milliseconds -- also becomes the wire's own `deadline_ms` (now +
|
|
59
|
+
* this), matching macula-go's own examples/quickstart/main.go, which
|
|
60
|
+
* derives both from one duration rather than treating the local wait
|
|
61
|
+
* and the wire deadline as independent numbers. */
|
|
62
|
+
export declare const DEFAULT_CALL_TIMEOUT_MS = 30000;
|
|
63
|
+
/** How long one Session.serve() poll tick blocks waiting for the next
|
|
64
|
+
* inbound CALL before checking whether stop() was requested --
|
|
65
|
+
* mirrors macula-go's own servePollInterval (connection/serve_loop.go),
|
|
66
|
+
* the exact tick length its ServeForever uses internally for the same
|
|
67
|
+
* "poll with a bounded per-tick wait, check for cancellation between
|
|
68
|
+
* ticks" shape. */
|
|
69
|
+
export declare const SERVE_POLL_MS = 2000;
|
package/dist/rpc.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Unary RPC -- the caller and provider roles built on top of a
|
|
2
|
+
// handshaked Session's control stream, via macula-go's own
|
|
3
|
+
// Session.Call (caller) and Session.Advertise + Session.ServeOneCall
|
|
4
|
+
// (provider) -- see connection/connection.go and connection/serve.go.
|
|
5
|
+
// The actual FFI plumbing lives on Session (session.ts); this file
|
|
6
|
+
// holds the shapes both directions share: the JSON-only payload model
|
|
7
|
+
// and the structured BOLT#4 error macula-go's own bolt4 package
|
|
8
|
+
// defines (bolt4/bolt4.go, 17 codes).
|
|
9
|
+
/** Thrown by Session.call() when the provider (or a relay in between)
|
|
10
|
+
* answered with a real BOLT#4 ERROR frame instead of a RESULT -- e.g.
|
|
11
|
+
* calling a procedure nobody has advertised comes back
|
|
12
|
+
* unknown_next_peer; a provider handler that threw comes back
|
|
13
|
+
* unknown_error (macula-go's connection/serve.go maps every handler
|
|
14
|
+
* error to that one code, matching macula_station_link.erl's own
|
|
15
|
+
* handle_inbound_call/2); a provider handler that panicked (recovered)
|
|
16
|
+
* comes back temporary_relay_failure. Distinct from a plain
|
|
17
|
+
* Error/rejection out of call() itself, which means this CALL never
|
|
18
|
+
* got a wire-level answer at all (a local timeout, a dead connection,
|
|
19
|
+
* a payload macula's CBOR can't represent). */
|
|
20
|
+
export class MaculaCallError extends Error {
|
|
21
|
+
code;
|
|
22
|
+
bolt4Name;
|
|
23
|
+
retryable;
|
|
24
|
+
detail;
|
|
25
|
+
constructor(info) {
|
|
26
|
+
super(`macula-ts: CALL failed: ${info.name} (bolt4 code ${info.code})${info.detail ? `: ${info.detail}` : ""}`);
|
|
27
|
+
this.name = "MaculaCallError";
|
|
28
|
+
this.code = info.code;
|
|
29
|
+
this.bolt4Name = info.name;
|
|
30
|
+
this.retryable = info.retryable;
|
|
31
|
+
this.detail = info.detail;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** How long Session.call() waits for a RESULT/ERROR before giving up,
|
|
35
|
+
* in milliseconds -- also becomes the wire's own `deadline_ms` (now +
|
|
36
|
+
* this), matching macula-go's own examples/quickstart/main.go, which
|
|
37
|
+
* derives both from one duration rather than treating the local wait
|
|
38
|
+
* and the wire deadline as independent numbers. */
|
|
39
|
+
export const DEFAULT_CALL_TIMEOUT_MS = 30_000;
|
|
40
|
+
/** How long one Session.serve() poll tick blocks waiting for the next
|
|
41
|
+
* inbound CALL before checking whether stop() was requested --
|
|
42
|
+
* mirrors macula-go's own servePollInterval (connection/serve_loop.go),
|
|
43
|
+
* the exact tick length its ServeForever uses internally for the same
|
|
44
|
+
* "poll with a bounded per-tick wait, check for cancellation between
|
|
45
|
+
* ticks" shape. */
|
|
46
|
+
export const SERVE_POLL_MS = 2_000;
|
|
47
|
+
//# sourceMappingURL=rpc.js.map
|
package/dist/rpc.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,2DAA2D;AAC3D,qEAAqE;AACrE,sEAAsE;AACtE,mEAAmE;AACnE,sEAAsE;AACtE,gEAAgE;AAChE,sCAAsC;AA6BtC;;;;;;;;;;+CAU+C;AAC/C,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAS;IACb,SAAS,CAAS;IAClB,SAAS,CAAU;IACnB,MAAM,CAAgB;IAE/B,YAAY,IAAoB;QAC9B,KAAK,CAAC,2BAA2B,IAAI,CAAC,IAAI,gBAAgB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChH,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;CACF;AAQD;;;;mDAImD;AACnD,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AAE9C;;;;;mBAKmB;AACnB,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC"}
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
import { DhtRecordType, type DhtRecord } from "./dht.js";
|
|
2
|
+
import type { AdvertiseDirectOptions, DirectDialTarget } from "./directdial.js";
|
|
3
|
+
import { Identity } from "./identity.js";
|
|
4
|
+
import type { PublishOptions, PubsubEvent, SubscribeOptions } from "./pubsub.js";
|
|
5
|
+
import { type JsonValue } from "./rpc.js";
|
|
6
|
+
import { Ucan } from "./ucan.js";
|
|
7
|
+
/** Options for Session.call()/callWithUcan(). */
|
|
8
|
+
export interface CallOptions {
|
|
9
|
+
/** How long to wait for a RESULT/ERROR before giving up, in
|
|
10
|
+
* milliseconds. Also becomes the wire's own `deadline_ms` (now +
|
|
11
|
+
* this) -- see rpc.ts's DEFAULT_CALL_TIMEOUT_MS for why both share
|
|
12
|
+
* one number. */
|
|
13
|
+
deadlineMs?: number;
|
|
14
|
+
/** The realm this CALL is scoped to, as a 64-character lowercase (or
|
|
15
|
+
* uppercase, case-insensitive) hex string -- 32 bytes, the same
|
|
16
|
+
* hex-string convention DhtRecord's own key/version/signature fields
|
|
17
|
+
* already use (dht.ts), not native.*'s raw-byte Uint8Array (this
|
|
18
|
+
* class converts internally -- see realmBytesFromHex below). Omitted
|
|
19
|
+
* means the all-zero realm, the same default every mesh operation in
|
|
20
|
+
* this SDK used exclusively before this option existed, and what
|
|
21
|
+
* macula-go's own realm32OrZero (cabi/main.go) falls back to when no
|
|
22
|
+
* realm pointer is given.
|
|
23
|
+
*
|
|
24
|
+
* Must match whatever realm the target procedure is actually served
|
|
25
|
+
* under, or this CALL comes back `unknown_next_peer` even for a
|
|
26
|
+
* procedure genuinely advertised elsewhere -- realm is an exact-match
|
|
27
|
+
* routing key, not a hierarchy or a default-realm fallback. This
|
|
28
|
+
* class's own `serve()` always advertises under the all-zero realm
|
|
29
|
+
* (a separate, later gap -- see README.md's "What's explicitly not
|
|
30
|
+
* yet implemented"); calling with a non-zero realm only reaches a
|
|
31
|
+
* provider serving under that same realm through some other means. */
|
|
32
|
+
realm?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare class Session {
|
|
35
|
+
#private;
|
|
36
|
+
private constructor();
|
|
37
|
+
/** Dials host:port and completes the full CONNECT/HELLO handshake
|
|
38
|
+
* against a real macula-station, via macula-go's connection.Connect
|
|
39
|
+
* (WebPKI trust -- standard CA-bundle validation, matching what the
|
|
40
|
+
* production fleet actually presents; Pinned/Insecure trust modes
|
|
41
|
+
* aren't exposed yet).
|
|
42
|
+
*
|
|
43
|
+
* This is real network I/O -- a QUIC dial plus a signed round trip,
|
|
44
|
+
* bounded by macula-go's own ~30s handshake timeout -- so it's async
|
|
45
|
+
* on both sides of the FFI boundary (see addon/binding.cc's
|
|
46
|
+
* ConnectWorker): awaiting this never blocks Node's event loop.
|
|
47
|
+
*
|
|
48
|
+
* `identity` must stay non-disposed for the life of the returned
|
|
49
|
+
* Session -- close() needs it again to sign GOODBYE. */
|
|
50
|
+
static connect(host: string, port: number, identity: Identity): Promise<Session>;
|
|
51
|
+
/** The address this session's underlying QUIC connection is with. */
|
|
52
|
+
get remoteAddr(): string;
|
|
53
|
+
/** The station's HELLO-verified 32-byte NodeID (Ed25519 public key)
|
|
54
|
+
* -- proof, beyond "connect() didn't throw", that this is a real,
|
|
55
|
+
* application-layer-verified session and not just a QUIC/TLS
|
|
56
|
+
* handshake: frame.Verify already checked this NodeID's signature
|
|
57
|
+
* over the HELLO frame inside connect(), this just surfaces it. */
|
|
58
|
+
get stationNodeId(): Uint8Array;
|
|
59
|
+
/** Sends a signed GOODBYE and closes the connection. Safe to call
|
|
60
|
+
* more than once -- a second call is a no-op, matching Identity's
|
|
61
|
+
* dispose() convention. `identity` must be the same (non-disposed)
|
|
62
|
+
* identity used to open this session; Close needs it to sign
|
|
63
|
+
* GOODBYE. Like connect(), this is real network I/O (a drain sleep
|
|
64
|
+
* plus a write) and runs off the main thread on the native side.
|
|
65
|
+
*
|
|
66
|
+
* Stops an active subscribe() FIRST, if there is one, sending its
|
|
67
|
+
* UNSUBSCRIBE over the still-open connection before that connection
|
|
68
|
+
* goes away -- unlike every other resource this SDK hands out (an
|
|
69
|
+
* Identity, an unstopped serve() loop), an unstopped subscription
|
|
70
|
+
* left dangling here does not just leak memory: its background reader
|
|
71
|
+
* goroutine holds a live Napi::ThreadSafeFunction, which deliberately
|
|
72
|
+
* keeps Node's event loop alive on its own (see subscribe()'s own doc
|
|
73
|
+
* -- a program that does nothing but subscribe() and wait needs
|
|
74
|
+
* exactly this to stay alive for events to arrive at all). Verified
|
|
75
|
+
* live: closing a Session out from under an active subscription
|
|
76
|
+
* without this hung the process forever, not merely leaked a handle
|
|
77
|
+
* -- close() closing it first is what makes "forgot to call the
|
|
78
|
+
* returned stop()" fail safe instead of fail hung. */
|
|
79
|
+
close(identity: Identity, reason?: string): Promise<void>;
|
|
80
|
+
/** Caller role: sends a signed CALL for `procedure` and waits for the
|
|
81
|
+
* matching RESULT or ERROR (macula-go's connection.Session.Call).
|
|
82
|
+
* `payload` is JSON, converted to a cbor.Value on the Go side
|
|
83
|
+
* (cabi/wirevalue.go) -- see rpc.ts's JsonValue for the wire's own
|
|
84
|
+
* restrictions (no booleans, bytes as hex strings).
|
|
85
|
+
*
|
|
86
|
+
* Resolves with the RESULT's payload on success. Rejects with a
|
|
87
|
+
* MaculaCallError (rpc.ts) when a real BOLT#4 ERROR frame came back
|
|
88
|
+
* instead -- e.g. `unknown_next_peer` for a procedure nobody has
|
|
89
|
+
* advertised -- carrying its code/name/retryable triple rather than
|
|
90
|
+
* a generic message; rejects with a plain Error for everything that
|
|
91
|
+
* isn't a wire-level answer at all (a local timeout, a dead session,
|
|
92
|
+
* a payload the wire can't represent).
|
|
93
|
+
*
|
|
94
|
+
* Real network I/O -- a signed frame out and a wait for the reply,
|
|
95
|
+
* up to opts.deadlineMs -- so this runs off the main thread on the
|
|
96
|
+
* native side, like connect()/close(). Do not call this
|
|
97
|
+
* concurrently with an active serve() on the SAME Session: both read
|
|
98
|
+
* frames off the one shared control stream, and macula-go's own
|
|
99
|
+
* ServeOneCall/Call docs both warn that mixing roles on one
|
|
100
|
+
* connection races (an unrelated frame arriving first is discarded,
|
|
101
|
+
* not queued) -- open a second Session for the other role instead,
|
|
102
|
+
* exactly what this SDK's own live test does. */
|
|
103
|
+
call(procedure: string, payload: JsonValue, opts?: CallOptions): Promise<JsonValue>;
|
|
104
|
+
/** Caller role: call(), attaching `ucanToken` to the outgoing CALL
|
|
105
|
+
* (macula-go's `connection.Session.CallWithUCAN`) -- for invoking a
|
|
106
|
+
* procedure a provider has gated behind a `ucan.Policy.Required`
|
|
107
|
+
* policy on its own side (this SDK does not implement that provider
|
|
108
|
+
* side itself -- see ucan.ts's own module doc). `ucanToken` may be a
|
|
109
|
+
* `Ucan` (as returned by `Ucan.mint()`/`Ucan.decode()` -- its `.token`
|
|
110
|
+
* is attached) or a raw token string directly.
|
|
111
|
+
*
|
|
112
|
+
* Same resolve/reject shape as `call()` in every other respect: the
|
|
113
|
+
* RESULT's payload on success, a `MaculaCallError` for a real BOLT#4
|
|
114
|
+
* ERROR frame (e.g. `unauthorized` for a token that fails the
|
|
115
|
+
* provider's policy check, or `unknown_next_peer` for a procedure
|
|
116
|
+
* nobody has advertised), a plain `Error` for anything that never got
|
|
117
|
+
* a wire-level answer at all.
|
|
118
|
+
*
|
|
119
|
+
* Deliberately attaches whatever token bytes it is given with NO local
|
|
120
|
+
* check relating this Session's own identity to `ucanToken`'s `aud`
|
|
121
|
+
* claim -- see ucan.ts's own module doc for why: macula's UCAN gate is
|
|
122
|
+
* a bearer-token check (signature + expiry against the token's
|
|
123
|
+
* issuer), and the real wire-level gate never looks at the caller's
|
|
124
|
+
* identity against `aud` either. A client-side guard here would both
|
|
125
|
+
* reject configurations the mesh accepts fine and misrepresent a
|
|
126
|
+
* security property that isn't actually enforced.
|
|
127
|
+
*
|
|
128
|
+
* Real network I/O, off the main thread on the native side, and
|
|
129
|
+
* subject to the identical same-Session exclusivity rule as `call()`
|
|
130
|
+
* (`#requireHandleNotServing`) -- both end up on the same shared
|
|
131
|
+
* control stream. */
|
|
132
|
+
callWithUcan(procedure: string, payload: JsonValue, ucanToken: string | Ucan, opts?: CallOptions): Promise<JsonValue>;
|
|
133
|
+
/** Provider role: advertises `procedure` (macula-go's
|
|
134
|
+
* connection.Session.Advertise) and answers inbound CALLs against it
|
|
135
|
+
* forever, invoking `handler` for each one (payload in, reply or
|
|
136
|
+
* thrown error out -- `handler` may be async), until the returned
|
|
137
|
+
* stop function is called. A thrown/rejected `handler` becomes a
|
|
138
|
+
* BOLT#4 UnknownError reply carrying the thrown value's message as
|
|
139
|
+
* detail (matching macula-go's connection/serve.go, which maps every
|
|
140
|
+
* handler error to that one code); a `handler` that panics on the Go
|
|
141
|
+
* side instead (not reachable from here -- there is no Go code
|
|
142
|
+
* between this and the JS handler) would map to
|
|
143
|
+
* TemporaryRelayFailure, per that same file.
|
|
144
|
+
*
|
|
145
|
+
* Only one serve() registration is allowed per Session at a time --
|
|
146
|
+
* see call()'s own doc on why mixing roles (or two server loops) on
|
|
147
|
+
* one connection is unsafe, not just inadvisable; open a second
|
|
148
|
+
* Session for a second procedure instead of trying to serve two
|
|
149
|
+
* procedures off one.
|
|
150
|
+
*
|
|
151
|
+
* The returned stop function is async: it unadvertises the
|
|
152
|
+
* procedure (a real network write) and waits for the current poll
|
|
153
|
+
* tick to finish (up to rpc.ts's SERVE_POLL_MS) before resolving --
|
|
154
|
+
* there is no way to interrupt a Go-side wait already in flight, the
|
|
155
|
+
* same bounded-latency shape macula-go's own ServeForever has
|
|
156
|
+
* internally. */
|
|
157
|
+
serve(procedure: string, handler: (payload: JsonValue) => JsonValue | Promise<JsonValue>): Promise<() => Promise<void>>;
|
|
158
|
+
/** DHT: returns every record of `recordType` currently visible from
|
|
159
|
+
* the station this Session is connected to (macula-go's
|
|
160
|
+
* dht.FindRecordsByType, via a signed CALL to `_dht.find_records_by_type`
|
|
161
|
+
* under the DHT's own reserved realm -- threaded internally, this
|
|
162
|
+
* method never touches a realm itself). Coverage depends on that
|
|
163
|
+
* station's own view of the mesh, not a global guarantee. Neither
|
|
164
|
+
* this nor findRecord/findRecords verifies a returned record's
|
|
165
|
+
* signature or checks its expiry -- see DhtRecord's own doc (dht.ts).
|
|
166
|
+
*
|
|
167
|
+
* Real network I/O, off the main thread on the native side, exactly
|
|
168
|
+
* like call() -- and subject to the same same-Session exclusivity
|
|
169
|
+
* rule as call() (see #requireHandleNotServing's own doc): do not
|
|
170
|
+
* call this while serve() is active on the same Session. */
|
|
171
|
+
findRecordsByType(recordType: DhtRecordType | number): Promise<DhtRecord[]>;
|
|
172
|
+
/** DHT: returns every record stored at `key` -- the full
|
|
173
|
+
* signer-deduped multiset at that storage key (macula-go's
|
|
174
|
+
* dht.FindRecords), e.g. every procedure_advertisement for one
|
|
175
|
+
* procedure, not just the first one found. `key` must be exactly 32
|
|
176
|
+
* bytes -- see dht/record.go's ProcedureKey/StationEndpointKey/
|
|
177
|
+
* ContentKey (macula-go) for how those are derived from the thing
|
|
178
|
+
* being looked up. Same I/O and exclusivity notes as
|
|
179
|
+
* findRecordsByType(). */
|
|
180
|
+
findRecords(key: Uint8Array): Promise<DhtRecord[]>;
|
|
181
|
+
/** DHT: returns ONE record by storage key (macula-go's
|
|
182
|
+
* dht.FindRecord). Resolves `null` when none exists -- mirrors
|
|
183
|
+
* macula-go's own dht.ErrNotFound, translated to a value instead of a
|
|
184
|
+
* thrown error since "not found" is an expected, routine outcome
|
|
185
|
+
* here, not exceptional. Same I/O and exclusivity notes as
|
|
186
|
+
* findRecordsByType(). */
|
|
187
|
+
findRecord(key: Uint8Array): Promise<DhtRecord | null>;
|
|
188
|
+
/** DHT: builds the realm-qualified discovery URI (macula-go's
|
|
189
|
+
* dht.DiscoveryURI), then builds (dht.NewProcedureAdvertisement),
|
|
190
|
+
* signs, and stores a procedure_advertisement naming this Session's
|
|
191
|
+
* own Identity as `procedure`'s advertiser and `servingStation` (32
|
|
192
|
+
* bytes -- a station's NodeID, typically this Session's own
|
|
193
|
+
* `stationNodeId`) as the station that serves it.
|
|
194
|
+
*
|
|
195
|
+
* `realm` should be the SAME realm `procedure` is (or will be) served
|
|
196
|
+
* under via `serve()` -- defaults to the all-zero realm, matching
|
|
197
|
+
* `call()`'s own default (see CallOptions.realm's own doc: `call()`/
|
|
198
|
+
* `callWithUcan()`/`publish()`/`subscribe()` now all take an optional
|
|
199
|
+
* realm; `serve()`/`advertise` remain all-zero-realm-only for this
|
|
200
|
+
* slice, unchanged). This method
|
|
201
|
+
* builds the qualified URI itself (dht.DiscoveryURI) rather than
|
|
202
|
+
* taking a pre-qualified string, since NewProcedureAdvertisement's own
|
|
203
|
+
* doc is explicit that "the advertiser and the resolver must derive
|
|
204
|
+
* the identical URI or the DHT storage key will not agree" -- a
|
|
205
|
+
* caller-supplied pre-qualified string invites exactly that class of
|
|
206
|
+
* bug (verified directly: an earlier draft of this SDK's own live
|
|
207
|
+
* test got this wrong by hand-qualifying the URI itself, and its
|
|
208
|
+
* following findRecord() came back not-found until this method built
|
|
209
|
+
* the URI internally instead).
|
|
210
|
+
*
|
|
211
|
+
* Wraps macula-go's REAL constructor rather than a generic
|
|
212
|
+
* JSON-payload builder deliberately -- see cabi/dht.go's own doc on
|
|
213
|
+
* why: two of this record type's payload fields (advertiser_node,
|
|
214
|
+
* serving_station) are raw 32-byte pubkeys that must be actual CBOR
|
|
215
|
+
* byte strings for a real resolver to read, and this SDK's generic
|
|
216
|
+
* JSON<->cbor.Value conversion (rpc.ts's JsonValue, wirevalue.go) has
|
|
217
|
+
* no way to produce those going IN -- only OUT, as "0x"-prefixed hex
|
|
218
|
+
* (see DhtRecord's own doc). `ttlMs` defaults to DHT_DEFAULT_TTL_MS
|
|
219
|
+
* (48h). Resolves with the signed record actually stored. Same I/O
|
|
220
|
+
* and exclusivity notes as findRecordsByType(). */
|
|
221
|
+
putProcedureAdvertisement(procedure: string, servingStation: Uint8Array, opts?: {
|
|
222
|
+
realm?: Uint8Array;
|
|
223
|
+
ttlMs?: number;
|
|
224
|
+
}): Promise<DhtRecord>;
|
|
225
|
+
/** DHT: builds (macula-go's dht.NewContentAnnouncement), signs, and
|
|
226
|
+
* stores a content_announcement naming this Session's own Identity as
|
|
227
|
+
* `mcid`'s (34 bytes) announcer, reachable at `endpoint` (a dialable
|
|
228
|
+
* seed URL, e.g. "https://host:4433" -- NOT a station_endpoint's
|
|
229
|
+
* split host/port). Same reasoning as putProcedureAdvertisement()'s
|
|
230
|
+
* own doc for wrapping macula-go's real constructor instead of a
|
|
231
|
+
* generic JSON-payload builder (announcer_node/mcid are the same kind
|
|
232
|
+
* of raw-byte field). `ttlMs` defaults to DHT_DEFAULT_TTL_MS (48h).
|
|
233
|
+
* Same I/O and exclusivity notes as findRecordsByType(). */
|
|
234
|
+
putContentAnnouncement(mcid: Uint8Array, endpoint: string, ttlMs?: number): Promise<DhtRecord>;
|
|
235
|
+
/** Direct-dial (caller side): finds `procedure`'s currently-advertised
|
|
236
|
+
* serving station and its dialable host/port (macula-go's
|
|
237
|
+
* `directdial.Resolve`), via this Session used only to query the DHT
|
|
238
|
+
* -- it does not need to be connected to the station that ends up
|
|
239
|
+
* serving `procedure`. Retries past DHT propagation lag internally (up
|
|
240
|
+
* to ~5s, macula-go's own fixed schedule, not configurable here); a
|
|
241
|
+
* `procedure` nobody ever called `advertiseDirect()` for rejects
|
|
242
|
+
* cleanly after that window (a plain `Error` wrapping macula-go's
|
|
243
|
+
* `ErrProcedureNotAdvertised`), never a hang.
|
|
244
|
+
*
|
|
245
|
+
* `opts.realm` must match whatever realm `procedure` was
|
|
246
|
+
* `advertiseDirect()`d under, or the discovery URI the two sides
|
|
247
|
+
* derive disagrees and this rejects the same way as if nothing was
|
|
248
|
+
* ever advertised at all -- see `AdvertiseDirectOptions.realm`'s own
|
|
249
|
+
* doc (directdial.ts). `callDirect()`/`callDirectWithUcan()` call this
|
|
250
|
+
* internally; it's exposed on its own for callers that just want the
|
|
251
|
+
* resolved station/host/port without also dialing and calling it (e.g.
|
|
252
|
+
* diagnostics).
|
|
253
|
+
*
|
|
254
|
+
* Real network I/O, off the main thread on the native side, subject to
|
|
255
|
+
* the same same-Session exclusivity rule as `call()`/the DHT methods
|
|
256
|
+
* (`#requireHandleNotServing`). */
|
|
257
|
+
resolveDirect(procedure: string, opts?: {
|
|
258
|
+
realm?: string;
|
|
259
|
+
}): Promise<DirectDialTarget>;
|
|
260
|
+
/** Direct-dial (caller side): resolves `procedure`'s provider (via
|
|
261
|
+
* `resolveDirect()`, through this Session) and calls it there, in one
|
|
262
|
+
* hop, in a SEPARATE connection macula-go opens, application-layer-pins
|
|
263
|
+
* against the resolved station identity, and closes again, entirely
|
|
264
|
+
* internally (macula-go's `directdial.Call`) -- this Session's own
|
|
265
|
+
* connection is never touched beyond the DHT lookup. The provider must
|
|
266
|
+
* have `advertiseDirect()`d `procedure` (or the Erlang/Rust/Go
|
|
267
|
+
* equivalent) -- a plain `serve()`-side `Advertise` alone publishes no
|
|
268
|
+
* discoverable DHT record, and this rejects with `ErrProcedureNotAdvertised`.
|
|
269
|
+
*
|
|
270
|
+
* Same resolve/reject shape as `call()`: the RESULT's payload on
|
|
271
|
+
* success, a `MaculaCallError` (rpc.ts) for a real BOLT#4 ERROR frame
|
|
272
|
+
* from the resolved provider, a plain `Error` for a resolve failure, a
|
|
273
|
+
* dial failure, an identity-trust violation (the dialed peer proved a
|
|
274
|
+
* DIFFERENT identity than the DHT chain resolved), or anything else
|
|
275
|
+
* that never got a wire-level answer at all.
|
|
276
|
+
*
|
|
277
|
+
* Real network I/O (DHT lookups, a fresh QUIC dial, then the CALL
|
|
278
|
+
* itself) -- off the main thread on the native side, subject to the
|
|
279
|
+
* same same-Session exclusivity rule as `call()` for THIS Session's own
|
|
280
|
+
* DHT-querying use (the separate dialed connection this opens
|
|
281
|
+
* internally is not this Session and is never exposed as one). */
|
|
282
|
+
callDirect(procedure: string, payload: JsonValue, opts?: CallOptions): Promise<JsonValue>;
|
|
283
|
+
/** Direct-dial (caller side): `callDirect()`, attaching `ucanToken` to
|
|
284
|
+
* the outgoing CALL (macula-go's `directdial.CallWithUCAN`) -- for
|
|
285
|
+
* reaching a direct-dial-advertised procedure a provider has gated
|
|
286
|
+
* behind a `ucan.Policy.Required` policy. Every hecate-om capability is
|
|
287
|
+
* advertised via `advertiseDirect()` specifically so it's reachable
|
|
288
|
+
* ONLY this way -- plain `callDirect()` cannot resolve or attach a
|
|
289
|
+
* token to it. Same `ucanToken` shape, no-audience-check, and
|
|
290
|
+
* resolve/reject conventions as `callWithUcan()` (see both that
|
|
291
|
+
* method's and ucan.ts's own doc for why: macula's UCAN gate is a
|
|
292
|
+
* bearer-token check, not an audience match).
|
|
293
|
+
*
|
|
294
|
+
* Same I/O and exclusivity notes as `callDirect()`. */
|
|
295
|
+
callDirectWithUcan(procedure: string, payload: JsonValue, ucanToken: string | Ucan, opts?: CallOptions): Promise<JsonValue>;
|
|
296
|
+
/** Direct-dial (provider side): publishes `procedure` as
|
|
297
|
+
* direct-dial-reachable at THIS Session's own currently-connected
|
|
298
|
+
* station (macula-go's `directdial.AdvertiseDirect`) -- a plain
|
|
299
|
+
* ADVERTISE (so an inbound CALL routed here via the DHT-resolved path
|
|
300
|
+
* still has something to route to -- a real bug macula-go fixed live
|
|
301
|
+
* 2026-08-30: skipping this let resolve+dial complete cleanly against a
|
|
302
|
+
* station with nothing registered to answer, `ServeOneCall` never
|
|
303
|
+
* seeing it) plus a signed `procedure_advertisement` DHT record naming
|
|
304
|
+
* this Session's own station.
|
|
305
|
+
*
|
|
306
|
+
* Unlike `serve()`'s own internal advertise (still all-zero-realm-only
|
|
307
|
+
* in this SDK -- see `serve()`'s own doc), this method threads
|
|
308
|
+
* `opts.realm` all the way through, matching `directdial.AdvertiseDirect`
|
|
309
|
+
* itself: `resolveDirect()`/`callDirect()`/`callDirectWithUcan()` only
|
|
310
|
+
* ever reach a procedure `advertiseDirect()`d under the EXACT SAME
|
|
311
|
+
* realm.
|
|
312
|
+
*
|
|
313
|
+
* A station's registration for a procedure does not survive the
|
|
314
|
+
* connection that sent it being replaced -- a long-lived provider needs
|
|
315
|
+
* to call this again on its own schedule; see `keepAdvertisedDirect()`
|
|
316
|
+
* (directdial.ts) for that loop, built on top of this method rather
|
|
317
|
+
* than duplicating macula-go's own `KeepAdvertisedDirect` here.
|
|
318
|
+
*
|
|
319
|
+
* **Must NOT be called on a Session that is also actively
|
|
320
|
+
* `serve()`-ing** -- enforced by the same `#requireHandleNotServing`
|
|
321
|
+
* guard `call()`/the DHT methods use, for the identical reason: this
|
|
322
|
+
* method's own `PutRecord` CALL reads a RESULT off the same shared
|
|
323
|
+
* control stream `serve()`'s poll loop is also reading, and the two
|
|
324
|
+
* would race (matches `directdial.AdvertiseDirect`'s own doc). A
|
|
325
|
+
* provider that also serves `procedure` needs a SEPARATE Session (and
|
|
326
|
+
* identity -- this fleet enforces one connection per identity, kicking
|
|
327
|
+
* whichever connects second) to call this on.
|
|
328
|
+
*
|
|
329
|
+
* Real network I/O (a fire-and-forget ADVERTISE write plus a signed
|
|
330
|
+
* PutRecord CALL) -- off the main thread on the native side. */
|
|
331
|
+
advertiseDirect(procedure: string, opts?: AdvertiseDirectOptions): Promise<void>;
|
|
332
|
+
/** Pubsub: sends a signed PUBLISH for `topic` (macula-go's
|
|
333
|
+
* connection.Session.Publish, which also attaches the end-to-end
|
|
334
|
+
* publisher_sig a relayed EVENT needs to survive beyond one hop --
|
|
335
|
+
* see that method's own doc, not reimplemented here). `payload`
|
|
336
|
+
* follows the same JsonValue rules as call()'s payload (no boolean,
|
|
337
|
+
* embedded bytes as "0x"-prefixed hex). Fire-and-forget: Publish's
|
|
338
|
+
* own doc is explicit that no reply is expected on the wire, so the
|
|
339
|
+
* returned Promise resolving only means this Session's own frame was
|
|
340
|
+
* encoded, signed, and sent -- never that any subscriber received it
|
|
341
|
+
* (macula-go's own live test for this, TestLivePubSubRoundTrip,
|
|
342
|
+
* observes a subscriber's own publish arriving back at it rather than
|
|
343
|
+
* asserting it as a hard guarantee, for the same reason).
|
|
344
|
+
*
|
|
345
|
+
* Deliberately NOT guarded by the same-Session exclusivity rule
|
|
346
|
+
* call()/serve()/subscribe()/the DHT methods share (see
|
|
347
|
+
* #requireHandleNotServing's own doc) -- publish() only ever writes,
|
|
348
|
+
* never reads off the shared control stream, so it does not race a
|
|
349
|
+
* concurrent serve()/subscribe() the way those do, and can run safely
|
|
350
|
+
* on the SAME Session a subscribe() of its own is active on -- exactly
|
|
351
|
+
* what a subscriber publishing to (and receiving) its own topic needs.
|
|
352
|
+
*
|
|
353
|
+
* `opts.realm` (see CallOptions.realm's own doc for the hex-string
|
|
354
|
+
* format and exact-match semantics) scopes which realm this EVENT is
|
|
355
|
+
* published under -- omitted means the all-zero realm, this SDK's
|
|
356
|
+
* sole default before this option existed. A subscribe() only
|
|
357
|
+
* receives this event if its own realm matches exactly.
|
|
358
|
+
*
|
|
359
|
+
* Real network I/O (one signed frame write) -- runs off the main
|
|
360
|
+
* thread on the native side, like every other network-touching method
|
|
361
|
+
* here. */
|
|
362
|
+
publish(topic: string, payload: JsonValue, opts?: PublishOptions): Promise<void>;
|
|
363
|
+
/** Pubsub: sends a signed SUBSCRIBE for `topic`, then delivers every
|
|
364
|
+
* inbound EVENT for it to `handler` -- macula-go's own
|
|
365
|
+
* connection.Session.RunSubscriber (connection/subscriber.go) drives
|
|
366
|
+
* the actual read loop on the Go side, in a background goroutine, NOT
|
|
367
|
+
* reimplemented on top of a hand-rolled poll here (see cabi/pubsub.go's
|
|
368
|
+
* own doc for why RunSubscriber specifically, over the lower-level
|
|
369
|
+
* RecvEvent). Delivery is Go-driven, not JS-driven: unlike serve()'s
|
|
370
|
+
* poll loop, nothing on this side calls into the native layer
|
|
371
|
+
* repeatedly to ask "did anything arrive yet" -- the addon calls INTO
|
|
372
|
+
* this handler asynchronously, via a Napi::ThreadSafeFunction wired to
|
|
373
|
+
* that background goroutine, whenever an EVENT actually shows up.
|
|
374
|
+
*
|
|
375
|
+
* Only one subscribe() (and no active serve()) is allowed per Session
|
|
376
|
+
* at a time -- same reasoning as serve()'s own one-at-a-time rule
|
|
377
|
+
* (#requireHandleNotServing's own doc): the background reader and any
|
|
378
|
+
* other read off this Session's shared control stream would race.
|
|
379
|
+
* Open a second Session for a second topic (or to serve/call
|
|
380
|
+
* concurrently) instead.
|
|
381
|
+
*
|
|
382
|
+
* Resolves with an async stop() function once the initial SUBSCRIBE
|
|
383
|
+
* has been sent and the background reader has started. stop() sends
|
|
384
|
+
* the matching UNSUBSCRIBE and does not resolve until the Go-side
|
|
385
|
+
* reader goroutine has genuinely exited -- calling it and awaiting the
|
|
386
|
+
* result is the actual guarantee that no further `handler` call can
|
|
387
|
+
* happen afterward, not just that one was requested.
|
|
388
|
+
*
|
|
389
|
+
* Real network I/O (the initial SUBSCRIBE send, and stop()'s
|
|
390
|
+
* UNSUBSCRIBE) -- both run off the main thread on the native side.
|
|
391
|
+
*
|
|
392
|
+
* `opts.realm` (see CallOptions.realm's own doc for the hex-string
|
|
393
|
+
* format and exact-match semantics) scopes which realm this SUBSCRIBE
|
|
394
|
+
* listens on -- omitted means the all-zero realm, this SDK's sole
|
|
395
|
+
* default before this option existed. Only an EVENT published under
|
|
396
|
+
* the SAME realm is ever delivered to `handler`.
|
|
397
|
+
*
|
|
398
|
+
* If the underlying connection dies (or any other transport error
|
|
399
|
+
* ends the background reader) rather than the returned stop() being
|
|
400
|
+
* called, this subscription tears itself down automatically -- the
|
|
401
|
+
* native handle is released and this Session is left closable and
|
|
402
|
+
* reusable for a fresh subscribe()/serve()/call() -- and, if
|
|
403
|
+
* provided, `opts.onClosed` is called once with the error. Verified
|
|
404
|
+
* live that, without this, such a subscription went silent forever
|
|
405
|
+
* (no further events, no error) and left this Session's handle
|
|
406
|
+
* permanently open even after close(). */
|
|
407
|
+
subscribe(topic: string, handler: (evt: PubsubEvent) => void, opts?: SubscribeOptions): Promise<() => Promise<void>>;
|
|
408
|
+
/** Content transfer: stores `data` (macula-go's content.Put, on this
|
|
409
|
+
* Session's own fresh dedicated QUIC stream -- Session.
|
|
410
|
+
* OpenDedicatedStream on the Go side, NOT the shared control stream
|
|
411
|
+
* call()/serve()/the DHT methods/subscribe() all read from), chunking
|
|
412
|
+
* automatically above manifest.DefaultChunkSize (256 KiB) and
|
|
413
|
+
* returning the hex-encoded mcid it's now addressable by. `name` is
|
|
414
|
+
* used ONLY on the chunked path (attached to the resulting manifest)
|
|
415
|
+
* -- a single-block put ignores it entirely, matching content.Put's
|
|
416
|
+
* own documented behavior; leave it unset for small blobs.
|
|
417
|
+
*
|
|
418
|
+
* NOT durable object storage -- see content.ts's own module doc: a
|
|
419
|
+
* station may forget this content later, and there is no list/delete
|
|
420
|
+
* operation. Treat this as "hand these bytes to a peer once."
|
|
421
|
+
*
|
|
422
|
+
* Because Put opens its own dedicated stream instead of reading the
|
|
423
|
+
* shared control stream, this is, unlike call()/serve()/the DHT
|
|
424
|
+
* methods/subscribe(), never subject to Session's same-Session
|
|
425
|
+
* exclusivity guard (#requireHandleNotServing) -- it can run
|
|
426
|
+
* concurrently with an active serve()/subscribe() (or another
|
|
427
|
+
* putContent()/getContent()) on the same Session without racing.
|
|
428
|
+
*
|
|
429
|
+
* Real network I/O (one or more signed CALLs on the new stream) --
|
|
430
|
+
* runs off the main thread on the native side, like every other
|
|
431
|
+
* network-touching method here. */
|
|
432
|
+
putContent(data: Uint8Array, name?: string): Promise<{
|
|
433
|
+
mcid: string;
|
|
434
|
+
}>;
|
|
435
|
+
/** Content transfer: fetches and verifies (macula-go's content.Get,
|
|
436
|
+
* on its own fresh dedicated QUIC stream, same reasoning as
|
|
437
|
+
* putContent() -- including content.Get's own client-side hash
|
|
438
|
+
* re-check against `mcid`: a station may only be relaying content it
|
|
439
|
+
* doesn't itself store, so its answer is never trusted blindly) the
|
|
440
|
+
* content addressed by `mcid` (the hex string putContent() returned).
|
|
441
|
+
*
|
|
442
|
+
* Rejects with ContentNotFoundError (content.ts) specifically when
|
|
443
|
+
* the station reports it doesn't know this mcid -- an expected,
|
|
444
|
+
* routine outcome for a one-time transfer mechanism with no
|
|
445
|
+
* durability guarantee, not a transport failure; every other failure
|
|
446
|
+
* (a bad session, a malformed mcid, a real transport error) rejects
|
|
447
|
+
* with a plain Error instead.
|
|
448
|
+
*
|
|
449
|
+
* Same dedicated-stream, no-exclusivity-guard reasoning as
|
|
450
|
+
* putContent() -- safe alongside an active serve()/subscribe() on the
|
|
451
|
+
* same Session. Real network I/O, runs off the main thread. */
|
|
452
|
+
getContent(mcid: string): Promise<Uint8Array>;
|
|
453
|
+
}
|