@objectstack/metadata 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.cjs CHANGED
@@ -2394,6 +2394,23 @@ var _MetadataManager = class _MetadataManager {
2394
2394
  this.notifyWatchers(type, legacyEvent);
2395
2395
  }
2396
2396
  notifyWatchers(type, event) {
2397
+ this.notifyWatchersLocal(type, event);
2398
+ if (this.clusterPubSub) {
2399
+ const payload = {
2400
+ originNode: this.clusterNodeId,
2401
+ type,
2402
+ event
2403
+ };
2404
+ const key = `${type}:${event.name ?? ""}`;
2405
+ void this.clusterPubSub.publish(_MetadataManager.CLUSTER_CHANNEL, payload, { partitionKey: key }).catch((err) => {
2406
+ this.logger.error("Cluster metadata publish failed", void 0, {
2407
+ type,
2408
+ error: err instanceof Error ? err.message : String(err)
2409
+ });
2410
+ });
2411
+ }
2412
+ }
2413
+ notifyWatchersLocal(type, event) {
2397
2414
  const callbacks = this.watchCallbacks.get(type);
2398
2415
  if (!callbacks) return;
2399
2416
  for (const callback of callbacks) {
@@ -2407,6 +2424,63 @@ var _MetadataManager = class _MetadataManager {
2407
2424
  }
2408
2425
  }
2409
2426
  }
2427
+ /**
2428
+ * Attach a cluster pub/sub transport so metadata-change events fan
2429
+ * out to peer nodes and remote events replay into local watchers.
2430
+ *
2431
+ * The bridge plugin in @objectstack/service-cluster calls this once
2432
+ * per kernel boot after both cluster and metadata services are
2433
+ * registered. Passing the same MetadataManager twice no-ops; passing
2434
+ * a different transport replaces the prior subscription.
2435
+ *
2436
+ * Pass `nodeId` matching the local cluster's nodeId so loopback
2437
+ * suppression works.
2438
+ *
2439
+ * @returns disposer that unsubscribes from cluster events.
2440
+ */
2441
+ attachClusterPubSub(pubsub, nodeId) {
2442
+ if (this.clusterPubSub === pubsub && this.clusterNodeId === nodeId) {
2443
+ return () => this.detachClusterPubSub();
2444
+ }
2445
+ this.detachClusterPubSub();
2446
+ this.clusterPubSub = pubsub;
2447
+ this.clusterNodeId = nodeId;
2448
+ this.clusterUnsubscribe = pubsub.subscribe(
2449
+ _MetadataManager.CLUSTER_CHANNEL,
2450
+ (msg) => {
2451
+ const p = msg.payload;
2452
+ if (p?.originNode && p.originNode === this.clusterNodeId) return;
2453
+ if (!p?.type || !p.event) return;
2454
+ setImmediate(() => {
2455
+ try {
2456
+ this.notifyWatchersLocal(p.type, p.event);
2457
+ } catch (err) {
2458
+ this.logger.error("Cluster remote replay failed", void 0, {
2459
+ type: p.type,
2460
+ error: err instanceof Error ? err.message : String(err)
2461
+ });
2462
+ }
2463
+ });
2464
+ }
2465
+ );
2466
+ this.logger.info("MetadataManager attached to cluster pubsub", {
2467
+ nodeId,
2468
+ channel: _MetadataManager.CLUSTER_CHANNEL
2469
+ });
2470
+ return () => this.detachClusterPubSub();
2471
+ }
2472
+ /** Tear down cluster wiring. Safe to call multiple times. */
2473
+ detachClusterPubSub() {
2474
+ if (this.clusterUnsubscribe) {
2475
+ try {
2476
+ this.clusterUnsubscribe();
2477
+ } catch {
2478
+ }
2479
+ this.clusterUnsubscribe = void 0;
2480
+ }
2481
+ this.clusterPubSub = void 0;
2482
+ this.clusterNodeId = void 0;
2483
+ }
2410
2484
  // ==========================================
2411
2485
  // Version History & Rollback
2412
2486
  // ==========================================
@@ -2507,6 +2581,7 @@ var _MetadataManager = class _MetadataManager {
2507
2581
  }
2508
2582
  };
2509
2583
  _MetadataManager.LIST_CACHE_TTL_MS = 3e4;
2584
+ _MetadataManager.CLUSTER_CHANNEL = "metadata.changed";
2510
2585
  var MetadataManager = _MetadataManager;
2511
2586
 
2512
2587
  // src/plugin.ts