@camstack/system 1.2.110 → 1.2.111
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/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/index.js +86 -8
- package/dist/index.mjs +86 -8
- package/dist/kernel/capability-registry.d.ts +51 -3
- package/dist/kernel/index.d.ts +1 -1
- package/dist/kernel/lifecycle/staging-area.d.ts +1 -0
- package/dist/kernel/transport/child-cap-protocol.d.ts +12 -0
- package/dist/kernel/transport/local-child-registry.d.ts +9 -2
- package/dist/{manifest-python-deps-5ei2xVUh.mjs → manifest-python-deps-Cb1ANt3i.mjs} +35 -8
- package/dist/{manifest-python-deps-GejnH--L.js → manifest-python-deps-MA_BZGBz.js} +35 -8
- package/package.json +1 -1
package/dist/addon-runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
2
|
+
const require_manifest_python_deps = require("./manifest-python-deps-MA_BZGBz.js");
|
|
3
3
|
const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
node_path = require_chunk.__toESM(node_path);
|
package/dist/addon-runner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as createUdsLoggerWithControl, L as LocalChildClient, Pt as startRunnerHeapWatch, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps, xt as resolveAddonClass } from "./manifest-python-deps-
|
|
1
|
+
import { I as createUdsLoggerWithControl, L as LocalChildClient, Pt as startRunnerHeapWatch, at as setWorkerNativeCapsChangeListener, bt as installManifestNativeDeps, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps, xt as resolveAddonClass } from "./manifest-python-deps-Cb1ANt3i.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
|
|
3
3
|
import { register } from "node:module";
|
|
4
4
|
import * as path$1 from "node:path";
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ const require_builtins_system_config_system_config_addon = require("./builtins/s
|
|
|
25
25
|
require("./builtins/system-config/index.js");
|
|
26
26
|
const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
|
|
27
27
|
const require_file_data_plane = require("./file-data-plane-DUHPHa-Y.js");
|
|
28
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
28
|
+
const require_manifest_python_deps = require("./manifest-python-deps-MA_BZGBz.js");
|
|
29
29
|
const require_resource_monitor = require("./resource-monitor-CdnzxBLP.js");
|
|
30
30
|
const require_custom_action_registry = require("./custom-action-registry-jY0NOZK8.js");
|
|
31
31
|
let zod = require("zod");
|
|
@@ -5367,6 +5367,7 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5367
5367
|
const state = {
|
|
5368
5368
|
definition,
|
|
5369
5369
|
providers: /* @__PURE__ */ new Map(),
|
|
5370
|
+
providerOwners: /* @__PURE__ */ new Map(),
|
|
5370
5371
|
activeAddonId: null,
|
|
5371
5372
|
router: null,
|
|
5372
5373
|
disabledProviders: /* @__PURE__ */ new Set()
|
|
@@ -5424,8 +5425,15 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5424
5425
|
* Double-register of the same pair is always a programmer error —
|
|
5425
5426
|
* two legitimately different addons implementing the same cap keep
|
|
5426
5427
|
* using different `addonId`s and the user picks one via `configReader`.
|
|
5427
|
-
|
|
5428
|
-
|
|
5428
|
+
*
|
|
5429
|
+
* `owner` (optional) stamps the registration with the incarnation that made
|
|
5430
|
+
* it — see {@link ProviderOwnerToken}. Once stamped, only an unregister
|
|
5431
|
+
* presenting the SAME token can delete it. Registration paths whose key is
|
|
5432
|
+
* reused across process generations (the UDS child manifest, the D3
|
|
5433
|
+
* `registerNode` handshake) MUST pass one; ownerless registrations keep the
|
|
5434
|
+
* historical unconditional behaviour.
|
|
5435
|
+
*/
|
|
5436
|
+
registerProvider(capabilityName, addonId, provider, owner) {
|
|
5429
5437
|
let state = this.capabilities.get(capabilityName);
|
|
5430
5438
|
if (!state) {
|
|
5431
5439
|
const colonIdx = capabilityName.indexOf(":");
|
|
@@ -5457,6 +5465,7 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5457
5465
|
if (missing.length > 0) throw new Error(`CapabilityRegistry: provider "${addonId}" for capability "${capabilityName}" declares exposesDeviceSettings: true but is missing DeviceSettingsContribution method(s): ${missing.join(", ")}. Implement them on the provider (or drop the flag from the cap def).`);
|
|
5458
5466
|
}
|
|
5459
5467
|
state.providers.set(addonId, provider);
|
|
5468
|
+
if (owner !== void 0) state.providerOwners.set(addonId, owner);
|
|
5460
5469
|
if (state.definition.mode === "singleton") {
|
|
5461
5470
|
const userChoice = this.configReader?.(capabilityName);
|
|
5462
5471
|
const preferred = state.definition.preferredProvider;
|
|
@@ -5487,7 +5496,8 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5487
5496
|
tags: { addonId },
|
|
5488
5497
|
meta: {
|
|
5489
5498
|
capability: capabilityName,
|
|
5490
|
-
mode: state.definition.mode
|
|
5499
|
+
mode: state.definition.mode,
|
|
5500
|
+
...owner === void 0 ? {} : { owner }
|
|
5491
5501
|
}
|
|
5492
5502
|
});
|
|
5493
5503
|
this.emitEvent("capability:provider-registered", {
|
|
@@ -5534,11 +5544,70 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5534
5544
|
const declarers = this.manifestDeclarers.get(capabilityName);
|
|
5535
5545
|
return declarers ? [...declarers] : [];
|
|
5536
5546
|
}
|
|
5537
|
-
/**
|
|
5538
|
-
|
|
5547
|
+
/**
|
|
5548
|
+
* The owner token a registration declared, or `null` when the pair is not
|
|
5549
|
+
* registered or was registered without one.
|
|
5550
|
+
*/
|
|
5551
|
+
getProviderOwner(capabilityName, addonId) {
|
|
5552
|
+
return this.capabilities.get(capabilityName)?.providerOwners.get(addonId) ?? null;
|
|
5553
|
+
}
|
|
5554
|
+
/**
|
|
5555
|
+
* Hand an EXISTING registration to a new owner, without touching the provider
|
|
5556
|
+
* instance, the active-singleton selection or any event.
|
|
5557
|
+
*
|
|
5558
|
+
* The takeover case: a replacement runner handshakes with an UNCHANGED cap
|
|
5559
|
+
* set, so the manifest diff correctly leaves the registration alone — but the
|
|
5560
|
+
* registration is still stamped with the dead generation's token, and nothing
|
|
5561
|
+
* the LIVE generation can present would ever match it. Re-stamping keeps the
|
|
5562
|
+
* capability cleanable when the successor eventually dies.
|
|
5563
|
+
*
|
|
5564
|
+
* Returns `false` when the pair is not registered (nothing to hand over) —
|
|
5565
|
+
* the caller decides whether that is a register-instead or a diagnosis.
|
|
5566
|
+
*/
|
|
5567
|
+
reassignProviderOwner(capabilityName, addonId, owner) {
|
|
5568
|
+
const state = this.capabilities.get(capabilityName);
|
|
5569
|
+
if (!state || !state.providers.has(addonId)) return false;
|
|
5570
|
+
const previous = state.providerOwners.get(addonId);
|
|
5571
|
+
if (previous === owner) return true;
|
|
5572
|
+
state.providerOwners.set(addonId, owner);
|
|
5573
|
+
this.logger.info("Capability registration ownership reassigned", {
|
|
5574
|
+
tags: { addonId },
|
|
5575
|
+
meta: {
|
|
5576
|
+
capability: capabilityName,
|
|
5577
|
+
owner,
|
|
5578
|
+
previousOwner: previous ?? null
|
|
5579
|
+
}
|
|
5580
|
+
});
|
|
5581
|
+
return true;
|
|
5582
|
+
}
|
|
5583
|
+
/**
|
|
5584
|
+
* Unregister a provider. For singleton: clears active. For collection:
|
|
5585
|
+
* removes from list.
|
|
5586
|
+
*
|
|
5587
|
+
* `owner` is the caller's PROOF that it owns the registration it is deleting.
|
|
5588
|
+
* When the registration declared an owner (see {@link ProviderOwnerToken})
|
|
5589
|
+
* and the caller presents a different one — or none at all — this is a
|
|
5590
|
+
* logged no-op: the caller belongs to a generation that no longer holds the
|
|
5591
|
+
* key. This is what stops a dead runner's teardown from erasing its
|
|
5592
|
+
* successor's live registration ([D188](../../../../docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md)).
|
|
5593
|
+
*/
|
|
5594
|
+
unregisterProvider(capabilityName, addonId, owner) {
|
|
5539
5595
|
const state = this.capabilities.get(capabilityName);
|
|
5540
5596
|
if (!state) return;
|
|
5597
|
+
const registeredOwner = state.providerOwners.get(addonId);
|
|
5598
|
+
if (registeredOwner !== void 0 && registeredOwner !== owner) {
|
|
5599
|
+
this.logger.info("Unregister ignored — registration is owned by another incarnation", {
|
|
5600
|
+
tags: { addonId },
|
|
5601
|
+
meta: {
|
|
5602
|
+
capability: capabilityName,
|
|
5603
|
+
registeredOwner,
|
|
5604
|
+
presentedOwner: owner ?? null
|
|
5605
|
+
}
|
|
5606
|
+
});
|
|
5607
|
+
return;
|
|
5608
|
+
}
|
|
5541
5609
|
state.providers.delete(addonId);
|
|
5610
|
+
state.providerOwners.delete(addonId);
|
|
5542
5611
|
if (state.definition.mode === "singleton" && state.activeAddonId === addonId) {
|
|
5543
5612
|
const userChoice = this.configReader?.(capabilityName);
|
|
5544
5613
|
const preferred = state.definition.preferredProvider;
|
|
@@ -94555,6 +94624,11 @@ var JobJournal = class {
|
|
|
94555
94624
|
function safeSegment(name) {
|
|
94556
94625
|
return name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
94557
94626
|
}
|
|
94627
|
+
/** An exact semver — the only shape the staged package.json can be compared
|
|
94628
|
+
* against literally. Anything else ('latest', 'beta', a range) is a dist-tag
|
|
94629
|
+
* the FETCHER resolves; comparing the resolved version to the literal tag
|
|
94630
|
+
* failed every tag-based update (30/30 on 2026-08-20). */
|
|
94631
|
+
var EXACT_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
94558
94632
|
var StagingArea = class {
|
|
94559
94633
|
stagingDir;
|
|
94560
94634
|
deps;
|
|
@@ -94572,8 +94646,12 @@ var StagingArea = class {
|
|
|
94572
94646
|
if (input.signal.aborted) throw new Error("staging aborted after fetch");
|
|
94573
94647
|
await this.deps.extract(tgz, destDir);
|
|
94574
94648
|
const raw = JSON.parse(node_fs.readFileSync(node_path.join(destDir, "package.json"), "utf-8"));
|
|
94575
|
-
if (raw.version !==
|
|
94576
|
-
|
|
94649
|
+
if (typeof raw.version !== "string" || raw.version.length === 0) throw new Error(`staging validation failed: ${input.name} staged a package.json with no version`);
|
|
94650
|
+
if (EXACT_SEMVER.test(input.version) && raw.version !== input.version) throw new Error(`staging validation failed: ${input.name} expected version ${input.version}, got ${raw.version}`);
|
|
94651
|
+
return {
|
|
94652
|
+
stagedPath: destDir,
|
|
94653
|
+
resolvedVersion: raw.version
|
|
94654
|
+
};
|
|
94577
94655
|
}
|
|
94578
94656
|
cleanup(jobId) {
|
|
94579
94657
|
node_fs.rmSync(node_path.join(this.stagingDir, jobId), {
|
package/dist/index.mjs
CHANGED
|
@@ -24,7 +24,7 @@ import { SystemConfigAddon } from "./builtins/system-config/system-config.addon.
|
|
|
24
24
|
import "./builtins/system-config/index.mjs";
|
|
25
25
|
import { WinstonDestination, WinstonLoggingAddon } from "./builtins/winston-logging/index.mjs";
|
|
26
26
|
import { a as parseTokenizedUrl, c as collectModelFiles, d as downloadModel, f as ensureModel, h as isModelDownloaded, i as parseRangeHeader, l as deleteModelFromDisk, m as getModelFilePath, n as contentTypeFor, o as resolveFilePath, p as fetchJson, r as createAuthenticatedFileServer, s as ModelDownloadService, t as createFileDataPlaneHandler, u as downloadFile } from "./file-data-plane-CuE_hBli.mjs";
|
|
27
|
-
import { $ as buildNativeCapProxy, A as createHubCapForwardService, At as buildHeapSample, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as runNpm, D as localProviderLink, Dt as HEAP_WATCH_INTERVAL_MS, E as ipcParentLink, Et as HEAP_RECLAIM_TRIGGER_MB, F as createUdsLogger, Ft as strandedMb, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, Mt as shouldReclaim, N as createUdsEventBus, Nt as startHeapWatch, O as HUB_CAP_FWD_ACTION, Ot as HEAP_WATCH_WARN_RATIO, P as udsChildLogToWorkerEntry, Pt as startRunnerHeapWatch, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as resolveNpmInvocation, T as ipcChildLink, Tt as HEAP_RECLAIM_MIN_INTERVAL_MS, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, jt as createV8Reclaimer, k as HUB_CAP_FWD_SERVICE, kt as RUNNER_HEAP_WATCH_INTERVAL_MS, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveAddonClass, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-
|
|
27
|
+
import { $ as buildNativeCapProxy, A as createHubCapForwardService, At as buildHeapSample, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as runNpm, D as localProviderLink, Dt as HEAP_WATCH_INTERVAL_MS, E as ipcParentLink, Et as HEAP_RECLAIM_TRIGGER_MB, F as createUdsLogger, Ft as strandedMb, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, Mt as shouldReclaim, N as createUdsEventBus, Nt as startHeapWatch, O as HUB_CAP_FWD_ACTION, Ot as HEAP_WATCH_WARN_RATIO, P as udsChildLogToWorkerEntry, Pt as startRunnerHeapWatch, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as resolveNpmInvocation, T as ipcChildLink, Tt as HEAP_RECLAIM_MIN_INTERVAL_MS, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, jt as createV8Reclaimer, k as HUB_CAP_FWD_SERVICE, kt as RUNNER_HEAP_WATCH_INTERVAL_MS, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveAddonClass, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-Cb1ANt3i.mjs";
|
|
28
28
|
import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BWmQ5i-o.mjs";
|
|
29
29
|
import { t as CustomActionRegistry } from "./custom-action-registry-F__gp_VX.mjs";
|
|
30
30
|
import { z } from "zod";
|
|
@@ -5360,6 +5360,7 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5360
5360
|
const state = {
|
|
5361
5361
|
definition,
|
|
5362
5362
|
providers: /* @__PURE__ */ new Map(),
|
|
5363
|
+
providerOwners: /* @__PURE__ */ new Map(),
|
|
5363
5364
|
activeAddonId: null,
|
|
5364
5365
|
router: null,
|
|
5365
5366
|
disabledProviders: /* @__PURE__ */ new Set()
|
|
@@ -5417,8 +5418,15 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5417
5418
|
* Double-register of the same pair is always a programmer error —
|
|
5418
5419
|
* two legitimately different addons implementing the same cap keep
|
|
5419
5420
|
* using different `addonId`s and the user picks one via `configReader`.
|
|
5420
|
-
|
|
5421
|
-
|
|
5421
|
+
*
|
|
5422
|
+
* `owner` (optional) stamps the registration with the incarnation that made
|
|
5423
|
+
* it — see {@link ProviderOwnerToken}. Once stamped, only an unregister
|
|
5424
|
+
* presenting the SAME token can delete it. Registration paths whose key is
|
|
5425
|
+
* reused across process generations (the UDS child manifest, the D3
|
|
5426
|
+
* `registerNode` handshake) MUST pass one; ownerless registrations keep the
|
|
5427
|
+
* historical unconditional behaviour.
|
|
5428
|
+
*/
|
|
5429
|
+
registerProvider(capabilityName, addonId, provider, owner) {
|
|
5422
5430
|
let state = this.capabilities.get(capabilityName);
|
|
5423
5431
|
if (!state) {
|
|
5424
5432
|
const colonIdx = capabilityName.indexOf(":");
|
|
@@ -5450,6 +5458,7 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5450
5458
|
if (missing.length > 0) throw new Error(`CapabilityRegistry: provider "${addonId}" for capability "${capabilityName}" declares exposesDeviceSettings: true but is missing DeviceSettingsContribution method(s): ${missing.join(", ")}. Implement them on the provider (or drop the flag from the cap def).`);
|
|
5451
5459
|
}
|
|
5452
5460
|
state.providers.set(addonId, provider);
|
|
5461
|
+
if (owner !== void 0) state.providerOwners.set(addonId, owner);
|
|
5453
5462
|
if (state.definition.mode === "singleton") {
|
|
5454
5463
|
const userChoice = this.configReader?.(capabilityName);
|
|
5455
5464
|
const preferred = state.definition.preferredProvider;
|
|
@@ -5480,7 +5489,8 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5480
5489
|
tags: { addonId },
|
|
5481
5490
|
meta: {
|
|
5482
5491
|
capability: capabilityName,
|
|
5483
|
-
mode: state.definition.mode
|
|
5492
|
+
mode: state.definition.mode,
|
|
5493
|
+
...owner === void 0 ? {} : { owner }
|
|
5484
5494
|
}
|
|
5485
5495
|
});
|
|
5486
5496
|
this.emitEvent("capability:provider-registered", {
|
|
@@ -5527,11 +5537,70 @@ var CapabilityRegistry = class CapabilityRegistry {
|
|
|
5527
5537
|
const declarers = this.manifestDeclarers.get(capabilityName);
|
|
5528
5538
|
return declarers ? [...declarers] : [];
|
|
5529
5539
|
}
|
|
5530
|
-
/**
|
|
5531
|
-
|
|
5540
|
+
/**
|
|
5541
|
+
* The owner token a registration declared, or `null` when the pair is not
|
|
5542
|
+
* registered or was registered without one.
|
|
5543
|
+
*/
|
|
5544
|
+
getProviderOwner(capabilityName, addonId) {
|
|
5545
|
+
return this.capabilities.get(capabilityName)?.providerOwners.get(addonId) ?? null;
|
|
5546
|
+
}
|
|
5547
|
+
/**
|
|
5548
|
+
* Hand an EXISTING registration to a new owner, without touching the provider
|
|
5549
|
+
* instance, the active-singleton selection or any event.
|
|
5550
|
+
*
|
|
5551
|
+
* The takeover case: a replacement runner handshakes with an UNCHANGED cap
|
|
5552
|
+
* set, so the manifest diff correctly leaves the registration alone — but the
|
|
5553
|
+
* registration is still stamped with the dead generation's token, and nothing
|
|
5554
|
+
* the LIVE generation can present would ever match it. Re-stamping keeps the
|
|
5555
|
+
* capability cleanable when the successor eventually dies.
|
|
5556
|
+
*
|
|
5557
|
+
* Returns `false` when the pair is not registered (nothing to hand over) —
|
|
5558
|
+
* the caller decides whether that is a register-instead or a diagnosis.
|
|
5559
|
+
*/
|
|
5560
|
+
reassignProviderOwner(capabilityName, addonId, owner) {
|
|
5561
|
+
const state = this.capabilities.get(capabilityName);
|
|
5562
|
+
if (!state || !state.providers.has(addonId)) return false;
|
|
5563
|
+
const previous = state.providerOwners.get(addonId);
|
|
5564
|
+
if (previous === owner) return true;
|
|
5565
|
+
state.providerOwners.set(addonId, owner);
|
|
5566
|
+
this.logger.info("Capability registration ownership reassigned", {
|
|
5567
|
+
tags: { addonId },
|
|
5568
|
+
meta: {
|
|
5569
|
+
capability: capabilityName,
|
|
5570
|
+
owner,
|
|
5571
|
+
previousOwner: previous ?? null
|
|
5572
|
+
}
|
|
5573
|
+
});
|
|
5574
|
+
return true;
|
|
5575
|
+
}
|
|
5576
|
+
/**
|
|
5577
|
+
* Unregister a provider. For singleton: clears active. For collection:
|
|
5578
|
+
* removes from list.
|
|
5579
|
+
*
|
|
5580
|
+
* `owner` is the caller's PROOF that it owns the registration it is deleting.
|
|
5581
|
+
* When the registration declared an owner (see {@link ProviderOwnerToken})
|
|
5582
|
+
* and the caller presents a different one — or none at all — this is a
|
|
5583
|
+
* logged no-op: the caller belongs to a generation that no longer holds the
|
|
5584
|
+
* key. This is what stops a dead runner's teardown from erasing its
|
|
5585
|
+
* successor's live registration ([D188](../../../../docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md)).
|
|
5586
|
+
*/
|
|
5587
|
+
unregisterProvider(capabilityName, addonId, owner) {
|
|
5532
5588
|
const state = this.capabilities.get(capabilityName);
|
|
5533
5589
|
if (!state) return;
|
|
5590
|
+
const registeredOwner = state.providerOwners.get(addonId);
|
|
5591
|
+
if (registeredOwner !== void 0 && registeredOwner !== owner) {
|
|
5592
|
+
this.logger.info("Unregister ignored — registration is owned by another incarnation", {
|
|
5593
|
+
tags: { addonId },
|
|
5594
|
+
meta: {
|
|
5595
|
+
capability: capabilityName,
|
|
5596
|
+
registeredOwner,
|
|
5597
|
+
presentedOwner: owner ?? null
|
|
5598
|
+
}
|
|
5599
|
+
});
|
|
5600
|
+
return;
|
|
5601
|
+
}
|
|
5534
5602
|
state.providers.delete(addonId);
|
|
5603
|
+
state.providerOwners.delete(addonId);
|
|
5535
5604
|
if (state.definition.mode === "singleton" && state.activeAddonId === addonId) {
|
|
5536
5605
|
const userChoice = this.configReader?.(capabilityName);
|
|
5537
5606
|
const preferred = state.definition.preferredProvider;
|
|
@@ -94548,6 +94617,11 @@ var JobJournal = class {
|
|
|
94548
94617
|
function safeSegment(name) {
|
|
94549
94618
|
return name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
94550
94619
|
}
|
|
94620
|
+
/** An exact semver — the only shape the staged package.json can be compared
|
|
94621
|
+
* against literally. Anything else ('latest', 'beta', a range) is a dist-tag
|
|
94622
|
+
* the FETCHER resolves; comparing the resolved version to the literal tag
|
|
94623
|
+
* failed every tag-based update (30/30 on 2026-08-20). */
|
|
94624
|
+
var EXACT_SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
94551
94625
|
var StagingArea = class {
|
|
94552
94626
|
stagingDir;
|
|
94553
94627
|
deps;
|
|
@@ -94565,8 +94639,12 @@ var StagingArea = class {
|
|
|
94565
94639
|
if (input.signal.aborted) throw new Error("staging aborted after fetch");
|
|
94566
94640
|
await this.deps.extract(tgz, destDir);
|
|
94567
94641
|
const raw = JSON.parse(fs$17.readFileSync(path$39.join(destDir, "package.json"), "utf-8"));
|
|
94568
|
-
if (raw.version !==
|
|
94569
|
-
|
|
94642
|
+
if (typeof raw.version !== "string" || raw.version.length === 0) throw new Error(`staging validation failed: ${input.name} staged a package.json with no version`);
|
|
94643
|
+
if (EXACT_SEMVER.test(input.version) && raw.version !== input.version) throw new Error(`staging validation failed: ${input.name} expected version ${input.version}, got ${raw.version}`);
|
|
94644
|
+
return {
|
|
94645
|
+
stagedPath: destDir,
|
|
94646
|
+
resolvedVersion: raw.version
|
|
94647
|
+
};
|
|
94570
94648
|
}
|
|
94571
94649
|
cleanup(jobId) {
|
|
94572
94650
|
fs$17.rmSync(path$39.join(this.stagingDir, jobId), {
|
|
@@ -19,6 +19,18 @@ export type ConfigReader = (capability: string) => string | undefined;
|
|
|
19
19
|
* {@link ConfigReader} does for singletons.
|
|
20
20
|
*/
|
|
21
21
|
export type CollectionConfigReader = (capability: string) => readonly string[] | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Proof that an unregister owns the registration it is about to delete.
|
|
24
|
+
*
|
|
25
|
+
* A `(capability, addonId)` key is NOT unique over time: a runner replaced by
|
|
26
|
+
* an update re-registers under the exact same key (`resolveRunnerId` is
|
|
27
|
+
* deterministic), so the dead generation's teardown and the live generation's
|
|
28
|
+
* registration are indistinguishable by key alone. The owner token names the
|
|
29
|
+
* INCARNATION that made a registration — e.g. `hub/recorder#7` — and
|
|
30
|
+
* {@link CapabilityRegistry.unregisterProvider} refuses any unregister that
|
|
31
|
+
* cannot present it. See docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md.
|
|
32
|
+
*/
|
|
33
|
+
export type ProviderOwnerToken = string;
|
|
22
34
|
export declare class CapabilityRegistry {
|
|
23
35
|
private readonly capabilities;
|
|
24
36
|
private readonly logger;
|
|
@@ -148,8 +160,15 @@ export declare class CapabilityRegistry {
|
|
|
148
160
|
* Double-register of the same pair is always a programmer error —
|
|
149
161
|
* two legitimately different addons implementing the same cap keep
|
|
150
162
|
* using different `addonId`s and the user picks one via `configReader`.
|
|
163
|
+
*
|
|
164
|
+
* `owner` (optional) stamps the registration with the incarnation that made
|
|
165
|
+
* it — see {@link ProviderOwnerToken}. Once stamped, only an unregister
|
|
166
|
+
* presenting the SAME token can delete it. Registration paths whose key is
|
|
167
|
+
* reused across process generations (the UDS child manifest, the D3
|
|
168
|
+
* `registerNode` handshake) MUST pass one; ownerless registrations keep the
|
|
169
|
+
* historical unconditional behaviour.
|
|
151
170
|
*/
|
|
152
|
-
registerProvider(capabilityName: string, addonId: string, provider: unknown): void;
|
|
171
|
+
registerProvider(capabilityName: string, addonId: string, provider: unknown, owner?: ProviderOwnerToken): void;
|
|
153
172
|
/**
|
|
154
173
|
* Whether a provider for the exact `(capabilityName, addonId)` pair is
|
|
155
174
|
* currently registered. Lets a re-registration path (e.g. the agent's
|
|
@@ -173,8 +192,37 @@ export declare class CapabilityRegistry {
|
|
|
173
192
|
* it is the diagnosis).
|
|
174
193
|
*/
|
|
175
194
|
getManifestDeclarers(capabilityName: string): readonly string[];
|
|
176
|
-
/**
|
|
177
|
-
|
|
195
|
+
/**
|
|
196
|
+
* The owner token a registration declared, or `null` when the pair is not
|
|
197
|
+
* registered or was registered without one.
|
|
198
|
+
*/
|
|
199
|
+
getProviderOwner(capabilityName: string, addonId: string): ProviderOwnerToken | null;
|
|
200
|
+
/**
|
|
201
|
+
* Hand an EXISTING registration to a new owner, without touching the provider
|
|
202
|
+
* instance, the active-singleton selection or any event.
|
|
203
|
+
*
|
|
204
|
+
* The takeover case: a replacement runner handshakes with an UNCHANGED cap
|
|
205
|
+
* set, so the manifest diff correctly leaves the registration alone — but the
|
|
206
|
+
* registration is still stamped with the dead generation's token, and nothing
|
|
207
|
+
* the LIVE generation can present would ever match it. Re-stamping keeps the
|
|
208
|
+
* capability cleanable when the successor eventually dies.
|
|
209
|
+
*
|
|
210
|
+
* Returns `false` when the pair is not registered (nothing to hand over) —
|
|
211
|
+
* the caller decides whether that is a register-instead or a diagnosis.
|
|
212
|
+
*/
|
|
213
|
+
reassignProviderOwner(capabilityName: string, addonId: string, owner: ProviderOwnerToken): boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Unregister a provider. For singleton: clears active. For collection:
|
|
216
|
+
* removes from list.
|
|
217
|
+
*
|
|
218
|
+
* `owner` is the caller's PROOF that it owns the registration it is deleting.
|
|
219
|
+
* When the registration declared an owner (see {@link ProviderOwnerToken})
|
|
220
|
+
* and the caller presents a different one — or none at all — this is a
|
|
221
|
+
* logged no-op: the caller belongs to a generation that no longer holds the
|
|
222
|
+
* key. This is what stops a dead runner's teardown from erasing its
|
|
223
|
+
* successor's live registration ([D188](../../../../docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md)).
|
|
224
|
+
*/
|
|
225
|
+
unregisterProvider(capabilityName: string, addonId: string, owner?: ProviderOwnerToken): void;
|
|
178
226
|
/** Enable a previously disabled collection provider. */
|
|
179
227
|
enableCollectionProvider(capability: string, addonId: string): void;
|
|
180
228
|
/** Disable a collection provider (keeps it registered but excluded from active list). */
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { buildCapRouters, builderMountedCapNames } from './cap-router-builder.js
|
|
|
17
17
|
export type { CapRouterPrimitives, CapRouterServices, CapProviderGetter, RemoteProxyFactory, ProcedureBuilder, ProcedureResolverArgs, } from './cap-router-builder.js';
|
|
18
18
|
export { describeProviderKindDrift } from './provider-kind-drift.js';
|
|
19
19
|
export type { ProviderKindHint } from './provider-kind-drift.js';
|
|
20
|
-
export type { CapabilityRouter, CapabilityRouterFactory, ConfigReader, } from './capability-registry.js';
|
|
20
|
+
export type { CapabilityRouter, CapabilityRouterFactory, ConfigReader, ProviderOwnerToken, } from './capability-registry.js';
|
|
21
21
|
export { CustomActionRegistry, type CustomActionEntry } from './custom-action-registry.js';
|
|
22
22
|
export { INFRA_CAPABILITIES, isInfraCapability } from './infra-capabilities.js';
|
|
23
23
|
export type { InfraCapability } from './infra-capabilities.js';
|
|
@@ -222,6 +222,18 @@ export type ParentToChildRequest = CapCallMessage | ParentEventMessage | Readine
|
|
|
222
222
|
export interface RegisteredChild {
|
|
223
223
|
readonly childId: string;
|
|
224
224
|
readonly caps: readonly ChildCapDescriptor[];
|
|
225
|
+
/**
|
|
226
|
+
* Monotonic id of the CONNECTION this registration arrived on, unique within
|
|
227
|
+
* the parent process.
|
|
228
|
+
*
|
|
229
|
+
* `childId` is deterministic (`resolveRunnerId`), so a runner replaced by an
|
|
230
|
+
* update reconnects under the exact same id — the two generations are
|
|
231
|
+
* indistinguishable without this. Consumers stamp it onto whatever they
|
|
232
|
+
* derive from the child (capability registrations, subtree manifests) so a
|
|
233
|
+
* teardown arriving from the DEAD generation can be told apart from the live
|
|
234
|
+
* one. See docs/decisions/adr-0188-an-unregister-carries-proof-of-ownership.md.
|
|
235
|
+
*/
|
|
236
|
+
readonly incarnation: number;
|
|
225
237
|
}
|
|
226
238
|
/** Arguments to route a cap method call (the `cap-call` message minus its discriminant). */
|
|
227
239
|
export type CapCallInput = Omit<CapCallMessage, 'kind'>;
|
|
@@ -139,6 +139,8 @@ export declare class LocalChildRegistry {
|
|
|
139
139
|
private readonly children;
|
|
140
140
|
private registeredHandler;
|
|
141
141
|
private goneHandler;
|
|
142
|
+
/** Source of {@link RegisteredChild.incarnation}; one bump per accepted connection. */
|
|
143
|
+
private nextIncarnation;
|
|
142
144
|
private eventHandler;
|
|
143
145
|
private logHandler;
|
|
144
146
|
private readinessHandler;
|
|
@@ -291,8 +293,13 @@ export declare class LocalChildRegistry {
|
|
|
291
293
|
listChildren(): readonly RegisteredChild[];
|
|
292
294
|
/** Register the (single) child-registered handler. Only one handler is active at a time. */
|
|
293
295
|
onChildRegistered(handler: (child: RegisteredChild) => void): void;
|
|
294
|
-
/**
|
|
295
|
-
|
|
296
|
+
/**
|
|
297
|
+
* Register the (single) child-gone handler. Only one handler is active at a
|
|
298
|
+
* time. The handler receives the {@link RegisteredChild.incarnation} of the
|
|
299
|
+
* connection that closed — carry it into whatever teardown it triggers, so a
|
|
300
|
+
* dead generation cannot tear down its successor's work.
|
|
301
|
+
*/
|
|
302
|
+
onChildGone(handler: (childId: string, incarnation: number) => void): void;
|
|
296
303
|
/**
|
|
297
304
|
* Register the (single) child-event handler. Invoked when a child sends an
|
|
298
305
|
* event via `LocalChildClient.emitEvent`. Only one handler is active at a
|
|
@@ -4832,6 +4832,8 @@ var LocalChildRegistry = class {
|
|
|
4832
4832
|
children = /* @__PURE__ */ new Map();
|
|
4833
4833
|
registeredHandler = () => {};
|
|
4834
4834
|
goneHandler = () => {};
|
|
4835
|
+
/** Source of {@link RegisteredChild.incarnation}; one bump per accepted connection. */
|
|
4836
|
+
nextIncarnation = 1;
|
|
4835
4837
|
eventHandler = null;
|
|
4836
4838
|
logHandler = null;
|
|
4837
4839
|
readinessHandler = null;
|
|
@@ -5106,14 +5108,20 @@ var LocalChildRegistry = class {
|
|
|
5106
5108
|
listChildren() {
|
|
5107
5109
|
return [...this.children.values()].map((e) => ({
|
|
5108
5110
|
childId: e.childId,
|
|
5109
|
-
caps: e.caps
|
|
5111
|
+
caps: e.caps,
|
|
5112
|
+
incarnation: e.incarnation
|
|
5110
5113
|
}));
|
|
5111
5114
|
}
|
|
5112
5115
|
/** Register the (single) child-registered handler. Only one handler is active at a time. */
|
|
5113
5116
|
onChildRegistered(handler) {
|
|
5114
5117
|
this.registeredHandler = handler;
|
|
5115
5118
|
}
|
|
5116
|
-
/**
|
|
5119
|
+
/**
|
|
5120
|
+
* Register the (single) child-gone handler. Only one handler is active at a
|
|
5121
|
+
* time. The handler receives the {@link RegisteredChild.incarnation} of the
|
|
5122
|
+
* connection that closed — carry it into whatever teardown it triggers, so a
|
|
5123
|
+
* dead generation cannot tear down its successor's work.
|
|
5124
|
+
*/
|
|
5117
5125
|
onChildGone(handler) {
|
|
5118
5126
|
this.goneHandler = handler;
|
|
5119
5127
|
}
|
|
@@ -5198,6 +5206,7 @@ var LocalChildRegistry = class {
|
|
|
5198
5206
|
}
|
|
5199
5207
|
onConnection(channel) {
|
|
5200
5208
|
let childId = null;
|
|
5209
|
+
const incarnation = this.nextIncarnation++;
|
|
5201
5210
|
channel.onEvent((body) => {
|
|
5202
5211
|
const msg = body;
|
|
5203
5212
|
if (msg.kind === "event") {
|
|
@@ -5226,16 +5235,24 @@ var LocalChildRegistry = class {
|
|
|
5226
5235
|
if (childId !== null && childId !== msg.childId) throw new Error(`child attempted to change identity from "${childId}" to "${msg.childId}"`);
|
|
5227
5236
|
childId = msg.childId;
|
|
5228
5237
|
const eventPatterns = msg.eventPatterns ?? null;
|
|
5238
|
+
const superseded = this.children.get(msg.childId);
|
|
5239
|
+
if (superseded !== void 0 && superseded.channel !== channel) this.logger?.info("local child superseded by a new connection", {
|
|
5240
|
+
childId: msg.childId,
|
|
5241
|
+
supersededIncarnation: superseded.incarnation,
|
|
5242
|
+
incarnation
|
|
5243
|
+
});
|
|
5229
5244
|
this.children.set(msg.childId, {
|
|
5230
5245
|
childId: msg.childId,
|
|
5231
5246
|
channel,
|
|
5232
5247
|
caps: msg.caps,
|
|
5233
|
-
eventPatterns
|
|
5248
|
+
eventPatterns,
|
|
5249
|
+
incarnation
|
|
5234
5250
|
});
|
|
5235
5251
|
this.logPatternSet(msg.childId, eventPatterns);
|
|
5236
5252
|
this.registeredHandler({
|
|
5237
5253
|
childId: msg.childId,
|
|
5238
|
-
caps: msg.caps
|
|
5254
|
+
caps: msg.caps,
|
|
5255
|
+
incarnation
|
|
5239
5256
|
});
|
|
5240
5257
|
return { ok: true };
|
|
5241
5258
|
}
|
|
@@ -5270,11 +5287,21 @@ var LocalChildRegistry = class {
|
|
|
5270
5287
|
throw new Error(`unknown child request kind: ${msg.kind}`);
|
|
5271
5288
|
});
|
|
5272
5289
|
channel.onClose(() => {
|
|
5273
|
-
if (childId
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5290
|
+
if (childId === null) return;
|
|
5291
|
+
const entry = this.children.get(childId);
|
|
5292
|
+
if (entry === void 0) return;
|
|
5293
|
+
if (entry.channel !== channel) {
|
|
5294
|
+
this.logger?.info("ignoring close of a superseded local child connection", {
|
|
5295
|
+
childId,
|
|
5296
|
+
incarnation,
|
|
5297
|
+
liveIncarnation: entry.incarnation
|
|
5298
|
+
});
|
|
5299
|
+
return;
|
|
5277
5300
|
}
|
|
5301
|
+
this.children.delete(childId);
|
|
5302
|
+
this.childEventStats.delete(childId);
|
|
5303
|
+
this.loggedPatternSet.delete(childId);
|
|
5304
|
+
this.goneHandler(childId, incarnation);
|
|
5278
5305
|
});
|
|
5279
5306
|
}
|
|
5280
5307
|
};
|
|
@@ -4836,6 +4836,8 @@ var LocalChildRegistry = class {
|
|
|
4836
4836
|
children = /* @__PURE__ */ new Map();
|
|
4837
4837
|
registeredHandler = () => {};
|
|
4838
4838
|
goneHandler = () => {};
|
|
4839
|
+
/** Source of {@link RegisteredChild.incarnation}; one bump per accepted connection. */
|
|
4840
|
+
nextIncarnation = 1;
|
|
4839
4841
|
eventHandler = null;
|
|
4840
4842
|
logHandler = null;
|
|
4841
4843
|
readinessHandler = null;
|
|
@@ -5110,14 +5112,20 @@ var LocalChildRegistry = class {
|
|
|
5110
5112
|
listChildren() {
|
|
5111
5113
|
return [...this.children.values()].map((e) => ({
|
|
5112
5114
|
childId: e.childId,
|
|
5113
|
-
caps: e.caps
|
|
5115
|
+
caps: e.caps,
|
|
5116
|
+
incarnation: e.incarnation
|
|
5114
5117
|
}));
|
|
5115
5118
|
}
|
|
5116
5119
|
/** Register the (single) child-registered handler. Only one handler is active at a time. */
|
|
5117
5120
|
onChildRegistered(handler) {
|
|
5118
5121
|
this.registeredHandler = handler;
|
|
5119
5122
|
}
|
|
5120
|
-
/**
|
|
5123
|
+
/**
|
|
5124
|
+
* Register the (single) child-gone handler. Only one handler is active at a
|
|
5125
|
+
* time. The handler receives the {@link RegisteredChild.incarnation} of the
|
|
5126
|
+
* connection that closed — carry it into whatever teardown it triggers, so a
|
|
5127
|
+
* dead generation cannot tear down its successor's work.
|
|
5128
|
+
*/
|
|
5121
5129
|
onChildGone(handler) {
|
|
5122
5130
|
this.goneHandler = handler;
|
|
5123
5131
|
}
|
|
@@ -5202,6 +5210,7 @@ var LocalChildRegistry = class {
|
|
|
5202
5210
|
}
|
|
5203
5211
|
onConnection(channel) {
|
|
5204
5212
|
let childId = null;
|
|
5213
|
+
const incarnation = this.nextIncarnation++;
|
|
5205
5214
|
channel.onEvent((body) => {
|
|
5206
5215
|
const msg = body;
|
|
5207
5216
|
if (msg.kind === "event") {
|
|
@@ -5230,16 +5239,24 @@ var LocalChildRegistry = class {
|
|
|
5230
5239
|
if (childId !== null && childId !== msg.childId) throw new Error(`child attempted to change identity from "${childId}" to "${msg.childId}"`);
|
|
5231
5240
|
childId = msg.childId;
|
|
5232
5241
|
const eventPatterns = msg.eventPatterns ?? null;
|
|
5242
|
+
const superseded = this.children.get(msg.childId);
|
|
5243
|
+
if (superseded !== void 0 && superseded.channel !== channel) this.logger?.info("local child superseded by a new connection", {
|
|
5244
|
+
childId: msg.childId,
|
|
5245
|
+
supersededIncarnation: superseded.incarnation,
|
|
5246
|
+
incarnation
|
|
5247
|
+
});
|
|
5233
5248
|
this.children.set(msg.childId, {
|
|
5234
5249
|
childId: msg.childId,
|
|
5235
5250
|
channel,
|
|
5236
5251
|
caps: msg.caps,
|
|
5237
|
-
eventPatterns
|
|
5252
|
+
eventPatterns,
|
|
5253
|
+
incarnation
|
|
5238
5254
|
});
|
|
5239
5255
|
this.logPatternSet(msg.childId, eventPatterns);
|
|
5240
5256
|
this.registeredHandler({
|
|
5241
5257
|
childId: msg.childId,
|
|
5242
|
-
caps: msg.caps
|
|
5258
|
+
caps: msg.caps,
|
|
5259
|
+
incarnation
|
|
5243
5260
|
});
|
|
5244
5261
|
return { ok: true };
|
|
5245
5262
|
}
|
|
@@ -5274,11 +5291,21 @@ var LocalChildRegistry = class {
|
|
|
5274
5291
|
throw new Error(`unknown child request kind: ${msg.kind}`);
|
|
5275
5292
|
});
|
|
5276
5293
|
channel.onClose(() => {
|
|
5277
|
-
if (childId
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5294
|
+
if (childId === null) return;
|
|
5295
|
+
const entry = this.children.get(childId);
|
|
5296
|
+
if (entry === void 0) return;
|
|
5297
|
+
if (entry.channel !== channel) {
|
|
5298
|
+
this.logger?.info("ignoring close of a superseded local child connection", {
|
|
5299
|
+
childId,
|
|
5300
|
+
incarnation,
|
|
5301
|
+
liveIncarnation: entry.incarnation
|
|
5302
|
+
});
|
|
5303
|
+
return;
|
|
5281
5304
|
}
|
|
5305
|
+
this.children.delete(childId);
|
|
5306
|
+
this.childEventStats.delete(childId);
|
|
5307
|
+
this.loggedPatternSet.delete(childId);
|
|
5308
|
+
this.goneHandler(childId, incarnation);
|
|
5282
5309
|
});
|
|
5283
5310
|
}
|
|
5284
5311
|
};
|