@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.
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_manifest_python_deps = require("./manifest-python-deps-PbdHKDpj.js");
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);
@@ -1,4 +1,4 @@
1
- import { $ as getWorkerNativeCapProvider, N as createUdsLoggerWithControl, P as LocalChildClient, _t as resolveAddonClass, et as getWorkerNativeCapSnapshot, gt as installManifestNativeDeps, i as createUdsAddonContext, it as validateProviderRegistrations, nt as setWorkerNativeCapsChangeListener, t as installManifestPythonDeps } from "./manifest-python-deps-X00KVYHa.mjs";
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-PbdHKDpj.js");
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 createUdsEventBus, B as CapRouteError, C as ipcParentLink, D as createHubCapForwardService, E as HUB_CAP_FWD_SERVICE, F as LocalChildRegistry, G as UdsLocalTransportServer, H as callWithServiceDiscovery, I as UDS_NO_ROUTE_PREFIX, J as FrameDecoder, K as SocketChannel, L as AGENT_CAP_FWD_ACTION, M as createUdsLogger, N as createUdsLoggerWithControl, O as createParentUnownedCallHandler, P as LocalChildClient, Q as createBrokerDeviceManagerApi, R as AGENT_CAP_FWD_SERVICE, S as ipcChildLink, T as HUB_CAP_FWD_ACTION, U as createLocalTransport, V as classifyCapRoute, W as UdsLocalTransportClient, X as buildNativeCapProxy, Y as encodeFrame, Z as buildUdsNativeCapProxy, _ as __resetCapUsageRegistryForTests, _t as resolveAddonClass, a as getWorkerDeviceRegistry, at as NATIVE_PROVIDER_SERVICE_INFIX, b as brokerTransportLink, c as setHubConnected, ct as capBareAction, d as registerEventBusService, dt as deserializeTypedArrays, f as AddonDepsManager, ft as serializeTypedArrays, g as CapUsageRegistry, gt as installManifestNativeDeps, h as createHwAccelService, ht as CapabilityUnavailableError, i as createUdsAddonContext, it as validateProviderRegistrations, j as udsChildLogToWorkerEntry, k as createUdsEventBridge, l as EVENT_TOPIC_PREFIX, lt as capServiceName, m as resolveHwAccel, mt as CapabilityHandle, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as capActionName, p as createKernelHwAccel, pt as DeviceRegistry, q as localEndpointPath, r as createAddonContext, rt as createAddonService, s as getOrInitReadinessRegistryForClient, st as capActionSuffix, t as installManifestPythonDeps, tt as mountNativeCapService, u as getBrokerEventBus, ut as parseCapAction, v as getCapUsageRegistry, w as localProviderLink, x as buildLinkChain, y as brokerCallForCap, z as CapRouteResolver } from "./manifest-python-deps-X00KVYHa.mjs";
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 };
@@ -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
- if (snap.hubInProcessProvides(capName)) {
3565
- const ref = snap.getInProcessProviderRef?.(capName) ?? null;
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 unsubscribe = parentBus.subscribe({}, (event) => {
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
- if (snap.hubInProcessProvides(capName)) {
3563
- const ref = snap.getInProcessProviderRef?.(capName) ?? null;
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 unsubscribe = parentBus.subscribe({}, (event) => {
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 { getWorkerNativeCapProvider as $, createUdsEventBus as A, CapRouteError as B, ipcParentLink as C, createHubCapForwardService as D, HUB_CAP_FWD_SERVICE as E, LocalChildRegistry as F, UdsLocalTransportServer as G, callWithServiceDiscovery as H, UDS_NO_ROUTE_PREFIX as I, FrameDecoder as J, SocketChannel as K, AGENT_CAP_FWD_ACTION as L, createUdsLogger as M, createUdsLoggerWithControl as N, createParentUnownedCallHandler as O, LocalChildClient as P, createBrokerDeviceManagerApi as Q, AGENT_CAP_FWD_SERVICE as R, ipcChildLink as S, HUB_CAP_FWD_ACTION as T, createLocalTransport as U, classifyCapRoute as V, UdsLocalTransportClient as W, buildNativeCapProxy as X, encodeFrame as Y, buildUdsNativeCapProxy as Z, __resetCapUsageRegistryForTests as _, resolveAddonClass as _t, getWorkerDeviceRegistry as a, NATIVE_PROVIDER_SERVICE_INFIX as at, brokerTransportLink as b, setHubConnected as c, capBareAction as ct, registerEventBusService as d, deserializeTypedArrays as dt, getWorkerNativeCapSnapshot as et, AddonDepsManager as f, serializeTypedArrays as ft, CapUsageRegistry as g, installManifestNativeDeps as gt, createHwAccelService as h, CapabilityUnavailableError as ht, createUdsAddonContext as i, validateProviderRegistrations as it, udsChildLogToWorkerEntry as j, createUdsEventBridge as k, EVENT_TOPIC_PREFIX as l, capServiceName as lt, resolveHwAccel as m, CapabilityHandle as mt, adaptBrokerToCluster as n, setWorkerNativeCapsChangeListener as nt, getOrInitReadinessRegistry as o, capActionName as ot, createKernelHwAccel as p, DeviceRegistry as pt, localEndpointPath as q, createAddonContext as r, createAddonService as rt, getOrInitReadinessRegistryForClient as s, capActionSuffix as st, installManifestPythonDeps as t, mountNativeCapService as tt, getBrokerEventBus as u, parseCapAction as ut, getCapUsageRegistry as v, localProviderLink as w, buildLinkChain as x, brokerCallForCap as y, CapRouteResolver as z };
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/system",
3
- "version": "1.1.20",
3
+ "version": "1.1.22",
4
4
  "description": "Core addon for CamStack — builtins, pipeline, process management, auth, logging, events",
5
5
  "keywords": [
6
6
  "camstack",