@qping/plugin-bus 0.9.0 → 0.11.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/dist/bootstrap.d.ts +4 -9
- package/dist/bootstrap.mjs +10 -49
- package/dist/node.mjs +11 -50
- package/dist/router.d.ts +0 -9
- package/dist/webClient.d.ts +2 -2
- package/dist/webClient.mjs +5 -16
- package/package.json +1 -1
- package/src/bootstrap.ts +8 -40
- package/src/node.ts +1 -2
- package/src/router.ts +6 -18
- package/src/webClient.ts +6 -19
package/dist/bootstrap.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* v3 Node SDK bootstrap entry. Reads the bootstrap line from stdin (pipePath\ttoken), connects to
|
|
3
|
-
* the named pipe, completes bus.handshake
|
|
4
|
-
* and starts a HandlerRouter stamped with that identity.
|
|
3
|
+
* the named pipe, completes bus.handshake by presenting the token, and starts a HandlerRouter.
|
|
5
4
|
*
|
|
6
5
|
* This mirrors the C# NodeProcessController's spawn contract: the host writes one line to the
|
|
7
6
|
* Node process's stdin — "<pipePath>\t<token>" — then waits for the Node side to connect the pipe
|
|
@@ -25,11 +24,7 @@ export interface PluginRuntime {
|
|
|
25
24
|
*/
|
|
26
25
|
export declare function runPlugin(handlers: PluginHandlers): Promise<PluginRuntime>;
|
|
27
26
|
/**
|
|
28
|
-
* Sends bus.handshake with the bootstrap token and waits for
|
|
29
|
-
*
|
|
27
|
+
* Sends bus.handshake with the bootstrap token and waits for host acknowledgement.
|
|
28
|
+
* Protocol compatibility has already been checked from the manifest before process startup.
|
|
30
29
|
*/
|
|
31
|
-
export declare function completeHandshake(transport: NodeTransport, token: string, timeoutMs?: number): Promise<
|
|
32
|
-
pluginId: string;
|
|
33
|
-
sessionId: string;
|
|
34
|
-
endpointId: string;
|
|
35
|
-
}>;
|
|
30
|
+
export declare function completeHandshake(transport: NodeTransport, token: string, timeoutMs?: number): Promise<void>;
|
package/dist/bootstrap.mjs
CHANGED
|
@@ -139,10 +139,6 @@ var ErrorCode = {
|
|
|
139
139
|
RateLimited: "RateLimited"
|
|
140
140
|
};
|
|
141
141
|
var ProtocolVersion = "3.0";
|
|
142
|
-
var EndpointIds = {
|
|
143
|
-
NodeMain: "node-main",
|
|
144
|
-
Host: "host"
|
|
145
|
-
};
|
|
146
142
|
var Routes = {
|
|
147
143
|
Bus: {
|
|
148
144
|
Handshake: "bus.handshake",
|
|
@@ -294,20 +290,11 @@ function deadlineFromTimeoutMs(timeoutMs) {
|
|
|
294
290
|
var HandlerRouter = class {
|
|
295
291
|
handlers = /* @__PURE__ */ new Map();
|
|
296
292
|
pendingHostCalls = /* @__PURE__ */ new Map();
|
|
297
|
-
pluginId = "p";
|
|
298
|
-
sessionId = "s";
|
|
299
|
-
endpointId = EndpointIds.NodeMain;
|
|
300
293
|
/** Injected transport send fn; tests can override `router.send` directly. */
|
|
301
294
|
send;
|
|
302
295
|
constructor(deps) {
|
|
303
296
|
this.send = deps.send;
|
|
304
297
|
}
|
|
305
|
-
/** Sets the bound identity stamped on outbound messages (after handshake). */
|
|
306
|
-
setIdentity(ids) {
|
|
307
|
-
this.pluginId = ids.pluginId;
|
|
308
|
-
this.sessionId = ids.sessionId;
|
|
309
|
-
this.endpointId = ids.endpointId;
|
|
310
|
-
}
|
|
311
298
|
handle(route, handler) {
|
|
312
299
|
this.handlers.set(route, handler);
|
|
313
300
|
}
|
|
@@ -351,9 +338,9 @@ var HandlerRouter = class {
|
|
|
351
338
|
version: ProtocolVersion,
|
|
352
339
|
id,
|
|
353
340
|
traceId: id,
|
|
354
|
-
sessionId:
|
|
355
|
-
pluginId:
|
|
356
|
-
endpointId:
|
|
341
|
+
sessionId: "",
|
|
342
|
+
pluginId: "",
|
|
343
|
+
endpointId: "",
|
|
357
344
|
kind: MessageKind.Request,
|
|
358
345
|
route,
|
|
359
346
|
timeoutMs: effectiveTimeoutMs,
|
|
@@ -397,9 +384,9 @@ var HandlerRouter = class {
|
|
|
397
384
|
id: randomBytesHex(),
|
|
398
385
|
correlationId: req.id,
|
|
399
386
|
traceId: req.traceId,
|
|
400
|
-
sessionId:
|
|
401
|
-
pluginId:
|
|
402
|
-
endpointId:
|
|
387
|
+
sessionId: "",
|
|
388
|
+
pluginId: "",
|
|
389
|
+
endpointId: "",
|
|
403
390
|
kind: MessageKind.Response,
|
|
404
391
|
route: req.route,
|
|
405
392
|
payload
|
|
@@ -418,29 +405,16 @@ function randomBytesHex() {
|
|
|
418
405
|
}
|
|
419
406
|
|
|
420
407
|
// src/bootstrap.ts
|
|
421
|
-
var SUPPORTED_VERSIONS = [ProtocolVersion];
|
|
422
408
|
async function runPlugin(handlers) {
|
|
423
409
|
const { pipePath, token } = await readBootstrapLine();
|
|
424
410
|
const transport = new NodeTransport();
|
|
425
411
|
await transport.connect(pipePath);
|
|
426
|
-
|
|
412
|
+
await completeHandshake(transport, token);
|
|
427
413
|
const router = new HandlerRouter({ send: (env) => transport.send(env) });
|
|
428
|
-
router.setIdentity(identity);
|
|
429
|
-
const HOST_LOST_MS = 15e3;
|
|
430
|
-
let lastPingAt = Date.now();
|
|
431
|
-
const watchdog = setInterval(() => {
|
|
432
|
-
if (Date.now() - lastPingAt > HOST_LOST_MS) {
|
|
433
|
-
clearInterval(watchdog);
|
|
434
|
-
process.exit(1);
|
|
435
|
-
}
|
|
436
|
-
}, 1e3);
|
|
437
|
-
watchdog.unref?.();
|
|
438
414
|
transport.onDisconnect(() => {
|
|
439
|
-
clearInterval(watchdog);
|
|
440
415
|
process.exit(1);
|
|
441
416
|
});
|
|
442
417
|
transport.onMessage((env) => {
|
|
443
|
-
if (env.route === Routes.Bus.Ping) lastPingAt = Date.now();
|
|
444
418
|
router.dispatch(env);
|
|
445
419
|
});
|
|
446
420
|
for (const [route, handler] of Object.entries(handlers)) {
|
|
@@ -450,7 +424,6 @@ async function runPlugin(handlers) {
|
|
|
450
424
|
transport,
|
|
451
425
|
router,
|
|
452
426
|
close: async () => {
|
|
453
|
-
clearInterval(watchdog);
|
|
454
427
|
await transport.close();
|
|
455
428
|
}
|
|
456
429
|
};
|
|
@@ -479,15 +452,11 @@ async function completeHandshake(transport, token, timeoutMs = 1e4) {
|
|
|
479
452
|
traceId: id,
|
|
480
453
|
sessionId: "",
|
|
481
454
|
pluginId: "",
|
|
482
|
-
endpointId:
|
|
455
|
+
endpointId: "",
|
|
483
456
|
kind: MessageKind.Request,
|
|
484
457
|
route: Routes.Bus.Handshake,
|
|
485
458
|
timeoutMs,
|
|
486
|
-
payload: {
|
|
487
|
-
version: ProtocolVersion,
|
|
488
|
-
supportedVersions: SUPPORTED_VERSIONS,
|
|
489
|
-
token
|
|
490
|
-
}
|
|
459
|
+
payload: { token }
|
|
491
460
|
};
|
|
492
461
|
return new Promise((resolve, reject) => {
|
|
493
462
|
const timer = setTimeout(() => {
|
|
@@ -502,15 +471,7 @@ async function completeHandshake(transport, token, timeoutMs = 1e4) {
|
|
|
502
471
|
reject(new Error(`${env.error.code}: ${env.error.message}`));
|
|
503
472
|
return;
|
|
504
473
|
}
|
|
505
|
-
|
|
506
|
-
const pluginId = String(p.pluginId ?? "");
|
|
507
|
-
const sessionId = String(p.sessionId ?? "");
|
|
508
|
-
const endpointId = String(p.endpointId ?? EndpointIds.NodeMain);
|
|
509
|
-
if (!pluginId || !sessionId) {
|
|
510
|
-
reject(new Error("bus.handshake success response missing bound identity"));
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
resolve({ pluginId, sessionId, endpointId });
|
|
474
|
+
resolve();
|
|
514
475
|
});
|
|
515
476
|
transport.send(req);
|
|
516
477
|
});
|
package/dist/node.mjs
CHANGED
|
@@ -139,10 +139,6 @@ var ErrorCode = {
|
|
|
139
139
|
RateLimited: "RateLimited"
|
|
140
140
|
};
|
|
141
141
|
var ProtocolVersion = "3.0";
|
|
142
|
-
var EndpointIds = {
|
|
143
|
-
NodeMain: "node-main",
|
|
144
|
-
Host: "host"
|
|
145
|
-
};
|
|
146
142
|
var Routes = {
|
|
147
143
|
Bus: {
|
|
148
144
|
Handshake: "bus.handshake",
|
|
@@ -303,20 +299,11 @@ function deadlineFromTimeoutMs(timeoutMs) {
|
|
|
303
299
|
var HandlerRouter = class {
|
|
304
300
|
handlers = /* @__PURE__ */ new Map();
|
|
305
301
|
pendingHostCalls = /* @__PURE__ */ new Map();
|
|
306
|
-
pluginId = "p";
|
|
307
|
-
sessionId = "s";
|
|
308
|
-
endpointId = EndpointIds.NodeMain;
|
|
309
302
|
/** Injected transport send fn; tests can override `router.send` directly. */
|
|
310
303
|
send;
|
|
311
304
|
constructor(deps) {
|
|
312
305
|
this.send = deps.send;
|
|
313
306
|
}
|
|
314
|
-
/** Sets the bound identity stamped on outbound messages (after handshake). */
|
|
315
|
-
setIdentity(ids) {
|
|
316
|
-
this.pluginId = ids.pluginId;
|
|
317
|
-
this.sessionId = ids.sessionId;
|
|
318
|
-
this.endpointId = ids.endpointId;
|
|
319
|
-
}
|
|
320
307
|
handle(route, handler) {
|
|
321
308
|
this.handlers.set(route, handler);
|
|
322
309
|
}
|
|
@@ -360,9 +347,9 @@ var HandlerRouter = class {
|
|
|
360
347
|
version: ProtocolVersion,
|
|
361
348
|
id,
|
|
362
349
|
traceId: id,
|
|
363
|
-
sessionId:
|
|
364
|
-
pluginId:
|
|
365
|
-
endpointId:
|
|
350
|
+
sessionId: "",
|
|
351
|
+
pluginId: "",
|
|
352
|
+
endpointId: "",
|
|
366
353
|
kind: MessageKind.Request,
|
|
367
354
|
route,
|
|
368
355
|
timeoutMs: effectiveTimeoutMs,
|
|
@@ -406,9 +393,9 @@ var HandlerRouter = class {
|
|
|
406
393
|
id: randomBytesHex(),
|
|
407
394
|
correlationId: req.id,
|
|
408
395
|
traceId: req.traceId,
|
|
409
|
-
sessionId:
|
|
410
|
-
pluginId:
|
|
411
|
-
endpointId:
|
|
396
|
+
sessionId: "",
|
|
397
|
+
pluginId: "",
|
|
398
|
+
endpointId: "",
|
|
412
399
|
kind: MessageKind.Response,
|
|
413
400
|
route: req.route,
|
|
414
401
|
payload
|
|
@@ -427,29 +414,16 @@ function randomBytesHex() {
|
|
|
427
414
|
}
|
|
428
415
|
|
|
429
416
|
// src/bootstrap.ts
|
|
430
|
-
var SUPPORTED_VERSIONS = [ProtocolVersion];
|
|
431
417
|
async function runPlugin(handlers) {
|
|
432
418
|
const { pipePath, token } = await readBootstrapLine();
|
|
433
419
|
const transport = new NodeTransport();
|
|
434
420
|
await transport.connect(pipePath);
|
|
435
|
-
|
|
421
|
+
await completeHandshake(transport, token);
|
|
436
422
|
const router = new HandlerRouter({ send: (env) => transport.send(env) });
|
|
437
|
-
router.setIdentity(identity);
|
|
438
|
-
const HOST_LOST_MS = 15e3;
|
|
439
|
-
let lastPingAt = Date.now();
|
|
440
|
-
const watchdog = setInterval(() => {
|
|
441
|
-
if (Date.now() - lastPingAt > HOST_LOST_MS) {
|
|
442
|
-
clearInterval(watchdog);
|
|
443
|
-
process.exit(1);
|
|
444
|
-
}
|
|
445
|
-
}, 1e3);
|
|
446
|
-
watchdog.unref?.();
|
|
447
423
|
transport.onDisconnect(() => {
|
|
448
|
-
clearInterval(watchdog);
|
|
449
424
|
process.exit(1);
|
|
450
425
|
});
|
|
451
426
|
transport.onMessage((env) => {
|
|
452
|
-
if (env.route === Routes.Bus.Ping) lastPingAt = Date.now();
|
|
453
427
|
router.dispatch(env);
|
|
454
428
|
});
|
|
455
429
|
for (const [route, handler] of Object.entries(handlers)) {
|
|
@@ -459,7 +433,6 @@ async function runPlugin(handlers) {
|
|
|
459
433
|
transport,
|
|
460
434
|
router,
|
|
461
435
|
close: async () => {
|
|
462
|
-
clearInterval(watchdog);
|
|
463
436
|
await transport.close();
|
|
464
437
|
}
|
|
465
438
|
};
|
|
@@ -488,15 +461,11 @@ async function completeHandshake(transport, token, timeoutMs = 1e4) {
|
|
|
488
461
|
traceId: id,
|
|
489
462
|
sessionId: "",
|
|
490
463
|
pluginId: "",
|
|
491
|
-
endpointId:
|
|
464
|
+
endpointId: "",
|
|
492
465
|
kind: MessageKind.Request,
|
|
493
466
|
route: Routes.Bus.Handshake,
|
|
494
467
|
timeoutMs,
|
|
495
|
-
payload: {
|
|
496
|
-
version: ProtocolVersion,
|
|
497
|
-
supportedVersions: SUPPORTED_VERSIONS,
|
|
498
|
-
token
|
|
499
|
-
}
|
|
468
|
+
payload: { token }
|
|
500
469
|
};
|
|
501
470
|
return new Promise((resolve, reject) => {
|
|
502
471
|
const timer = setTimeout(() => {
|
|
@@ -511,15 +480,7 @@ async function completeHandshake(transport, token, timeoutMs = 1e4) {
|
|
|
511
480
|
reject(new Error(`${env.error.code}: ${env.error.message}`));
|
|
512
481
|
return;
|
|
513
482
|
}
|
|
514
|
-
|
|
515
|
-
const pluginId = String(p.pluginId ?? "");
|
|
516
|
-
const sessionId = String(p.sessionId ?? "");
|
|
517
|
-
const endpointId = String(p.endpointId ?? EndpointIds.NodeMain);
|
|
518
|
-
if (!pluginId || !sessionId) {
|
|
519
|
-
reject(new Error("bus.handshake success response missing bound identity"));
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
resolve({ pluginId, sessionId, endpointId });
|
|
483
|
+
resolve();
|
|
523
484
|
});
|
|
524
485
|
transport.send(req);
|
|
525
486
|
});
|
|
@@ -691,7 +652,7 @@ var Plugin = class {
|
|
|
691
652
|
traceId: crypto.randomUUID().replace(/-/g, "").slice(0, 32),
|
|
692
653
|
sessionId: "",
|
|
693
654
|
pluginId: "",
|
|
694
|
-
endpointId:
|
|
655
|
+
endpointId: "",
|
|
695
656
|
kind: MessageKind.Event,
|
|
696
657
|
route,
|
|
697
658
|
payload
|
package/dist/router.d.ts
CHANGED
|
@@ -15,20 +15,11 @@ export declare function resolveHostCallTimeoutMs(explicit?: number): number;
|
|
|
15
15
|
export declare class HandlerRouter {
|
|
16
16
|
private handlers;
|
|
17
17
|
private pendingHostCalls;
|
|
18
|
-
private pluginId;
|
|
19
|
-
private sessionId;
|
|
20
|
-
private endpointId;
|
|
21
18
|
/** Injected transport send fn; tests can override `router.send` directly. */
|
|
22
19
|
send: Sender;
|
|
23
20
|
constructor(deps: {
|
|
24
21
|
send: Sender;
|
|
25
22
|
});
|
|
26
|
-
/** Sets the bound identity stamped on outbound messages (after handshake). */
|
|
27
|
-
setIdentity(ids: {
|
|
28
|
-
pluginId: string;
|
|
29
|
-
sessionId: string;
|
|
30
|
-
endpointId: string;
|
|
31
|
-
}): void;
|
|
32
23
|
handle(route: string, handler: Handler): void;
|
|
33
24
|
/** Dispatches an inbound request/response. Returns once handled. */
|
|
34
25
|
dispatch(env: Envelope): Promise<void>;
|
package/dist/webClient.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* v3 Web SDK: speaks protocol envelopes over chrome.webview.postMessage.
|
|
3
3
|
* Host stamps identity; the page does not supply plugin/entry/session ids.
|
|
4
4
|
*
|
|
5
|
-
* Connections start with bus.handshake
|
|
6
|
-
*
|
|
5
|
+
* Connections start with a payload-free bus.handshake readiness signal. The host validates the
|
|
6
|
+
* plugin manifest version before loading the page. call("refresh") is sent as plugin.call.refresh.
|
|
7
7
|
*/
|
|
8
8
|
import { mytoolsI18n } from "./i18n.ts";
|
|
9
9
|
import type { MyToolsThemePayload } from "./webTypes.ts";
|
package/dist/webClient.mjs
CHANGED
|
@@ -2494,19 +2494,11 @@ function applyHostSideEffects(env) {
|
|
|
2494
2494
|
mytoolsTheme.apply(payload);
|
|
2495
2495
|
}
|
|
2496
2496
|
}
|
|
2497
|
-
function negotiatedVersionFrom(payload) {
|
|
2498
|
-
if (payload && typeof payload === "object" && "negotiatedVersion" in payload) {
|
|
2499
|
-
const value = payload.negotiatedVersion;
|
|
2500
|
-
if (typeof value === "string" && value.length > 0) return value;
|
|
2501
|
-
}
|
|
2502
|
-
return ProtocolVersion;
|
|
2503
|
-
}
|
|
2504
2497
|
function createWebBusClient(options) {
|
|
2505
2498
|
const pending = /* @__PURE__ */ new Map();
|
|
2506
2499
|
const eventHandlers = /* @__PURE__ */ new Set();
|
|
2507
2500
|
const lastByRoute = /* @__PURE__ */ new Map();
|
|
2508
2501
|
const defaultTimeout = options?.timeoutMs ?? 3e4;
|
|
2509
|
-
let wireVersion = ProtocolVersion;
|
|
2510
2502
|
let handshakeError = null;
|
|
2511
2503
|
const onMessage = (event) => {
|
|
2512
2504
|
const env = parseMessage(event.data);
|
|
@@ -2531,9 +2523,7 @@ function createWebBusClient(options) {
|
|
|
2531
2523
|
let handshakeDone = Promise.resolve();
|
|
2532
2524
|
if (hasWebView()) {
|
|
2533
2525
|
window.chrome.webview.addEventListener("message", onMessage);
|
|
2534
|
-
handshakeDone =
|
|
2535
|
-
wireVersion = version;
|
|
2536
|
-
}).catch((err) => {
|
|
2526
|
+
handshakeDone = signalPageReady(HandshakeTimeoutMs).catch((err) => {
|
|
2537
2527
|
handshakeError = err instanceof Error ? err : new Error(String(err));
|
|
2538
2528
|
throw handshakeError;
|
|
2539
2529
|
});
|
|
@@ -2564,7 +2554,7 @@ function createWebBusClient(options) {
|
|
|
2564
2554
|
}
|
|
2565
2555
|
});
|
|
2566
2556
|
post({
|
|
2567
|
-
version:
|
|
2557
|
+
version: ProtocolVersion,
|
|
2568
2558
|
id,
|
|
2569
2559
|
traceId: id,
|
|
2570
2560
|
sessionId: "",
|
|
@@ -2602,7 +2592,7 @@ function createWebBusClient(options) {
|
|
|
2602
2592
|
}
|
|
2603
2593
|
};
|
|
2604
2594
|
}
|
|
2605
|
-
function
|
|
2595
|
+
function signalPageReady(timeoutMs) {
|
|
2606
2596
|
const id = randomId();
|
|
2607
2597
|
return new Promise((resolve, reject) => {
|
|
2608
2598
|
const timer = window.setTimeout(() => {
|
|
@@ -2616,7 +2606,7 @@ function handshake(timeoutMs) {
|
|
|
2616
2606
|
if (env.error) {
|
|
2617
2607
|
reject(new Error(`${env.error.code}: ${env.error.message}`));
|
|
2618
2608
|
} else {
|
|
2619
|
-
resolve(
|
|
2609
|
+
resolve();
|
|
2620
2610
|
}
|
|
2621
2611
|
};
|
|
2622
2612
|
const cleanup = () => {
|
|
@@ -2633,8 +2623,7 @@ function handshake(timeoutMs) {
|
|
|
2633
2623
|
endpointId: "web",
|
|
2634
2624
|
kind: MessageKind.Request,
|
|
2635
2625
|
route: Routes.Bus.Handshake,
|
|
2636
|
-
timeoutMs
|
|
2637
|
-
payload: { version: ProtocolVersion, supportedVersions: [ProtocolVersion] }
|
|
2626
|
+
timeoutMs
|
|
2638
2627
|
});
|
|
2639
2628
|
});
|
|
2640
2629
|
}
|
package/package.json
CHANGED
package/src/bootstrap.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* v3 Node SDK bootstrap entry. Reads the bootstrap line from stdin (pipePath\ttoken), connects to
|
|
3
|
-
* the named pipe, completes bus.handshake
|
|
4
|
-
* and starts a HandlerRouter stamped with that identity.
|
|
3
|
+
* the named pipe, completes bus.handshake by presenting the token, and starts a HandlerRouter.
|
|
5
4
|
*
|
|
6
5
|
* This mirrors the C# NodeProcessController's spawn contract: the host writes one line to the
|
|
7
6
|
* Node process's stdin — "<pipePath>\t<token>" — then waits for the Node side to connect the pipe
|
|
@@ -14,7 +13,6 @@ import { NodeTransport } from "./transport.ts";
|
|
|
14
13
|
import { HandlerRouter } from "./router.ts";
|
|
15
14
|
import {
|
|
16
15
|
type Envelope,
|
|
17
|
-
EndpointIds,
|
|
18
16
|
MessageKind,
|
|
19
17
|
ProtocolVersion,
|
|
20
18
|
Routes,
|
|
@@ -30,8 +28,6 @@ export interface PluginRuntime {
|
|
|
30
28
|
close(): Promise<void>;
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
const SUPPORTED_VERSIONS = [ProtocolVersion];
|
|
34
|
-
|
|
35
31
|
/**
|
|
36
32
|
* Connects to the host pipe (reading the bootstrap line from stdin), completes handshake, and
|
|
37
33
|
* returns a runtime whose router dispatches inbound plugin.call.* requests to the given handlers.
|
|
@@ -42,29 +38,14 @@ export async function runPlugin(handlers: PluginHandlers): Promise<PluginRuntime
|
|
|
42
38
|
const transport = new NodeTransport();
|
|
43
39
|
await transport.connect(pipePath);
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
await completeHandshake(transport, token);
|
|
46
42
|
const router = new HandlerRouter({ send: (env: Envelope) => transport.send(env) });
|
|
47
|
-
router.setIdentity(identity);
|
|
48
|
-
|
|
49
|
-
// Passive host-liveness watchdog: exit if Host stops sending bus.ping (orphan process).
|
|
50
|
-
// Threshold is well above the host ping interval (~2s) to tolerate brief host stalls.
|
|
51
|
-
const HOST_LOST_MS = 15_000;
|
|
52
|
-
let lastPingAt = Date.now();
|
|
53
|
-
const watchdog = setInterval(() => {
|
|
54
|
-
if (Date.now() - lastPingAt > HOST_LOST_MS) {
|
|
55
|
-
clearInterval(watchdog);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
}, 1_000);
|
|
59
|
-
watchdog.unref?.();
|
|
60
43
|
|
|
61
44
|
transport.onDisconnect(() => {
|
|
62
|
-
clearInterval(watchdog);
|
|
63
45
|
process.exit(1);
|
|
64
46
|
});
|
|
65
47
|
|
|
66
48
|
transport.onMessage((env) => {
|
|
67
|
-
if (env.route === Routes.Bus.Ping) lastPingAt = Date.now();
|
|
68
49
|
router.dispatch(env);
|
|
69
50
|
});
|
|
70
51
|
|
|
@@ -76,7 +57,6 @@ export async function runPlugin(handlers: PluginHandlers): Promise<PluginRuntime
|
|
|
76
57
|
transport,
|
|
77
58
|
router,
|
|
78
59
|
close: async () => {
|
|
79
|
-
clearInterval(watchdog);
|
|
80
60
|
await transport.close();
|
|
81
61
|
},
|
|
82
62
|
};
|
|
@@ -101,14 +81,14 @@ async function readBootstrapLine(): Promise<{ pipePath: string; token: string }>
|
|
|
101
81
|
}
|
|
102
82
|
|
|
103
83
|
/**
|
|
104
|
-
* Sends bus.handshake with the bootstrap token and waits for
|
|
105
|
-
*
|
|
84
|
+
* Sends bus.handshake with the bootstrap token and waits for host acknowledgement.
|
|
85
|
+
* Protocol compatibility has already been checked from the manifest before process startup.
|
|
106
86
|
*/
|
|
107
87
|
export async function completeHandshake(
|
|
108
88
|
transport: NodeTransport,
|
|
109
89
|
token: string,
|
|
110
90
|
timeoutMs = 10000,
|
|
111
|
-
): Promise<
|
|
91
|
+
): Promise<void> {
|
|
112
92
|
const id = randomBytes(16).toString("hex");
|
|
113
93
|
const req: Envelope = {
|
|
114
94
|
version: ProtocolVersion,
|
|
@@ -116,15 +96,11 @@ export async function completeHandshake(
|
|
|
116
96
|
traceId: id,
|
|
117
97
|
sessionId: "",
|
|
118
98
|
pluginId: "",
|
|
119
|
-
endpointId:
|
|
99
|
+
endpointId: "",
|
|
120
100
|
kind: MessageKind.Request,
|
|
121
101
|
route: Routes.Bus.Handshake,
|
|
122
102
|
timeoutMs,
|
|
123
|
-
payload: {
|
|
124
|
-
version: ProtocolVersion,
|
|
125
|
-
supportedVersions: SUPPORTED_VERSIONS,
|
|
126
|
-
token,
|
|
127
|
-
},
|
|
103
|
+
payload: { token },
|
|
128
104
|
};
|
|
129
105
|
|
|
130
106
|
return new Promise((resolve, reject) => {
|
|
@@ -141,15 +117,7 @@ export async function completeHandshake(
|
|
|
141
117
|
reject(new Error(`${env.error.code}: ${env.error.message}`));
|
|
142
118
|
return;
|
|
143
119
|
}
|
|
144
|
-
|
|
145
|
-
const pluginId = String(p.pluginId ?? "");
|
|
146
|
-
const sessionId = String(p.sessionId ?? "");
|
|
147
|
-
const endpointId = String(p.endpointId ?? EndpointIds.NodeMain);
|
|
148
|
-
if (!pluginId || !sessionId) {
|
|
149
|
-
reject(new Error("bus.handshake success response missing bound identity"));
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
resolve({ pluginId, sessionId, endpointId });
|
|
120
|
+
resolve();
|
|
153
121
|
});
|
|
154
122
|
|
|
155
123
|
transport.send(req);
|
package/src/node.ts
CHANGED
|
@@ -14,7 +14,6 @@ import { runPlugin, type PluginRuntime } from "./bootstrap.ts";
|
|
|
14
14
|
import { asHostEnv, type PluginHostEnv, type PluginTheme } from "./hostEnv.ts";
|
|
15
15
|
import { toActionManifest, type ActionDefinition, type ActionOutcome } from "./actions.ts";
|
|
16
16
|
import {
|
|
17
|
-
EndpointIds,
|
|
18
17
|
MessageKind,
|
|
19
18
|
ProtocolVersion,
|
|
20
19
|
Routes,
|
|
@@ -153,7 +152,7 @@ export class Plugin {
|
|
|
153
152
|
traceId: crypto.randomUUID().replace(/-/g, "").slice(0, 32),
|
|
154
153
|
sessionId: "",
|
|
155
154
|
pluginId: "",
|
|
156
|
-
endpointId:
|
|
155
|
+
endpointId: "",
|
|
157
156
|
kind: MessageKind.Event,
|
|
158
157
|
route,
|
|
159
158
|
payload,
|
package/src/router.ts
CHANGED
|
@@ -9,7 +9,6 @@ import { randomBytes } from "node:crypto";
|
|
|
9
9
|
import {
|
|
10
10
|
type Envelope,
|
|
11
11
|
type BusError,
|
|
12
|
-
EndpointIds,
|
|
13
12
|
ErrorCode,
|
|
14
13
|
MessageKind,
|
|
15
14
|
ProtocolVersion,
|
|
@@ -61,10 +60,6 @@ function deadlineFromTimeoutMs(timeoutMs: number | null | undefined): number | n
|
|
|
61
60
|
export class HandlerRouter {
|
|
62
61
|
private handlers = new Map<string, Handler>();
|
|
63
62
|
private pendingHostCalls = new Map<string, PendingHostCall>();
|
|
64
|
-
private pluginId = "p";
|
|
65
|
-
private sessionId = "s";
|
|
66
|
-
private endpointId: string = EndpointIds.NodeMain;
|
|
67
|
-
|
|
68
63
|
/** Injected transport send fn; tests can override `router.send` directly. */
|
|
69
64
|
send: Sender;
|
|
70
65
|
|
|
@@ -72,13 +67,6 @@ export class HandlerRouter {
|
|
|
72
67
|
this.send = deps.send;
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
/** Sets the bound identity stamped on outbound messages (after handshake). */
|
|
76
|
-
setIdentity(ids: { pluginId: string; sessionId: string; endpointId: string }): void {
|
|
77
|
-
this.pluginId = ids.pluginId;
|
|
78
|
-
this.sessionId = ids.sessionId;
|
|
79
|
-
this.endpointId = ids.endpointId;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
70
|
handle(route: string, handler: Handler): void {
|
|
83
71
|
this.handlers.set(route, handler);
|
|
84
72
|
}
|
|
@@ -129,9 +117,9 @@ export class HandlerRouter {
|
|
|
129
117
|
version: ProtocolVersion,
|
|
130
118
|
id,
|
|
131
119
|
traceId: id,
|
|
132
|
-
sessionId:
|
|
133
|
-
pluginId:
|
|
134
|
-
endpointId:
|
|
120
|
+
sessionId: "",
|
|
121
|
+
pluginId: "",
|
|
122
|
+
endpointId: "",
|
|
135
123
|
kind: MessageKind.Request,
|
|
136
124
|
route,
|
|
137
125
|
timeoutMs: effectiveTimeoutMs,
|
|
@@ -171,9 +159,9 @@ export class HandlerRouter {
|
|
|
171
159
|
id: randomBytesHex(),
|
|
172
160
|
correlationId: req.id,
|
|
173
161
|
traceId: req.traceId,
|
|
174
|
-
sessionId:
|
|
175
|
-
pluginId:
|
|
176
|
-
endpointId:
|
|
162
|
+
sessionId: "",
|
|
163
|
+
pluginId: "",
|
|
164
|
+
endpointId: "",
|
|
177
165
|
kind: MessageKind.Response,
|
|
178
166
|
route: req.route,
|
|
179
167
|
payload,
|
package/src/webClient.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* v3 Web SDK: speaks protocol envelopes over chrome.webview.postMessage.
|
|
3
3
|
* Host stamps identity; the page does not supply plugin/entry/session ids.
|
|
4
4
|
*
|
|
5
|
-
* Connections start with bus.handshake
|
|
6
|
-
*
|
|
5
|
+
* Connections start with a payload-free bus.handshake readiness signal. The host validates the
|
|
6
|
+
* plugin manifest version before loading the page. call("refresh") is sent as plugin.call.refresh.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { mytoolsI18n } from "./i18n.ts";
|
|
@@ -116,14 +116,6 @@ function applyHostSideEffects(env: Envelope): void {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
function negotiatedVersionFrom(payload: unknown): string {
|
|
120
|
-
if (payload && typeof payload === "object" && "negotiatedVersion" in payload) {
|
|
121
|
-
const value = (payload as { negotiatedVersion?: unknown }).negotiatedVersion;
|
|
122
|
-
if (typeof value === "string" && value.length > 0) return value;
|
|
123
|
-
}
|
|
124
|
-
return ProtocolVersion;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
119
|
/**
|
|
128
120
|
* Creates a Web bus client. Registers the message listener immediately so host
|
|
129
121
|
* events that arrive before `on()` are buffered and replayed.
|
|
@@ -138,7 +130,6 @@ export function createWebBusClient(options?: {
|
|
|
138
130
|
const eventHandlers = new Set<(env: Envelope) => void>();
|
|
139
131
|
const lastByRoute = new Map<string, Envelope>();
|
|
140
132
|
const defaultTimeout = options?.timeoutMs ?? 30_000;
|
|
141
|
-
let wireVersion = ProtocolVersion;
|
|
142
133
|
let handshakeError: Error | null = null;
|
|
143
134
|
|
|
144
135
|
const onMessage = (event: MessageEvent) => {
|
|
@@ -165,10 +156,7 @@ export function createWebBusClient(options?: {
|
|
|
165
156
|
let handshakeDone: Promise<void> = Promise.resolve();
|
|
166
157
|
if (hasWebView()) {
|
|
167
158
|
(window as any).chrome.webview.addEventListener("message", onMessage);
|
|
168
|
-
handshakeDone =
|
|
169
|
-
.then((version) => {
|
|
170
|
-
wireVersion = version;
|
|
171
|
-
})
|
|
159
|
+
handshakeDone = signalPageReady(HandshakeTimeoutMs)
|
|
172
160
|
.catch((err: unknown) => {
|
|
173
161
|
handshakeError = err instanceof Error ? err : new Error(String(err));
|
|
174
162
|
throw handshakeError;
|
|
@@ -203,7 +191,7 @@ export function createWebBusClient(options?: {
|
|
|
203
191
|
},
|
|
204
192
|
});
|
|
205
193
|
post({
|
|
206
|
-
version:
|
|
194
|
+
version: ProtocolVersion,
|
|
207
195
|
id,
|
|
208
196
|
traceId: id,
|
|
209
197
|
sessionId: "",
|
|
@@ -245,7 +233,7 @@ export function createWebBusClient(options?: {
|
|
|
245
233
|
};
|
|
246
234
|
}
|
|
247
235
|
|
|
248
|
-
function
|
|
236
|
+
function signalPageReady(timeoutMs: number): Promise<void> {
|
|
249
237
|
const id = randomId();
|
|
250
238
|
return new Promise((resolve, reject) => {
|
|
251
239
|
const timer = window.setTimeout(() => {
|
|
@@ -260,7 +248,7 @@ function handshake(timeoutMs: number): Promise<string> {
|
|
|
260
248
|
if (env.error) {
|
|
261
249
|
reject(new Error(`${env.error.code}: ${env.error.message}`));
|
|
262
250
|
} else {
|
|
263
|
-
resolve(
|
|
251
|
+
resolve();
|
|
264
252
|
}
|
|
265
253
|
};
|
|
266
254
|
|
|
@@ -280,7 +268,6 @@ function handshake(timeoutMs: number): Promise<string> {
|
|
|
280
268
|
kind: MessageKind.Request,
|
|
281
269
|
route: Routes.Bus.Handshake,
|
|
282
270
|
timeoutMs,
|
|
283
|
-
payload: { version: ProtocolVersion, supportedVersions: [ProtocolVersion] },
|
|
284
271
|
});
|
|
285
272
|
});
|
|
286
273
|
}
|