@holo-js/broadcast 0.2.6 → 0.3.1

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/auth.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BroadcastChannelAuthRequest, BroadcastChannelAuthRuntimeBindings, BroadcastChannelAuthResult, GeneratedChannelAuthRegistryEntry, ChannelDefinition, BroadcastAuthEndpointPayload, BroadcastAuthEndpointOptions, BroadcastJsonObject, BroadcastWhisperValidationResult } from './contracts.js';
2
- import '@holo-js/config';
2
+ import './config.js';
3
3
  import '@holo-js/validation';
4
4
 
5
5
  type LoadedChannelDefinitions = Readonly<Record<string, ChannelDefinition>>;
@@ -7,6 +7,7 @@ type MatchedChannelDefinition = {
7
7
  readonly definition: ChannelDefinition;
8
8
  readonly params: Readonly<Record<string, string>>;
9
9
  };
10
+ declare function normalizeRegistryEntry(entry: GeneratedChannelAuthRegistryEntry): GeneratedChannelAuthRegistryEntry;
10
11
  declare function importChannelDefinition(entry: GeneratedChannelAuthRegistryEntry, bindings: BroadcastChannelAuthRuntimeBindings): Promise<ChannelDefinition>;
11
12
  declare function loadChannelDefinitions(bindings: BroadcastChannelAuthRuntimeBindings): Promise<LoadedChannelDefinitions>;
12
13
  declare function matchPattern(pattern: string, channel: string): Readonly<Record<string, string>> | null;
@@ -26,6 +27,7 @@ declare const broadcastAuthInternals: {
26
27
  importChannelDefinition: typeof importChannelDefinition;
27
28
  loadChannelDefinitions: typeof loadChannelDefinitions;
28
29
  matchPattern: typeof matchPattern;
30
+ normalizeRegistryEntry: typeof normalizeRegistryEntry;
29
31
  parseBroadcastAuthEndpointPayload: typeof parseBroadcastAuthEndpointPayload;
30
32
  resolveAuthDefinitions: typeof resolveAuthDefinitions;
31
33
  resolveChannelMatch: typeof resolveChannelMatch;
package/dist/auth.mjs CHANGED
@@ -6,8 +6,9 @@ import {
6
6
  resolveBroadcastChannelGuard,
7
7
  resolveBroadcastWhisperSchema,
8
8
  validateBroadcastWhisperPayload
9
- } from "./chunk-U5JDBKXC.mjs";
10
- import "./chunk-TTKGDABI.mjs";
9
+ } from "./chunk-JVDFH2TW.mjs";
10
+ import "./chunk-6DCCSFFM.mjs";
11
+ import "./chunk-XLUGF257.mjs";
11
12
  import "./chunk-HE6HN7ID.mjs";
12
13
  export {
13
14
  authorizeBroadcastChannel,
@@ -1,3 +1,6 @@
1
+ import {
2
+ holoBroadcastDefaults
3
+ } from "./chunk-XLUGF257.mjs";
1
4
  import {
2
5
  formatChannelPattern,
3
6
  isBroadcastDefinition,
@@ -8,7 +11,6 @@ import {
8
11
 
9
12
  // src/runtime.ts
10
13
  import { randomUUID } from "crypto";
11
- import { holoBroadcastDefaults } from "@holo-js/config";
12
14
 
13
15
  // src/registry.ts
14
16
  var registeredDrivers = /* @__PURE__ */ new Map();
@@ -36,6 +38,9 @@ function registerBroadcastDriver(name, driver, options = {}) {
36
38
  function getRegisteredBroadcastDriver(name) {
37
39
  return registeredDrivers.get(normalizeDriverName(name));
38
40
  }
41
+ function unregisterBroadcastDriver(name) {
42
+ return registeredDrivers.delete(normalizeDriverName(name));
43
+ }
39
44
  function listRegisteredBroadcastDrivers() {
40
45
  return Object.freeze(
41
46
  [...registeredDrivers.entries()].map(([name, driver]) => Object.freeze({ name, driver }))
@@ -48,6 +53,63 @@ var broadcastRegistryInternals = {
48
53
  normalizeDriverName
49
54
  };
50
55
 
56
+ // src/plugins.ts
57
+ import {
58
+ loadHoloPluginContributionModules,
59
+ loadHoloPluginDefinitions
60
+ } from "@holo-js/kernel";
61
+ var loadedProjectRoots = /* @__PURE__ */ new Set();
62
+ var registeredPluginDrivers = /* @__PURE__ */ new Map();
63
+ function isRecord(value) {
64
+ return !!value && typeof value === "object" && !Array.isArray(value);
65
+ }
66
+ function resolveBroadcastDriver(moduleValue, packageName, driverName) {
67
+ const candidate = isRecord(moduleValue) && typeof moduleValue.default !== "undefined" ? moduleValue.default : isRecord(moduleValue) && typeof moduleValue.driver !== "undefined" ? moduleValue.driver : moduleValue;
68
+ if (!isRecord(candidate) || typeof candidate.send !== "function") {
69
+ throw new Error(`[@holo-js/broadcast] Plugin ${packageName} broadcast driver "${driverName}" must export send().`);
70
+ }
71
+ return {
72
+ send: candidate.send
73
+ };
74
+ }
75
+ async function loadBroadcastPluginDrivers(projectRoot = process.cwd(), pluginNames = []) {
76
+ const root = projectRoot;
77
+ const loadKey = `${root}\0${[...pluginNames].sort().join("\0")}`;
78
+ if (loadedProjectRoots.has(loadKey)) {
79
+ return;
80
+ }
81
+ const plugins = await loadHoloPluginDefinitions(root, pluginNames);
82
+ const contributions = await loadHoloPluginContributionModules(root, plugins, "broadcast", "drivers");
83
+ for (const contribution of contributions) {
84
+ const previous = getRegisteredBroadcastDriver(contribution.name);
85
+ const driver = resolveBroadcastDriver(contribution.module, contribution.plugin.packageName, contribution.name);
86
+ const registered = registerBroadcastDriver(
87
+ contribution.name,
88
+ driver,
89
+ { replace: true }
90
+ );
91
+ const existingPluginDriver = registeredPluginDrivers.get(registered.name);
92
+ registeredPluginDrivers.set(registered.name, {
93
+ driver,
94
+ previous: existingPluginDriver ? existingPluginDriver.previous : previous
95
+ });
96
+ }
97
+ loadedProjectRoots.add(loadKey);
98
+ }
99
+ function resetBroadcastPluginDrivers() {
100
+ for (const [driverName, registered] of registeredPluginDrivers) {
101
+ if (getRegisteredBroadcastDriver(driverName) !== registered.driver) {
102
+ continue;
103
+ }
104
+ unregisterBroadcastDriver(driverName);
105
+ if (registered.previous) {
106
+ registerBroadcastDriver(driverName, registered.previous, { replace: true });
107
+ }
108
+ }
109
+ loadedProjectRoots.clear();
110
+ registeredPluginDrivers.clear();
111
+ }
112
+
51
113
  // src/runtime.ts
52
114
  var HOLO_BROADCAST_DELIVER_JOB = "holo.broadcast.deliver";
53
115
  function getRuntimeState() {
@@ -272,7 +334,7 @@ function resolveDriver(connectionName) {
272
334
  });
273
335
  }
274
336
  async function runQueuedBroadcastDelivery(payload) {
275
- const driver = resolveDriver(payload.context.connection);
337
+ const driver = await resolveDriverWithPluginFallback(payload.context.connection);
276
338
  const context = createExecutionContext(payload.messageId, driver, true);
277
339
  return await deliverResolvedRawBroadcast(payload.raw, driver, context);
278
340
  }
@@ -358,7 +420,7 @@ async function deliverResolvedRawBroadcast(input, driver, context) {
358
420
  return normalizeDriverResult(result, context, frozenInput.channels);
359
421
  }
360
422
  async function executeResolvedRawBroadcast(input, definition, options) {
361
- const driver = resolveDriver(input.connection);
423
+ const driver = await resolveDriverWithPluginFallback(input.connection);
362
424
  const plan = resolveQueuePlan(definition, options);
363
425
  const context = createExecutionContext(randomUUID(), driver, plan.queued);
364
426
  if (plan.afterCommit) {
@@ -440,7 +502,18 @@ var PendingDispatch = class {
440
502
  }
441
503
  };
442
504
  function configureBroadcastRuntime(bindings) {
443
- getRuntimeState().bindings = bindings;
505
+ const state = getRuntimeState();
506
+ if (!bindings) {
507
+ state.bindings = void 0;
508
+ state.projectRoot = void 0;
509
+ state.pluginNames = void 0;
510
+ return;
511
+ }
512
+ const { projectRoot, plugins, ...runtimeBindings } = bindings;
513
+ state.bindings = runtimeBindings;
514
+ const normalizedProjectRoot = typeof projectRoot === "string" ? projectRoot.trim() : "";
515
+ state.projectRoot = normalizedProjectRoot ? normalizedProjectRoot : void 0;
516
+ state.pluginNames = Object.freeze([...new Set((plugins ?? []).map((plugin) => plugin.trim()).filter((plugin) => plugin.length > 0))]);
444
517
  }
445
518
  function getBroadcastRuntimeBindings() {
446
519
  return getRuntimeBindings();
@@ -448,9 +521,27 @@ function getBroadcastRuntimeBindings() {
448
521
  function resetBroadcastRuntime() {
449
522
  const state = getRuntimeState();
450
523
  state.bindings = void 0;
524
+ state.projectRoot = void 0;
525
+ state.pluginNames = void 0;
451
526
  state.loadQueueModule = void 0;
452
527
  state.loadDbModule = void 0;
453
528
  state.queueJobRegistration = void 0;
529
+ resetBroadcastPluginDrivers();
530
+ }
531
+ function isBroadcastDriverNotRegisteredError(error) {
532
+ return error instanceof Error && error.message.startsWith('[@holo-js/broadcast] Broadcast driver "') && error.message.endsWith('" is not registered.');
533
+ }
534
+ async function resolveDriverWithPluginFallback(connectionName) {
535
+ try {
536
+ return resolveDriver(connectionName);
537
+ } catch (error) {
538
+ if (!isBroadcastDriverNotRegisteredError(error)) {
539
+ throw error;
540
+ }
541
+ }
542
+ const state = getRuntimeState();
543
+ await loadBroadcastPluginDrivers(state.projectRoot, state.pluginNames);
544
+ return resolveDriver(connectionName);
454
545
  }
455
546
  function broadcast(definition) {
456
547
  return new PendingDispatch(async (options) => {
@@ -497,6 +588,8 @@ export {
497
588
  listRegisteredBroadcastDrivers,
498
589
  resetBroadcastDriverRegistry,
499
590
  broadcastRegistryInternals,
591
+ loadBroadcastPluginDrivers,
592
+ resetBroadcastPluginDrivers,
500
593
  configureBroadcastRuntime,
501
594
  getBroadcastRuntimeBindings,
502
595
  resetBroadcastRuntime,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getBroadcastRuntimeBindings
3
- } from "./chunk-TTKGDABI.mjs";
3
+ } from "./chunk-6DCCSFFM.mjs";
4
4
  import {
5
5
  isChannelDefinition,
6
6
  isPlainObject,
@@ -348,8 +348,7 @@ function jsonResponse(body, status) {
348
348
  });
349
349
  }
350
350
  function signBroadcastAuth(appSecret, socketId, channel, channelData) {
351
- const value = channelData ? `${socketId}:${channel}:${channelData}` : `${socketId}:${channel}`;
352
- return createHmac("sha256", appSecret).update(value).digest("hex");
351
+ return createHmac("sha256", appSecret).update(`${socketId}:${channel}:${channelData}`).digest("hex");
353
352
  }
354
353
  async function renderBroadcastAuthResponse(request, options = {}) {
355
354
  let payload;
@@ -422,6 +421,7 @@ var broadcastAuthInternals = {
422
421
  importChannelDefinition,
423
422
  loadChannelDefinitions,
424
423
  matchPattern,
424
+ normalizeRegistryEntry,
425
425
  parseBroadcastAuthEndpointPayload,
426
426
  resolveAuthDefinitions,
427
427
  resolveChannelMatch,
@@ -0,0 +1,268 @@
1
+ // src/config.ts
2
+ import { registerConfigNormalizer } from "@holo-js/config/registry";
3
+ var DEFAULT_BROADCAST_CONNECTION = "null";
4
+ var DEFAULT_BROADCAST_HOST = "127.0.0.1";
5
+ var DEFAULT_BROADCAST_HTTP_PORT = 80;
6
+ var DEFAULT_BROADCAST_HTTPS_PORT = 443;
7
+ var DEFAULT_BROADCAST_PORT = DEFAULT_BROADCAST_HTTPS_PORT;
8
+ var DEFAULT_BROADCAST_WORKER_HOST = "0.0.0.0";
9
+ var DEFAULT_BROADCAST_WORKER_PORT = 8080;
10
+ var DEFAULT_BROADCAST_WORKER_PATH = "/app";
11
+ var DEFAULT_BROADCAST_HEALTH_PATH = "/health";
12
+ var DEFAULT_BROADCAST_STATS_PATH = "/stats";
13
+ var DEFAULT_BROADCAST_MAX_REQUEST_BYTES = 1048576;
14
+ var DEFAULT_BROADCAST_MAX_MESSAGE_BYTES = 65536;
15
+ var holoBroadcastDefaults = Object.freeze({
16
+ default: DEFAULT_BROADCAST_CONNECTION,
17
+ connections: Object.freeze({
18
+ log: Object.freeze({
19
+ name: "log",
20
+ driver: "log",
21
+ clientOptions: Object.freeze({})
22
+ }),
23
+ null: Object.freeze({
24
+ name: "null",
25
+ driver: "null",
26
+ clientOptions: Object.freeze({})
27
+ })
28
+ }),
29
+ worker: Object.freeze({
30
+ host: DEFAULT_BROADCAST_WORKER_HOST,
31
+ port: DEFAULT_BROADCAST_WORKER_PORT,
32
+ path: DEFAULT_BROADCAST_WORKER_PATH,
33
+ publicHost: void 0,
34
+ publicPort: void 0,
35
+ publicScheme: "https",
36
+ healthPath: DEFAULT_BROADCAST_HEALTH_PATH,
37
+ statsPath: DEFAULT_BROADCAST_STATS_PATH,
38
+ allowedOrigins: Object.freeze(["https://127.0.0.1"]),
39
+ maxRequestBytes: DEFAULT_BROADCAST_MAX_REQUEST_BYTES,
40
+ maxMessageBytes: DEFAULT_BROADCAST_MAX_MESSAGE_BYTES,
41
+ statsEnabled: false,
42
+ scaling: false
43
+ })
44
+ });
45
+ function parseInteger(value, label) {
46
+ const normalized = typeof value === "number" ? value : value.trim() ? Number(value.trim()) : Number.NaN;
47
+ if (!Number.isFinite(normalized) || !Number.isInteger(normalized)) throw new Error(`[Holo Broadcast] ${label} must be an integer.`);
48
+ if (normalized < 1) throw new Error(`[Holo Broadcast] ${label} must be greater than or equal to 1.`);
49
+ return normalized;
50
+ }
51
+ function normalizeOptionalBroadcastString(value, label) {
52
+ if (typeof value === "undefined") {
53
+ return void 0;
54
+ }
55
+ const normalized = String(value).trim();
56
+ if (!normalized) {
57
+ throw new Error(`[Holo Broadcast] ${label} must be a non-empty string when provided.`);
58
+ }
59
+ return normalized;
60
+ }
61
+ function normalizeBroadcastPort(value, fallback, label) {
62
+ const normalized = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value.trim()) : fallback;
63
+ if (!Number.isInteger(normalized) || normalized <= 0) {
64
+ throw new Error(`[Holo Broadcast] ${label} must be a positive integer.`);
65
+ }
66
+ return normalized;
67
+ }
68
+ function normalizeBroadcastScheme(value, fallback, label) {
69
+ const normalized = normalizeOptionalBroadcastString(value, label)?.toLowerCase();
70
+ if (typeof normalized === "undefined") {
71
+ return fallback;
72
+ }
73
+ if (normalized !== "http" && normalized !== "https") {
74
+ throw new Error(`[Holo Broadcast] ${label} must be "http" or "https".`);
75
+ }
76
+ return normalized;
77
+ }
78
+ function normalizeBroadcastConnectionOptions(options, fallbackHost, label) {
79
+ const scheme = normalizeBroadcastScheme(
80
+ options?.scheme,
81
+ options?.useTLS === false ? "http" : "https",
82
+ `${label} scheme`
83
+ );
84
+ const resolvedFallbackPort = scheme === "http" ? DEFAULT_BROADCAST_HTTP_PORT : DEFAULT_BROADCAST_HTTPS_PORT;
85
+ return Object.freeze({
86
+ host: normalizeOptionalBroadcastString(options?.host, `${label} host`) ?? fallbackHost,
87
+ port: normalizeBroadcastPort(options?.port, resolvedFallbackPort, `${label} port`),
88
+ scheme,
89
+ useTLS: options?.useTLS ?? scheme === "https",
90
+ cluster: normalizeOptionalBroadcastString(options?.cluster, `${label} cluster`) ?? void 0
91
+ });
92
+ }
93
+ function normalizeBroadcastWorkerConfig(worker, connectionOptions, configuredConnectionPort) {
94
+ const scaling = worker?.scaling;
95
+ const publicScheme = normalizeBroadcastScheme(worker?.publicScheme, connectionOptions?.scheme ?? "https", "Broadcast worker public scheme");
96
+ const fallbackPort = typeof configuredConnectionPort === "undefined" ? DEFAULT_BROADCAST_WORKER_PORT : normalizeBroadcastPort(configuredConnectionPort, DEFAULT_BROADCAST_WORKER_PORT, "Broadcast worker port");
97
+ if (scaling && scaling.driver !== "redis") {
98
+ throw new Error('[Holo Broadcast] Broadcast worker scaling driver must be "redis".');
99
+ }
100
+ const publicHost = normalizeOptionalBroadcastString(worker?.publicHost, "Broadcast worker public host") ?? void 0;
101
+ const publicPort = typeof worker?.publicPort === "undefined" ? publicScheme === "http" ? DEFAULT_BROADCAST_HTTP_PORT : void 0 : normalizeBroadcastPort(
102
+ worker.publicPort,
103
+ publicScheme === "http" ? DEFAULT_BROADCAST_HTTP_PORT : DEFAULT_BROADCAST_HTTPS_PORT,
104
+ "Broadcast worker public port"
105
+ );
106
+ const defaultOriginPort = publicPort ?? connectionOptions?.port;
107
+ const defaultOriginHost = publicHost ?? connectionOptions?.host ?? "127.0.0.1";
108
+ const defaultOrigin = new URL(`${publicScheme}://${defaultOriginHost}${typeof defaultOriginPort === "number" ? `:${defaultOriginPort}` : ""}`).origin;
109
+ const allowedOrigins = worker?.allowedOrigins?.map((value, index) => {
110
+ const normalized = value.trim();
111
+ if (normalized === "*") {
112
+ return normalized;
113
+ }
114
+ let origin;
115
+ try {
116
+ origin = new URL(normalized).origin;
117
+ } catch {
118
+ throw new Error(`[Holo Broadcast] Broadcast worker allowed origin at index ${index} must be "*" or an absolute URL origin.`);
119
+ }
120
+ if (origin === "null" || normalized.replace(/\/$/, "") !== origin) {
121
+ throw new Error(`[Holo Broadcast] Broadcast worker allowed origin at index ${index} must not include a path, query, or fragment.`);
122
+ }
123
+ return origin;
124
+ }) ?? [defaultOrigin];
125
+ return Object.freeze({
126
+ host: normalizeOptionalBroadcastString(worker?.host, "Broadcast worker host") ?? DEFAULT_BROADCAST_WORKER_HOST,
127
+ port: normalizeBroadcastPort(worker?.port, fallbackPort, "Broadcast worker port"),
128
+ path: normalizeOptionalBroadcastString(worker?.path, "Broadcast worker path") ?? DEFAULT_BROADCAST_WORKER_PATH,
129
+ publicHost,
130
+ publicPort,
131
+ publicScheme,
132
+ healthPath: normalizeOptionalBroadcastString(worker?.healthPath, "Broadcast worker health path") ?? DEFAULT_BROADCAST_HEALTH_PATH,
133
+ statsPath: normalizeOptionalBroadcastString(worker?.statsPath, "Broadcast worker stats path") ?? DEFAULT_BROADCAST_STATS_PATH,
134
+ allowedOrigins: Object.freeze(allowedOrigins),
135
+ maxRequestBytes: parseInteger(worker?.maxRequestBytes ?? DEFAULT_BROADCAST_MAX_REQUEST_BYTES, "broadcast worker maxRequestBytes"),
136
+ maxMessageBytes: parseInteger(worker?.maxMessageBytes ?? DEFAULT_BROADCAST_MAX_MESSAGE_BYTES, "broadcast worker maxMessageBytes"),
137
+ statsEnabled: worker?.statsEnabled ?? false,
138
+ scaling: scaling && typeof scaling === "object" ? Object.freeze({
139
+ driver: "redis",
140
+ connection: normalizeOptionalBroadcastString(scaling.connection, "Broadcast worker scaling connection") ?? "default"
141
+ }) : holoBroadcastDefaults.worker.scaling
142
+ });
143
+ }
144
+ function normalizeBroadcastConnection(name, connection) {
145
+ const normalizedName = normalizeOptionalBroadcastString(name, "Broadcast connection name");
146
+ const driver = normalizeOptionalBroadcastString(connection.driver, `Broadcast connection "${name}" driver`);
147
+ if (!normalizedName || !driver) {
148
+ throw new Error("[Holo Broadcast] Broadcast connections must define a name and driver.");
149
+ }
150
+ const clientOptions = Object.freeze({
151
+ ...connection.clientOptions ?? {}
152
+ });
153
+ if (driver === "holo") {
154
+ return Object.freeze({
155
+ name: normalizedName,
156
+ driver: "holo",
157
+ key: normalizeOptionalBroadcastString(connection.key, `Broadcast connection "${name}" key`) ?? (() => {
158
+ throw new Error(`[Holo Broadcast] Broadcast connection "${name}" must define a key.`);
159
+ })(),
160
+ secret: normalizeOptionalBroadcastString(connection.secret, `Broadcast connection "${name}" secret`) ?? (() => {
161
+ throw new Error(`[Holo Broadcast] Broadcast connection "${name}" must define a secret.`);
162
+ })(),
163
+ appId: normalizeOptionalBroadcastString(connection.appId, `Broadcast connection "${name}" appId`) ?? (() => {
164
+ throw new Error(`[Holo Broadcast] Broadcast connection "${name}" must define an appId.`);
165
+ })(),
166
+ options: normalizeBroadcastConnectionOptions(connection.options, DEFAULT_BROADCAST_HOST, `Broadcast connection "${name}" options`),
167
+ clientOptions
168
+ });
169
+ }
170
+ if (driver === "pusher") {
171
+ const cluster = normalizeOptionalBroadcastString(connection.options?.cluster, `Broadcast connection "${name}" cluster`) ?? void 0;
172
+ return Object.freeze({
173
+ name: normalizedName,
174
+ driver: "pusher",
175
+ key: normalizeOptionalBroadcastString(connection.key, `Broadcast connection "${name}" key`) ?? (() => {
176
+ throw new Error(`[Holo Broadcast] Broadcast connection "${name}" must define a key.`);
177
+ })(),
178
+ secret: normalizeOptionalBroadcastString(connection.secret, `Broadcast connection "${name}" secret`) ?? (() => {
179
+ throw new Error(`[Holo Broadcast] Broadcast connection "${name}" must define a secret.`);
180
+ })(),
181
+ appId: normalizeOptionalBroadcastString(connection.appId, `Broadcast connection "${name}" appId`) ?? (() => {
182
+ throw new Error(`[Holo Broadcast] Broadcast connection "${name}" must define an appId.`);
183
+ })(),
184
+ options: normalizeBroadcastConnectionOptions(
185
+ {
186
+ ...connection.options,
187
+ cluster
188
+ },
189
+ normalizeOptionalBroadcastString(connection.options?.host, `Broadcast connection "${name}" host`) ?? (cluster ? `api-${cluster}.pusher.com` : "api-mt1.pusher.com"),
190
+ `Broadcast connection "${name}" options`
191
+ ),
192
+ clientOptions
193
+ });
194
+ }
195
+ if (driver === "log") {
196
+ return Object.freeze({
197
+ name: normalizedName,
198
+ driver: "log",
199
+ clientOptions
200
+ });
201
+ }
202
+ if (driver === "null") {
203
+ return Object.freeze({
204
+ name: normalizedName,
205
+ driver: "null",
206
+ clientOptions
207
+ });
208
+ }
209
+ if (driver === "ably") {
210
+ throw new Error('[Holo Broadcast] Broadcast driver "ably" is not supported yet.');
211
+ }
212
+ const {
213
+ driver: _ignoredDriver,
214
+ clientOptions: _ignoredClientOptions,
215
+ ...customConfig
216
+ } = connection;
217
+ return Object.freeze({
218
+ driver,
219
+ clientOptions,
220
+ ...customConfig,
221
+ name: normalizedName
222
+ });
223
+ }
224
+ function normalizeBroadcastConfig(config = {}) {
225
+ const normalizedConnections = Object.fromEntries(
226
+ Object.entries(config.connections ?? holoBroadcastDefaults.connections).map(([name, connection]) => [name, normalizeBroadcastConnection(name, connection)])
227
+ );
228
+ const defaultConnection = normalizeOptionalBroadcastString(config.default, "Default broadcast connection") ?? holoBroadcastDefaults.default;
229
+ if (!normalizedConnections[defaultConnection]) {
230
+ throw new Error(
231
+ `[Holo Broadcast] default broadcast connection "${defaultConnection}" is not configured. Available connections: ${Object.keys(normalizedConnections).join(", ")}`
232
+ );
233
+ }
234
+ const defaultBroadcastConnection = normalizedConnections[defaultConnection];
235
+ const defaultHoloConnectionOptions = defaultBroadcastConnection && defaultBroadcastConnection.driver === "holo" && "options" in defaultBroadcastConnection ? defaultBroadcastConnection.options : void 0;
236
+ const configuredDefaultConnection = config.connections?.[defaultConnection];
237
+ const configuredDefaultHoloConnectionPort = configuredDefaultConnection?.driver === "holo" ? configuredDefaultConnection.options?.port : void 0;
238
+ return Object.freeze({
239
+ default: defaultConnection,
240
+ connections: Object.freeze(normalizedConnections),
241
+ worker: normalizeBroadcastWorkerConfig(config.worker, defaultHoloConnectionOptions, configuredDefaultHoloConnectionPort)
242
+ });
243
+ }
244
+ function defineBroadcastConfig(config) {
245
+ return Object.freeze({ ...config });
246
+ }
247
+ registerConfigNormalizer({
248
+ name: "broadcast",
249
+ normalize: normalizeBroadcastConfig
250
+ });
251
+
252
+ export {
253
+ DEFAULT_BROADCAST_CONNECTION,
254
+ DEFAULT_BROADCAST_HOST,
255
+ DEFAULT_BROADCAST_HTTP_PORT,
256
+ DEFAULT_BROADCAST_HTTPS_PORT,
257
+ DEFAULT_BROADCAST_PORT,
258
+ DEFAULT_BROADCAST_WORKER_HOST,
259
+ DEFAULT_BROADCAST_WORKER_PORT,
260
+ DEFAULT_BROADCAST_WORKER_PATH,
261
+ DEFAULT_BROADCAST_HEALTH_PATH,
262
+ DEFAULT_BROADCAST_STATS_PATH,
263
+ DEFAULT_BROADCAST_MAX_REQUEST_BYTES,
264
+ DEFAULT_BROADCAST_MAX_MESSAGE_BYTES,
265
+ holoBroadcastDefaults,
266
+ normalizeBroadcastConfig,
267
+ defineBroadcastConfig
268
+ };
@@ -1,4 +1,4 @@
1
- import { LoadedHoloConfig } from '@holo-js/config';
1
+ import { NormalizedHoloBroadcastConfig } from './config.js';
2
2
 
3
3
  type BroadcastClientConfig = {
4
4
  readonly key: string;
@@ -8,7 +8,7 @@ type BroadcastClientConfig = {
8
8
  readonly scheme: 'http' | 'https';
9
9
  readonly authEndpoint?: string;
10
10
  };
11
- declare function resolveBroadcastClientConfig(config: LoadedHoloConfig['broadcast']): BroadcastClientConfig;
12
- declare function renderBroadcastClientConfigResponse(config: LoadedHoloConfig['broadcast']): Response;
11
+ declare function resolveBroadcastClientConfig(config: NormalizedHoloBroadcastConfig): BroadcastClientConfig;
12
+ declare function renderBroadcastClientConfigResponse(config: NormalizedHoloBroadcastConfig): Response;
13
13
 
14
14
  export { type BroadcastClientConfig, renderBroadcastClientConfigResponse, resolveBroadcastClientConfig };
@@ -0,0 +1,138 @@
1
+ type BroadcastConnectionDriver = 'holo' | 'pusher' | 'log' | 'null';
2
+ type BroadcastConnectionScheme = 'http' | 'https';
3
+ interface BroadcastConnectionOptionsConfig {
4
+ readonly host?: string;
5
+ readonly port?: number | string;
6
+ readonly scheme?: BroadcastConnectionScheme;
7
+ readonly useTLS?: boolean;
8
+ readonly cluster?: string;
9
+ }
10
+ interface BroadcastWorkerScalingConfig {
11
+ readonly driver: 'redis';
12
+ readonly connection?: string;
13
+ }
14
+ interface BroadcastWorkerConfig {
15
+ readonly host?: string;
16
+ readonly port?: number | string;
17
+ readonly path?: string;
18
+ readonly publicHost?: string;
19
+ readonly publicPort?: number | string;
20
+ readonly publicScheme?: BroadcastConnectionScheme;
21
+ readonly healthPath?: string;
22
+ readonly statsPath?: string;
23
+ readonly allowedOrigins?: readonly string[];
24
+ readonly maxRequestBytes?: number | string;
25
+ readonly maxMessageBytes?: number | string;
26
+ readonly statsEnabled?: boolean;
27
+ readonly scaling?: false | BroadcastWorkerScalingConfig;
28
+ }
29
+ type BroadcastConnectionConfigValue = string | number | boolean | object | undefined;
30
+ interface BaseBroadcastConnectionConfig {
31
+ readonly driver: BroadcastConnectionDriver | (string & {});
32
+ readonly options?: BroadcastConnectionOptionsConfig;
33
+ readonly clientOptions?: Readonly<Record<string, unknown>>;
34
+ readonly [key: string]: BroadcastConnectionConfigValue;
35
+ }
36
+ interface HoloBroadcastConnectionConfig extends BaseBroadcastConnectionConfig {
37
+ readonly driver: 'holo';
38
+ readonly key?: string;
39
+ readonly secret?: string;
40
+ readonly appId?: string | number;
41
+ }
42
+ interface PusherBroadcastConnectionConfig extends BaseBroadcastConnectionConfig {
43
+ readonly driver: 'pusher';
44
+ readonly key?: string;
45
+ readonly secret?: string;
46
+ readonly appId?: string | number;
47
+ }
48
+ interface LogBroadcastConnectionConfig extends BaseBroadcastConnectionConfig {
49
+ readonly driver: 'log';
50
+ }
51
+ interface NullBroadcastConnectionConfig extends BaseBroadcastConnectionConfig {
52
+ readonly driver: 'null';
53
+ }
54
+ type HoloBroadcastConnection = HoloBroadcastConnectionConfig | PusherBroadcastConnectionConfig | LogBroadcastConnectionConfig | NullBroadcastConnectionConfig | BaseBroadcastConnectionConfig;
55
+ interface HoloBroadcastConfig {
56
+ readonly default?: string;
57
+ readonly connections?: Readonly<Record<string, HoloBroadcastConnection>>;
58
+ readonly worker?: BroadcastWorkerConfig;
59
+ }
60
+ interface NormalizedBroadcastConnectionOptionsConfig {
61
+ readonly host: string;
62
+ readonly port: number;
63
+ readonly scheme: BroadcastConnectionScheme;
64
+ readonly useTLS: boolean;
65
+ readonly cluster?: string;
66
+ }
67
+ interface NormalizedBroadcastWorkerScalingConfig {
68
+ readonly driver: 'redis';
69
+ readonly connection: string;
70
+ }
71
+ interface NormalizedBroadcastWorkerConfig {
72
+ readonly host: string;
73
+ readonly port: number;
74
+ readonly path: string;
75
+ readonly publicHost?: string;
76
+ readonly publicPort?: number;
77
+ readonly publicScheme: BroadcastConnectionScheme;
78
+ readonly healthPath: string;
79
+ readonly statsPath: string;
80
+ readonly allowedOrigins: readonly string[];
81
+ readonly maxRequestBytes: number;
82
+ readonly maxMessageBytes: number;
83
+ readonly statsEnabled: boolean;
84
+ readonly scaling: false | NormalizedBroadcastWorkerScalingConfig;
85
+ }
86
+ interface NormalizedBaseBroadcastConnectionConfig {
87
+ readonly name: string;
88
+ readonly driver: string;
89
+ readonly clientOptions: Readonly<Record<string, unknown>>;
90
+ }
91
+ interface NormalizedHoloBroadcastConnectionConfig extends NormalizedBaseBroadcastConnectionConfig {
92
+ readonly driver: 'holo';
93
+ readonly key: string;
94
+ readonly secret: string;
95
+ readonly appId: string;
96
+ readonly options: NormalizedBroadcastConnectionOptionsConfig;
97
+ }
98
+ interface NormalizedPusherBroadcastConnectionConfig extends NormalizedBaseBroadcastConnectionConfig {
99
+ readonly driver: 'pusher';
100
+ readonly key: string;
101
+ readonly secret: string;
102
+ readonly appId: string;
103
+ readonly options: NormalizedBroadcastConnectionOptionsConfig;
104
+ }
105
+ interface NormalizedLogBroadcastConnectionConfig extends NormalizedBaseBroadcastConnectionConfig {
106
+ readonly driver: 'log';
107
+ }
108
+ interface NormalizedNullBroadcastConnectionConfig extends NormalizedBaseBroadcastConnectionConfig {
109
+ readonly driver: 'null';
110
+ }
111
+ type NormalizedHoloBroadcastConnection = NormalizedHoloBroadcastConnectionConfig | NormalizedPusherBroadcastConnectionConfig | NormalizedLogBroadcastConnectionConfig | NormalizedNullBroadcastConnectionConfig | NormalizedBaseBroadcastConnectionConfig;
112
+ interface NormalizedHoloBroadcastConfig {
113
+ readonly default: string;
114
+ readonly connections: Readonly<Record<string, NormalizedHoloBroadcastConnection>>;
115
+ readonly worker: NormalizedBroadcastWorkerConfig;
116
+ }
117
+ declare const DEFAULT_BROADCAST_CONNECTION = "null";
118
+ declare const DEFAULT_BROADCAST_HOST = "127.0.0.1";
119
+ declare const DEFAULT_BROADCAST_HTTP_PORT = 80;
120
+ declare const DEFAULT_BROADCAST_HTTPS_PORT = 443;
121
+ declare const DEFAULT_BROADCAST_PORT = 443;
122
+ declare const DEFAULT_BROADCAST_WORKER_HOST = "0.0.0.0";
123
+ declare const DEFAULT_BROADCAST_WORKER_PORT = 8080;
124
+ declare const DEFAULT_BROADCAST_WORKER_PATH = "/app";
125
+ declare const DEFAULT_BROADCAST_HEALTH_PATH = "/health";
126
+ declare const DEFAULT_BROADCAST_STATS_PATH = "/stats";
127
+ declare const DEFAULT_BROADCAST_MAX_REQUEST_BYTES = 1048576;
128
+ declare const DEFAULT_BROADCAST_MAX_MESSAGE_BYTES = 65536;
129
+ declare const holoBroadcastDefaults: Readonly<NormalizedHoloBroadcastConfig>;
130
+ declare function normalizeBroadcastConfig(config?: HoloBroadcastConfig): NormalizedHoloBroadcastConfig;
131
+ declare function defineBroadcastConfig<TConfig extends HoloBroadcastConfig>(config: TConfig): Readonly<TConfig>;
132
+ declare module '@holo-js/config' {
133
+ interface HoloConfigRegistry {
134
+ broadcast: NormalizedHoloBroadcastConfig;
135
+ }
136
+ }
137
+
138
+ export { type BaseBroadcastConnectionConfig, type BroadcastConnectionConfigValue, type BroadcastConnectionDriver, type BroadcastConnectionOptionsConfig, type BroadcastConnectionScheme, type BroadcastWorkerConfig, type BroadcastWorkerScalingConfig, DEFAULT_BROADCAST_CONNECTION, DEFAULT_BROADCAST_HEALTH_PATH, DEFAULT_BROADCAST_HOST, DEFAULT_BROADCAST_HTTPS_PORT, DEFAULT_BROADCAST_HTTP_PORT, DEFAULT_BROADCAST_MAX_MESSAGE_BYTES, DEFAULT_BROADCAST_MAX_REQUEST_BYTES, DEFAULT_BROADCAST_PORT, DEFAULT_BROADCAST_STATS_PATH, DEFAULT_BROADCAST_WORKER_HOST, DEFAULT_BROADCAST_WORKER_PATH, DEFAULT_BROADCAST_WORKER_PORT, type HoloBroadcastConfig, type HoloBroadcastConnection, type HoloBroadcastConnectionConfig, type LogBroadcastConnectionConfig, type NormalizedBaseBroadcastConnectionConfig, type NormalizedBroadcastConnectionOptionsConfig, type NormalizedBroadcastWorkerConfig, type NormalizedBroadcastWorkerScalingConfig, type NormalizedHoloBroadcastConfig, type NormalizedHoloBroadcastConnection, type NormalizedHoloBroadcastConnectionConfig, type NormalizedLogBroadcastConnectionConfig, type NormalizedNullBroadcastConnectionConfig, type NormalizedPusherBroadcastConnectionConfig, type NullBroadcastConnectionConfig, type PusherBroadcastConnectionConfig, defineBroadcastConfig, holoBroadcastDefaults, normalizeBroadcastConfig };
@@ -0,0 +1,34 @@
1
+ import {
2
+ DEFAULT_BROADCAST_CONNECTION,
3
+ DEFAULT_BROADCAST_HEALTH_PATH,
4
+ DEFAULT_BROADCAST_HOST,
5
+ DEFAULT_BROADCAST_HTTPS_PORT,
6
+ DEFAULT_BROADCAST_HTTP_PORT,
7
+ DEFAULT_BROADCAST_MAX_MESSAGE_BYTES,
8
+ DEFAULT_BROADCAST_MAX_REQUEST_BYTES,
9
+ DEFAULT_BROADCAST_PORT,
10
+ DEFAULT_BROADCAST_STATS_PATH,
11
+ DEFAULT_BROADCAST_WORKER_HOST,
12
+ DEFAULT_BROADCAST_WORKER_PATH,
13
+ DEFAULT_BROADCAST_WORKER_PORT,
14
+ defineBroadcastConfig,
15
+ holoBroadcastDefaults,
16
+ normalizeBroadcastConfig
17
+ } from "./chunk-XLUGF257.mjs";
18
+ export {
19
+ DEFAULT_BROADCAST_CONNECTION,
20
+ DEFAULT_BROADCAST_HEALTH_PATH,
21
+ DEFAULT_BROADCAST_HOST,
22
+ DEFAULT_BROADCAST_HTTPS_PORT,
23
+ DEFAULT_BROADCAST_HTTP_PORT,
24
+ DEFAULT_BROADCAST_MAX_MESSAGE_BYTES,
25
+ DEFAULT_BROADCAST_MAX_REQUEST_BYTES,
26
+ DEFAULT_BROADCAST_PORT,
27
+ DEFAULT_BROADCAST_STATS_PATH,
28
+ DEFAULT_BROADCAST_WORKER_HOST,
29
+ DEFAULT_BROADCAST_WORKER_PATH,
30
+ DEFAULT_BROADCAST_WORKER_PORT,
31
+ defineBroadcastConfig,
32
+ holoBroadcastDefaults,
33
+ normalizeBroadcastConfig
34
+ };