@camstack/system 1.1.20 → 1.1.22
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 +4 -1
- package/dist/index.mjs +2 -2
- package/dist/kernel/index.d.ts +2 -1
- package/dist/kernel/moleculer/addon-context-factory.d.ts +3 -3
- package/dist/kernel/moleculer/event-bus-core.d.ts +54 -0
- package/dist/kernel/moleculer/event-bus.d.ts +34 -1
- package/dist/kernel/transport/cap-route-resolver.d.ts +10 -1
- package/dist/kernel/transport/cap-route.d.ts +20 -0
- package/dist/kernel/transport/local-child-registry.d.ts +23 -0
- package/dist/kernel/transport/uds-event-bridge.d.ts +12 -0
- package/dist/{manifest-python-deps-PbdHKDpj.js → manifest-python-deps-D7iR07uA.js} +249 -6
- package/dist/{manifest-python-deps-X00KVYHa.mjs → manifest-python-deps-hDWMDe_b.mjs} +232 -7
- 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-D7iR07uA.js");
|
|
3
3
|
const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
node_fs = require_chunk.__toESM(node_fs);
|
package/dist/addon-runner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I as createUdsLoggerWithControl, L as LocalChildClient, at as setWorkerNativeCapsChangeListener, bt as resolveAddonClass, i as createUdsAddonContext, nt as getWorkerNativeCapProvider, rt as getWorkerNativeCapSnapshot, st as validateProviderRegistrations, t as installManifestPythonDeps, yt as installManifestNativeDeps } from "./manifest-python-deps-hDWMDe_b.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
3
3
|
import { register } from "node:module";
|
|
4
4
|
import * as fs from "node:fs";
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ const require_builtins_local_auth_local_auth_addon = require("./builtins/local-a
|
|
|
20
20
|
require("./builtins/local-auth/index.js");
|
|
21
21
|
const require_builtins_device_manager_device_manager_addon = require("./builtins/device-manager/device-manager.addon.js");
|
|
22
22
|
require("./builtins/device-manager/index.js");
|
|
23
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
23
|
+
const require_manifest_python_deps = require("./manifest-python-deps-D7iR07uA.js");
|
|
24
24
|
const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
|
|
25
25
|
let _camstack_types_node = require("@camstack/types/node");
|
|
26
26
|
let node_http = require("node:http");
|
|
@@ -93487,6 +93487,7 @@ Object.defineProperty(exports, "getFfmpegDownloadUrl", {
|
|
|
93487
93487
|
}
|
|
93488
93488
|
});
|
|
93489
93489
|
exports.getModelFilePath = require_model_download_service.getModelFilePath;
|
|
93490
|
+
exports.getMoleculerEventStats = require_manifest_python_deps.getMoleculerEventStats;
|
|
93490
93491
|
exports.getOrInitReadinessRegistry = require_manifest_python_deps.getOrInitReadinessRegistry;
|
|
93491
93492
|
exports.getOrInitReadinessRegistryForClient = require_manifest_python_deps.getOrInitReadinessRegistryForClient;
|
|
93492
93493
|
exports.getPidStats = require_resource_monitor.getPidStats;
|
|
@@ -93556,8 +93557,10 @@ Object.defineProperty(exports, "scopeKey", {
|
|
|
93556
93557
|
exports.scopesAllowDeviceCap = scopesAllowDeviceCap;
|
|
93557
93558
|
exports.serializeTypedArrays = require_manifest_python_deps.serializeTypedArrays;
|
|
93558
93559
|
exports.setHubConnected = require_manifest_python_deps.setHubConnected;
|
|
93560
|
+
exports.setNodeEventInterest = require_manifest_python_deps.setNodeEventInterest;
|
|
93559
93561
|
exports.stageFrameworkLockstep = stageFrameworkLockstep;
|
|
93560
93562
|
exports.stripCamstackDeps = stripCamstackDeps;
|
|
93563
|
+
exports.subscribePassthrough = require_manifest_python_deps.subscribePassthrough;
|
|
93561
93564
|
exports.udsChildLogToWorkerEntry = require_manifest_python_deps.udsChildLogToWorkerEntry;
|
|
93562
93565
|
exports.validateProviderRegistrations = require_manifest_python_deps.validateProviderRegistrations;
|
|
93563
93566
|
exports.writePendingRestart = writePendingRestart;
|
package/dist/index.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import { LocalAuthAddon, a as require_ms, c as __esmMin, d as __toCommonJS, f as
|
|
|
18
18
|
import "./builtins/local-auth/index.mjs";
|
|
19
19
|
import { DeviceManagerAddon } from "./builtins/device-manager/device-manager.addon.mjs";
|
|
20
20
|
import "./builtins/device-manager/index.mjs";
|
|
21
|
-
import { A as
|
|
21
|
+
import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as resolveHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as resolveAddonClass, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getMoleculerEventStats, dt as capBareAction, et as buildUdsNativeCapProxy, f as registerEventBusService, ft as capServiceName, g as createKernelHwAccel, gt as DeviceRegistry, h as AddonDepsManager, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as subscribePassthrough, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as setNodeEventInterest, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as getBrokerEventBus, ut as capActionSuffix, v as createHwAccelService, vt as CapabilityUnavailableError, w as buildLinkChain, x as getCapUsageRegistry, y as CapUsageRegistry, yt as installManifestNativeDeps, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-hDWMDe_b.mjs";
|
|
22
22
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
23
23
|
import { PYTHON_VERSION, buildBinaryPath, downloadBinary, ensureBinary, ensureFfmpeg, ensurePython, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
|
|
24
24
|
import { request } from "node:http";
|
|
@@ -93252,4 +93252,4 @@ async function stageFrameworkLockstep(input) {
|
|
|
93252
93252
|
return results;
|
|
93253
93253
|
}
|
|
93254
93254
|
//#endregion
|
|
93255
|
-
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FRAMEWORK_LOCKSTEP, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, RESTART_MARKER_FILE, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyCapRoute, clearPendingRestart, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createHwAccelService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensurePython, ensureTlsCert, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isClusterSecretMismatchError, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, serializeTypedArrays, setHubConnected, stageFrameworkLockstep, stripCamstackDeps, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
|
93255
|
+
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FRAMEWORK_LOCKSTEP, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, RESTART_MARKER_FILE, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyCapRoute, clearPendingRestart, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createHwAccelService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensurePython, ensureTlsCert, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isClusterSecretMismatchError, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, serializeTypedArrays, setHubConnected, setNodeEventInterest, stageFrameworkLockstep, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -33,7 +33,8 @@ export { type AddonDeploySource, isAddonDeploySource } from './moleculer/addon-d
|
|
|
33
33
|
export { createAddonService, validateProviderRegistrations, } from './moleculer/addon-service-factory.js';
|
|
34
34
|
export { createCoreCapService, CORE_CAP_SERVICE_NAME } from './moleculer/core-cap-service.js';
|
|
35
35
|
export type { CoreCapAction, CoreCapServiceOptions } from './moleculer/core-cap-service.js';
|
|
36
|
-
export { createAddonContext, createUdsAddonContext, setHubConnected, adaptBrokerToCluster, registerEventBusService, getBrokerEventBus, EVENT_TOPIC_PREFIX, } from './moleculer/addon-context-factory.js';
|
|
36
|
+
export { createAddonContext, createUdsAddonContext, setHubConnected, adaptBrokerToCluster, registerEventBusService, setNodeEventInterest, getMoleculerEventStats, subscribePassthrough, getBrokerEventBus, EVENT_TOPIC_PREFIX, } from './moleculer/addon-context-factory.js';
|
|
37
|
+
export type { MoleculerEventStats } from './moleculer/addon-context-factory.js';
|
|
37
38
|
export { HubLogForwarder } from './moleculer/hub-log-forwarder.js';
|
|
38
39
|
export type { AddonContextOptions } from './moleculer/addon-context-factory.js';
|
|
39
40
|
export { createHubService } from './moleculer/hub-service.js';
|
|
@@ -5,7 +5,7 @@ import { CapabilityRegistry } from '../capability-registry.js';
|
|
|
5
5
|
import { CapCallInput } from '../transport/index.js';
|
|
6
6
|
import { LocalChildClient } from '../transport/local-child-client.js';
|
|
7
7
|
import { DataPlaneSink } from './addon-data-plane-facility.js';
|
|
8
|
-
import { getBrokerEventBus, registerEventBusService, clusterEventTopic, EVENT_TOPIC_PREFIX } from './event-bus.js';
|
|
8
|
+
import { getBrokerEventBus, registerEventBusService, setNodeEventInterest, getMoleculerEventStats, subscribePassthrough, clusterEventTopic, EVENT_TOPIC_PREFIX, MoleculerEventStats } from './event-bus.js';
|
|
9
9
|
import { createRunnerLogger, setHubConnected, ___resetHubConnectedForTests, ___createRemoteLoggerForTests } from './remote-logger.js';
|
|
10
10
|
import { withAddonScope, setPreferredProvider, ___resetBindingSubscriptionForTests } from './provider-registry.js';
|
|
11
11
|
/**
|
|
@@ -99,5 +99,5 @@ declare function createUdsAddonContext(client: LocalChildClient, nodeId: string,
|
|
|
99
99
|
* next addon instance loads.
|
|
100
100
|
*/
|
|
101
101
|
declare function drainWorkerDisposerChain(nodeId: string, addonId: string): Promise<void>;
|
|
102
|
-
export { createAddonContext, createUdsAddonContext, setHubConnected, adaptBrokerToCluster, drainWorkerDisposerChain, registerEventBusService, clusterEventTopic, EVENT_TOPIC_PREFIX, getBrokerEventBus, withAddonScope, setPreferredProvider, createRunnerLogger, __resetHubConnectedForTests, __resetBindingSubscriptionForTests, __createRemoteLoggerForTests, };
|
|
103
|
-
export type { AddonContextOptions };
|
|
102
|
+
export { createAddonContext, createUdsAddonContext, setHubConnected, adaptBrokerToCluster, drainWorkerDisposerChain, registerEventBusService, setNodeEventInterest, getMoleculerEventStats, subscribePassthrough, clusterEventTopic, EVENT_TOPIC_PREFIX, getBrokerEventBus, withAddonScope, setPreferredProvider, createRunnerLogger, __resetHubConnectedForTests, __resetBindingSubscriptionForTests, __createRemoteLoggerForTests, };
|
|
103
|
+
export type { AddonContextOptions, MoleculerEventStats };
|
|
@@ -77,6 +77,60 @@ export declare const RING_BUFFER_DENY_PATTERNS: readonly string[];
|
|
|
77
77
|
*/
|
|
78
78
|
export declare const MAX_RECENT_EVENTS = 1000;
|
|
79
79
|
export declare function isHighFrequencyCategory(category: string): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Fan-out mode for cross-node (Moleculer) event delivery, read from the
|
|
82
|
+
* `CAMSTACK_MOLECULER_EVENT_FANOUT` env. The inter-node analogue of the
|
|
83
|
+
* intra-host `CAMSTACK_UDS_EVENT_FANOUT` gate (`local-child-registry.ts`):
|
|
84
|
+
* - `filter` (default) — drop an inbound cross-node event whose category
|
|
85
|
+
* no local subscriber on THIS node wants (the union of its children's
|
|
86
|
+
* declared patterns).
|
|
87
|
+
* - `shadow` — compute the drop decision + count would-suppresses, but
|
|
88
|
+
* STILL deliver every event (burn-in: prove zero would-be-starved deliveries
|
|
89
|
+
* before flipping to `filter`).
|
|
90
|
+
* - `broadcast` — ignore the gate entirely (pre-D2 behaviour).
|
|
91
|
+
*
|
|
92
|
+
* ROLLBACK CONTRACT (operators, read this): this env governs ONLY the inter-node
|
|
93
|
+
* (Moleculer) gate. The intra-host UDS fan-out has a SEPARATE, independent
|
|
94
|
+
* kill-switch, `CAMSTACK_UDS_EVENT_FANOUT` (see `local-child-registry.ts`). A
|
|
95
|
+
* FULL rollback to pre-D2 / pre-#44 delivery therefore requires BOTH env vars
|
|
96
|
+
* set to `broadcast` AND a process restart on EVERY node (hub + all agents) —
|
|
97
|
+
* each is read ONCE at registration/construction, so a running process will not
|
|
98
|
+
* pick up a change. Flipping only one leg leaves the other filtering and yields
|
|
99
|
+
* inconsistent behaviour. Recommended first fleet-wide enable: `shadow` on both,
|
|
100
|
+
* watch the suppressed counters (`getMoleculerEventStats` /
|
|
101
|
+
* `getChildEventStats`), then flip to `filter` once burn-in shows zero
|
|
102
|
+
* would-be-starved deliveries.
|
|
103
|
+
*/
|
|
104
|
+
export type MoleculerEventFanoutMode = 'filter' | 'shadow' | 'broadcast';
|
|
105
|
+
export declare function readMoleculerFanoutMode(): MoleculerEventFanoutMode;
|
|
106
|
+
/**
|
|
107
|
+
* A node's inbound-interest snapshot used by {@link crossNodeAcceptsCategory}.
|
|
108
|
+
* - `retainRecent` — the receiving bus retains events for `getRecent`/audit
|
|
109
|
+
* (the hub main process). Such a bus genuinely wants EVERY category (its
|
|
110
|
+
* interest set is the admin-ui live stream + recent[] ring), so it always
|
|
111
|
+
* fails OPEN — it never filters inbound cross-node events.
|
|
112
|
+
* - `interest` — the union of this node's local subscribers' declared category
|
|
113
|
+
* patterns (aggregated from its UDS children). `null` = undeclared / unknown
|
|
114
|
+
* (a legacy child, or no oracle installed) → fail OPEN (accept everything).
|
|
115
|
+
* An EMPTY array is a *declared* "wants nothing" and drops every category.
|
|
116
|
+
*/
|
|
117
|
+
export interface CrossNodeInterestSnapshot {
|
|
118
|
+
readonly retainRecent: boolean;
|
|
119
|
+
readonly interest: readonly string[] | null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Does this node want to accept an inbound cross-node event in `category`?
|
|
123
|
+
* The receiving-side mirror of `LocalChildRegistry.childWantsEvent`:
|
|
124
|
+
* - `broadcast` mode → always (gate disabled).
|
|
125
|
+
* - a retaining bus (hub main) → always (it serves `getRecent` + admin-ui;
|
|
126
|
+
* its interest is genuinely everything).
|
|
127
|
+
* - undeclared interest (`null`) → always (fail-open).
|
|
128
|
+
* - else the declared union must match via `matchesPattern` — the SAME
|
|
129
|
+
* function the local subscriber map-key gate uses, giving delivery parity.
|
|
130
|
+
* `shadow` is handled by the caller (it counts the would-suppress but still
|
|
131
|
+
* delivers), exactly like the UDS fan-out.
|
|
132
|
+
*/
|
|
133
|
+
export declare function crossNodeAcceptsCategory(mode: MoleculerEventFanoutMode, snapshot: CrossNodeInterestSnapshot, category: string): boolean;
|
|
80
134
|
/**
|
|
81
135
|
* Deliver `event` to all matching local subscribers and — ONLY on a bus that
|
|
82
136
|
* opted into retention (`state.retainRecent`, i.e. the hub main process) and
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IEventBus } from '@camstack/types';
|
|
1
|
+
import { IEventBus, SystemEvent } from '@camstack/types';
|
|
2
2
|
import { ServiceBroker } from 'moleculer';
|
|
3
3
|
/**
|
|
4
4
|
* Narrow interface for the Moleculer ServiceBroker surface used in this file.
|
|
@@ -28,6 +28,39 @@ export interface BrokerLike {
|
|
|
28
28
|
export declare const EVENT_TOPIC_PREFIX = "camstack.evt.";
|
|
29
29
|
/** Build the Moleculer broadcast topic for an event category. */
|
|
30
30
|
export declare function clusterEventTopic(category: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Subscribe a PASS-THROUGH relay handler to the broker's shared bus WITHOUT
|
|
33
|
+
* registering it in the local-interest surface. Used exclusively by the UDS
|
|
34
|
+
* event bridge for its cluster/parent-local → children fan-out subscription:
|
|
35
|
+
* the bridge must receive every event the gate lets through so it can relay to
|
|
36
|
+
* forked children, but it is not itself a terminal consumer, so it must not
|
|
37
|
+
* count toward this node's interest (a counted `*` would defeat the filter).
|
|
38
|
+
* Returns an unsubscribe function.
|
|
39
|
+
*/
|
|
40
|
+
export declare function subscribePassthrough(broker: ServiceBroker, handler: (event: SystemEvent) => void): () => void;
|
|
41
|
+
/** Cross-node inbound fan-out counters for a broker, `{ delivered, suppressed }`. */
|
|
42
|
+
export interface MoleculerEventStats {
|
|
43
|
+
readonly delivered: number;
|
|
44
|
+
readonly suppressed: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Install (or clear, with `null`) the per-node interest oracle consulted by the
|
|
48
|
+
* `$event-bus` inbound handler to decide whether an inbound cross-node event is
|
|
49
|
+
* wanted by any local subscriber on this node. The oracle is read PER-EVENT so a
|
|
50
|
+
* child (un)subscribing takes effect immediately with no re-registration.
|
|
51
|
+
*
|
|
52
|
+
* Only non-retaining nodes (agents) meaningfully filter — a retaining bus (hub
|
|
53
|
+
* main) always accepts every category (it serves `getRecent` + the admin-ui live
|
|
54
|
+
* stream). Installing the oracle on the hub too is therefore harmless and keeps
|
|
55
|
+
* the wiring uniform.
|
|
56
|
+
*/
|
|
57
|
+
export declare function setNodeEventInterest(broker: ServiceBroker, oracle: (() => readonly string[] | null) | null): void;
|
|
58
|
+
/**
|
|
59
|
+
* Cross-node inbound counters for `broker`, or `null` if no cross-node event has
|
|
60
|
+
* been processed yet. Exposed for the D2 shadow burn-in + operator debug —
|
|
61
|
+
* mirror of `LocalChildRegistry.getChildEventStats`.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getMoleculerEventStats(broker: ServiceBroker): MoleculerEventStats | null;
|
|
31
64
|
/**
|
|
32
65
|
* Register the `$event-bus` service on a broker. MUST be called BEFORE
|
|
33
66
|
* `broker.start()` — Moleculer announces a service's event
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { CapCallInput } from './child-cap-protocol.js';
|
|
2
1
|
import { ServiceDiscoveryBroker } from '../moleculer/resilient-cap-call.js';
|
|
3
2
|
import { CapRoute, CapRouteOpts, HubLocalUdsRoute, InProcessProviderRef } from './cap-route.js';
|
|
3
|
+
import { CapCallInput } from './child-cap-protocol.js';
|
|
4
4
|
/**
|
|
5
5
|
* Minimal surface of LocalChildRegistry that the resolver needs.
|
|
6
6
|
* Keeping a narrow interface here prevents the resolver from depending on the
|
|
@@ -80,6 +80,14 @@ export interface CapRouteResolverDeps {
|
|
|
80
80
|
* Decouples the resolver from CapabilityRegistry.
|
|
81
81
|
*/
|
|
82
82
|
readonly inProcessProviders: InProcessProviderLookup;
|
|
83
|
+
/**
|
|
84
|
+
* Optional NODE-ACCURATE lookup for providers physically resident on the
|
|
85
|
+
* hub node — consulted ONLY when a call is EXPLICITLY pinned to the hub.
|
|
86
|
+
* `inProcessProviders` may resolve the cluster-elected ACTIVE singleton
|
|
87
|
+
* (a remote-proxy provider registered in the hub registry), which must not
|
|
88
|
+
* satisfy a `nodePin('hub')`. See `CapRouteSnapshot.hubResidentProvides`.
|
|
89
|
+
*/
|
|
90
|
+
readonly hubResidentProviders?: InProcessProviderLookup;
|
|
83
91
|
/**
|
|
84
92
|
* Per-cap-method RPC timeout (ms). Lets a long-running cap method (e.g.
|
|
85
93
|
* `model-convert.convert`, minutes) override the default 60s remote/forward
|
|
@@ -133,6 +141,7 @@ export declare class CapRouteResolver {
|
|
|
133
141
|
private readonly hubLocalRegistry;
|
|
134
142
|
private readonly nodeAuthority;
|
|
135
143
|
private readonly inProcessProviders;
|
|
144
|
+
private readonly hubResidentProviders?;
|
|
136
145
|
private readonly capTimeoutMs?;
|
|
137
146
|
private readonly snapshot;
|
|
138
147
|
constructor(deps: CapRouteResolverDeps);
|
|
@@ -108,6 +108,26 @@ export interface CapRouteSnapshot {
|
|
|
108
108
|
* null, classifyCapRoute throws no-provider rather than casting.
|
|
109
109
|
*/
|
|
110
110
|
readonly getInProcessProviderRef?: (cap: string) => InProcessProviderRef | null;
|
|
111
|
+
/**
|
|
112
|
+
* Optional NODE-ACCURATE variant of `hubInProcessProvides`, consulted ONLY
|
|
113
|
+
* for an EXPLICIT hub pin (`opts.nodeId === hubNodeId`). Returns true only
|
|
114
|
+
* when a provider physically resident on the hub node exists.
|
|
115
|
+
*
|
|
116
|
+
* Rationale: `hubInProcessProvides` may be backed by the cluster-elected
|
|
117
|
+
* ACTIVE singleton — which can be a REMOTE-proxy provider registered in the
|
|
118
|
+
* hub registry. That is correct for UNPINNED dispatch (election adherence)
|
|
119
|
+
* but must NOT satisfy an explicit `nodePin('hub')`: dispatching the remote
|
|
120
|
+
* proxy as "hub-in-process" silently executes the call on another node
|
|
121
|
+
* (the shm frame-plane cross-node decoder-session storm). When absent,
|
|
122
|
+
* classifyExplicitNode falls back to `hubInProcessProvides` (legacy).
|
|
123
|
+
*/
|
|
124
|
+
readonly hubResidentProvides?: (cap: string) => boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Optional: resolves the InProcessProviderRef of a HUB-RESIDENT provider
|
|
127
|
+
* (paired with `hubResidentProvides` — same node-accuracy contract). Used
|
|
128
|
+
* instead of `getInProcessProviderRef` when `hubResidentProvides` is set.
|
|
129
|
+
*/
|
|
130
|
+
readonly getHubResidentProviderRef?: (cap: string) => InProcessProviderRef | null;
|
|
111
131
|
/**
|
|
112
132
|
* Optional: enumerates all known remote nodeIds for singleton cap resolution
|
|
113
133
|
* (the case where no explicit nodeId is given by the caller).
|
|
@@ -123,6 +123,29 @@ export declare class LocalChildRegistry {
|
|
|
123
123
|
* that explicitly subscribes to a ring-denied category must still RECEIVE it.
|
|
124
124
|
*/
|
|
125
125
|
private childWantsEvent;
|
|
126
|
+
/**
|
|
127
|
+
* D2: this node's aggregate cross-node event interest — the UNION of every
|
|
128
|
+
* connected child's declared category patterns. Consumed by the parent's
|
|
129
|
+
* `$event-bus` inbound gate (via `setNodeEventInterest`) so the node only
|
|
130
|
+
* accepts cross-node (Moleculer) categories at least one local child wants.
|
|
131
|
+
*
|
|
132
|
+
* Returns `null` (fail-open — accept everything) when:
|
|
133
|
+
* - NO child is currently connected (boot window before the first forked
|
|
134
|
+
* child completes its UDS handshake, or every child has disconnected): a
|
|
135
|
+
* node MUST NOT drop 100% of inbound cross-node events during that window,
|
|
136
|
+
* and a child may connect imminently. This is distinct from the
|
|
137
|
+
* declared-empty case below.
|
|
138
|
+
* - ANY connected child is UNDECLARED (`eventPatterns === null`, a legacy
|
|
139
|
+
* runner): a node cannot safely narrow its inbound set while a child's
|
|
140
|
+
* real interest is unknown. Mirrors the per-child fail-open in
|
|
141
|
+
* {@link childWantsEvent}, aggregated conservatively.
|
|
142
|
+
*
|
|
143
|
+
* Returns an EMPTY array only when ≥1 child is connected and EVERY connected
|
|
144
|
+
* child declared an explicit empty set — a genuine "wants nothing". Reads the
|
|
145
|
+
* live `children` map, so a child registering / disconnecting / sending
|
|
146
|
+
* `event-sub` is reflected on the next call with no extra bookkeeping.
|
|
147
|
+
*/
|
|
148
|
+
aggregateEventInterest(): readonly string[] | null;
|
|
126
149
|
private statsFor;
|
|
127
150
|
/** Deduped INFO line whenever a child's declared pattern set changes. */
|
|
128
151
|
private logPatternSet;
|
|
@@ -32,6 +32,18 @@ export interface UdsEventBridgeDeps {
|
|
|
32
32
|
readonly parentBus: IEventBus;
|
|
33
33
|
/** This process's Moleculer node ID (e.g. `'hub'` or `'agent-a1b2c3'`). */
|
|
34
34
|
readonly parentNodeId: string;
|
|
35
|
+
/**
|
|
36
|
+
* D2: subscribe the bridge's cluster/parent-local → children RELAY handler to
|
|
37
|
+
* the parent bus WITHOUT registering it in the node's local-interest surface
|
|
38
|
+
* (`subscribePassthrough` from `@camstack/system`). The bridge is a conduit to
|
|
39
|
+
* forked children, not a terminal local consumer — counting its wildcard `*`
|
|
40
|
+
* subscription would make every cross-node category appear "wanted" and defeat
|
|
41
|
+
* the inbound gate. When omitted (tests / legacy callers) the bridge falls
|
|
42
|
+
* back to `parentBus.subscribe({}, …)`, which is functionally correct but
|
|
43
|
+
* counts toward local interest — acceptable only where the D2 gate is not
|
|
44
|
+
* under test.
|
|
45
|
+
*/
|
|
46
|
+
readonly subscribePassthrough?: (handler: (event: SystemEvent) => void) => () => void;
|
|
35
47
|
}
|
|
36
48
|
/**
|
|
37
49
|
* Create the parent-side UDS ↔ Moleculer event bridge.
|
|
@@ -3461,6 +3461,29 @@ function isHighFrequencyCategory(category) {
|
|
|
3461
3461
|
for (const pattern of RING_BUFFER_DENY_PATTERNS) if (matchesPattern(pattern, category)) return true;
|
|
3462
3462
|
return false;
|
|
3463
3463
|
}
|
|
3464
|
+
function readMoleculerFanoutMode() {
|
|
3465
|
+
const raw = process.env.CAMSTACK_MOLECULER_EVENT_FANOUT;
|
|
3466
|
+
if (raw === "shadow" || raw === "broadcast") return raw;
|
|
3467
|
+
return "filter";
|
|
3468
|
+
}
|
|
3469
|
+
/**
|
|
3470
|
+
* Does this node want to accept an inbound cross-node event in `category`?
|
|
3471
|
+
* The receiving-side mirror of `LocalChildRegistry.childWantsEvent`:
|
|
3472
|
+
* - `broadcast` mode → always (gate disabled).
|
|
3473
|
+
* - a retaining bus (hub main) → always (it serves `getRecent` + admin-ui;
|
|
3474
|
+
* its interest is genuinely everything).
|
|
3475
|
+
* - undeclared interest (`null`) → always (fail-open).
|
|
3476
|
+
* - else the declared union must match via `matchesPattern` — the SAME
|
|
3477
|
+
* function the local subscriber map-key gate uses, giving delivery parity.
|
|
3478
|
+
* `shadow` is handled by the caller (it counts the would-suppress but still
|
|
3479
|
+
* delivers), exactly like the UDS fan-out.
|
|
3480
|
+
*/
|
|
3481
|
+
function crossNodeAcceptsCategory(mode, snapshot, category) {
|
|
3482
|
+
if (mode === "broadcast") return true;
|
|
3483
|
+
if (snapshot.retainRecent) return true;
|
|
3484
|
+
if (snapshot.interest === null) return true;
|
|
3485
|
+
return snapshot.interest.some((p) => matchesPattern(p, category));
|
|
3486
|
+
}
|
|
3464
3487
|
/**
|
|
3465
3488
|
* Deliver `event` to all matching local subscribers and — ONLY on a bus that
|
|
3466
3489
|
* opted into retention (`state.retainRecent`, i.e. the hub main process) and
|
|
@@ -3561,8 +3584,11 @@ function classifyCapRoute(capName, opts, snapshot) {
|
|
|
3561
3584
|
}
|
|
3562
3585
|
function classifyExplicitNode(capName, nodeId, deviceId, snap, rejected) {
|
|
3563
3586
|
if (nodeId === snap.hubNodeId) {
|
|
3564
|
-
|
|
3565
|
-
|
|
3587
|
+
const residentProvides = snap.hubResidentProvides;
|
|
3588
|
+
const providesOnHub = residentProvides ?? snap.hubInProcessProvides;
|
|
3589
|
+
const getRef = residentProvides !== void 0 ? snap.getHubResidentProviderRef : snap.getInProcessProviderRef;
|
|
3590
|
+
if (providesOnHub(capName)) {
|
|
3591
|
+
const ref = getRef?.(capName) ?? null;
|
|
3566
3592
|
if (ref !== null) return {
|
|
3567
3593
|
kind: "hub-in-process",
|
|
3568
3594
|
capName,
|
|
@@ -3727,6 +3753,7 @@ var CapRouteResolver = class {
|
|
|
3727
3753
|
hubLocalRegistry;
|
|
3728
3754
|
nodeAuthority;
|
|
3729
3755
|
inProcessProviders;
|
|
3756
|
+
hubResidentProviders;
|
|
3730
3757
|
capTimeoutMs;
|
|
3731
3758
|
snapshot;
|
|
3732
3759
|
constructor(deps) {
|
|
@@ -3735,6 +3762,7 @@ var CapRouteResolver = class {
|
|
|
3735
3762
|
this.hubLocalRegistry = deps.hubLocalRegistry;
|
|
3736
3763
|
this.nodeAuthority = deps.nodeAuthority;
|
|
3737
3764
|
this.inProcessProviders = deps.inProcessProviders;
|
|
3765
|
+
this.hubResidentProviders = deps.hubResidentProviders;
|
|
3738
3766
|
this.capTimeoutMs = deps.capTimeoutMs;
|
|
3739
3767
|
this.snapshot = this.buildSnapshot();
|
|
3740
3768
|
}
|
|
@@ -3856,9 +3884,14 @@ var CapRouteResolver = class {
|
|
|
3856
3884
|
const hubLocalRegistry = this.hubLocalRegistry;
|
|
3857
3885
|
const nodeAuthority = this.nodeAuthority;
|
|
3858
3886
|
const inProcessProviders = this.inProcessProviders;
|
|
3887
|
+
const hubResidentProviders = this.hubResidentProviders;
|
|
3859
3888
|
return {
|
|
3860
3889
|
hubNodeId: this.hubNodeId,
|
|
3861
3890
|
hubInProcessProvides: (cap) => inProcessProviders(cap) !== null,
|
|
3891
|
+
...hubResidentProviders !== void 0 ? {
|
|
3892
|
+
hubResidentProvides: (cap) => hubResidentProviders(cap) !== null,
|
|
3893
|
+
getHubResidentProviderRef: (cap) => hubResidentProviders(cap)
|
|
3894
|
+
} : {},
|
|
3862
3895
|
hubLocalChildProvides: (cap, deviceId) => hubLocalRegistry !== null && hubLocalRegistry.resolveChildId(cap, deviceId) !== null,
|
|
3863
3896
|
nodeKnowsCap: (nodeId, cap) => nodeAuthority.nodeKnowsCap(nodeId, cap),
|
|
3864
3897
|
nodeIsAgent: (nodeId) => nodeAuthority.nodeIsAgent(nodeId),
|
|
@@ -3977,6 +4010,37 @@ var LocalChildRegistry = class {
|
|
|
3977
4010
|
if (entry.eventPatterns === null) return true;
|
|
3978
4011
|
return entry.eventPatterns.some((p) => matchesPattern(p, category));
|
|
3979
4012
|
}
|
|
4013
|
+
/**
|
|
4014
|
+
* D2: this node's aggregate cross-node event interest — the UNION of every
|
|
4015
|
+
* connected child's declared category patterns. Consumed by the parent's
|
|
4016
|
+
* `$event-bus` inbound gate (via `setNodeEventInterest`) so the node only
|
|
4017
|
+
* accepts cross-node (Moleculer) categories at least one local child wants.
|
|
4018
|
+
*
|
|
4019
|
+
* Returns `null` (fail-open — accept everything) when:
|
|
4020
|
+
* - NO child is currently connected (boot window before the first forked
|
|
4021
|
+
* child completes its UDS handshake, or every child has disconnected): a
|
|
4022
|
+
* node MUST NOT drop 100% of inbound cross-node events during that window,
|
|
4023
|
+
* and a child may connect imminently. This is distinct from the
|
|
4024
|
+
* declared-empty case below.
|
|
4025
|
+
* - ANY connected child is UNDECLARED (`eventPatterns === null`, a legacy
|
|
4026
|
+
* runner): a node cannot safely narrow its inbound set while a child's
|
|
4027
|
+
* real interest is unknown. Mirrors the per-child fail-open in
|
|
4028
|
+
* {@link childWantsEvent}, aggregated conservatively.
|
|
4029
|
+
*
|
|
4030
|
+
* Returns an EMPTY array only when ≥1 child is connected and EVERY connected
|
|
4031
|
+
* child declared an explicit empty set — a genuine "wants nothing". Reads the
|
|
4032
|
+
* live `children` map, so a child registering / disconnecting / sending
|
|
4033
|
+
* `event-sub` is reflected on the next call with no extra bookkeeping.
|
|
4034
|
+
*/
|
|
4035
|
+
aggregateEventInterest() {
|
|
4036
|
+
if (this.children.size === 0) return null;
|
|
4037
|
+
const union = /* @__PURE__ */ new Set();
|
|
4038
|
+
for (const entry of this.children.values()) {
|
|
4039
|
+
if (entry.eventPatterns === null) return null;
|
|
4040
|
+
for (const p of entry.eventPatterns) union.add(p);
|
|
4041
|
+
}
|
|
4042
|
+
return [...union];
|
|
4043
|
+
}
|
|
3980
4044
|
statsFor(childId) {
|
|
3981
4045
|
let s = this.childEventStats.get(childId);
|
|
3982
4046
|
if (s === void 0) {
|
|
@@ -4749,7 +4813,7 @@ var MAX_RECENT = 512;
|
|
|
4749
4813
|
* and clears the child-event handler. Call it on process shutdown.
|
|
4750
4814
|
*/
|
|
4751
4815
|
function createUdsEventBridge(deps) {
|
|
4752
|
-
const { registry, parentBus, parentNodeId } = deps;
|
|
4816
|
+
const { registry, parentBus, parentNodeId, subscribePassthrough } = deps;
|
|
4753
4817
|
/**
|
|
4754
4818
|
* In-memory set of event ids for events that originated from one of this
|
|
4755
4819
|
* bridge's UDS children and have already been fanned to siblings + the
|
|
@@ -4768,10 +4832,11 @@ function createUdsEventBridge(deps) {
|
|
|
4768
4832
|
}
|
|
4769
4833
|
parentBus.emit(event);
|
|
4770
4834
|
});
|
|
4771
|
-
const
|
|
4835
|
+
const relayHandler = (event) => {
|
|
4772
4836
|
if (recentlyFannedIds.has(event.id)) return;
|
|
4773
4837
|
registry.broadcastEventToChildren(event, parentNodeId, void 0);
|
|
4774
|
-
}
|
|
4838
|
+
};
|
|
4839
|
+
const unsubscribe = subscribePassthrough !== void 0 ? subscribePassthrough(relayHandler) : parentBus.subscribe({}, relayHandler);
|
|
4775
4840
|
return () => {
|
|
4776
4841
|
unsubscribe();
|
|
4777
4842
|
registry.onChildEvent(null);
|
|
@@ -5863,6 +5928,163 @@ function getSharedBusState(broker, retainRecent = false) {
|
|
|
5863
5928
|
var eventBusServiceInstalled = /* @__PURE__ */ new WeakSet();
|
|
5864
5929
|
var brokerBusWarnTimestamps = /* @__PURE__ */ new WeakMap();
|
|
5865
5930
|
/**
|
|
5931
|
+
* Per-broker node-interest oracle. Returns the FORKED-CHILD interest (the union
|
|
5932
|
+
* of this node's UDS children's declared category patterns), or `null` when it
|
|
5933
|
+
* is undeclared/unknown (a legacy child, or no forked child has connected yet)
|
|
5934
|
+
* → fail-open. Absent (no entry) is also fail-open: pre-D2 behaviour is
|
|
5935
|
+
* preserved until a node wires its interest via {@link setNodeEventInterest}.
|
|
5936
|
+
*
|
|
5937
|
+
* IMPORTANT: this oracle covers ONLY forked/UDS children. In-process
|
|
5938
|
+
* ('broker'-mode) subscribers on this same broker (e.g. each addon context's
|
|
5939
|
+
* `device.bindings-changed` cache-invalidation sub) are tracked SEPARATELY in
|
|
5940
|
+
* {@link brokerLocalInterest}. The gate unions BOTH so it can never starve a
|
|
5941
|
+
* live local subscriber of either kind.
|
|
5942
|
+
*/
|
|
5943
|
+
var brokerNodeInterest = /* @__PURE__ */ new WeakMap();
|
|
5944
|
+
/**
|
|
5945
|
+
* Per-broker IN-PROCESS subscriber interest: category pattern → live refcount.
|
|
5946
|
+
* Populated by `getBrokerEventBus(...).subscribe` (every in-process /
|
|
5947
|
+
* 'broker'-mode subscription) and decremented by its unsubscribe. Does NOT
|
|
5948
|
+
* include the UDS event bridge's pass-through relay subscription — that is a
|
|
5949
|
+
* conduit to forked children (whose real interest the oracle already reports),
|
|
5950
|
+
* not a genuine local consumer, so it registers via {@link subscribePassthrough}
|
|
5951
|
+
* which bypasses this surface. Keeping the bridge out is what lets the gate
|
|
5952
|
+
* actually filter (otherwise the bridge's `*` would make every category wanted).
|
|
5953
|
+
*/
|
|
5954
|
+
var brokerLocalInterest = /* @__PURE__ */ new WeakMap();
|
|
5955
|
+
function bumpLocalInterest(broker, pattern) {
|
|
5956
|
+
let m = brokerLocalInterest.get(broker);
|
|
5957
|
+
if (m === void 0) {
|
|
5958
|
+
m = /* @__PURE__ */ new Map();
|
|
5959
|
+
brokerLocalInterest.set(broker, m);
|
|
5960
|
+
}
|
|
5961
|
+
m.set(pattern, (m.get(pattern) ?? 0) + 1);
|
|
5962
|
+
}
|
|
5963
|
+
function dropLocalInterest(broker, pattern) {
|
|
5964
|
+
const m = brokerLocalInterest.get(broker);
|
|
5965
|
+
if (m === void 0) return;
|
|
5966
|
+
const next = (m.get(pattern) ?? 0) - 1;
|
|
5967
|
+
if (next <= 0) m.delete(pattern);
|
|
5968
|
+
else m.set(pattern, next);
|
|
5969
|
+
}
|
|
5970
|
+
/** Snapshot of the in-process subscriber category patterns for `broker`. */
|
|
5971
|
+
function localSubscriberPatterns(broker) {
|
|
5972
|
+
const m = brokerLocalInterest.get(broker);
|
|
5973
|
+
return m === void 0 ? [] : [...m.keys()];
|
|
5974
|
+
}
|
|
5975
|
+
/**
|
|
5976
|
+
* Subscribe a PASS-THROUGH relay handler to the broker's shared bus WITHOUT
|
|
5977
|
+
* registering it in the local-interest surface. Used exclusively by the UDS
|
|
5978
|
+
* event bridge for its cluster/parent-local → children fan-out subscription:
|
|
5979
|
+
* the bridge must receive every event the gate lets through so it can relay to
|
|
5980
|
+
* forked children, but it is not itself a terminal consumer, so it must not
|
|
5981
|
+
* count toward this node's interest (a counted `*` would defeat the filter).
|
|
5982
|
+
* Returns an unsubscribe function.
|
|
5983
|
+
*/
|
|
5984
|
+
function subscribePassthrough(broker, handler) {
|
|
5985
|
+
const state = getSharedBusState(broker);
|
|
5986
|
+
const entry = {
|
|
5987
|
+
filter: {},
|
|
5988
|
+
categoryPattern: "*",
|
|
5989
|
+
handler
|
|
5990
|
+
};
|
|
5991
|
+
const set = state.handlers.get("*") ?? /* @__PURE__ */ new Set();
|
|
5992
|
+
set.add(entry);
|
|
5993
|
+
state.handlers.set("*", set);
|
|
5994
|
+
return () => {
|
|
5995
|
+
state.handlers.get("*")?.delete(entry);
|
|
5996
|
+
};
|
|
5997
|
+
}
|
|
5998
|
+
/** Per-broker cross-node inbound counters (delivered / would-suppress). */
|
|
5999
|
+
var brokerCrossNodeStats = /* @__PURE__ */ new WeakMap();
|
|
6000
|
+
/**
|
|
6001
|
+
* Install (or clear, with `null`) the per-node interest oracle consulted by the
|
|
6002
|
+
* `$event-bus` inbound handler to decide whether an inbound cross-node event is
|
|
6003
|
+
* wanted by any local subscriber on this node. The oracle is read PER-EVENT so a
|
|
6004
|
+
* child (un)subscribing takes effect immediately with no re-registration.
|
|
6005
|
+
*
|
|
6006
|
+
* Only non-retaining nodes (agents) meaningfully filter — a retaining bus (hub
|
|
6007
|
+
* main) always accepts every category (it serves `getRecent` + the admin-ui live
|
|
6008
|
+
* stream). Installing the oracle on the hub too is therefore harmless and keeps
|
|
6009
|
+
* the wiring uniform.
|
|
6010
|
+
*/
|
|
6011
|
+
function setNodeEventInterest(broker, oracle) {
|
|
6012
|
+
if (oracle === null) brokerNodeInterest.delete(broker);
|
|
6013
|
+
else brokerNodeInterest.set(broker, oracle);
|
|
6014
|
+
}
|
|
6015
|
+
/**
|
|
6016
|
+
* Cross-node inbound counters for `broker`, or `null` if no cross-node event has
|
|
6017
|
+
* been processed yet. Exposed for the D2 shadow burn-in + operator debug —
|
|
6018
|
+
* mirror of `LocalChildRegistry.getChildEventStats`.
|
|
6019
|
+
*/
|
|
6020
|
+
function getMoleculerEventStats(broker) {
|
|
6021
|
+
const s = brokerCrossNodeStats.get(broker);
|
|
6022
|
+
return s === void 0 ? null : {
|
|
6023
|
+
delivered: s.delivered,
|
|
6024
|
+
suppressed: s.suppressed
|
|
6025
|
+
};
|
|
6026
|
+
}
|
|
6027
|
+
function crossNodeStatsFor(broker) {
|
|
6028
|
+
let s = brokerCrossNodeStats.get(broker);
|
|
6029
|
+
if (s === void 0) {
|
|
6030
|
+
s = {
|
|
6031
|
+
delivered: 0,
|
|
6032
|
+
suppressed: 0
|
|
6033
|
+
};
|
|
6034
|
+
brokerCrossNodeStats.set(broker, s);
|
|
6035
|
+
}
|
|
6036
|
+
return s;
|
|
6037
|
+
}
|
|
6038
|
+
/**
|
|
6039
|
+
* Compute this node's COMBINED cross-node interest snapshot for the gate:
|
|
6040
|
+
* - `retainRecent` — from the shared bus (hub main → accept everything).
|
|
6041
|
+
* - `interest` — union of (a) forked-child interest (the oracle) and (b) live
|
|
6042
|
+
* in-process subscriber patterns. `null` (fail-open) when the oracle reports
|
|
6043
|
+
* `null` (undeclared / no forked child yet / oracle threw) — we cannot
|
|
6044
|
+
* safely narrow while a forked child's real interest is unknown, and a boot
|
|
6045
|
+
* window with no children yet must not drop everything.
|
|
6046
|
+
*
|
|
6047
|
+
* The oracle invocation is wrapped in try/catch and fails OPEN on throw — it
|
|
6048
|
+
* sits in front of the per-handler try/catch, in the subsystem that previously
|
|
6049
|
+
* caused an OOM, so a throwing oracle must never break inbound delivery.
|
|
6050
|
+
*/
|
|
6051
|
+
function nodeInterestSnapshot(broker) {
|
|
6052
|
+
const state = getSharedBusState(broker);
|
|
6053
|
+
let childInterest;
|
|
6054
|
+
try {
|
|
6055
|
+
const oracle = brokerNodeInterest.get(broker);
|
|
6056
|
+
childInterest = oracle ? oracle() : null;
|
|
6057
|
+
} catch {
|
|
6058
|
+
childInterest = null;
|
|
6059
|
+
}
|
|
6060
|
+
if (childInterest === null) return {
|
|
6061
|
+
retainRecent: state.retainRecent,
|
|
6062
|
+
interest: null
|
|
6063
|
+
};
|
|
6064
|
+
const local = localSubscriberPatterns(broker);
|
|
6065
|
+
const interest = [...new Set([...local, ...childInterest])];
|
|
6066
|
+
return {
|
|
6067
|
+
retainRecent: state.retainRecent,
|
|
6068
|
+
interest
|
|
6069
|
+
};
|
|
6070
|
+
}
|
|
6071
|
+
/**
|
|
6072
|
+
* Decide + account for an inbound cross-node event on `broker`. Returns whether
|
|
6073
|
+
* the event should be delivered to the local bus. Honours the
|
|
6074
|
+
* `CAMSTACK_MOLECULER_EVENT_FANOUT` mode (`mode`): in `shadow` it still delivers
|
|
6075
|
+
* but records the would-suppress; in `filter` it drops unwanted categories.
|
|
6076
|
+
* The interest is the UNION of forked-child AND in-process subscribers — the
|
|
6077
|
+
* gate never starves a live local subscriber of either kind.
|
|
6078
|
+
*/
|
|
6079
|
+
function acceptInboundCrossNode(broker, mode, category) {
|
|
6080
|
+
const wants = crossNodeAcceptsCategory(mode, nodeInterestSnapshot(broker), category);
|
|
6081
|
+
const stats = crossNodeStatsFor(broker);
|
|
6082
|
+
if (!wants) stats.suppressed++;
|
|
6083
|
+
const deliver = wants || mode === "shadow";
|
|
6084
|
+
if (deliver) stats.delivered++;
|
|
6085
|
+
return deliver;
|
|
6086
|
+
}
|
|
6087
|
+
/**
|
|
5866
6088
|
* Register the `$event-bus` service on a broker. MUST be called BEFORE
|
|
5867
6089
|
* `broker.start()` — Moleculer announces a service's event
|
|
5868
6090
|
* subscriptions to remote nodes only during the discovery handshake;
|
|
@@ -5886,6 +6108,7 @@ function registerEventBusService(broker) {
|
|
|
5886
6108
|
if (eventBusServiceInstalled.has(broker)) return;
|
|
5887
6109
|
eventBusServiceInstalled.add(broker);
|
|
5888
6110
|
const bkr = broker;
|
|
6111
|
+
const fanoutMode = readMoleculerFanoutMode();
|
|
5889
6112
|
try {
|
|
5890
6113
|
bkr.createService({
|
|
5891
6114
|
name: "$event-bus",
|
|
@@ -5893,6 +6116,7 @@ function registerEventBusService(broker) {
|
|
|
5893
6116
|
const event = ctx.params;
|
|
5894
6117
|
if (!event) return;
|
|
5895
6118
|
if (event.sourceNodeId === bkr.nodeID) return;
|
|
6119
|
+
if (!acceptInboundCrossNode(broker, fanoutMode, event.category)) return;
|
|
5896
6120
|
deliverShared(getSharedBusState(broker), event);
|
|
5897
6121
|
} }
|
|
5898
6122
|
});
|
|
@@ -5953,8 +6177,9 @@ function getBrokerEventBus(broker, options) {
|
|
|
5953
6177
|
const set = state.handlers.get(pattern) ?? /* @__PURE__ */ new Set();
|
|
5954
6178
|
set.add(entry);
|
|
5955
6179
|
state.handlers.set(pattern, set);
|
|
6180
|
+
bumpLocalInterest(broker, pattern);
|
|
5956
6181
|
return () => {
|
|
5957
|
-
state.handlers.get(pattern)?.delete(entry);
|
|
6182
|
+
if (state.handlers.get(pattern)?.delete(entry) === true) dropLocalInterest(broker, pattern);
|
|
5958
6183
|
};
|
|
5959
6184
|
},
|
|
5960
6185
|
getRecent(filter, limit) {
|
|
@@ -7154,6 +7379,12 @@ Object.defineProperty(exports, "getCapUsageRegistry", {
|
|
|
7154
7379
|
return getCapUsageRegistry;
|
|
7155
7380
|
}
|
|
7156
7381
|
});
|
|
7382
|
+
Object.defineProperty(exports, "getMoleculerEventStats", {
|
|
7383
|
+
enumerable: true,
|
|
7384
|
+
get: function() {
|
|
7385
|
+
return getMoleculerEventStats;
|
|
7386
|
+
}
|
|
7387
|
+
});
|
|
7157
7388
|
Object.defineProperty(exports, "getOrInitReadinessRegistry", {
|
|
7158
7389
|
enumerable: true,
|
|
7159
7390
|
get: function() {
|
|
@@ -7262,12 +7493,24 @@ Object.defineProperty(exports, "setHubConnected", {
|
|
|
7262
7493
|
return setHubConnected;
|
|
7263
7494
|
}
|
|
7264
7495
|
});
|
|
7496
|
+
Object.defineProperty(exports, "setNodeEventInterest", {
|
|
7497
|
+
enumerable: true,
|
|
7498
|
+
get: function() {
|
|
7499
|
+
return setNodeEventInterest;
|
|
7500
|
+
}
|
|
7501
|
+
});
|
|
7265
7502
|
Object.defineProperty(exports, "setWorkerNativeCapsChangeListener", {
|
|
7266
7503
|
enumerable: true,
|
|
7267
7504
|
get: function() {
|
|
7268
7505
|
return setWorkerNativeCapsChangeListener;
|
|
7269
7506
|
}
|
|
7270
7507
|
});
|
|
7508
|
+
Object.defineProperty(exports, "subscribePassthrough", {
|
|
7509
|
+
enumerable: true,
|
|
7510
|
+
get: function() {
|
|
7511
|
+
return subscribePassthrough;
|
|
7512
|
+
}
|
|
7513
|
+
});
|
|
7271
7514
|
Object.defineProperty(exports, "udsChildLogToWorkerEntry", {
|
|
7272
7515
|
enumerable: true,
|
|
7273
7516
|
get: function() {
|
|
@@ -3459,6 +3459,29 @@ function isHighFrequencyCategory(category) {
|
|
|
3459
3459
|
for (const pattern of RING_BUFFER_DENY_PATTERNS) if (matchesPattern(pattern, category)) return true;
|
|
3460
3460
|
return false;
|
|
3461
3461
|
}
|
|
3462
|
+
function readMoleculerFanoutMode() {
|
|
3463
|
+
const raw = process.env.CAMSTACK_MOLECULER_EVENT_FANOUT;
|
|
3464
|
+
if (raw === "shadow" || raw === "broadcast") return raw;
|
|
3465
|
+
return "filter";
|
|
3466
|
+
}
|
|
3467
|
+
/**
|
|
3468
|
+
* Does this node want to accept an inbound cross-node event in `category`?
|
|
3469
|
+
* The receiving-side mirror of `LocalChildRegistry.childWantsEvent`:
|
|
3470
|
+
* - `broadcast` mode → always (gate disabled).
|
|
3471
|
+
* - a retaining bus (hub main) → always (it serves `getRecent` + admin-ui;
|
|
3472
|
+
* its interest is genuinely everything).
|
|
3473
|
+
* - undeclared interest (`null`) → always (fail-open).
|
|
3474
|
+
* - else the declared union must match via `matchesPattern` — the SAME
|
|
3475
|
+
* function the local subscriber map-key gate uses, giving delivery parity.
|
|
3476
|
+
* `shadow` is handled by the caller (it counts the would-suppress but still
|
|
3477
|
+
* delivers), exactly like the UDS fan-out.
|
|
3478
|
+
*/
|
|
3479
|
+
function crossNodeAcceptsCategory(mode, snapshot, category) {
|
|
3480
|
+
if (mode === "broadcast") return true;
|
|
3481
|
+
if (snapshot.retainRecent) return true;
|
|
3482
|
+
if (snapshot.interest === null) return true;
|
|
3483
|
+
return snapshot.interest.some((p) => matchesPattern(p, category));
|
|
3484
|
+
}
|
|
3462
3485
|
/**
|
|
3463
3486
|
* Deliver `event` to all matching local subscribers and — ONLY on a bus that
|
|
3464
3487
|
* opted into retention (`state.retainRecent`, i.e. the hub main process) and
|
|
@@ -3559,8 +3582,11 @@ function classifyCapRoute(capName, opts, snapshot) {
|
|
|
3559
3582
|
}
|
|
3560
3583
|
function classifyExplicitNode(capName, nodeId, deviceId, snap, rejected) {
|
|
3561
3584
|
if (nodeId === snap.hubNodeId) {
|
|
3562
|
-
|
|
3563
|
-
|
|
3585
|
+
const residentProvides = snap.hubResidentProvides;
|
|
3586
|
+
const providesOnHub = residentProvides ?? snap.hubInProcessProvides;
|
|
3587
|
+
const getRef = residentProvides !== void 0 ? snap.getHubResidentProviderRef : snap.getInProcessProviderRef;
|
|
3588
|
+
if (providesOnHub(capName)) {
|
|
3589
|
+
const ref = getRef?.(capName) ?? null;
|
|
3564
3590
|
if (ref !== null) return {
|
|
3565
3591
|
kind: "hub-in-process",
|
|
3566
3592
|
capName,
|
|
@@ -3725,6 +3751,7 @@ var CapRouteResolver = class {
|
|
|
3725
3751
|
hubLocalRegistry;
|
|
3726
3752
|
nodeAuthority;
|
|
3727
3753
|
inProcessProviders;
|
|
3754
|
+
hubResidentProviders;
|
|
3728
3755
|
capTimeoutMs;
|
|
3729
3756
|
snapshot;
|
|
3730
3757
|
constructor(deps) {
|
|
@@ -3733,6 +3760,7 @@ var CapRouteResolver = class {
|
|
|
3733
3760
|
this.hubLocalRegistry = deps.hubLocalRegistry;
|
|
3734
3761
|
this.nodeAuthority = deps.nodeAuthority;
|
|
3735
3762
|
this.inProcessProviders = deps.inProcessProviders;
|
|
3763
|
+
this.hubResidentProviders = deps.hubResidentProviders;
|
|
3736
3764
|
this.capTimeoutMs = deps.capTimeoutMs;
|
|
3737
3765
|
this.snapshot = this.buildSnapshot();
|
|
3738
3766
|
}
|
|
@@ -3854,9 +3882,14 @@ var CapRouteResolver = class {
|
|
|
3854
3882
|
const hubLocalRegistry = this.hubLocalRegistry;
|
|
3855
3883
|
const nodeAuthority = this.nodeAuthority;
|
|
3856
3884
|
const inProcessProviders = this.inProcessProviders;
|
|
3885
|
+
const hubResidentProviders = this.hubResidentProviders;
|
|
3857
3886
|
return {
|
|
3858
3887
|
hubNodeId: this.hubNodeId,
|
|
3859
3888
|
hubInProcessProvides: (cap) => inProcessProviders(cap) !== null,
|
|
3889
|
+
...hubResidentProviders !== void 0 ? {
|
|
3890
|
+
hubResidentProvides: (cap) => hubResidentProviders(cap) !== null,
|
|
3891
|
+
getHubResidentProviderRef: (cap) => hubResidentProviders(cap)
|
|
3892
|
+
} : {},
|
|
3860
3893
|
hubLocalChildProvides: (cap, deviceId) => hubLocalRegistry !== null && hubLocalRegistry.resolveChildId(cap, deviceId) !== null,
|
|
3861
3894
|
nodeKnowsCap: (nodeId, cap) => nodeAuthority.nodeKnowsCap(nodeId, cap),
|
|
3862
3895
|
nodeIsAgent: (nodeId) => nodeAuthority.nodeIsAgent(nodeId),
|
|
@@ -3975,6 +4008,37 @@ var LocalChildRegistry = class {
|
|
|
3975
4008
|
if (entry.eventPatterns === null) return true;
|
|
3976
4009
|
return entry.eventPatterns.some((p) => matchesPattern(p, category));
|
|
3977
4010
|
}
|
|
4011
|
+
/**
|
|
4012
|
+
* D2: this node's aggregate cross-node event interest — the UNION of every
|
|
4013
|
+
* connected child's declared category patterns. Consumed by the parent's
|
|
4014
|
+
* `$event-bus` inbound gate (via `setNodeEventInterest`) so the node only
|
|
4015
|
+
* accepts cross-node (Moleculer) categories at least one local child wants.
|
|
4016
|
+
*
|
|
4017
|
+
* Returns `null` (fail-open — accept everything) when:
|
|
4018
|
+
* - NO child is currently connected (boot window before the first forked
|
|
4019
|
+
* child completes its UDS handshake, or every child has disconnected): a
|
|
4020
|
+
* node MUST NOT drop 100% of inbound cross-node events during that window,
|
|
4021
|
+
* and a child may connect imminently. This is distinct from the
|
|
4022
|
+
* declared-empty case below.
|
|
4023
|
+
* - ANY connected child is UNDECLARED (`eventPatterns === null`, a legacy
|
|
4024
|
+
* runner): a node cannot safely narrow its inbound set while a child's
|
|
4025
|
+
* real interest is unknown. Mirrors the per-child fail-open in
|
|
4026
|
+
* {@link childWantsEvent}, aggregated conservatively.
|
|
4027
|
+
*
|
|
4028
|
+
* Returns an EMPTY array only when ≥1 child is connected and EVERY connected
|
|
4029
|
+
* child declared an explicit empty set — a genuine "wants nothing". Reads the
|
|
4030
|
+
* live `children` map, so a child registering / disconnecting / sending
|
|
4031
|
+
* `event-sub` is reflected on the next call with no extra bookkeeping.
|
|
4032
|
+
*/
|
|
4033
|
+
aggregateEventInterest() {
|
|
4034
|
+
if (this.children.size === 0) return null;
|
|
4035
|
+
const union = /* @__PURE__ */ new Set();
|
|
4036
|
+
for (const entry of this.children.values()) {
|
|
4037
|
+
if (entry.eventPatterns === null) return null;
|
|
4038
|
+
for (const p of entry.eventPatterns) union.add(p);
|
|
4039
|
+
}
|
|
4040
|
+
return [...union];
|
|
4041
|
+
}
|
|
3978
4042
|
statsFor(childId) {
|
|
3979
4043
|
let s = this.childEventStats.get(childId);
|
|
3980
4044
|
if (s === void 0) {
|
|
@@ -4747,7 +4811,7 @@ var MAX_RECENT = 512;
|
|
|
4747
4811
|
* and clears the child-event handler. Call it on process shutdown.
|
|
4748
4812
|
*/
|
|
4749
4813
|
function createUdsEventBridge(deps) {
|
|
4750
|
-
const { registry, parentBus, parentNodeId } = deps;
|
|
4814
|
+
const { registry, parentBus, parentNodeId, subscribePassthrough } = deps;
|
|
4751
4815
|
/**
|
|
4752
4816
|
* In-memory set of event ids for events that originated from one of this
|
|
4753
4817
|
* bridge's UDS children and have already been fanned to siblings + the
|
|
@@ -4766,10 +4830,11 @@ function createUdsEventBridge(deps) {
|
|
|
4766
4830
|
}
|
|
4767
4831
|
parentBus.emit(event);
|
|
4768
4832
|
});
|
|
4769
|
-
const
|
|
4833
|
+
const relayHandler = (event) => {
|
|
4770
4834
|
if (recentlyFannedIds.has(event.id)) return;
|
|
4771
4835
|
registry.broadcastEventToChildren(event, parentNodeId, void 0);
|
|
4772
|
-
}
|
|
4836
|
+
};
|
|
4837
|
+
const unsubscribe = subscribePassthrough !== void 0 ? subscribePassthrough(relayHandler) : parentBus.subscribe({}, relayHandler);
|
|
4773
4838
|
return () => {
|
|
4774
4839
|
unsubscribe();
|
|
4775
4840
|
registry.onChildEvent(null);
|
|
@@ -5861,6 +5926,163 @@ function getSharedBusState(broker, retainRecent = false) {
|
|
|
5861
5926
|
var eventBusServiceInstalled = /* @__PURE__ */ new WeakSet();
|
|
5862
5927
|
var brokerBusWarnTimestamps = /* @__PURE__ */ new WeakMap();
|
|
5863
5928
|
/**
|
|
5929
|
+
* Per-broker node-interest oracle. Returns the FORKED-CHILD interest (the union
|
|
5930
|
+
* of this node's UDS children's declared category patterns), or `null` when it
|
|
5931
|
+
* is undeclared/unknown (a legacy child, or no forked child has connected yet)
|
|
5932
|
+
* → fail-open. Absent (no entry) is also fail-open: pre-D2 behaviour is
|
|
5933
|
+
* preserved until a node wires its interest via {@link setNodeEventInterest}.
|
|
5934
|
+
*
|
|
5935
|
+
* IMPORTANT: this oracle covers ONLY forked/UDS children. In-process
|
|
5936
|
+
* ('broker'-mode) subscribers on this same broker (e.g. each addon context's
|
|
5937
|
+
* `device.bindings-changed` cache-invalidation sub) are tracked SEPARATELY in
|
|
5938
|
+
* {@link brokerLocalInterest}. The gate unions BOTH so it can never starve a
|
|
5939
|
+
* live local subscriber of either kind.
|
|
5940
|
+
*/
|
|
5941
|
+
var brokerNodeInterest = /* @__PURE__ */ new WeakMap();
|
|
5942
|
+
/**
|
|
5943
|
+
* Per-broker IN-PROCESS subscriber interest: category pattern → live refcount.
|
|
5944
|
+
* Populated by `getBrokerEventBus(...).subscribe` (every in-process /
|
|
5945
|
+
* 'broker'-mode subscription) and decremented by its unsubscribe. Does NOT
|
|
5946
|
+
* include the UDS event bridge's pass-through relay subscription — that is a
|
|
5947
|
+
* conduit to forked children (whose real interest the oracle already reports),
|
|
5948
|
+
* not a genuine local consumer, so it registers via {@link subscribePassthrough}
|
|
5949
|
+
* which bypasses this surface. Keeping the bridge out is what lets the gate
|
|
5950
|
+
* actually filter (otherwise the bridge's `*` would make every category wanted).
|
|
5951
|
+
*/
|
|
5952
|
+
var brokerLocalInterest = /* @__PURE__ */ new WeakMap();
|
|
5953
|
+
function bumpLocalInterest(broker, pattern) {
|
|
5954
|
+
let m = brokerLocalInterest.get(broker);
|
|
5955
|
+
if (m === void 0) {
|
|
5956
|
+
m = /* @__PURE__ */ new Map();
|
|
5957
|
+
brokerLocalInterest.set(broker, m);
|
|
5958
|
+
}
|
|
5959
|
+
m.set(pattern, (m.get(pattern) ?? 0) + 1);
|
|
5960
|
+
}
|
|
5961
|
+
function dropLocalInterest(broker, pattern) {
|
|
5962
|
+
const m = brokerLocalInterest.get(broker);
|
|
5963
|
+
if (m === void 0) return;
|
|
5964
|
+
const next = (m.get(pattern) ?? 0) - 1;
|
|
5965
|
+
if (next <= 0) m.delete(pattern);
|
|
5966
|
+
else m.set(pattern, next);
|
|
5967
|
+
}
|
|
5968
|
+
/** Snapshot of the in-process subscriber category patterns for `broker`. */
|
|
5969
|
+
function localSubscriberPatterns(broker) {
|
|
5970
|
+
const m = brokerLocalInterest.get(broker);
|
|
5971
|
+
return m === void 0 ? [] : [...m.keys()];
|
|
5972
|
+
}
|
|
5973
|
+
/**
|
|
5974
|
+
* Subscribe a PASS-THROUGH relay handler to the broker's shared bus WITHOUT
|
|
5975
|
+
* registering it in the local-interest surface. Used exclusively by the UDS
|
|
5976
|
+
* event bridge for its cluster/parent-local → children fan-out subscription:
|
|
5977
|
+
* the bridge must receive every event the gate lets through so it can relay to
|
|
5978
|
+
* forked children, but it is not itself a terminal consumer, so it must not
|
|
5979
|
+
* count toward this node's interest (a counted `*` would defeat the filter).
|
|
5980
|
+
* Returns an unsubscribe function.
|
|
5981
|
+
*/
|
|
5982
|
+
function subscribePassthrough(broker, handler) {
|
|
5983
|
+
const state = getSharedBusState(broker);
|
|
5984
|
+
const entry = {
|
|
5985
|
+
filter: {},
|
|
5986
|
+
categoryPattern: "*",
|
|
5987
|
+
handler
|
|
5988
|
+
};
|
|
5989
|
+
const set = state.handlers.get("*") ?? /* @__PURE__ */ new Set();
|
|
5990
|
+
set.add(entry);
|
|
5991
|
+
state.handlers.set("*", set);
|
|
5992
|
+
return () => {
|
|
5993
|
+
state.handlers.get("*")?.delete(entry);
|
|
5994
|
+
};
|
|
5995
|
+
}
|
|
5996
|
+
/** Per-broker cross-node inbound counters (delivered / would-suppress). */
|
|
5997
|
+
var brokerCrossNodeStats = /* @__PURE__ */ new WeakMap();
|
|
5998
|
+
/**
|
|
5999
|
+
* Install (or clear, with `null`) the per-node interest oracle consulted by the
|
|
6000
|
+
* `$event-bus` inbound handler to decide whether an inbound cross-node event is
|
|
6001
|
+
* wanted by any local subscriber on this node. The oracle is read PER-EVENT so a
|
|
6002
|
+
* child (un)subscribing takes effect immediately with no re-registration.
|
|
6003
|
+
*
|
|
6004
|
+
* Only non-retaining nodes (agents) meaningfully filter — a retaining bus (hub
|
|
6005
|
+
* main) always accepts every category (it serves `getRecent` + the admin-ui live
|
|
6006
|
+
* stream). Installing the oracle on the hub too is therefore harmless and keeps
|
|
6007
|
+
* the wiring uniform.
|
|
6008
|
+
*/
|
|
6009
|
+
function setNodeEventInterest(broker, oracle) {
|
|
6010
|
+
if (oracle === null) brokerNodeInterest.delete(broker);
|
|
6011
|
+
else brokerNodeInterest.set(broker, oracle);
|
|
6012
|
+
}
|
|
6013
|
+
/**
|
|
6014
|
+
* Cross-node inbound counters for `broker`, or `null` if no cross-node event has
|
|
6015
|
+
* been processed yet. Exposed for the D2 shadow burn-in + operator debug —
|
|
6016
|
+
* mirror of `LocalChildRegistry.getChildEventStats`.
|
|
6017
|
+
*/
|
|
6018
|
+
function getMoleculerEventStats(broker) {
|
|
6019
|
+
const s = brokerCrossNodeStats.get(broker);
|
|
6020
|
+
return s === void 0 ? null : {
|
|
6021
|
+
delivered: s.delivered,
|
|
6022
|
+
suppressed: s.suppressed
|
|
6023
|
+
};
|
|
6024
|
+
}
|
|
6025
|
+
function crossNodeStatsFor(broker) {
|
|
6026
|
+
let s = brokerCrossNodeStats.get(broker);
|
|
6027
|
+
if (s === void 0) {
|
|
6028
|
+
s = {
|
|
6029
|
+
delivered: 0,
|
|
6030
|
+
suppressed: 0
|
|
6031
|
+
};
|
|
6032
|
+
brokerCrossNodeStats.set(broker, s);
|
|
6033
|
+
}
|
|
6034
|
+
return s;
|
|
6035
|
+
}
|
|
6036
|
+
/**
|
|
6037
|
+
* Compute this node's COMBINED cross-node interest snapshot for the gate:
|
|
6038
|
+
* - `retainRecent` — from the shared bus (hub main → accept everything).
|
|
6039
|
+
* - `interest` — union of (a) forked-child interest (the oracle) and (b) live
|
|
6040
|
+
* in-process subscriber patterns. `null` (fail-open) when the oracle reports
|
|
6041
|
+
* `null` (undeclared / no forked child yet / oracle threw) — we cannot
|
|
6042
|
+
* safely narrow while a forked child's real interest is unknown, and a boot
|
|
6043
|
+
* window with no children yet must not drop everything.
|
|
6044
|
+
*
|
|
6045
|
+
* The oracle invocation is wrapped in try/catch and fails OPEN on throw — it
|
|
6046
|
+
* sits in front of the per-handler try/catch, in the subsystem that previously
|
|
6047
|
+
* caused an OOM, so a throwing oracle must never break inbound delivery.
|
|
6048
|
+
*/
|
|
6049
|
+
function nodeInterestSnapshot(broker) {
|
|
6050
|
+
const state = getSharedBusState(broker);
|
|
6051
|
+
let childInterest;
|
|
6052
|
+
try {
|
|
6053
|
+
const oracle = brokerNodeInterest.get(broker);
|
|
6054
|
+
childInterest = oracle ? oracle() : null;
|
|
6055
|
+
} catch {
|
|
6056
|
+
childInterest = null;
|
|
6057
|
+
}
|
|
6058
|
+
if (childInterest === null) return {
|
|
6059
|
+
retainRecent: state.retainRecent,
|
|
6060
|
+
interest: null
|
|
6061
|
+
};
|
|
6062
|
+
const local = localSubscriberPatterns(broker);
|
|
6063
|
+
const interest = [...new Set([...local, ...childInterest])];
|
|
6064
|
+
return {
|
|
6065
|
+
retainRecent: state.retainRecent,
|
|
6066
|
+
interest
|
|
6067
|
+
};
|
|
6068
|
+
}
|
|
6069
|
+
/**
|
|
6070
|
+
* Decide + account for an inbound cross-node event on `broker`. Returns whether
|
|
6071
|
+
* the event should be delivered to the local bus. Honours the
|
|
6072
|
+
* `CAMSTACK_MOLECULER_EVENT_FANOUT` mode (`mode`): in `shadow` it still delivers
|
|
6073
|
+
* but records the would-suppress; in `filter` it drops unwanted categories.
|
|
6074
|
+
* The interest is the UNION of forked-child AND in-process subscribers — the
|
|
6075
|
+
* gate never starves a live local subscriber of either kind.
|
|
6076
|
+
*/
|
|
6077
|
+
function acceptInboundCrossNode(broker, mode, category) {
|
|
6078
|
+
const wants = crossNodeAcceptsCategory(mode, nodeInterestSnapshot(broker), category);
|
|
6079
|
+
const stats = crossNodeStatsFor(broker);
|
|
6080
|
+
if (!wants) stats.suppressed++;
|
|
6081
|
+
const deliver = wants || mode === "shadow";
|
|
6082
|
+
if (deliver) stats.delivered++;
|
|
6083
|
+
return deliver;
|
|
6084
|
+
}
|
|
6085
|
+
/**
|
|
5864
6086
|
* Register the `$event-bus` service on a broker. MUST be called BEFORE
|
|
5865
6087
|
* `broker.start()` — Moleculer announces a service's event
|
|
5866
6088
|
* subscriptions to remote nodes only during the discovery handshake;
|
|
@@ -5884,6 +6106,7 @@ function registerEventBusService(broker) {
|
|
|
5884
6106
|
if (eventBusServiceInstalled.has(broker)) return;
|
|
5885
6107
|
eventBusServiceInstalled.add(broker);
|
|
5886
6108
|
const bkr = broker;
|
|
6109
|
+
const fanoutMode = readMoleculerFanoutMode();
|
|
5887
6110
|
try {
|
|
5888
6111
|
bkr.createService({
|
|
5889
6112
|
name: "$event-bus",
|
|
@@ -5891,6 +6114,7 @@ function registerEventBusService(broker) {
|
|
|
5891
6114
|
const event = ctx.params;
|
|
5892
6115
|
if (!event) return;
|
|
5893
6116
|
if (event.sourceNodeId === bkr.nodeID) return;
|
|
6117
|
+
if (!acceptInboundCrossNode(broker, fanoutMode, event.category)) return;
|
|
5894
6118
|
deliverShared(getSharedBusState(broker), event);
|
|
5895
6119
|
} }
|
|
5896
6120
|
});
|
|
@@ -5951,8 +6175,9 @@ function getBrokerEventBus(broker, options) {
|
|
|
5951
6175
|
const set = state.handlers.get(pattern) ?? /* @__PURE__ */ new Set();
|
|
5952
6176
|
set.add(entry);
|
|
5953
6177
|
state.handlers.set(pattern, set);
|
|
6178
|
+
bumpLocalInterest(broker, pattern);
|
|
5954
6179
|
return () => {
|
|
5955
|
-
state.handlers.get(pattern)?.delete(entry);
|
|
6180
|
+
if (state.handlers.get(pattern)?.delete(entry) === true) dropLocalInterest(broker, pattern);
|
|
5956
6181
|
};
|
|
5957
6182
|
},
|
|
5958
6183
|
getRecent(filter, limit) {
|
|
@@ -6852,4 +7077,4 @@ async function installManifestPythonDeps(declaration, addonDir, deps, logger) {
|
|
|
6852
7077
|
await deps.installPythonRequirements(reqAbs);
|
|
6853
7078
|
}
|
|
6854
7079
|
//#endregion
|
|
6855
|
-
export {
|
|
7080
|
+
export { buildNativeCapProxy as $, createHubCapForwardService as A, AGENT_CAP_FWD_ACTION as B, brokerTransportLink as C, localProviderLink as D, ipcParentLink as E, createUdsLogger as F, callWithServiceDiscovery as G, CapRouteResolver as H, createUdsLoggerWithControl as I, UdsLocalTransportServer as J, createLocalTransport as K, LocalChildClient as L, createUdsEventBridge as M, createUdsEventBus as N, HUB_CAP_FWD_ACTION as O, udsChildLogToWorkerEntry as P, encodeFrame as Q, LocalChildRegistry as R, brokerCallForCap as S, ipcChildLink as T, CapRouteError as U, AGENT_CAP_FWD_SERVICE as V, classifyCapRoute as W, localEndpointPath as X, SocketChannel as Y, FrameDecoder as Z, resolveHwAccel as _, CapabilityHandle as _t, getWorkerDeviceRegistry as a, setWorkerNativeCapsChangeListener as at, __resetCapUsageRegistryForTests as b, resolveAddonClass as bt, setHubConnected as c, NATIVE_PROVIDER_SERVICE_INFIX as ct, getMoleculerEventStats as d, capBareAction as dt, buildUdsNativeCapProxy as et, registerEventBusService as f, capServiceName as ft, createKernelHwAccel as g, DeviceRegistry as gt, AddonDepsManager as h, serializeTypedArrays as ht, createUdsAddonContext as i, mountNativeCapService as it, createParentUnownedCallHandler as j, HUB_CAP_FWD_SERVICE as k, EVENT_TOPIC_PREFIX as l, capActionName as lt, subscribePassthrough as m, deserializeTypedArrays as mt, adaptBrokerToCluster as n, getWorkerNativeCapProvider as nt, getOrInitReadinessRegistry as o, createAddonService as ot, setNodeEventInterest as p, parseCapAction as pt, UdsLocalTransportClient as q, createAddonContext as r, getWorkerNativeCapSnapshot as rt, getOrInitReadinessRegistryForClient as s, validateProviderRegistrations as st, installManifestPythonDeps as t, createBrokerDeviceManagerApi as tt, getBrokerEventBus as u, capActionSuffix as ut, createHwAccelService as v, CapabilityUnavailableError as vt, buildLinkChain as w, getCapUsageRegistry as x, CapUsageRegistry as y, installManifestNativeDeps as yt, UDS_NO_ROUTE_PREFIX as z };
|