@voltro/plugin-broadcast 0.84.0 → 0.86.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/index.js CHANGED
@@ -1,441 +1,7 @@
1
- import { Effect as e, Metric as t, Schema as n } from "effect";
2
- import { definePlugin as r } from "@voltro/protocol";
3
- //#region src/namespace.ts
4
- var i = "voltro", a = (e) => e.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, ""), o = (e) => {
5
- let t = s(e), n = [e.option, e.env].find((e) => e !== void 0 && a(e).length > 0);
6
- return {
7
- resolved: t,
8
- configured: n,
9
- changed: n !== void 0 && n !== t
10
- };
11
- }, s = (e) => {
12
- for (let t of [
13
- e.option,
14
- e.env,
15
- e.appName
16
- ]) {
17
- if (t === void 0) continue;
18
- let e = a(t);
19
- if (e.length > 0) return e;
20
- }
21
- return i;
22
- }, c = (e, t) => `${e}:${t}`, l = (e, t) => `${c(e, "events")}:${a(t)}`, u = class extends n.TaggedError()("BroadcastError", {
23
- provider: n.String,
24
- message: n.String,
25
- transient: n.Boolean,
26
- cause: n.optional(n.Unknown)
27
- }) {}, d = c(i, "changes"), f = (e, t, n) => new u({
28
- provider: e,
29
- message: t,
30
- transient: !0,
31
- ...n === void 0 ? {} : { cause: n }
32
- }), p = (e, t, n) => new u({
33
- provider: e,
34
- message: t,
35
- transient: !1,
36
- ...n === void 0 ? {} : { cause: n }
37
- }), m = () => {
38
- let e = /* @__PURE__ */ new Set();
39
- return {
40
- subscribe: (t) => (e.add(t), () => {
41
- e.delete(t);
42
- }),
43
- emit: (t) => {
44
- for (let n of e) try {
45
- n(t);
46
- } catch {}
47
- }
48
- };
49
- }, h = /* @__PURE__ */ new Map(), g = (e) => {
50
- let t = h.get(e);
51
- return t || (t = { handlers: /* @__PURE__ */ new Map() }, h.set(e, t)), t;
52
- }, _ = (e) => {
53
- h.get(e)?.handlers.clear();
54
- }, v = (t = `default-${Math.random().toString(36).slice(2)}`) => {
55
- let n = g(t);
56
- return {
57
- name: "memory",
58
- publish: (t, r) => e.sync(() => {
59
- let e = n.handlers.get(t);
60
- if (e) for (let t of e) try {
61
- t(r);
62
- } catch {}
63
- }),
64
- subscribe: (t, r) => e.sync(() => {
65
- let e = n.handlers.get(t) ?? /* @__PURE__ */ new Set();
66
- return e.add(r), n.handlers.set(t, e), () => {
67
- e.delete(r);
68
- };
69
- }),
70
- close: () => e.void
71
- };
72
- }, y = (t) => {
73
- let n = null, r = m(), i = !0, a = () => n || (n = (async () => {
74
- let e;
75
- if (t.client) e = t.client;
76
- else {
77
- let n;
78
- try {
79
- n = (await import("ioredis")).default;
80
- } catch (e) {
81
- throw p("redis", "the 'redis' broadcast provider requires the 'ioredis' optional dependency. Install it to enable it.", e);
82
- }
83
- e = new n(t.url);
84
- }
85
- let n = e.duplicate();
86
- return e.on("error", () => {}), n.on("error", () => {}), n.on("reconnecting", () => {
87
- i && (i = !1, r.emit({
88
- kind: "disconnected",
89
- detail: "ioredis is re-dialling"
90
- }));
91
- }), n.on("ready", () => {
92
- i || (i = !0, r.emit({ kind: "reconnected" }));
93
- }), {
94
- pub: e,
95
- sub: n
96
- };
97
- })().catch((e) => {
98
- throw n = null, e;
99
- }), n);
100
- return {
101
- name: "redis",
102
- onTransportEvent: r.subscribe,
103
- publish: (t, n) => e.tryPromise({
104
- try: async () => {
105
- let { pub: e } = await a();
106
- await e.publish(t, n);
107
- },
108
- catch: (e) => f("redis", `publish failed: ${String(e?.message ?? e)}`, e)
109
- }),
110
- subscribe: (t, n) => e.tryPromise({
111
- try: async () => {
112
- let { sub: e } = await a(), r = (e, r) => {
113
- e === t && n(r);
114
- };
115
- return e.on("message", r), await e.subscribe(t), () => {
116
- e.off("message", r), e.unsubscribe(t).catch(() => void 0);
117
- };
118
- },
119
- catch: (e) => f("redis", `subscribe failed: ${String(e?.message ?? e)}`, e)
120
- }),
121
- close: () => e.promise(async () => {
122
- if (n) try {
123
- let { pub: e, sub: r } = await n;
124
- await r.quit(), t.client || await e.quit();
125
- } catch {}
126
- })
127
- };
128
- }, b = new TextEncoder(), x = new TextDecoder(), S = (t) => {
129
- let n = null, r = m(), i = !0, a = (e) => {
130
- (async () => {
131
- try {
132
- for await (let t of e.status()) t.type === "disconnect" && i ? (i = !1, r.emit({
133
- kind: "disconnected",
134
- detail: "nats disconnected"
135
- })) : t.type === "reconnect" && !i && (i = !0, r.emit({ kind: "reconnected" }));
136
- } catch {}
137
- })();
138
- }, o = () => import("@nats-io/transport-node").catch((e) => {
139
- throw p("nats", "the 'nats' broadcast provider requires the '@nats-io/transport-node' optional dependency. Install it to enable it.", e);
140
- }), s = () => n || (n = (async () => {
141
- if (t.connection) return t.connection;
142
- let e = await (await o()).connect({
143
- servers: t.url,
144
- maxReconnectAttempts: -1,
145
- waitOnFirstConnect: !0,
146
- reconnectTimeWait: 2e3
147
- });
148
- return a(e), e;
149
- })().catch((e) => {
150
- throw n = null, e;
151
- }), n);
152
- return {
153
- name: "nats",
154
- onTransportEvent: r.subscribe,
155
- publish: (t, n) => e.tryPromise({
156
- try: async () => {
157
- (await s()).publish(t, b.encode(n));
158
- },
159
- catch: (e) => f("nats", `publish failed: ${String(e?.message ?? e)}`, e)
160
- }),
161
- subscribe: (t, n) => e.tryPromise({
162
- try: async () => {
163
- let e = (await s()).subscribe(t), a = !1;
164
- return (async () => {
165
- for await (let t of e) n(x.decode(t.data));
166
- !a && i && (i = !1, r.emit({
167
- kind: "disconnected",
168
- detail: `the nats subscription to '${t}' ended while the connection was up`
169
- }));
170
- })(), () => {
171
- a = !0, e.unsubscribe();
172
- };
173
- },
174
- catch: (e) => f("nats", `subscribe failed: ${String(e?.message ?? e)}`, e)
175
- }),
176
- close: () => e.promise(async () => {
177
- if (n) try {
178
- let e = await n;
179
- t.connection || await e.drain();
180
- } catch {}
181
- })
182
- };
183
- }, C = (e, t) => t[`${e.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_REDIS_URL`], w = (e, t) => {
184
- if (e.provider && typeof e.provider == "object") {
185
- let t = e.provider;
186
- return {
187
- provider: t,
188
- url: e.url ?? null,
189
- crossReplica: t.name !== "memory"
190
- };
191
- }
192
- let n = e.url ?? t.BROADCAST_URL ?? null, r = C(e.connection ?? "broadcast", t), i = (e.provider ?? t.BROADCAST_PROVIDER)?.toLowerCase() ?? (n?.startsWith("nats://") ? "nats" : n?.startsWith("redis://") || n?.startsWith("rediss://") || r || t.REDIS_URL ? "redis" : void 0), a = (e.provider ?? t.BROADCAST_PROVIDER)?.toLowerCase();
193
- if (a !== void 0 && a !== "redis" && a !== "nats" && a !== "memory") throw Error(`broadcastPlugin: unknown provider "${a}".\n Valid: 'redis', 'nats', 'memory'.
194
- A name we do not recognise used to fall through to the in-process bus, which boots fine on one replica and drops cross-replica traffic on the second.`);
195
- if (n !== null && n !== "" && !/^(redis|rediss|nats):\/\//.test(n)) throw Error(`broadcastPlugin: url "${n}" names no provider we support.\n Expected a redis://, rediss:// or nats:// url.`);
196
- switch (i) {
197
- case "redis": {
198
- let e = n ?? r ?? t.REDIS_URL;
199
- if (!e) {
200
- if (a === "redis") throw Error("broadcastPlugin: provider \"redis\" was requested but no url resolves.\n Set BROADCAST_URL, BROADCAST_REDIS_URL or REDIS_URL, or pass `url`.\n Falling back to the in-process bus here would answer a stated requirement with a silent downgrade.");
201
- return {
202
- provider: v(),
203
- url: null,
204
- crossReplica: !1
205
- };
206
- }
207
- return {
208
- provider: y({ url: e }),
209
- url: e,
210
- crossReplica: !0
211
- };
212
- }
213
- case "nats": {
214
- let e = n ?? t.NATS_URL;
215
- return e ? {
216
- provider: S({ url: e }),
217
- url: e,
218
- crossReplica: !0
219
- } : {
220
- provider: v(),
221
- url: null,
222
- crossReplica: !1
223
- };
224
- }
225
- default: return {
226
- provider: v(),
227
- url: null,
228
- crossReplica: !1
229
- };
230
- }
231
- }, T = ["network:outbound:*"], E = [
232
- {
233
- name: "BROADCAST_URL",
234
- required: !1,
235
- secret: !0,
236
- description: "Broker URL for the cross-replica bus, e.g. redis://host:6379 or nats://host:4222 (the scheme selects the provider). Absent → in-process memory fallback (single-process).",
237
- example: "redis://localhost:6379"
238
- },
239
- {
240
- name: "BROADCAST_REDIS_URL",
241
- required: !1,
242
- secret: !0,
243
- description: "Named-connection redis url for the bus (the shared <NAME>_REDIS_URL convention, like CACHE_REDIS_URL / KV_REDIS_URL). Used when the redis provider is selected and no broker-agnostic BROADCAST_URL is set; falls back to REDIS_URL. Set it to put broadcast on its own server.",
244
- example: "redis://localhost:6379"
245
- },
246
- {
247
- name: "BROADCAST_PROVIDER",
248
- required: !1,
249
- secret: !1,
250
- description: "Explicit provider: 'redis' | 'nats' | 'memory'. Overrides the scheme inferred from BROADCAST_URL. Default: inferred from the URL, else memory.",
251
- example: "redis"
252
- },
253
- {
254
- name: "REDIS_URL",
255
- required: !1,
256
- secret: !0,
257
- description: "Fallback broker URL used when the resolved provider is redis and no BROADCAST_URL is set (also infers the redis provider when no provider/URL is given).",
258
- example: "redis://localhost:6379"
259
- },
260
- {
261
- name: "NATS_URL",
262
- required: !1,
263
- secret: !0,
264
- description: "Fallback broker URL used when the resolved provider is nats and no BROADCAST_URL is set (comma-separated for a cluster).",
265
- example: "nats://localhost:4222"
266
- }
267
- ], D = (t = {}) => {
268
- let n = w({
269
- ...t.provider === void 0 ? {} : { provider: t.provider },
270
- ...t.url === void 0 ? {} : { url: t.url },
271
- ...t.connection === void 0 ? {} : { connection: t.connection }
272
- }, process.env);
273
- return {
274
- ...r({
275
- name: t.name ? `@voltro/plugin-broadcast#${t.name}` : "@voltro/plugin-broadcast",
276
- description: "Cross-replica reactivity — fans out app-mutation ChangeEvents to every replica over a pub/sub bus (Redis / NATS). ADDITIVE to inline emit: local reactivity survives a broker outage. Closes the single-instance gap for every non-postgres dialect.",
277
- permissions: T,
278
- declaredEnv: E,
279
- onActivate: (t) => e.sync(() => {
280
- n.crossReplica ? t.logger.info("broadcast active", {
281
- provider: n.provider.name,
282
- url: n.url
283
- }) : t.logger.warn("broadcast: resolved to the in-process memory provider — NO cross-replica fan-out. Set BROADCAST_URL (redis:// or nats://) for a real bus.");
284
- }),
285
- onDeactivate: () => e.void
286
- }),
287
- broadcast: {
288
- provider: n.provider,
289
- url: n.url,
290
- crossReplica: n.crossReplica,
291
- namespace: t.namespace
292
- }
293
- };
294
- }, O = (e) => {
295
- for (let t of e) if (k(t)) return t;
296
- return null;
297
- }, k = (e) => typeof e.broadcast == "object" && e.broadcast !== null && typeof e.broadcast.provider == "object", A = ({ origin: e, ...t }) => t, j = (e) => Math.min(500 * 2 ** Math.min(Math.max(e, 1) - 1, 5), 1e4), M = {
298
- info: () => {},
299
- warn: () => {},
300
- debug: () => {}
301
- }, N = async (n) => {
302
- let { store: r, provider: i, replicaId: a } = n, o = n.logger ?? M, s = n.channel ?? d, c = !1, l, u = !1, f = 0, p = /* @__PURE__ */ new Map(), m = `${Date.now().toString(36)}.${Math.random().toString(36).slice(2, 10)}`, h = t.gauge("voltro_broadcast_connected").pipe(t.tagged("provider", i.name)), g = t.counter("voltro_broadcast_gaps_total").pipe(t.tagged("provider", i.name)), _ = t.counter("voltro_broadcast_missed_total").pipe(t.tagged("provider", i.name)), v = t.counter("voltro_broadcast_reconnects_total").pipe(t.tagged("provider", i.name)), y = {
303
- connected: !1,
304
- lastReceivedAt: null,
305
- lastPublishedAt: null,
306
- reconnects: 0,
307
- gaps: 0,
308
- missed: 0
309
- }, b = (n) => {
310
- y.connected = n, e.runSync(t.set(h, +!!n));
311
- };
312
- b(!1);
313
- let x = (r) => {
314
- y.gaps += 1, e.runSync(t.increment(g)), r.missed !== void 0 && r.missed > 0 && (y.missed += r.missed, e.runSync(t.incrementBy(_, r.missed))), o.warn(`broadcast: ${r.reason} — refreshing live queries, which is safe because a query is idempotent`, {
315
- ...r.origin === void 0 ? {} : { origin: r.origin },
316
- ...r.missed === void 0 ? {} : { missed: r.missed }
317
- });
318
- try {
319
- n.onGap?.(r);
320
- } catch (e) {
321
- o.warn("broadcast: gap recovery threw", { err: e });
322
- }
323
- }, S = (e) => {
324
- y.lastReceivedAt = Date.now();
325
- let t;
326
- try {
327
- t = JSON.parse(e);
328
- } catch {
329
- o.warn("broadcast: bad payload (not JSON)", { channel: s });
330
- return;
331
- }
332
- if (t.origin === a || !t.event || typeof t.event != "object") return;
333
- if (typeof t.n == "number" && Number.isFinite(t.n)) {
334
- let e = t.epoch ?? "", n = p.get(t.origin);
335
- n === void 0 ? p.set(t.origin, {
336
- epoch: e,
337
- n: t.n
338
- }) : n.epoch === e ? (t.n > n.n + 1 && x({
339
- reason: `missed ${t.n - n.n - 1} change(s) from ${t.origin}`,
340
- origin: t.origin,
341
- missed: t.n - n.n - 1
342
- }), t.n > n.n && p.set(t.origin, {
343
- epoch: e,
344
- n: t.n
345
- })) : p.set(t.origin, {
346
- epoch: e,
347
- n: t.n
348
- });
349
- }
350
- let n = A(t.event);
351
- c = !0, l = n;
352
- try {
353
- r.injectExternalChange(n);
354
- } catch (e) {
355
- o.warn("broadcast: inject failed", { err: e });
356
- } finally {
357
- c = !1, l = void 0;
358
- }
359
- }, C, w = !1, T = 0, E = async () => {
360
- try {
361
- return C = await e.runPromise(i.subscribe(s, S)), b(!0), !0;
362
- } catch (e) {
363
- return T += 1, T === 1 && o.warn("broadcast: could not subscribe — this replica will not receive other replicas' changes yet. Retrying in the background; local reactivity is unaffected.", {
364
- provider: i.name,
365
- channel: s,
366
- err: e
367
- }), !1;
368
- }
369
- };
370
- await E() || (async () => {
371
- let e = Date.now();
372
- for (; !w && C === void 0;) {
373
- if (await new Promise((e) => setTimeout(e, j(T))), w) return;
374
- if (await E()) {
375
- o.info("broadcast: subscribed", {
376
- provider: i.name,
377
- channel: s,
378
- attempts: T
379
- }), x({ reason: `this replica was not subscribed to '${s}' for ${Math.round((Date.now() - e) / 1e3)}s` });
380
- return;
381
- }
382
- }
383
- })();
384
- let D = i.onTransportEvent?.((n) => {
385
- if (n.kind === "disconnected") {
386
- b(!1), o.warn("broadcast: transport disconnected — cross-replica fan-out is down until it re-dials", {
387
- provider: i.name,
388
- ...n.detail === void 0 ? {} : { detail: n.detail }
389
- });
390
- return;
391
- }
392
- b(!0), y.reconnects += 1, e.runSync(t.increment(v)), o.warn("broadcast transport reconnected", {
393
- provider: i.name,
394
- channel: s,
395
- replicaId: a,
396
- reconnects: y.reconnects,
397
- lastReceivedAt: y.lastReceivedAt
398
- }), x({ reason: `the ${i.name} transport reconnected after an outage` });
399
- }), O = r.onChange((t) => {
400
- if (t.origin === "injected" || c && t === l) return;
401
- f += 1;
402
- let n = {
403
- origin: a,
404
- event: t,
405
- n: f,
406
- epoch: m
407
- };
408
- y.lastPublishedAt = Date.now(), e.runPromise(i.publish(s, JSON.stringify(n)).pipe(e.tap(() => e.sync(() => {
409
- u = !1;
410
- })), e.catchAll((t) => e.sync(() => {
411
- u || (u = !0, o.warn("broadcast: publish failed — cross-replica fan-out degraded, local reactivity unaffected", {
412
- provider: i.name,
413
- transient: t.transient,
414
- message: t.message
415
- }));
416
- }))));
417
- });
418
- return o.info("broadcast bus attached", {
419
- provider: i.name,
420
- channel: s,
421
- replicaId: a
422
- }), {
423
- health: () => ({
424
- provider: i.name,
425
- channel: s,
426
- replicaId: a,
427
- ...y
428
- }),
429
- close: async () => {
430
- if (!w) {
431
- w = !0, O(), D?.();
432
- try {
433
- C?.();
434
- } catch {}
435
- await e.runPromise(i.close());
436
- }
437
- }
438
- };
439
- };
440
- //#endregion
441
- export { d as BROADCAST_CHANNEL, u as BroadcastError, i as DEFAULT_BROADCAST_NAMESPACE, N as attachBroadcastBus, D as broadcastPlugin, c as channelFor, o as describeNamespaceResolution, l as eventChannelFor, O as getBroadcastPlugin, v as memoryProvider, S as natsProvider, y as redisProvider, _ as resetMemoryBus, s as resolveBroadcastNamespace, w as resolveBroadcastProvider, j as subscribeRetryDelayMs };
1
+ import { DEFAULT_BROADCAST_NAMESPACE as e, channelFor as t, describeNamespaceResolution as n, eventChannelFor as r, resolveBroadcastNamespace as i } from "./namespace.js";
2
+ import { BROADCAST_CHANNEL as a, BroadcastError as o } from "./types.js";
3
+ import { memoryProvider as s, natsProvider as c, redisProvider as l, resetMemoryBus as u } from "./providers.js";
4
+ import { resolveBroadcastProvider as d } from "./resolve.js";
5
+ import { broadcastPlugin as f, getBroadcastPlugin as p } from "./plugin.js";
6
+ import { attachBroadcastBus as m, subscribeRetryDelayMs as h } from "./bus.js";
7
+ export { a as BROADCAST_CHANNEL, o as BroadcastError, e as DEFAULT_BROADCAST_NAMESPACE, m as attachBroadcastBus, f as broadcastPlugin, t as channelFor, n as describeNamespaceResolution, r as eventChannelFor, p as getBroadcastPlugin, s as memoryProvider, c as natsProvider, l as redisProvider, u as resetMemoryBus, i as resolveBroadcastNamespace, d as resolveBroadcastProvider, h as subscribeRetryDelayMs };
@@ -0,0 +1,22 @@
1
+ //#region src/namespace.ts
2
+ var e = "voltro", t = (e) => e.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, ""), n = (e) => {
3
+ let n = r(e), i = [e.option, e.env].find((e) => e !== void 0 && t(e).length > 0);
4
+ return {
5
+ resolved: n,
6
+ configured: i,
7
+ changed: i !== void 0 && i !== n
8
+ };
9
+ }, r = (n) => {
10
+ for (let e of [
11
+ n.option,
12
+ n.env,
13
+ n.appName
14
+ ]) {
15
+ if (e === void 0) continue;
16
+ let n = t(e);
17
+ if (n.length > 0) return n;
18
+ }
19
+ return e;
20
+ }, i = (e, t) => `${e}:${t}`, a = (e, n) => `${i(e, "events")}:${t(n)}`;
21
+ //#endregion
22
+ export { e as DEFAULT_BROADCAST_NAMESPACE, i as channelFor, n as describeNamespaceResolution, a as eventChannelFor, r as resolveBroadcastNamespace };
package/dist/plugin.js ADDED
@@ -0,0 +1,73 @@
1
+ import { resolveBroadcastProvider as e } from "./resolve.js";
2
+ import { Effect as t } from "effect";
3
+ import { definePlugin as n } from "@voltro/protocol";
4
+ //#region src/plugin.ts
5
+ var r = ["network:outbound:*"], i = [
6
+ {
7
+ name: "BROADCAST_URL",
8
+ required: !1,
9
+ secret: !0,
10
+ description: "Broker URL for the cross-replica bus, e.g. redis://host:6379 or nats://host:4222 (the scheme selects the provider). Absent → in-process memory fallback (single-process).",
11
+ example: "redis://localhost:6379"
12
+ },
13
+ {
14
+ name: "BROADCAST_REDIS_URL",
15
+ required: !1,
16
+ secret: !0,
17
+ description: "Named-connection redis url for the bus (the shared <NAME>_REDIS_URL convention, like CACHE_REDIS_URL / KV_REDIS_URL). Used when the redis provider is selected and no broker-agnostic BROADCAST_URL is set; falls back to REDIS_URL. Set it to put broadcast on its own server.",
18
+ example: "redis://localhost:6379"
19
+ },
20
+ {
21
+ name: "BROADCAST_PROVIDER",
22
+ required: !1,
23
+ secret: !1,
24
+ description: "Explicit provider: 'redis' | 'nats' | 'memory'. Overrides the scheme inferred from BROADCAST_URL. Default: inferred from the URL, else memory.",
25
+ example: "redis"
26
+ },
27
+ {
28
+ name: "REDIS_URL",
29
+ required: !1,
30
+ secret: !0,
31
+ description: "Fallback broker URL used when the resolved provider is redis and no BROADCAST_URL is set (also infers the redis provider when no provider/URL is given).",
32
+ example: "redis://localhost:6379"
33
+ },
34
+ {
35
+ name: "NATS_URL",
36
+ required: !1,
37
+ secret: !0,
38
+ description: "Fallback broker URL used when the resolved provider is nats and no BROADCAST_URL is set (comma-separated for a cluster).",
39
+ example: "nats://localhost:4222"
40
+ }
41
+ ], a = (a = {}) => {
42
+ let o = e({
43
+ ...a.provider === void 0 ? {} : { provider: a.provider },
44
+ ...a.url === void 0 ? {} : { url: a.url },
45
+ ...a.connection === void 0 ? {} : { connection: a.connection }
46
+ }, process.env);
47
+ return {
48
+ ...n({
49
+ name: a.name ? `@voltro/plugin-broadcast#${a.name}` : "@voltro/plugin-broadcast",
50
+ description: "Cross-replica reactivity — fans out app-mutation ChangeEvents to every replica over a pub/sub bus (Redis / NATS). ADDITIVE to inline emit: local reactivity survives a broker outage. Closes the single-instance gap for every non-postgres dialect.",
51
+ permissions: r,
52
+ declaredEnv: i,
53
+ onActivate: (e) => t.sync(() => {
54
+ o.crossReplica ? e.logger.info("broadcast active", {
55
+ provider: o.provider.name,
56
+ url: o.url
57
+ }) : e.logger.warn("broadcast: resolved to the in-process memory provider — NO cross-replica fan-out. Set BROADCAST_URL (redis:// or nats://) for a real bus.");
58
+ }),
59
+ onDeactivate: () => t.void
60
+ }),
61
+ broadcast: {
62
+ provider: o.provider,
63
+ url: o.url,
64
+ crossReplica: o.crossReplica,
65
+ namespace: a.namespace
66
+ }
67
+ };
68
+ }, o = (e) => {
69
+ for (let t of e) if (s(t)) return t;
70
+ return null;
71
+ }, s = (e) => typeof e.broadcast == "object" && e.broadcast !== null && typeof e.broadcast.provider == "object";
72
+ //#endregion
73
+ export { a as broadcastPlugin, o as getBroadcastPlugin };