wire-mesh-core 1.54.0 → 1.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/adapters/frame-codec.d.cts +1 -1
  2. package/dist/adapters/frame-codec.d.mts +1 -1
  3. package/dist/adapters/node-identity.d.cts +2 -2
  4. package/dist/adapters/node-identity.d.mts +2 -2
  5. package/dist/adapters/tcp-transport.d.cts +1 -1
  6. package/dist/adapters/tcp-transport.d.mts +1 -1
  7. package/dist/adapters/threshold-identity.d.cts +2 -2
  8. package/dist/adapters/threshold-identity.d.mts +2 -2
  9. package/dist/adapters/threshold-wasm.d.cts +1 -1
  10. package/dist/adapters/threshold-wasm.d.mts +1 -1
  11. package/dist/adapters/tls-transport.d.cts +1 -1
  12. package/dist/adapters/tls-transport.d.mts +1 -1
  13. package/dist/domain/bulk.d.cts +2 -2
  14. package/dist/domain/bulk.d.mts +2 -2
  15. package/dist/domain/capability-grant.d.cts +3 -3
  16. package/dist/domain/capability-grant.d.mts +3 -3
  17. package/dist/domain/capability-request.d.cts +2 -2
  18. package/dist/domain/capability-request.d.mts +2 -2
  19. package/dist/domain/data-sync.d.cts +2 -2
  20. package/dist/domain/data-sync.d.mts +2 -2
  21. package/dist/domain/device-id.d.cts +1 -1
  22. package/dist/domain/device-id.d.mts +1 -1
  23. package/dist/domain/direct-manage-request.cjs +64 -0
  24. package/dist/domain/direct-manage-request.d.cts +16 -0
  25. package/dist/domain/direct-manage-request.d.mts +16 -0
  26. package/dist/domain/direct-manage-request.mjs +62 -0
  27. package/dist/domain/gossip-expansion.cjs +44 -0
  28. package/dist/domain/gossip-expansion.d.cts +29 -0
  29. package/dist/domain/gossip-expansion.d.mts +29 -0
  30. package/dist/domain/gossip-expansion.mjs +43 -0
  31. package/dist/domain/grant-candidates.d.cts +1 -1
  32. package/dist/domain/grant-candidates.d.mts +1 -1
  33. package/dist/domain/handshake.d.cts +1 -1
  34. package/dist/domain/handshake.d.mts +1 -1
  35. package/dist/domain/mesh-session.cjs +40 -70
  36. package/dist/domain/mesh-session.d.cts +8 -15
  37. package/dist/domain/mesh-session.d.mts +8 -15
  38. package/dist/domain/mesh-session.mjs +39 -69
  39. package/dist/domain/notice-board.d.cts +3 -3
  40. package/dist/domain/notice-board.d.mts +3 -3
  41. package/dist/domain/relay-hub.d.cts +2 -2
  42. package/dist/domain/relay-hub.d.mts +2 -2
  43. package/dist/domain/relay-use-gate.d.cts +2 -2
  44. package/dist/domain/relay-use-gate.d.mts +2 -2
  45. package/dist/domain/revocation-view.d.cts +2 -2
  46. package/dist/domain/revocation-view.d.mts +2 -2
  47. package/dist/domain/room-rekey.d.cts +3 -3
  48. package/dist/domain/room-rekey.d.mts +3 -3
  49. package/dist/domain/room-token-verification.d.cts +1 -1
  50. package/dist/domain/room-token-verification.d.mts +1 -1
  51. package/dist/domain/room.d.cts +4 -4
  52. package/dist/domain/room.d.mts +4 -4
  53. package/dist/domain/shard-manifest.d.cts +1 -1
  54. package/dist/domain/shard-manifest.d.mts +1 -1
  55. package/dist/domain/tokens.d.cts +1 -1
  56. package/dist/domain/tokens.d.mts +1 -1
  57. package/dist/domain/webrtc-signaling.d.cts +2 -2
  58. package/dist/domain/webrtc-signaling.d.mts +2 -2
  59. package/dist/generated/protocol.cjs +5 -1
  60. package/dist/generated/protocol.d.cts +2 -2
  61. package/dist/generated/protocol.d.mts +2 -2
  62. package/dist/generated/protocol.mjs +5 -2
  63. package/dist/{identity-BXrJ6_YM.d.mts → identity-Br6e0Rx5.d.mts} +1 -1
  64. package/dist/{identity-CmD4l_Mp.d.cts → identity-pOt5KxJB.d.cts} +1 -1
  65. package/dist/ports/identity.d.cts +1 -1
  66. package/dist/ports/identity.d.mts +1 -1
  67. package/dist/ports/transport.d.cts +1 -1
  68. package/dist/ports/transport.d.mts +1 -1
  69. package/dist/{protocol-PhUVprXA.d.cts → protocol-D2Ue2nd2.d.cts} +25 -11
  70. package/dist/{protocol-PhUVprXA.d.mts → protocol-D2Ue2nd2.d.mts} +25 -11
  71. package/dist/{room-token-verification-D8HndWYM.d.cts → room-token-verification-CNXMjIQd.d.cts} +2 -2
  72. package/dist/{room-token-verification-Dv1O6-FV.d.mts → room-token-verification-ZHgJhnsu.d.mts} +2 -2
  73. package/dist/{tokens-DIPzZ9JJ.d.cts → tokens-BtczHfrc.d.cts} +2 -2
  74. package/dist/{tokens-BVrgBeqF.d.mts → tokens-Hnq9oURj.d.mts} +2 -2
  75. package/dist/{transport-CbXX1Xd-.d.cts → transport-C5wU0UtA.d.mts} +1 -1
  76. package/dist/{transport-DG9ouGe-.d.mts → transport-fUY3alfO.d.cts} +1 -1
  77. package/package.json +9 -1
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-PhUVprXA.cjs";
1
+ import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-D2Ue2nd2.cjs";
2
2
  //#region src/adapters/frame-codec.d.ts
3
3
  export declare function messageFromFrame(frame: Frame): Uint8Array<ArrayBuffer>;
4
4
  /** Wraps frame as a relay-data-frame's own opaque payload, stamping to-device when the caller knows which established pairing to address it to (wire-mesh#30) -- the outbound counterpart to tryDecodeFrame's own doc comment below, which describes the inbound side of the same relay-data envelope. Omitting toDevice leaves the frame unaddressed, which the receiving hub then routes via its own most-recently-established-pairing fallback. */
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-PhUVprXA.mjs";
1
+ import { D as DeviceId, N as Frame, ht as RelayDataFrame } from "../protocol-D2Ue2nd2.mjs";
2
2
  //#region src/adapters/frame-codec.d.ts
3
3
  export declare function messageFromFrame(frame: Frame): Uint8Array<ArrayBuffer>;
4
4
  /** Wraps frame as a relay-data-frame's own opaque payload, stamping to-device when the caller knows which established pairing to address it to (wire-mesh#30) -- the outbound counterpart to tryDecodeFrame's own doc comment below, which describes the inbound side of the same relay-data envelope. Omitting toDevice leaves the frame unaddressed, which the receiving hub then routes via its own most-recently-established-pairing fallback. */
@@ -1,5 +1,5 @@
1
- import { B as IdentityKey, D as DeviceId } from "../protocol-PhUVprXA.cjs";
2
- import { t as IdentityPort } from "../identity-CmD4l_Mp.cjs";
1
+ import { B as IdentityKey, D as DeviceId } from "../protocol-D2Ue2nd2.cjs";
2
+ import { t as IdentityPort } from "../identity-pOt5KxJB.cjs";
3
3
  import { webcrypto } from "node:crypto";
4
4
  //#region src/adapters/node-identity.d.ts
5
5
  export declare function deriveDeviceId(publicKey: Uint8Array): Promise<DeviceId>;
@@ -1,5 +1,5 @@
1
- import { B as IdentityKey, D as DeviceId } from "../protocol-PhUVprXA.mjs";
2
- import { t as IdentityPort } from "../identity-BXrJ6_YM.mjs";
1
+ import { B as IdentityKey, D as DeviceId } from "../protocol-D2Ue2nd2.mjs";
2
+ import { t as IdentityPort } from "../identity-Br6e0Rx5.mjs";
3
3
  import { webcrypto } from "node:crypto";
4
4
  //#region src/adapters/node-identity.d.ts
5
5
  export declare function deriveDeviceId(publicKey: Uint8Array): Promise<DeviceId>;
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-CbXX1Xd-.cjs";
1
+ import { r as Transport } from "../transport-fUY3alfO.cjs";
2
2
  //#region src/adapters/tcp-transport.d.ts
3
3
  /** A Node net.Socket-based Transport: length-prefixed, CBOR-encoded frames over plain TCP -- matching Cascade's own transport shape, since interop with Cascade nodes is wire-mesh's stated goal. Framing (not TLS) is this adapter's own concern; a TLS-terminated variant is a separate adapter behind the same Transport contract. */
4
4
  export declare function createTcpTransport(): Transport;
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-DG9ouGe-.mjs";
1
+ import { r as Transport } from "../transport-C5wU0UtA.mjs";
2
2
  //#region src/adapters/tcp-transport.d.ts
3
3
  /** A Node net.Socket-based Transport: length-prefixed, CBOR-encoded frames over plain TCP -- matching Cascade's own transport shape, since interop with Cascade nodes is wire-mesh's stated goal. Framing (not TLS) is this adapter's own concern; a TLS-terminated variant is a separate adapter behind the same Transport contract. */
4
4
  export declare function createTcpTransport(): Transport;
@@ -1,5 +1,5 @@
1
- import { D as DeviceId } from "../protocol-PhUVprXA.cjs";
2
- import { t as IdentityPort } from "../identity-CmD4l_Mp.cjs";
1
+ import { D as DeviceId } from "../protocol-D2Ue2nd2.cjs";
2
+ import { t as IdentityPort } from "../identity-pOt5KxJB.cjs";
3
3
  import { DeviceKeyed } from "./threshold-wasm.cjs";
4
4
  import { ThresholdSubject } from "../domain/threshold-subject.cjs";
5
5
  //#region src/adapters/threshold-identity.d.ts
@@ -1,5 +1,5 @@
1
- import { D as DeviceId } from "../protocol-PhUVprXA.mjs";
2
- import { t as IdentityPort } from "../identity-BXrJ6_YM.mjs";
1
+ import { D as DeviceId } from "../protocol-D2Ue2nd2.mjs";
2
+ import { t as IdentityPort } from "../identity-Br6e0Rx5.mjs";
3
3
  import { DeviceKeyed } from "./threshold-wasm.mjs";
4
4
  import { ThresholdSubject } from "../domain/threshold-subject.mjs";
5
5
  //#region src/adapters/threshold-identity.d.ts
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-PhUVprXA.cjs";
1
+ import { D as DeviceId } from "../protocol-D2Ue2nd2.cjs";
2
2
  //#region src/adapters/threshold-wasm.d.ts
3
3
  /** One `(deviceId, bytes)` pair -- the wasm boundary's own encoding of a map keyed by participant, since wasm-bindgen has no built-in `Map<Uint8Array, Uint8Array>` marshalling. */
4
4
  export interface DeviceKeyed<T extends Uint8Array = Uint8Array<ArrayBuffer>> {
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-PhUVprXA.mjs";
1
+ import { D as DeviceId } from "../protocol-D2Ue2nd2.mjs";
2
2
  //#region src/adapters/threshold-wasm.d.ts
3
3
  /** One `(deviceId, bytes)` pair -- the wasm boundary's own encoding of a map keyed by participant, since wasm-bindgen has no built-in `Map<Uint8Array, Uint8Array>` marshalling. */
4
4
  export interface DeviceKeyed<T extends Uint8Array = Uint8Array<ArrayBuffer>> {
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-CbXX1Xd-.cjs";
1
+ import { r as Transport } from "../transport-fUY3alfO.cjs";
2
2
  //#region src/adapters/tls-transport.d.ts
3
3
  export interface TlsIdentity {
4
4
  certificatePem: string;
@@ -1,4 +1,4 @@
1
- import { r as Transport } from "../transport-DG9ouGe-.mjs";
1
+ import { r as Transport } from "../transport-C5wU0UtA.mjs";
2
2
  //#region src/adapters/tls-transport.d.ts
3
3
  export interface TlsIdentity {
4
4
  certificatePem: string;
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-PhUVprXA.cjs";
1
+ import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.cjs";
2
2
  import { KeyValueStorage } from "../ports/storage.cjs";
3
- import { t as Connection } from "../transport-CbXX1Xd-.cjs";
3
+ import { t as Connection } from "../transport-fUY3alfO.cjs";
4
4
  import { IncomingManageRequest, MeshSession } from "./mesh-session.cjs";
5
5
  //#region src/domain/bulk.d.ts
6
6
  /** The two capabilities matching the direction each manage-command actually flows (spec/bulk.cddl): the sender proves write authority to open a transfer, the receiver proves read authority to ask for one to continue. */
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-PhUVprXA.mjs";
1
+ import { D as DeviceId, N as Frame, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.mjs";
2
2
  import { KeyValueStorage } from "../ports/storage.mjs";
3
- import { t as Connection } from "../transport-DG9ouGe-.mjs";
3
+ import { t as Connection } from "../transport-C5wU0UtA.mjs";
4
4
  import { IncomingManageRequest, MeshSession } from "./mesh-session.mjs";
5
5
  //#region src/domain/bulk.d.ts
6
6
  /** The two capabilities matching the direction each manage-command actually flows (spec/bulk.cddl): the sender proves write authority to open a transfer, the receiver proves read authority to ask for one to continue. */
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-PhUVprXA.cjs";
2
- import { t as IdentityPort } from "../identity-CmD4l_Mp.cjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.cjs";
2
+ import { t as IdentityPort } from "../identity-pOt5KxJB.cjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.cjs";
4
4
  import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.cjs";
5
- import { a as RevocationCheck } from "../tokens-DIPzZ9JJ.cjs";
5
+ import { a as RevocationCheck } from "../tokens-BtczHfrc.cjs";
6
6
  //#region src/domain/capability-grant.d.ts
7
7
  /**
8
8
  * Builds a capability-grant command per management.cddl. The outer `manage-command.verb` is the capability string itself, the identical convention capability-request.ts's own buildCapabilityRequestCommand already establishes -- a receiver's per-capability handler is how it knows which grant this push is even for. `params.verb` is the fixed "capability.grant" marker. No scope or invitee field: manage-request-frame's own top-level scope already carries the former, and the request's own destination already carries the latter -- naming either a second time inside params could only ever disagree with the fact it duplicates.
@@ -1,8 +1,8 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-PhUVprXA.mjs";
2
- import { t as IdentityPort } from "../identity-BXrJ6_YM.mjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.mjs";
2
+ import { t as IdentityPort } from "../identity-Br6e0Rx5.mjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.mjs";
4
4
  import { IncomingManageRequest, ManageOutcome, MeshSession } from "./mesh-session.mjs";
5
- import { a as RevocationCheck } from "../tokens-BVrgBeqF.mjs";
5
+ import { a as RevocationCheck } from "../tokens-Hnq9oURj.mjs";
6
6
  //#region src/domain/capability-grant.d.ts
7
7
  /**
8
8
  * Builds a capability-grant command per management.cddl. The outer `manage-command.verb` is the capability string itself, the identical convention capability-request.ts's own buildCapabilityRequestCommand already establishes -- a receiver's per-capability handler is how it knows which grant this push is even for. `params.verb` is the fixed "capability.grant" marker. No scope or invitee field: manage-request-frame's own top-level scope already carries the former, and the request's own destination already carries the latter -- naming either a second time inside params could only ever disagree with the fact it duplicates.
@@ -1,5 +1,5 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-PhUVprXA.cjs";
2
- import { t as IdentityPort } from "../identity-CmD4l_Mp.cjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-D2Ue2nd2.cjs";
2
+ import { t as IdentityPort } from "../identity-pOt5KxJB.cjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.cjs";
4
4
  import { IncomingManageRequest, MeshSession } from "./mesh-session.cjs";
5
5
  //#region src/domain/capability-request.d.ts
@@ -1,5 +1,5 @@
1
- import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-PhUVprXA.mjs";
2
- import { t as IdentityPort } from "../identity-BXrJ6_YM.mjs";
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, u as CapabilityGrantOk } from "../protocol-D2Ue2nd2.mjs";
2
+ import { t as IdentityPort } from "../identity-Br6e0Rx5.mjs";
3
3
  import { t as Clock } from "../clock-DiSx-WKM.mjs";
4
4
  import { IncomingManageRequest, MeshSession } from "./mesh-session.mjs";
5
5
  //#region src/domain/capability-request.d.ts
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-PhUVprXA.cjs";
1
+ import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-D2Ue2nd2.cjs";
2
2
  import { KeyValueStorage } from "../ports/storage.cjs";
3
- import { t as IdentityPort } from "../identity-CmD4l_Mp.cjs";
3
+ import { t as IdentityPort } from "../identity-pOt5KxJB.cjs";
4
4
  //#region src/domain/data-sync.d.ts
5
5
  export interface DataSyncOptions {
6
6
  identity: IdentityPort;
@@ -1,6 +1,6 @@
1
- import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-PhUVprXA.mjs";
1
+ import { D as DeviceId, E as DataRequestFrame, T as DataHaveFrame, w as DataEntriesFrame } from "../protocol-D2Ue2nd2.mjs";
2
2
  import { KeyValueStorage } from "../ports/storage.mjs";
3
- import { t as IdentityPort } from "../identity-BXrJ6_YM.mjs";
3
+ import { t as IdentityPort } from "../identity-Br6e0Rx5.mjs";
4
4
  //#region src/domain/data-sync.d.ts
5
5
  export interface DataSyncOptions {
6
6
  identity: IdentityPort;
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-PhUVprXA.cjs";
1
+ import { D as DeviceId } from "../protocol-D2Ue2nd2.cjs";
2
2
  //#region src/domain/device-id.d.ts
3
3
  /** Lowercase, byte-exact hex for an arbitrary-length byte string -- the same encoding convention deviceIdToHex uses for the fixed-length device-id case, extracted so any other byte string needing a stable, displayable, map-keyable text form (e.g. a token-id, which tokens.cddl defines as an arbitrary-length bstr rather than a 32-byte device-id) can use the identical convention without going through a device-id-shaped function. */
4
4
  export declare function bytesToHex(bytes: Uint8Array): string;
@@ -1,4 +1,4 @@
1
- import { D as DeviceId } from "../protocol-PhUVprXA.mjs";
1
+ import { D as DeviceId } from "../protocol-D2Ue2nd2.mjs";
2
2
  //#region src/domain/device-id.d.ts
3
3
  /** Lowercase, byte-exact hex for an arbitrary-length byte string -- the same encoding convention deviceIdToHex uses for the fixed-length device-id case, extracted so any other byte string needing a stable, displayable, map-keyable text form (e.g. a token-id, which tokens.cddl defines as an arbitrary-length bstr rather than a 32-byte device-id) can use the identical convention without going through a device-id-shaped function. */
4
4
  export declare function bytesToHex(bytes: Uint8Array): string;
@@ -0,0 +1,64 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_domain_device_id = require("./device-id.cjs");
3
+ //#region src/domain/direct-manage-request.ts
4
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against mesh-session.ts's own applyManageRequest/applyFrame). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
5
+ async function acceptDirectManageRequest(connection) {
6
+ const result = await connection.receive()[Symbol.asyncIterator]().next();
7
+ if (result.done === true || result.value.type !== "manage-request") return null;
8
+ const frame = result.value;
9
+ return {
10
+ requestId: frame["request-id"],
11
+ command: frame.command,
12
+ scope: frame.scope,
13
+ ...frame.token !== void 0 ? { token: frame.token } : {},
14
+ respond: async (outcome) => {
15
+ const response = {
16
+ type: "manage-response",
17
+ "request-id": frame["request-id"],
18
+ outcome
19
+ };
20
+ await connection.send(response);
21
+ await connection.close();
22
+ }
23
+ };
24
+ }
25
+ async function waitForDirectManageResponse(link, requestId) {
26
+ for await (const frame of link.receive()) if (frame.type === "manage-response" && frame["request-id"] === requestId) return frame.outcome;
27
+ throw new Error("connection closed before a response arrived");
28
+ }
29
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
30
+ async function sendDirectManageRequest(transport, address, command, scope, options = {}) {
31
+ const link = await transport.connect(address);
32
+ if (options.expectedPeerDeviceId !== void 0 && link.peerDeviceId !== void 0 && require_domain_device_id.deviceIdToHex(link.peerDeviceId) !== require_domain_device_id.deviceIdToHex(options.expectedPeerDeviceId)) {
33
+ await link.close();
34
+ throw new Error("direct connection's authenticated peer-device-id does not match the expected target -- refusing as a possible spoofing attempt");
35
+ }
36
+ const requestId = 0;
37
+ const frame = {
38
+ type: "manage-request",
39
+ "request-id": requestId,
40
+ command,
41
+ scope,
42
+ ...options.token !== void 0 ? { token: options.token } : {}
43
+ };
44
+ await link.send(frame);
45
+ const responsePromise = waitForDirectManageResponse(link, requestId);
46
+ let outcome;
47
+ if (options.timeoutMs === void 0) outcome = await responsePromise;
48
+ else {
49
+ outcome = await Promise.race([responsePromise, new Promise((resolve) => {
50
+ setTimeout(() => {
51
+ resolve({
52
+ result: "error",
53
+ code: "timeout"
54
+ });
55
+ }, options.timeoutMs);
56
+ })]);
57
+ responsePromise.catch(() => void 0);
58
+ }
59
+ await link.close();
60
+ return outcome;
61
+ }
62
+ //#endregion
63
+ exports.acceptDirectManageRequest = acceptDirectManageRequest;
64
+ exports.sendDirectManageRequest = sendDirectManageRequest;
@@ -0,0 +1,16 @@
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.cjs";
2
+ import { r as Transport, t as Connection } from "../transport-fUY3alfO.cjs";
3
+ import { IncomingManageRequest, ManageOutcome } from "./mesh-session.cjs";
4
+ //#region src/domain/direct-manage-request.d.ts
5
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against mesh-session.ts's own applyManageRequest/applyFrame). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
6
+ export declare function acceptDirectManageRequest(connection: Readonly<Connection>): Promise<IncomingManageRequest | null>;
7
+ export interface DirectManageRequestOptions {
8
+ /** Refuse the connection as a possible spoofing attempt if the transport's own authenticated Connection.peerDeviceId (never a value read off the wire -- see the Transport port's own doc comment) doesn't match this. Left unchecked when the transport gives no authenticated peerDeviceId at all (an unauthenticated transport, or a peer that presented no credential) -- the caller proceeds at its own risk in that case, exactly the same trust boundary Connection.peerDeviceId already documents for every other consumer of it. */
9
+ expectedPeerDeviceId?: DeviceId;
10
+ token?: CapabilityToken;
11
+ /** Omit to wait indefinitely, matching sendManageRequest's own default. */
12
+ timeoutMs?: number;
13
+ }
14
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
15
+ export declare function sendDirectManageRequest(transport: Readonly<Pick<Transport, "connect">>, address: string, command: ManageCommand, scope: Readonly<CapabilityScope>, options?: Readonly<DirectManageRequestOptions>): Promise<ManageOutcome>;
16
+ //#endregion
@@ -0,0 +1,16 @@
1
+ import { D as DeviceId, V as ManageCommand, f as CapabilityScope, p as CapabilityToken } from "../protocol-D2Ue2nd2.mjs";
2
+ import { r as Transport, t as Connection } from "../transport-C5wU0UtA.mjs";
3
+ import { IncomingManageRequest, ManageOutcome } from "./mesh-session.mjs";
4
+ //#region src/domain/direct-manage-request.d.ts
5
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against mesh-session.ts's own applyManageRequest/applyFrame). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
6
+ export declare function acceptDirectManageRequest(connection: Readonly<Connection>): Promise<IncomingManageRequest | null>;
7
+ export interface DirectManageRequestOptions {
8
+ /** Refuse the connection as a possible spoofing attempt if the transport's own authenticated Connection.peerDeviceId (never a value read off the wire -- see the Transport port's own doc comment) doesn't match this. Left unchecked when the transport gives no authenticated peerDeviceId at all (an unauthenticated transport, or a peer that presented no credential) -- the caller proceeds at its own risk in that case, exactly the same trust boundary Connection.peerDeviceId already documents for every other consumer of it. */
9
+ expectedPeerDeviceId?: DeviceId;
10
+ token?: CapabilityToken;
11
+ /** Omit to wait indefinitely, matching sendManageRequest's own default. */
12
+ timeoutMs?: number;
13
+ }
14
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
15
+ export declare function sendDirectManageRequest(transport: Readonly<Pick<Transport, "connect">>, address: string, command: ManageCommand, scope: Readonly<CapabilityScope>, options?: Readonly<DirectManageRequestOptions>): Promise<ManageOutcome>;
16
+ //#endregion
@@ -0,0 +1,62 @@
1
+ import { deviceIdToHex } from "./device-id.mjs";
2
+ //#region src/domain/direct-manage-request.ts
3
+ /** Reads exactly one frame off a freshly-accepted, bare connection and, if it's a manage-request, returns an IncomingManageRequest ready to hand to the same application-level dispatch logic session.incomingManageRequests already feeds elsewhere -- no handshake or gossip is ever read or sent on this connection (wire-mesh#38: the manage-request/manage-response exchange has no dependency on handshake state at the dispatch layer, confirmed against mesh-session.ts's own applyManageRequest/applyFrame). respond() sends the manage-response directly and closes the connection, since a one-off request/response is this connection's entire purpose -- unlike a real MeshSession, there is nothing further to do with it afterwards. Resolves null, without closing the connection (that's the caller's call), for any other first frame or if the connection ends before one arrives: interpreting either case is a Transport.listen() caller's own business, e.g. peeking the first frame to route between this path and acceptMeshSession's own handshake path. */
4
+ async function acceptDirectManageRequest(connection) {
5
+ const result = await connection.receive()[Symbol.asyncIterator]().next();
6
+ if (result.done === true || result.value.type !== "manage-request") return null;
7
+ const frame = result.value;
8
+ return {
9
+ requestId: frame["request-id"],
10
+ command: frame.command,
11
+ scope: frame.scope,
12
+ ...frame.token !== void 0 ? { token: frame.token } : {},
13
+ respond: async (outcome) => {
14
+ const response = {
15
+ type: "manage-response",
16
+ "request-id": frame["request-id"],
17
+ outcome
18
+ };
19
+ await connection.send(response);
20
+ await connection.close();
21
+ }
22
+ };
23
+ }
24
+ async function waitForDirectManageResponse(link, requestId) {
25
+ for await (const frame of link.receive()) if (frame.type === "manage-response" && frame["request-id"] === requestId) return frame.outcome;
26
+ throw new Error("connection closed before a response arrived");
27
+ }
28
+ /** Sends exactly one manage-request over a fresh, bare connection with no handshake, negotiation, or gossip exchanged, and no session left behind afterwards -- the connection closes once the correlated response arrives, the wait times out, or the peer-device-id check below refuses it. For attempting a direct connection to a peer whose reachable address is already known (wire-mesh#38), as an alternative to routing the same request through a relay via an established MeshSession's own sendManageRequest(targetDevice, ...). Resolves the same ManageOutcome shape sendManageRequest does -- including `{ result: "error", code: "timeout" }` on a timeout, never a rejection -- so a caller can fall back to the relay path uniformly regardless of which kind of failure this returns for the non-spoofing cases; a peer-device-id mismatch is the one case that rejects outright, since it is not an ordinary reachability failure a relay fallback should silently paper over. */
29
+ async function sendDirectManageRequest(transport, address, command, scope, options = {}) {
30
+ const link = await transport.connect(address);
31
+ if (options.expectedPeerDeviceId !== void 0 && link.peerDeviceId !== void 0 && deviceIdToHex(link.peerDeviceId) !== deviceIdToHex(options.expectedPeerDeviceId)) {
32
+ await link.close();
33
+ throw new Error("direct connection's authenticated peer-device-id does not match the expected target -- refusing as a possible spoofing attempt");
34
+ }
35
+ const requestId = 0;
36
+ const frame = {
37
+ type: "manage-request",
38
+ "request-id": requestId,
39
+ command,
40
+ scope,
41
+ ...options.token !== void 0 ? { token: options.token } : {}
42
+ };
43
+ await link.send(frame);
44
+ const responsePromise = waitForDirectManageResponse(link, requestId);
45
+ let outcome;
46
+ if (options.timeoutMs === void 0) outcome = await responsePromise;
47
+ else {
48
+ outcome = await Promise.race([responsePromise, new Promise((resolve) => {
49
+ setTimeout(() => {
50
+ resolve({
51
+ result: "error",
52
+ code: "timeout"
53
+ });
54
+ }, options.timeoutMs);
55
+ })]);
56
+ responsePromise.catch(() => void 0);
57
+ }
58
+ await link.close();
59
+ return outcome;
60
+ }
61
+ //#endregion
62
+ export { acceptDirectManageRequest, sendDirectManageRequest };
@@ -0,0 +1,44 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_domain_device_id = require("./device-id.cjs");
3
+ //#region src/domain/gossip-expansion.ts
4
+ function toError(value) {
5
+ return value instanceof Error ? value : new Error(String(value));
6
+ }
7
+ /** Builds the per-device expansion state machine: ask once, dial every advertised address in order on approval, report exactly one outcome. */
8
+ function createGossipExpansion(options) {
9
+ const selfHex = require_domain_device_id.deviceIdToHex(options.selfDeviceId);
10
+ const considered = /* @__PURE__ */ new Set();
11
+ async function expand(candidate) {
12
+ let approved;
13
+ try {
14
+ approved = await options.shouldExpand(candidate);
15
+ } catch (error) {
16
+ options.onExpansionFailed?.(candidate, [toError(error)]);
17
+ return;
18
+ }
19
+ if (!approved) {
20
+ options.onExpansionDeclined?.(candidate);
21
+ return;
22
+ }
23
+ const errors = [];
24
+ for (const address of candidate.addresses) try {
25
+ const session = await options.dial(address);
26
+ options.onExpanded(candidate, address, session);
27
+ return;
28
+ } catch (error) {
29
+ errors.push(toError(error));
30
+ }
31
+ options.onExpansionFailed?.(candidate, errors);
32
+ }
33
+ return { considerAdvert(advert) {
34
+ const hex = require_domain_device_id.deviceIdToHex(advert.device);
35
+ if (hex === selfHex || considered.has(hex) || advert.addresses.length === 0) return;
36
+ considered.add(hex);
37
+ expand({
38
+ device: advert.device,
39
+ addresses: advert.addresses
40
+ });
41
+ } };
42
+ }
43
+ //#endregion
44
+ exports.createGossipExpansion = createGossipExpansion;
@@ -0,0 +1,29 @@
1
+ import { D as DeviceId, et as PeerAdvert } from "../protocol-D2Ue2nd2.cjs";
2
+ import { MeshSession } from "./mesh-session.cjs";
3
+ //#region src/domain/gossip-expansion.d.ts
4
+ /** One not-yet-considered device this session's gossip directory has surfaced, carrying only what a dial/trust decision needs: the device-id to decide identity, and the "host:port" addresses its own peer-advert claims are directly reachable at, in the order it advertised them. */
5
+ export interface GossipExpansionCandidate {
6
+ readonly device: DeviceId;
7
+ readonly addresses: readonly string[];
8
+ }
9
+ export interface GossipExpansionOptions {
10
+ /** This session's own device-id, so its own self-advert (looped back through gossip forwarding or a hub's catch-up frame) is never offered to shouldExpand as if it were a distinct peer. */
11
+ selfDeviceId: DeviceId;
12
+ /** Decides whether to attempt a direct connection to a newly-discovered device at all -- called at most once per device for this GossipExpansion instance's lifetime, never re-asked if the same device later re-adverts (an address change, a fresher snapshot-seconds). Returning true does not guarantee a connection: dial may still fail for every one of the candidate's addresses, reported via onExpansionFailed. */
13
+ shouldExpand: (candidate: Readonly<GossipExpansionCandidate>) => Promise<boolean> | boolean;
14
+ /** Attempts one direct connection to `address`, resolving with a fully connected MeshSession or rejecting if the dial/handshake fails. Typically wraps createMeshSession(transport, identity, ...) followed by session.connect(address, localDomains). Called once per address of an approved candidate, in the order it advertised them, stopping at the first that succeeds -- the caller owns cleanup of any partially-established session behind a rejected dial, since this module never holds a reference to one that didn't resolve. */
15
+ dial: (address: string) => Promise<MeshSession>;
16
+ /** Called once a candidate's dial succeeds, with the specific address that connected (one of candidate.addresses, not necessarily the first -- earlier ones may have failed) and the now-connected session, so the caller can wire it into whatever it already does for its own sessions (render it, merge its directory, route messages). */
17
+ onExpanded: (candidate: Readonly<GossipExpansionCandidate>, address: string, session: MeshSession) => void;
18
+ /** Called when shouldExpand itself declines a candidate. */
19
+ onExpansionDeclined?: (candidate: Readonly<GossipExpansionCandidate>) => void;
20
+ /** Called once every one of an approved candidate's addresses has failed to connect, or shouldExpand itself rejected -- in the latter case, errors holds exactly the one rejection reason, so a caller doesn't need two separate failure channels to observe every way expansion can fail short of success. */
21
+ onExpansionFailed?: (candidate: Readonly<GossipExpansionCandidate>, errors: readonly Error[]) => void;
22
+ }
23
+ export interface GossipExpansion {
24
+ /** Feed one just-received peer-advert -- from the session whose peer-adverts this expansion watches, via its own onPeerAdvert hook -- to decide whether it names a newly-discoverable peer worth expanding to, and if so kick off an expansion attempt for it. Never awaits a dial to completion inline: expansion happens in the background, observable only through onExpanded/onExpansionDeclined/onExpansionFailed. */
25
+ considerAdvert: (advert: Readonly<PeerAdvert>) => void;
26
+ }
27
+ /** Builds the per-device expansion state machine: ask once, dial every advertised address in order on approval, report exactly one outcome. */
28
+ export declare function createGossipExpansion(options: Readonly<GossipExpansionOptions>): GossipExpansion;
29
+ //#endregion
@@ -0,0 +1,29 @@
1
+ import { D as DeviceId, et as PeerAdvert } from "../protocol-D2Ue2nd2.mjs";
2
+ import { MeshSession } from "./mesh-session.mjs";
3
+ //#region src/domain/gossip-expansion.d.ts
4
+ /** One not-yet-considered device this session's gossip directory has surfaced, carrying only what a dial/trust decision needs: the device-id to decide identity, and the "host:port" addresses its own peer-advert claims are directly reachable at, in the order it advertised them. */
5
+ export interface GossipExpansionCandidate {
6
+ readonly device: DeviceId;
7
+ readonly addresses: readonly string[];
8
+ }
9
+ export interface GossipExpansionOptions {
10
+ /** This session's own device-id, so its own self-advert (looped back through gossip forwarding or a hub's catch-up frame) is never offered to shouldExpand as if it were a distinct peer. */
11
+ selfDeviceId: DeviceId;
12
+ /** Decides whether to attempt a direct connection to a newly-discovered device at all -- called at most once per device for this GossipExpansion instance's lifetime, never re-asked if the same device later re-adverts (an address change, a fresher snapshot-seconds). Returning true does not guarantee a connection: dial may still fail for every one of the candidate's addresses, reported via onExpansionFailed. */
13
+ shouldExpand: (candidate: Readonly<GossipExpansionCandidate>) => Promise<boolean> | boolean;
14
+ /** Attempts one direct connection to `address`, resolving with a fully connected MeshSession or rejecting if the dial/handshake fails. Typically wraps createMeshSession(transport, identity, ...) followed by session.connect(address, localDomains). Called once per address of an approved candidate, in the order it advertised them, stopping at the first that succeeds -- the caller owns cleanup of any partially-established session behind a rejected dial, since this module never holds a reference to one that didn't resolve. */
15
+ dial: (address: string) => Promise<MeshSession>;
16
+ /** Called once a candidate's dial succeeds, with the specific address that connected (one of candidate.addresses, not necessarily the first -- earlier ones may have failed) and the now-connected session, so the caller can wire it into whatever it already does for its own sessions (render it, merge its directory, route messages). */
17
+ onExpanded: (candidate: Readonly<GossipExpansionCandidate>, address: string, session: MeshSession) => void;
18
+ /** Called when shouldExpand itself declines a candidate. */
19
+ onExpansionDeclined?: (candidate: Readonly<GossipExpansionCandidate>) => void;
20
+ /** Called once every one of an approved candidate's addresses has failed to connect, or shouldExpand itself rejected -- in the latter case, errors holds exactly the one rejection reason, so a caller doesn't need two separate failure channels to observe every way expansion can fail short of success. */
21
+ onExpansionFailed?: (candidate: Readonly<GossipExpansionCandidate>, errors: readonly Error[]) => void;
22
+ }
23
+ export interface GossipExpansion {
24
+ /** Feed one just-received peer-advert -- from the session whose peer-adverts this expansion watches, via its own onPeerAdvert hook -- to decide whether it names a newly-discoverable peer worth expanding to, and if so kick off an expansion attempt for it. Never awaits a dial to completion inline: expansion happens in the background, observable only through onExpanded/onExpansionDeclined/onExpansionFailed. */
25
+ considerAdvert: (advert: Readonly<PeerAdvert>) => void;
26
+ }
27
+ /** Builds the per-device expansion state machine: ask once, dial every advertised address in order on approval, report exactly one outcome. */
28
+ export declare function createGossipExpansion(options: Readonly<GossipExpansionOptions>): GossipExpansion;
29
+ //#endregion
@@ -0,0 +1,43 @@
1
+ import { deviceIdToHex } from "./device-id.mjs";
2
+ //#region src/domain/gossip-expansion.ts
3
+ function toError(value) {
4
+ return value instanceof Error ? value : new Error(String(value));
5
+ }
6
+ /** Builds the per-device expansion state machine: ask once, dial every advertised address in order on approval, report exactly one outcome. */
7
+ function createGossipExpansion(options) {
8
+ const selfHex = deviceIdToHex(options.selfDeviceId);
9
+ const considered = /* @__PURE__ */ new Set();
10
+ async function expand(candidate) {
11
+ let approved;
12
+ try {
13
+ approved = await options.shouldExpand(candidate);
14
+ } catch (error) {
15
+ options.onExpansionFailed?.(candidate, [toError(error)]);
16
+ return;
17
+ }
18
+ if (!approved) {
19
+ options.onExpansionDeclined?.(candidate);
20
+ return;
21
+ }
22
+ const errors = [];
23
+ for (const address of candidate.addresses) try {
24
+ const session = await options.dial(address);
25
+ options.onExpanded(candidate, address, session);
26
+ return;
27
+ } catch (error) {
28
+ errors.push(toError(error));
29
+ }
30
+ options.onExpansionFailed?.(candidate, errors);
31
+ }
32
+ return { considerAdvert(advert) {
33
+ const hex = deviceIdToHex(advert.device);
34
+ if (hex === selfHex || considered.has(hex) || advert.addresses.length === 0) return;
35
+ considered.add(hex);
36
+ expand({
37
+ device: advert.device,
38
+ addresses: advert.addresses
39
+ });
40
+ } };
41
+ }
42
+ //#endregion
43
+ export { createGossipExpansion };
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, f as CapabilityScope } from "../protocol-PhUVprXA.cjs";
1
+ import { D as DeviceId, f as CapabilityScope } from "../protocol-D2Ue2nd2.cjs";
2
2
  import { DirectoryEntry, MeshSession } from "./mesh-session.cjs";
3
3
  import { z } from "zod";
4
4
  //#region src/domain/grant-candidates.d.ts
@@ -1,4 +1,4 @@
1
- import { D as DeviceId, f as CapabilityScope } from "../protocol-PhUVprXA.mjs";
1
+ import { D as DeviceId, f as CapabilityScope } from "../protocol-D2Ue2nd2.mjs";
2
2
  import { DirectoryEntry, MeshSession } from "./mesh-session.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/domain/grant-candidates.d.ts
@@ -1,4 +1,4 @@
1
- import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-PhUVprXA.cjs";
1
+ import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-D2Ue2nd2.cjs";
2
2
  //#region src/domain/handshake.d.ts
3
3
  /** The highest protocol version this build of core understands. */
4
4
  export declare const SUPPORTED_PROTOCOL_VERSION: ProtocolVersion;
@@ -1,4 +1,4 @@
1
- import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-PhUVprXA.mjs";
1
+ import { A as DomainId, R as HandshakeFrame, lt as ProtocolVersion } from "../protocol-D2Ue2nd2.mjs";
2
2
  //#region src/domain/handshake.d.ts
3
3
  /** The highest protocol version this build of core understands. */
4
4
  export declare const SUPPORTED_PROTOCOL_VERSION: ProtocolVersion;