@mindmakr/reins 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +6 -2
- package/dist/adapters.d.ts +25 -0
- package/dist/adapters.js +24 -0
- package/dist/bridge/args.d.ts +15 -0
- package/dist/bridge/args.js +59 -0
- package/dist/bridge/audit.d.ts +13 -0
- package/dist/bridge/audit.js +13 -0
- package/dist/bridge/clipboard.d.ts +2 -0
- package/dist/bridge/clipboard.js +31 -0
- package/dist/bridge/hub.d.ts +56 -0
- package/dist/bridge/hub.js +162 -0
- package/dist/bridge/index.d.ts +11 -0
- package/dist/bridge/index.js +11 -0
- package/dist/bridge/mcp.d.ts +5 -0
- package/dist/bridge/mcp.js +22 -0
- package/dist/bridge/origin.d.ts +7 -0
- package/dist/bridge/origin.js +23 -0
- package/dist/bridge/report-cli.d.ts +13 -0
- package/dist/bridge/report-cli.js +82 -0
- package/dist/bridge/session.d.ts +24 -0
- package/dist/bridge/session.js +116 -0
- package/dist/bridge/snap.d.ts +28 -0
- package/dist/bridge/snap.js +65 -0
- package/dist/bridge/token.d.ts +10 -0
- package/dist/bridge/token.js +35 -0
- package/dist/bridge/tools.d.ts +18 -0
- package/dist/bridge/tools.js +174 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +67 -0
- package/dist/dom.d.ts +2 -0
- package/dist/dom.js +48 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +6 -0
- package/dist/expo.d.ts +27 -0
- package/dist/expo.js +48 -0
- package/dist/index.d.ts +70 -0
- package/dist/index.js +267 -0
- package/dist/metro.d.ts +16 -0
- package/dist/metro.js +45 -0
- package/dist/network.d.ts +14 -0
- package/dist/network.js +50 -0
- package/dist/next-token-route.d.ts +9 -0
- package/dist/next-token-route.js +17 -0
- package/dist/next.d.ts +13 -0
- package/dist/next.js +27 -0
- package/dist/node.d.ts +80 -0
- package/dist/node.js +414 -0
- package/dist/react.d.ts +281 -0
- package/dist/react.js +33 -0
- package/dist/record-stub.d.ts +18 -0
- package/dist/record-stub.js +6 -0
- package/dist/record.d.ts +28 -0
- package/dist/record.js +104 -0
- package/dist/redact.d.ts +7 -0
- package/dist/redact.js +30 -0
- package/dist/report.d.ts +121 -0
- package/dist/report.js +353 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +27 -0
- package/dist/stub.d.ts +88 -0
- package/dist/stub.js +38 -0
- package/dist/types.d.ts +248 -0
- package/dist/types.js +1 -0
- package/dist/verify-cli.d.ts +2 -0
- package/dist/verify-cli.js +3 -0
- package/dist/verify.d.ts +17 -0
- package/dist/verify.js +60 -0
- package/dist/vite.d.ts +32 -0
- package/dist/vite.js +33 -0
- package/dist/webmcp.d.ts +3 -0
- package/dist/webmcp.js +20 -0
- package/package.json +59 -4
package/dist/node.js
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
// @mindmakr/reins/node: dev-only backend instrumentation for Node servers. Node only: never import it from browser code.
|
|
2
|
+
// It pairs with the bridge like the browser client does, records inbound requests, spans and outbound fetches,
|
|
3
|
+
// and serves them to `reins_server`, `reins_perf` and `reins_report`.
|
|
4
|
+
// Like the browser core, it records nothing until the bridge has accepted its token (or `standalone` says otherwise).
|
|
5
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
6
|
+
import { channel } from 'node:diagnostics_channel';
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { monitorEventLoopDelay, performance } from 'node:perf_hooks';
|
|
10
|
+
import { msg, ring } from './network.js';
|
|
11
|
+
import { redactHeaders, redactUrl } from './redact.js';
|
|
12
|
+
import { routeStats } from './report.js';
|
|
13
|
+
export const VERSION = '0.1.0';
|
|
14
|
+
/** The header the browser core adds to same-origin calls while paired. */
|
|
15
|
+
export const TRACE_HEADER = 'x-reins-trace';
|
|
16
|
+
const NAME = /^[\w][\w.-]{0,63}$/;
|
|
17
|
+
const TRACE = /^[\w-]{1,64}$/;
|
|
18
|
+
const CHANNELS = ['undici:request:create', 'undici:request:headers', 'undici:request:error'];
|
|
19
|
+
const r1 = (n) => Math.round(n * 10) / 10;
|
|
20
|
+
const fail = (s) => {
|
|
21
|
+
throw new Error('reins: ' + s);
|
|
22
|
+
};
|
|
23
|
+
/** Whether this process may run reins: not in production unless REINS_ENABLE=1 (private staging only). */
|
|
24
|
+
export function nodeEnabled(env = process.env) {
|
|
25
|
+
return env.NODE_ENV !== 'production' || /^(1|true)$/.test(env.REINS_ENABLE || '');
|
|
26
|
+
}
|
|
27
|
+
/** Creates an instance. Most apps use the `reinsNode` singleton. */
|
|
28
|
+
export function createReinsNode(env = process.env) {
|
|
29
|
+
const als = new AsyncLocalStorage();
|
|
30
|
+
const actions = new Map();
|
|
31
|
+
const loop = monitorEventLoopDelay({ resolution: 10 });
|
|
32
|
+
const pending = new WeakMap();
|
|
33
|
+
let opts = {};
|
|
34
|
+
let requests = ring(200);
|
|
35
|
+
let spans = ring(100);
|
|
36
|
+
let outbound = ring(100);
|
|
37
|
+
/** `connect()` was called and `disconnect()` was not: keep trying to pair. */
|
|
38
|
+
let wanted = false;
|
|
39
|
+
/** Recording: the middleware, spans, the outbound hooks and the event-loop monitor are on. */
|
|
40
|
+
let active = false;
|
|
41
|
+
let paired = false;
|
|
42
|
+
let sock = null;
|
|
43
|
+
let retry = null;
|
|
44
|
+
let delay = 2000;
|
|
45
|
+
let bridgeHttp = '';
|
|
46
|
+
const subs = [];
|
|
47
|
+
const now = () => performance.now();
|
|
48
|
+
const allow = () => opts.allow || {};
|
|
49
|
+
// ---- inbound requests -------------------------------------------------------------------------
|
|
50
|
+
function begin(req) {
|
|
51
|
+
const url = String(req.originalUrl || req.url || '/');
|
|
52
|
+
const t = req.headers?.[TRACE_HEADER];
|
|
53
|
+
const headers = {};
|
|
54
|
+
for (const [k, v] of Object.entries(req.headers || {}))
|
|
55
|
+
if (typeof v === 'string')
|
|
56
|
+
headers[k] = v;
|
|
57
|
+
return {
|
|
58
|
+
method: String(req.method || 'GET').toUpperCase(),
|
|
59
|
+
route: url.split(/[?#]/)[0],
|
|
60
|
+
url: redactUrl(url, allow().queryKeys),
|
|
61
|
+
status: 0,
|
|
62
|
+
ms: 0,
|
|
63
|
+
startMs: Math.round(now()),
|
|
64
|
+
at: Date.now(),
|
|
65
|
+
traceId: typeof t === 'string' && TRACE.test(t) ? t : undefined,
|
|
66
|
+
dbMs: 0,
|
|
67
|
+
spans: [],
|
|
68
|
+
outbound: [],
|
|
69
|
+
requestHeaders: redactHeaders(headers, allow().headers),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const dbMs = (rec) => r1(rec.spans.filter((s) => s.name.startsWith('db.')).reduce((a, s) => a + s.ms, 0));
|
|
73
|
+
function end(rec, status, route) {
|
|
74
|
+
if (rec.done)
|
|
75
|
+
return;
|
|
76
|
+
rec.done = true;
|
|
77
|
+
rec.status = status;
|
|
78
|
+
rec.ms = Math.round(now() - rec.startMs);
|
|
79
|
+
if (typeof route === 'string' && route)
|
|
80
|
+
rec.route = route;
|
|
81
|
+
rec.dbMs = dbMs(rec);
|
|
82
|
+
requests.push(rec);
|
|
83
|
+
}
|
|
84
|
+
const expressRoute = (req) => (req.route && typeof req.route.path === 'string' ? (req.baseUrl || '') + req.route.path : undefined);
|
|
85
|
+
function timing(rec) {
|
|
86
|
+
const el = runtime().eventLoop.p99Ms;
|
|
87
|
+
const desc = `${rec.method} ${rec.route}`.replace(/["\\\r\n]/g, '');
|
|
88
|
+
return `app;dur=${Math.round(now() - rec.startMs)};desc="${desc}", db;dur=${dbMs(rec)}, el-p99;dur=${el}`;
|
|
89
|
+
}
|
|
90
|
+
function track(rec, req, res, o) {
|
|
91
|
+
const fin = () => end(rec, res.statusCode, expressRoute(req));
|
|
92
|
+
res.once?.('finish', fin);
|
|
93
|
+
res.once?.('close', fin);
|
|
94
|
+
if (o.serverTiming && typeof res.writeHead === 'function') {
|
|
95
|
+
const writeHead = res.writeHead;
|
|
96
|
+
res.writeHead = function (...args) {
|
|
97
|
+
try {
|
|
98
|
+
if (!res.headersSent)
|
|
99
|
+
res.setHeader('Server-Timing', timing(rec));
|
|
100
|
+
}
|
|
101
|
+
catch { }
|
|
102
|
+
return writeHead.apply(this, args);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Express/Connect middleware. Register it early, but after body parsers if you want spans in
|
|
108
|
+
* handlers of POST routes to attach (stream callbacks lose the async context).
|
|
109
|
+
*/
|
|
110
|
+
function middleware(o = {}) {
|
|
111
|
+
return (req, res, next) => {
|
|
112
|
+
if (!active)
|
|
113
|
+
return next();
|
|
114
|
+
const rec = begin(req);
|
|
115
|
+
track(rec, req, res, o);
|
|
116
|
+
als.run(rec, () => next());
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** Wraps a plain `http.createServer` handler (or any `(req, res)` handler). */
|
|
120
|
+
function wrapHandler(handler, o = {}) {
|
|
121
|
+
return function (req, res, ...rest) {
|
|
122
|
+
if (!active)
|
|
123
|
+
return handler.call(this, req, res, ...rest);
|
|
124
|
+
const rec = begin(req);
|
|
125
|
+
track(rec, req, res, o);
|
|
126
|
+
return als.run(rec, () => handler.call(this, req, res, ...rest));
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/** Fastify hooks: `reinsNode.fastify(app)`. Spans attach in handlers of routes without a body. */
|
|
130
|
+
function fastify(app) {
|
|
131
|
+
const KEY = Symbol.for('reins.rec');
|
|
132
|
+
app.addHook('onRequest', (req, _reply, done) => {
|
|
133
|
+
if (!active)
|
|
134
|
+
return done();
|
|
135
|
+
const rec = begin(req.raw || req);
|
|
136
|
+
req[KEY] = rec;
|
|
137
|
+
als.run(rec, done);
|
|
138
|
+
});
|
|
139
|
+
app.addHook('onResponse', (req, reply, done) => {
|
|
140
|
+
const rec = req[KEY];
|
|
141
|
+
if (rec)
|
|
142
|
+
end(rec, reply.statusCode, req.routeOptions?.url || req.routerPath);
|
|
143
|
+
done();
|
|
144
|
+
});
|
|
145
|
+
return app;
|
|
146
|
+
}
|
|
147
|
+
// ---- spans and outbound calls ---------------------------------------------------------------
|
|
148
|
+
/** Times `fn` (sync or async) as a span of the current request, or at top level outside one. */
|
|
149
|
+
function span(name, fn) {
|
|
150
|
+
if (!active)
|
|
151
|
+
return fn();
|
|
152
|
+
const rec = als.getStore();
|
|
153
|
+
const t0 = now();
|
|
154
|
+
const done = (error) => {
|
|
155
|
+
const s = { name: String(name).slice(0, 80), startMs: r1(t0 - (rec ? rec.startMs : 0)), ms: r1(now() - t0), error };
|
|
156
|
+
rec ? rec.spans.push(s) : spans.push(s);
|
|
157
|
+
};
|
|
158
|
+
try {
|
|
159
|
+
const v = fn();
|
|
160
|
+
if (v && typeof v.then === 'function')
|
|
161
|
+
return v.then((x) => (done(), x), (e) => (done(msg(e)), Promise.reject(e)));
|
|
162
|
+
done();
|
|
163
|
+
return v;
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
done(msg(e));
|
|
167
|
+
throw e;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function subscribe() {
|
|
171
|
+
const finish = (request, status, error) => {
|
|
172
|
+
const p = pending.get(request);
|
|
173
|
+
if (!p)
|
|
174
|
+
return;
|
|
175
|
+
pending.delete(request);
|
|
176
|
+
p.e.status = status;
|
|
177
|
+
p.e.ms = r1(now() - p.t0);
|
|
178
|
+
if (error)
|
|
179
|
+
p.e.error = error;
|
|
180
|
+
p.rec ? p.rec.outbound.push(p.e) : outbound.push(p.e);
|
|
181
|
+
};
|
|
182
|
+
const handlers = {
|
|
183
|
+
'undici:request:create': ({ request }) => {
|
|
184
|
+
if (!active || !request)
|
|
185
|
+
return;
|
|
186
|
+
const origin = String(request.origin || '');
|
|
187
|
+
if (bridgeHttp && origin === bridgeHttp)
|
|
188
|
+
return;
|
|
189
|
+
const rec = als.getStore();
|
|
190
|
+
const t0 = now();
|
|
191
|
+
pending.set(request, { e: { method: String(request.method || 'GET'), url: redactUrl(origin + (request.path || ''), allow().queryKeys), status: 0, startMs: r1(t0 - (rec ? rec.startMs : 0)), ms: 0 }, rec, t0 });
|
|
192
|
+
},
|
|
193
|
+
'undici:request:headers': ({ request, response }) => finish(request, response?.statusCode || 0),
|
|
194
|
+
'undici:request:error': ({ request, error }) => finish(request, 0, msg(error)),
|
|
195
|
+
};
|
|
196
|
+
for (const name of CHANNELS) {
|
|
197
|
+
const ch = channel(name);
|
|
198
|
+
const fn = (m) => {
|
|
199
|
+
try {
|
|
200
|
+
handlers[name](m);
|
|
201
|
+
}
|
|
202
|
+
catch { }
|
|
203
|
+
};
|
|
204
|
+
ch.subscribe(fn);
|
|
205
|
+
subs.push(() => ch.unsubscribe(fn));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// ---- runtime and report ---------------------------------------------------------------------
|
|
209
|
+
function runtime() {
|
|
210
|
+
const m = process.memoryUsage();
|
|
211
|
+
const pct = (p) => {
|
|
212
|
+
try {
|
|
213
|
+
return r1(loop.percentile(p) / 1e6);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
return {
|
|
220
|
+
node: process.version,
|
|
221
|
+
pid: process.pid,
|
|
222
|
+
uptimeS: Math.round(process.uptime()),
|
|
223
|
+
rssMb: r1(m.rss / 1048576),
|
|
224
|
+
heapUsedMb: r1(m.heapUsed / 1048576),
|
|
225
|
+
eventLoop: { p50Ms: pct(50), p99Ms: pct(99), maxMs: r1((loop.max || 0) / 1e6) },
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/** The same data `reins_server` returns, for this process. */
|
|
229
|
+
function report() {
|
|
230
|
+
const reqs = requests.list().map(({ done, ...r }) => ({ ...r, dbMs: dbMs(r) }));
|
|
231
|
+
return { runtime: active ? runtime() : null, requests: reqs, slowest: routeStats(reqs), spans: spans.list(), outbound: outbound.list(), actions: list() };
|
|
232
|
+
}
|
|
233
|
+
// ---- server actions -------------------------------------------------------------------------
|
|
234
|
+
/** Registers a server action. Same rules as in the browser: consequential ones need `confirm: true`. Never register one that skips auth. */
|
|
235
|
+
function action(def) {
|
|
236
|
+
if (!NAME.test(def?.name))
|
|
237
|
+
fail('bad action name');
|
|
238
|
+
if (typeof def.run !== 'function')
|
|
239
|
+
fail('no run function');
|
|
240
|
+
actions.set(def.name, def);
|
|
241
|
+
return () => void (actions.get(def.name) === def && actions.delete(def.name));
|
|
242
|
+
}
|
|
243
|
+
const list = () => [...actions.values()].map(({ name, description, input = { type: 'object', properties: {} }, consequential, readOnly }) => ({
|
|
244
|
+
name,
|
|
245
|
+
description,
|
|
246
|
+
input,
|
|
247
|
+
consequential: !!consequential,
|
|
248
|
+
readOnly: !!readOnly,
|
|
249
|
+
}));
|
|
250
|
+
async function run(name, input, confirm = false) {
|
|
251
|
+
const a = actions.get(name) || fail('unknown action');
|
|
252
|
+
if (a.consequential && !confirm)
|
|
253
|
+
fail('confirm: true required');
|
|
254
|
+
return a.run(input ?? {});
|
|
255
|
+
}
|
|
256
|
+
const methods = {
|
|
257
|
+
list: () => ({ actions: list(), states: [], panels: [] }),
|
|
258
|
+
run: (p) => run(p.name, p.input, p.confirm === true),
|
|
259
|
+
server: () => report(),
|
|
260
|
+
};
|
|
261
|
+
// ---- recording on and off -------------------------------------------------------------------
|
|
262
|
+
function start() {
|
|
263
|
+
if (active)
|
|
264
|
+
return;
|
|
265
|
+
active = true;
|
|
266
|
+
loop.enable();
|
|
267
|
+
subscribe();
|
|
268
|
+
}
|
|
269
|
+
function stop() {
|
|
270
|
+
if (!active)
|
|
271
|
+
return;
|
|
272
|
+
active = false;
|
|
273
|
+
loop.disable();
|
|
274
|
+
subs.splice(0).forEach((f) => f());
|
|
275
|
+
}
|
|
276
|
+
// ---- pairing --------------------------------------------------------------------------------
|
|
277
|
+
async function open() {
|
|
278
|
+
let token = opts.token;
|
|
279
|
+
if (!token)
|
|
280
|
+
try {
|
|
281
|
+
token = readFileSync(opts.tokenFile || join(process.cwd(), '.reins', 'token'), 'utf8').trim();
|
|
282
|
+
}
|
|
283
|
+
catch { }
|
|
284
|
+
if (!token)
|
|
285
|
+
return schedule(), false;
|
|
286
|
+
const url = opts.url || 'ws://127.0.0.1:7357';
|
|
287
|
+
bridgeHttp = url.replace(/^ws/, 'http').replace(/\/+$/, '');
|
|
288
|
+
const WS = opts.WebSocket || globalThis.WebSocket || (await import('ws')).default;
|
|
289
|
+
return new Promise((resolve) => {
|
|
290
|
+
let done = false;
|
|
291
|
+
let s;
|
|
292
|
+
try {
|
|
293
|
+
s = new WS(url);
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return resolve(false), schedule();
|
|
297
|
+
}
|
|
298
|
+
const send = (m) => s.send(JSON.stringify(m));
|
|
299
|
+
s.onopen = () => send({ type: 'hello', token, version: VERSION, url: `node://${process.pid}`, title: opts.meta?.name || 'node server', meta: { ...opts.meta, platform: 'node', runtime: process.version } });
|
|
300
|
+
s.onerror = () => { };
|
|
301
|
+
s.onmessage = async (ev) => {
|
|
302
|
+
let m;
|
|
303
|
+
try {
|
|
304
|
+
m = JSON.parse(String(ev.data));
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
// An unhandled rejection here would end the dev server, so ignore anything that is not a message object.
|
|
310
|
+
if (!m || typeof m !== 'object')
|
|
311
|
+
return;
|
|
312
|
+
if (!done) {
|
|
313
|
+
done = true;
|
|
314
|
+
if (m.type === 'welcome') {
|
|
315
|
+
sock = s;
|
|
316
|
+
paired = true;
|
|
317
|
+
delay = 2000;
|
|
318
|
+
start();
|
|
319
|
+
return resolve(true);
|
|
320
|
+
}
|
|
321
|
+
s.close();
|
|
322
|
+
return resolve(false);
|
|
323
|
+
}
|
|
324
|
+
if (m.type !== 'call')
|
|
325
|
+
return;
|
|
326
|
+
try {
|
|
327
|
+
const fn = methods[m.method] || fail('bad method');
|
|
328
|
+
send({ type: 'result', id: m.id, ok: true, result: (await fn(m.params || {})) ?? null });
|
|
329
|
+
}
|
|
330
|
+
catch (e) {
|
|
331
|
+
send({ type: 'result', id: m.id, ok: false, error: msg(e) });
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
s.onclose = () => {
|
|
335
|
+
if (!done) {
|
|
336
|
+
done = true;
|
|
337
|
+
resolve(false);
|
|
338
|
+
}
|
|
339
|
+
if (sock === s || !paired) {
|
|
340
|
+
sock = null;
|
|
341
|
+
paired = false;
|
|
342
|
+
// No bridge, nothing to record for (unless the app asked to record on its own).
|
|
343
|
+
if (!opts.standalone)
|
|
344
|
+
stop();
|
|
345
|
+
schedule();
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function schedule() {
|
|
351
|
+
if (!wanted || opts.reconnect === false || retry)
|
|
352
|
+
return;
|
|
353
|
+
retry = setTimeout(() => {
|
|
354
|
+
retry = null;
|
|
355
|
+
void open();
|
|
356
|
+
}, delay);
|
|
357
|
+
retry.unref?.();
|
|
358
|
+
delay = Math.min(delay * 2, 30000);
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Pairs this server with the bridge. Recording starts once the bridge accepts the token and stops when
|
|
362
|
+
* it goes away (`standalone: true` records from this call instead). Dev only: refuses when NODE_ENV is
|
|
363
|
+
* `production` unless REINS_ENABLE=1. Resolves true once paired.
|
|
364
|
+
*/
|
|
365
|
+
function connect(o = {}) {
|
|
366
|
+
if (!nodeEnabled(env))
|
|
367
|
+
return Promise.resolve(false);
|
|
368
|
+
if (!wanted) {
|
|
369
|
+
opts = o;
|
|
370
|
+
const n = o.bufferSize || 200;
|
|
371
|
+
requests = ring(n);
|
|
372
|
+
spans = ring(Math.ceil(n / 2));
|
|
373
|
+
outbound = ring(Math.ceil(n / 2));
|
|
374
|
+
wanted = true;
|
|
375
|
+
if (o.standalone)
|
|
376
|
+
start();
|
|
377
|
+
}
|
|
378
|
+
return paired ? Promise.resolve(true) : open();
|
|
379
|
+
}
|
|
380
|
+
function disconnect() {
|
|
381
|
+
wanted = false;
|
|
382
|
+
paired = false;
|
|
383
|
+
if (retry)
|
|
384
|
+
clearTimeout(retry);
|
|
385
|
+
retry = null;
|
|
386
|
+
const s = sock;
|
|
387
|
+
sock = null;
|
|
388
|
+
s?.close();
|
|
389
|
+
stop();
|
|
390
|
+
}
|
|
391
|
+
function clear() {
|
|
392
|
+
const n = opts.bufferSize || 200;
|
|
393
|
+
requests = ring(n);
|
|
394
|
+
spans = ring(Math.ceil(n / 2));
|
|
395
|
+
outbound = ring(Math.ceil(n / 2));
|
|
396
|
+
}
|
|
397
|
+
return {
|
|
398
|
+
connect,
|
|
399
|
+
disconnect,
|
|
400
|
+
clear,
|
|
401
|
+
isPaired: () => paired,
|
|
402
|
+
isActive: () => active,
|
|
403
|
+
middleware,
|
|
404
|
+
wrapHandler,
|
|
405
|
+
fastify,
|
|
406
|
+
span,
|
|
407
|
+
action,
|
|
408
|
+
actions: list,
|
|
409
|
+
run,
|
|
410
|
+
report,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
export const reinsNode = createReinsNode();
|
|
414
|
+
export default reinsNode;
|