hrpc-inspector 0.1.0 → 0.1.1
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/dist/detect.d.ts +10 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +2 -1
- package/dist/observe.d.ts +119 -0
- package/dist/observe.js +2 -1
- package/dist/reporters.d.ts +25 -0
- package/dist/wrap-client.d.ts +27 -0
- package/package.json +13 -10
- package/scripts/rewrite-dts-specifiers.mjs +32 -0
- package/src/externals.d.ts +16 -0
- package/src/observe.ts +3 -1
package/dist/detect.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Runtime = 'pear' | 'bare' | 'react-native' | 'electron' | 'node' | 'browser' | 'unknown';
|
|
2
|
+
export interface RuntimeInfo {
|
|
3
|
+
runtime: Runtime;
|
|
4
|
+
/** Bare is available (Pear, plain Bare, or an RN bare-kit worklet) → hyperswarm can run in-process. */
|
|
5
|
+
isBareRuntime: boolean;
|
|
6
|
+
/** A browser/RN JS context where we must export over a bridge, not a socket. */
|
|
7
|
+
isBridgeOnly: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Pure and injectable (pass a fake global in tests). Order matters: most specific first. */
|
|
10
|
+
export declare function detectRuntime(env?: any): RuntimeInfo;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { observe, DEFAULT_TOPIC } from './observe.js';
|
|
2
|
+
export type { ObserveOptions, ObserveHandle } from './observe.js';
|
|
3
|
+
export { detectRuntime } from './detect.js';
|
|
4
|
+
export type { Runtime, RuntimeInfo } from './detect.js';
|
|
5
|
+
export { wrapClient } from './wrap-client.js';
|
|
6
|
+
export type { Report, WrapClientOptions } from './wrap-client.js';
|
|
7
|
+
export { createWebSocketReporter } from './reporters.js';
|
|
8
|
+
export type { Reporter, WebSocketReporterOptions } from './reporters.js';
|
|
9
|
+
export { instrumentDataChannel, instrumentPeerConnection, } from 'hrpc-inspector-probe/adapters/webrtc';
|
|
10
|
+
export { instrumentWebSocket } from 'hrpc-inspector-probe/adapters/websocket';
|
|
11
|
+
export { instrumentHyperswarmStream } from 'hrpc-inspector-probe/adapters/hyperswarm';
|
|
12
|
+
export { CollectorSink } from 'hrpc-inspector-probe/core/sink';
|
|
13
|
+
export { BatchFlusher } from 'hrpc-inspector-probe/flush';
|
|
14
|
+
export { createHyperswarmExporter } from 'hrpc-inspector-probe/exporters/hyperswarm';
|
|
15
|
+
export { StreamFramer, encodeFrame } from 'hrpc-inspector-probe/transport/framing';
|
|
16
|
+
export type { EventSink, L2Event, L2EventType } from 'hrpc-inspector-probe/sink';
|
package/dist/index.js
CHANGED
|
@@ -247,7 +247,8 @@ async function autoSwarm(topic, onConnection) {
|
|
|
247
247
|
const Hyperswarm = (await import("hyperswarm")).default;
|
|
248
248
|
const crypto = (await import("hypercore-crypto")).default;
|
|
249
249
|
const swarm = new Hyperswarm();
|
|
250
|
-
const
|
|
250
|
+
const enc = new TextEncoder();
|
|
251
|
+
const topicKey = crypto.hash(enc.encode(topic));
|
|
251
252
|
swarm.on("connection", (conn) => onConnection(conn));
|
|
252
253
|
await swarm.join(topicKey, { client: true, server: false }).flushed();
|
|
253
254
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { BatchFlusher } from 'hrpc-inspector-probe/flush';
|
|
2
|
+
import type { WritableStreamLike, HyperswarmExporter } from 'hrpc-inspector-probe/exporters/hyperswarm';
|
|
3
|
+
import { Redactor } from 'hrpc-inspector-probe/redactor';
|
|
4
|
+
import type { EventSink, L2Event } from 'hrpc-inspector-probe/sink';
|
|
5
|
+
import type { Runtime } from './detect.js';
|
|
6
|
+
import type { Report, WrapClientOptions } from './wrap-client.js';
|
|
7
|
+
export declare const DEFAULT_TOPIC = "p2p-observability-hub:v0";
|
|
8
|
+
export interface SourceInfo {
|
|
9
|
+
/** Stable id used to group/select this source in the GUI. */
|
|
10
|
+
id: string;
|
|
11
|
+
/** Human label shown in the selector. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Detected runtime (react-native | pear | bare | electron | node | browser). */
|
|
14
|
+
runtime: string;
|
|
15
|
+
/** Physical device id (client-supplied; e.g. an app's own options.deviceId). Lets the GUI select by device. */
|
|
16
|
+
deviceId?: string;
|
|
17
|
+
/** App / install instance id — distinguishes MULTIPLE apps sharing one package. */
|
|
18
|
+
appId?: string;
|
|
19
|
+
[extra: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface ObserveOptions {
|
|
22
|
+
/** Flush cadence; must be in the collector's verified 100-250ms window. Default 200. */
|
|
23
|
+
intervalMs?: number;
|
|
24
|
+
/** Explicit hub connection (a NoiseSecretStream / socket). Skips auto-swarm. */
|
|
25
|
+
stream?: WritableStreamLike;
|
|
26
|
+
/** Fully custom exporter (e.g. Rozenite bridge, OTLP). Skips everything else. */
|
|
27
|
+
exporter?: HyperswarmExporter;
|
|
28
|
+
/** Hub topic string; must match the hub's. Default DEFAULT_TOPIC. */
|
|
29
|
+
topic?: string;
|
|
30
|
+
/** Set false to disable Bare/Pear auto-dial (then attach() a stream yourself). */
|
|
31
|
+
autoSwarm?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Send events to a local GUI over WebSocket instead of the Hyperswarm hub — the reliable
|
|
34
|
+
* dev-viewer path for EVERY runtime (RN / Pear / desktop / browser),
|
|
35
|
+
* e.g. 'ws://127.0.0.1:9420/ws'. When set, redaction defaults OFF (local viewing);
|
|
36
|
+
* pass redact:true to force it. Takes precedence over stream/auto-swarm.
|
|
37
|
+
*/
|
|
38
|
+
websocket?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Redact every event on the way OUT (peer ids hashed, URLs host-only, bodies off)
|
|
41
|
+
* — ON by default, because the export path (hub / .p2plog) leaves the device.
|
|
42
|
+
* Set false ONLY for a purely-local view that never leaves the machine (F3/F4/F9).
|
|
43
|
+
*/
|
|
44
|
+
redact?: boolean;
|
|
45
|
+
/** Supply a shared Redactor (e.g. to reuse its local peer reverse-map). Default: a fresh one. */
|
|
46
|
+
redactor?: Redactor;
|
|
47
|
+
/** Per-`method` opt-in allowlist for full payload bodies (default: none — bodies summarised). */
|
|
48
|
+
bodyAllowlist?: Iterable<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Identity of THIS device/app, for the GUI's device & app selectors. A string label, or an
|
|
51
|
+
* object `{ id?, label?, deviceId?, appId?, app?, device?, variant? }`.
|
|
52
|
+
* • deviceId — physical device (client-supplied; e.g. the app's own options.deviceId) → select by device
|
|
53
|
+
* • appId — app/install instance → distinguishes MULTIPLE apps sharing one package
|
|
54
|
+
* Omit → auto: browser reuses localStorage for a stable id (clears on delete-data); other
|
|
55
|
+
* runtimes get a session id (pass deviceId/appId for stability). Only the client knows its
|
|
56
|
+
* app name / device / variant, so it supplies those.
|
|
57
|
+
*/
|
|
58
|
+
source?: string | Record<string, unknown>;
|
|
59
|
+
/**
|
|
60
|
+
* Allow the GUI to REPLAY a logged call back into this client (re-run a call the app already
|
|
61
|
+
* made). Boundary: the GUI references a call by corrId and the METHOD is resolved from this
|
|
62
|
+
* client's own call log — it never crosses the wire, so a replay cannot be redirected to a
|
|
63
|
+
* method the app never called. The ARGS may be supplied by the GUI (edit-and-replay) and are
|
|
64
|
+
* NOT validated here beyond "must be a JSON array"; use `canReplay` to check them.
|
|
65
|
+
*
|
|
66
|
+
* Fails CLOSED: must be set to `true` EXPLICITLY. There is no dev-detection default — omit it
|
|
67
|
+
* and replay is off, so a shipped build never installs the inbound handler. Only meaningful
|
|
68
|
+
* together with `websocket`.
|
|
69
|
+
*/
|
|
70
|
+
allowInvoke?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Optional gate consulted before a replay runs — return false to block (e.g. to refuse
|
|
73
|
+
* re-running a mutating method like a send). Default: allow. Receives the logged method and the
|
|
74
|
+
* args that will actually be used — which may be UNVALIDATED, GUI-supplied args. This is the
|
|
75
|
+
* only argument check in the system, so an app whose methods mutate state should implement it.
|
|
76
|
+
*/
|
|
77
|
+
canReplay?: (method: string, args: unknown[]) => boolean;
|
|
78
|
+
/** Max logged calls retained for replay (corrId→method+args). Default 500. */
|
|
79
|
+
replayLogMax?: number;
|
|
80
|
+
}
|
|
81
|
+
export interface ObserveHandle {
|
|
82
|
+
/** Wire your L1 adapters into this: `instrumentDataChannel(dc, peerId, obs.sink)`. */
|
|
83
|
+
sink: EventSink;
|
|
84
|
+
flusher: BatchFlusher<L2Event>;
|
|
85
|
+
runtime: Runtime;
|
|
86
|
+
mode: 'websocket' | 'stream' | 'custom' | 'buffering';
|
|
87
|
+
/** The redactor applied on export (null if redact:false). Use `.resolvePeer()` for a local names view. */
|
|
88
|
+
redactor: Redactor | null;
|
|
89
|
+
/** This device/app's resolved identity — stamped on every event as `src` and announced to the GUI. */
|
|
90
|
+
source: SourceInfo;
|
|
91
|
+
/** Report one event directly to the sink (this is what wrapClient uses). */
|
|
92
|
+
report: Report;
|
|
93
|
+
/** Wrap an RPC/service client so its calls + subscription streams are observed. */
|
|
94
|
+
wrapClient<T extends Record<string, any>>(client: T, opts?: WrapClientOptions): T;
|
|
95
|
+
/**
|
|
96
|
+
* Snapshot of request/response calls currently in flight — started (request.start) but not yet
|
|
97
|
+
* settled (request.end). Each `{ method, corrId, t }`. Subscription streams are not included (see
|
|
98
|
+
* observe.ts). Wire this to a thread-occupancy probe's `getInflight` so a stall names the call
|
|
99
|
+
* that was running (e.g. hrpc-inspector-threads).
|
|
100
|
+
*/
|
|
101
|
+
inflight(): Array<{
|
|
102
|
+
method: string;
|
|
103
|
+
corrId: string;
|
|
104
|
+
t: number;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Bridge a hypertrace tracer into the SAME event stream: pass hypertrace's `setTraceFunction`
|
|
108
|
+
* and every `trace()` from instrumented classes (e.g. a WebRTC/call layer) becomes a
|
|
109
|
+
* `type:'trace'` event on a distinct "trace" plane in the GUI. Install this BEFORE the traced
|
|
110
|
+
* objects are constructed (hypertrace only traces objects created after the function is set).
|
|
111
|
+
* Dependency-free: the app supplies `setTraceFunction`, so the package never imports hypertrace.
|
|
112
|
+
* Returns a disposer that stops forwarding.
|
|
113
|
+
*/
|
|
114
|
+
bridgeTraces(setTraceFunction: (fn: (trace: any) => void) => void): () => void;
|
|
115
|
+
/** Attach a hub connection later (auto-swarm calls this on 'connection'). */
|
|
116
|
+
attach(stream: WritableStreamLike): void;
|
|
117
|
+
stop(): void;
|
|
118
|
+
}
|
|
119
|
+
export declare function observe(opts?: ObserveOptions): ObserveHandle;
|
package/dist/observe.js
CHANGED
|
@@ -247,7 +247,8 @@ async function autoSwarm(topic, onConnection) {
|
|
|
247
247
|
const Hyperswarm = (await import("hyperswarm")).default;
|
|
248
248
|
const crypto = (await import("hypercore-crypto")).default;
|
|
249
249
|
const swarm = new Hyperswarm();
|
|
250
|
-
const
|
|
250
|
+
const enc = new TextEncoder();
|
|
251
|
+
const topicKey = crypto.hash(enc.encode(topic));
|
|
251
252
|
swarm.on("connection", (conn) => onConnection(conn));
|
|
252
253
|
await swarm.join(topicKey, { client: true, server: false }).flushed();
|
|
253
254
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface Reporter {
|
|
2
|
+
export(batch: unknown[]): void;
|
|
3
|
+
close(): void;
|
|
4
|
+
}
|
|
5
|
+
export interface WebSocketReporterOptions {
|
|
6
|
+
/** WebSocket implementation. Default globalThis.WebSocket (RN/browser/Bare/Node ≥22 have it). */
|
|
7
|
+
WebSocketImpl?: any;
|
|
8
|
+
/** Max events buffered while disconnected (oldest dropped past this). Default 1000. */
|
|
9
|
+
maxQueue?: number;
|
|
10
|
+
/** Reconnect delay ms. Default 3000. */
|
|
11
|
+
reconnectMs?: number;
|
|
12
|
+
/** Source identity sent once per connection as `{ __source }` so the GUI can build a device selector. */
|
|
13
|
+
announce?: unknown;
|
|
14
|
+
/**
|
|
15
|
+
* Inbound frame handler. When set, the reporter becomes BIDIRECTIONAL — it installs
|
|
16
|
+
* `ws.onmessage` and forwards parsed frames here. observe() passes this ONLY in dev (for the
|
|
17
|
+
* replay/invoke path); leaving it undefined keeps the reporter strictly send-only, as before.
|
|
18
|
+
*/
|
|
19
|
+
onMessage?: (msg: any) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Send each event as one JSON WebSocket message to `url` (e.g. ws://127.0.0.1:9420/ws).
|
|
23
|
+
* Auto-connects, queues while down, flushes on open, reconnects on close. Never throws.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createWebSocketReporter(url: string, opts?: WebSocketReporterOptions): Reporter;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type Report = (event: Record<string, unknown>) => void;
|
|
2
|
+
export interface WrapClientOptions {
|
|
3
|
+
/** Summarise args/response/stream items before reporting. Default: bounded JSON. */
|
|
4
|
+
preview?: (v: unknown) => unknown;
|
|
5
|
+
/** Monitor subscription streams' data/end/error. Default true. */
|
|
6
|
+
monitorStreams?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Called with the RAW (un-previewed) args at each call site, keyed by the minted corrId.
|
|
9
|
+
* observe() uses this to keep a bounded call-log so a GUI can REPLAY a logged call by corrId.
|
|
10
|
+
* The METHOD comes from this log, never from the wire, so a replay cannot be redirected to a
|
|
11
|
+
* method the app never called. ARGS, however, may be supplied by the GUI (edit-and-replay) and
|
|
12
|
+
* are not validated by this library — the app's `canReplay(method, args)` hook is the only
|
|
13
|
+
* argument check. Wired only when `allowInvoke: true` is passed explicitly.
|
|
14
|
+
*/
|
|
15
|
+
onCall?: (corrId: string, method: string, args: unknown[]) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function setInvokeContext(ctx: {
|
|
18
|
+
invokeId: string;
|
|
19
|
+
replayOf: string;
|
|
20
|
+
method: string;
|
|
21
|
+
} | null): void;
|
|
22
|
+
/**
|
|
23
|
+
* Wrap a client object of namespaced methods (`client.namespace.method(...)`) so every call
|
|
24
|
+
* reports endpoint + request + response/stream. Mutates and returns the same client; method
|
|
25
|
+
* return values and `this` binding are preserved (a wrapped call behaves identically).
|
|
26
|
+
*/
|
|
27
|
+
export declare function wrapClient<T extends Record<string, any>>(client: T, report: Report, opts?: WrapClientOptions): T;
|
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hrpc-inspector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "hrpc based debugging and observability for Bare. Tap an RPC client or a raw transport and read endpoint, request, response, latency and live subscription streams in a local GUI, or merge multi-peer timelines over a Hyperswarm hub.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
|
-
"types": "./
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
9
|
"hrpc-inspector": "bin/hrpc-inspector.mjs"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
-
"types": "./
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
14
|
"default": "./dist/index.js"
|
|
15
15
|
},
|
|
16
16
|
"./detect": {
|
|
17
|
-
"types": "./
|
|
17
|
+
"types": "./dist/detect.d.ts",
|
|
18
18
|
"default": "./dist/detect.js"
|
|
19
19
|
},
|
|
20
20
|
"./observe": {
|
|
21
|
-
"types": "./
|
|
21
|
+
"types": "./dist/observe.d.ts",
|
|
22
22
|
"default": "./dist/observe.js"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
@@ -30,18 +30,20 @@
|
|
|
30
30
|
"LICENSE",
|
|
31
31
|
"NOTICE",
|
|
32
32
|
"README.md",
|
|
33
|
-
"USAGE.md"
|
|
33
|
+
"USAGE.md",
|
|
34
|
+
"scripts"
|
|
34
35
|
],
|
|
35
36
|
"scripts": {
|
|
36
|
-
"build": "rm -rf dist && esbuild src/index.ts src/detect.ts src/observe.ts --bundle --format=esm --platform=neutral --external:hrpc-inspector-probe --external:hrpc-inspector-protocol --external:hyperswarm --external:hypercore-crypto --outdir=dist",
|
|
37
|
+
"build": "rm -rf dist && esbuild src/index.ts src/detect.ts src/observe.ts --bundle --format=esm --platform=neutral --external:hrpc-inspector-probe --external:hrpc-inspector-protocol --external:hyperswarm --external:hypercore-crypto --outdir=dist && npm run build:types",
|
|
37
38
|
"prepare": "npm run build",
|
|
38
|
-
"test": "bash test/run-all.sh"
|
|
39
|
+
"test": "bash test/run-all.sh",
|
|
40
|
+
"build:types": "tsc -p tsconfig.build.json && node scripts/rewrite-dts-specifiers.mjs dist"
|
|
39
41
|
},
|
|
40
42
|
"repository": "git+https://github.com/holepunchto/hrpc-inspector.git",
|
|
41
43
|
"license": "Apache-2.0",
|
|
42
44
|
"sideEffects": false,
|
|
43
45
|
"dependencies": {
|
|
44
|
-
"hrpc-inspector-probe": "^0.1.
|
|
46
|
+
"hrpc-inspector-probe": "^0.1.1"
|
|
45
47
|
},
|
|
46
48
|
"peerDependencies": {
|
|
47
49
|
"hypercore-crypto": "^3.0.0",
|
|
@@ -60,6 +62,7 @@
|
|
|
60
62
|
}
|
|
61
63
|
},
|
|
62
64
|
"devDependencies": {
|
|
63
|
-
"esbuild": "^0.28.2"
|
|
65
|
+
"esbuild": "^0.28.2",
|
|
66
|
+
"typescript": "^7.0.2"
|
|
64
67
|
}
|
|
65
68
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// tsc emits declarations that keep the source's explicit `.ts` specifiers, and a consumer
|
|
2
|
+
// type-checking those hits TS5097 unless they enable allowImportingTsExtensions — which no
|
|
3
|
+
// consumer should. rewriteRelativeImportExtensions only rewrites emitted JS, not .d.ts, so
|
|
4
|
+
// rewrite relative `./x.ts` -> `./x.js` here. Resolution then finds ./x.d.ts beside it.
|
|
5
|
+
import { readdirSync, readFileSync, writeFileSync, statSync } from 'node:fs'
|
|
6
|
+
import { join } from 'node:path'
|
|
7
|
+
|
|
8
|
+
function walk(dir) {
|
|
9
|
+
return readdirSync(dir).flatMap((entry) => {
|
|
10
|
+
const full = join(dir, entry)
|
|
11
|
+
return statSync(full).isDirectory() ? walk(full) : [full]
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const dir = process.argv[2] ?? 'dist'
|
|
16
|
+
const SPECIFIER = /(from\s+|import\s*\(\s*)(['"])(\.\.?\/[^'"]+)\.ts\2/g
|
|
17
|
+
|
|
18
|
+
const declarations = walk(dir).filter((f) => f.endsWith('.d.ts'))
|
|
19
|
+
|
|
20
|
+
let changed = 0
|
|
21
|
+
for (const path of declarations) {
|
|
22
|
+
const before = readFileSync(path, 'utf8')
|
|
23
|
+
const after = before.replace(SPECIFIER, (_m, kw, q, spec) => `${kw}${q}${spec}.js${q}`)
|
|
24
|
+
if (after !== before) { writeFileSync(path, after); changed++ }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const leftover = declarations.filter((f) => SPECIFIER.test(readFileSync(f, 'utf8')))
|
|
28
|
+
if (leftover.length) {
|
|
29
|
+
console.error(`rewrite-dts-specifiers: .ts specifiers survive in ${leftover.join(', ')}`)
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
console.log(`rewrite-dts-specifiers: ${changed} file(s) rewritten in ${dir}`)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// hyperswarm / hypercore-crypto are optional peers pulled in by a guarded dynamic import in the
|
|
2
|
+
// Bare/Pear auto-dial path, and marked --external in the build. Neither ships types, so declare
|
|
3
|
+
// the shape this package actually uses rather than depending on the packages being installed.
|
|
4
|
+
declare module 'hyperswarm' {
|
|
5
|
+
interface SwarmDiscovery { flushed(): Promise<void> }
|
|
6
|
+
export default class Hyperswarm {
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- the stream shape is the consumer's to name
|
|
8
|
+
on(event: 'connection', listener: (conn: any) => void): this
|
|
9
|
+
join(topic: Uint8Array, opts?: { client?: boolean; server?: boolean }): SwarmDiscovery
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare module 'hypercore-crypto' {
|
|
14
|
+
const crypto: { hash(data: Uint8Array): Uint8Array }
|
|
15
|
+
export default crypto
|
|
16
|
+
}
|
package/src/observe.ts
CHANGED
|
@@ -232,7 +232,9 @@ async function autoSwarm(topic: string, onConnection: (stream: WritableStreamLik
|
|
|
232
232
|
const Hyperswarm = (await import('hyperswarm')).default;
|
|
233
233
|
const crypto = (await import('hypercore-crypto')).default;
|
|
234
234
|
const swarm = new Hyperswarm();
|
|
235
|
-
|
|
235
|
+
// Buffer is a Bare/Node global and absent in browsers; this path only runs under Bare.
|
|
236
|
+
const enc = new TextEncoder();
|
|
237
|
+
const topicKey = crypto.hash(enc.encode(topic));
|
|
236
238
|
swarm.on('connection', (conn: WritableStreamLike) => onConnection(conn));
|
|
237
239
|
await swarm.join(topicKey, { client: true, server: false }).flushed();
|
|
238
240
|
}
|