@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.js CHANGED
@@ -2348,6 +2348,23 @@ var _MetadataManager = class _MetadataManager {
2348
2348
  this.notifyWatchers(type, legacyEvent);
2349
2349
  }
2350
2350
  notifyWatchers(type, event) {
2351
+ this.notifyWatchersLocal(type, event);
2352
+ if (this.clusterPubSub) {
2353
+ const payload = {
2354
+ originNode: this.clusterNodeId,
2355
+ type,
2356
+ event
2357
+ };
2358
+ const key = `${type}:${event.name ?? ""}`;
2359
+ void this.clusterPubSub.publish(_MetadataManager.CLUSTER_CHANNEL, payload, { partitionKey: key }).catch((err) => {
2360
+ this.logger.error("Cluster metadata publish failed", void 0, {
2361
+ type,
2362
+ error: err instanceof Error ? err.message : String(err)
2363
+ });
2364
+ });
2365
+ }
2366
+ }
2367
+ notifyWatchersLocal(type, event) {
2351
2368
  const callbacks = this.watchCallbacks.get(type);
2352
2369
  if (!callbacks) return;
2353
2370
  for (const callback of callbacks) {
@@ -2361,6 +2378,63 @@ var _MetadataManager = class _MetadataManager {
2361
2378
  }
2362
2379
  }
2363
2380
  }
2381
+ /**
2382
+ * Attach a cluster pub/sub transport so metadata-change events fan
2383
+ * out to peer nodes and remote events replay into local watchers.
2384
+ *
2385
+ * The bridge plugin in @objectstack/service-cluster calls this once
2386
+ * per kernel boot after both cluster and metadata services are
2387
+ * registered. Passing the same MetadataManager twice no-ops; passing
2388
+ * a different transport replaces the prior subscription.
2389
+ *
2390
+ * Pass `nodeId` matching the local cluster's nodeId so loopback
2391
+ * suppression works.
2392
+ *
2393
+ * @returns disposer that unsubscribes from cluster events.
2394
+ */
2395
+ attachClusterPubSub(pubsub, nodeId) {
2396
+ if (this.clusterPubSub === pubsub && this.clusterNodeId === nodeId) {
2397
+ return () => this.detachClusterPubSub();
2398
+ }
2399
+ this.detachClusterPubSub();
2400
+ this.clusterPubSub = pubsub;
2401
+ this.clusterNodeId = nodeId;
2402
+ this.clusterUnsubscribe = pubsub.subscribe(
2403
+ _MetadataManager.CLUSTER_CHANNEL,
2404
+ (msg) => {
2405
+ const p = msg.payload;
2406
+ if (p?.originNode && p.originNode === this.clusterNodeId) return;
2407
+ if (!p?.type || !p.event) return;
2408
+ setImmediate(() => {
2409
+ try {
2410
+ this.notifyWatchersLocal(p.type, p.event);
2411
+ } catch (err) {
2412
+ this.logger.error("Cluster remote replay failed", void 0, {
2413
+ type: p.type,
2414
+ error: err instanceof Error ? err.message : String(err)
2415
+ });
2416
+ }
2417
+ });
2418
+ }
2419
+ );
2420
+ this.logger.info("MetadataManager attached to cluster pubsub", {
2421
+ nodeId,
2422
+ channel: _MetadataManager.CLUSTER_CHANNEL
2423
+ });
2424
+ return () => this.detachClusterPubSub();
2425
+ }
2426
+ /** Tear down cluster wiring. Safe to call multiple times. */
2427
+ detachClusterPubSub() {
2428
+ if (this.clusterUnsubscribe) {
2429
+ try {
2430
+ this.clusterUnsubscribe();
2431
+ } catch {
2432
+ }
2433
+ this.clusterUnsubscribe = void 0;
2434
+ }
2435
+ this.clusterPubSub = void 0;
2436
+ this.clusterNodeId = void 0;
2437
+ }
2364
2438
  // ==========================================
2365
2439
  // Version History & Rollback
2366
2440
  // ==========================================
@@ -2461,6 +2535,7 @@ var _MetadataManager = class _MetadataManager {
2461
2535
  }
2462
2536
  };
2463
2537
  _MetadataManager.LIST_CACHE_TTL_MS = 3e4;
2538
+ _MetadataManager.CLUSTER_CHANNEL = "metadata.changed";
2464
2539
  var MetadataManager = _MetadataManager;
2465
2540
 
2466
2541
  // src/plugin.ts