@holo-js/notifications 0.1.4 → 0.1.6

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.
@@ -88,6 +88,9 @@ interface NotificationDefinition<TNotifiable = unknown, TBuild extends Notificat
88
88
  readonly queue?: boolean | NotificationQueueOptions | NotificationQueueResolver<TNotifiable, string>;
89
89
  readonly delay?: NotificationDelayValue | Partial<Record<Extract<keyof TBuild, string>, NotificationDelayValue>> | NotificationDelayResolver<TNotifiable, string>;
90
90
  }
91
+ type NotificationDefinitionInput<TNotifiable, TBuild extends NotificationBuildFactories<TNotifiable>> = Omit<NotificationDefinition<TNotifiable, TBuild>, 'via'> & {
92
+ via(notifiable: TNotifiable, context: NotificationContext): readonly Extract<keyof TBuild, string>[];
93
+ };
91
94
  type InferNotificationNotifiable<TNotification> = TNotification extends NotificationDefinition<infer TNotifiable, NotificationBuildFactories<unknown>> ? TNotifiable : TNotification extends NotificationDefinition<infer TNotifiable, infer _TBuild> ? TNotifiable : never;
92
95
  type InferNotificationChannels<TNotification> = TNotification extends NotificationDefinition<unknown, infer TBuild> ? Extract<keyof TBuild, string> : never;
93
96
  interface AnonymousNotificationTarget<TRoutes extends Partial<{
@@ -190,9 +193,9 @@ interface RegisteredNotificationChannel<TChannel extends string = string> {
190
193
  }
191
194
  declare function isNotificationDefinition(value: unknown): value is NotificationDefinition;
192
195
  declare function normalizeQueueOptions(value: NotificationQueueOptions | undefined): NotificationQueueOptions | undefined;
193
- declare function normalizeDelayConfig<TChannels extends string>(value: NotificationDefinition<unknown, NotificationBuildFactories<unknown>>['delay'] | undefined): NotificationDefinition<unknown, NotificationBuildFactories<unknown>>['delay'] | undefined;
194
- declare function normalizeNotificationDefinition<TNotifiable, TBuild extends NotificationBuildFactories<TNotifiable>>(definition: NotificationDefinition<TNotifiable, TBuild>): NotificationDefinition<TNotifiable, TBuild>;
195
- declare function defineNotification<TNotifiable, TBuild extends NotificationBuildFactories<TNotifiable>>(definition: NotificationDefinition<TNotifiable, TBuild>): NotificationDefinition<TNotifiable, TBuild>;
196
+ declare function normalizeDelayConfig<TNotifiable, TChannels extends string>(value: NotificationDelayValue | Partial<Record<TChannels, NotificationDelayValue>> | NotificationDelayResolver<TNotifiable, string> | undefined): NotificationDelayValue | Partial<Record<TChannels, NotificationDelayValue>> | NotificationDelayResolver<TNotifiable, string> | undefined;
197
+ declare function normalizeNotificationDefinition<TNotifiable, const TBuild extends NotificationBuildFactories<TNotifiable>>(definition: NotificationDefinitionInput<TNotifiable, TBuild>): NotificationDefinition<TNotifiable, TBuild>;
198
+ declare function defineNotification<TNotifiable, const TBuild extends NotificationBuildFactories<TNotifiable>>(definition: NotificationDefinitionInput<TNotifiable, TBuild>): NotificationDefinition<TNotifiable, TBuild>;
196
199
  declare function createAnonymousNotificationTarget<TRoutes extends Partial<{
197
200
  readonly [TChannel in NotificationChannelName]: NotificationRouteFor<TChannel>;
198
201
  }>>(routes: TRoutes): AnonymousNotificationTarget<TRoutes>;
package/dist/index.d.ts CHANGED
@@ -1,12 +1,30 @@
1
- import { NotificationRuntimeBindings, NotificationDatabaseRoute, NotificationRecord, NotificationDefinition, InferNotificationNotifiable, PendingNotificationDispatch, NotificationDispatchResult, PendingAnonymousNotification, NotificationChannelName, NotificationRouteFor, AnonymousNotificationTarget, NotificationDispatchTarget, NotificationDelayValue, NotificationChannel, NotificationBuildContext, NotificationSendContext, NotificationDispatchInput, NotificationBroadcastRoute, NotificationEmailRoute, NotificationDatabaseMessage, NotificationDispatchOptions, RegisteredNotificationChannel, RegisterNotificationChannelOptions } from './contracts.js';
2
- export { BuiltInNotificationChannelRegistry, HoloNotificationChannelRegistry, InferNotificationChannels, NotificationBroadcastMessage, NotificationBroadcaster, NotificationBuildFactories, NotificationChannelDispatchResult, NotificationContext, NotificationDelayResolver, NotificationJsonValue, NotificationMailMessage, NotificationMailSender, NotificationPayloadFor, NotificationQueueOptions, NotificationQueueResolver, NotificationResultFor, NotificationStore, defineNotification, isNotificationDefinition, normalizeNotificationDefinition, notificationsInternals } from './contracts.js';
1
+ import { NotificationChannel, NotificationBuildContext, AnonymousNotificationTarget, NotificationBroadcastRoute, NotificationDatabaseRoute, NotificationEmailRoute, NotificationDatabaseMessage, NotificationRecord, NotificationRuntimeBindings, InferNotificationNotifiable, NotificationDefinition, PendingNotificationDispatch, NotificationDispatchResult, PendingAnonymousNotification, NotificationChannelName, NotificationRouteFor, NotificationDispatchTarget, NotificationDelayValue, NotificationSendContext, NotificationDispatchInput, NotificationDispatchOptions, NotificationQueueOptions, RegisteredNotificationChannel, RegisterNotificationChannelOptions } from './contracts.js';
2
+ export { BuiltInNotificationChannelRegistry, HoloNotificationChannelRegistry, InferNotificationChannels, NotificationBroadcastMessage, NotificationBroadcaster, NotificationBuildFactories, NotificationChannelDispatchResult, NotificationContext, NotificationDelayResolver, NotificationJsonValue, NotificationMailMessage, NotificationMailSender, NotificationPayloadFor, NotificationQueueResolver, NotificationResultFor, NotificationStore, defineNotification, isNotificationDefinition, normalizeNotificationDefinition, notificationsInternals } from './contracts.js';
3
3
  import { HoloNotificationsConfig } from '@holo-js/config';
4
4
  export { HoloNotificationsConfig, NormalizedHoloNotificationsConfig } from '@holo-js/config';
5
5
 
6
- declare function normalizeOptionalString(value: string, label: string): string;
7
- declare function normalizeDelayValue(value: NotificationDelayValue, label: string): NotificationDelayValue;
6
+ type RouteResolver = (notifiable: unknown) => unknown;
7
+ type BuiltInChannelDefinition = NotificationChannel & {
8
+ readonly resolveRoute?: RouteResolver;
9
+ };
8
10
  declare function isObject(value: unknown): value is Record<string, unknown>;
9
11
  declare function isAnonymousTarget(value: unknown): value is AnonymousNotificationTarget;
12
+ declare function createNotificationContext(anonymous: boolean): {
13
+ readonly anonymous: boolean;
14
+ };
15
+ declare function createBuildContext<TChannel extends string>(channel: TChannel, anonymous: boolean): NotificationBuildContext<TChannel>;
16
+ declare function normalizeEmailRouteFromValue(value: unknown): NotificationEmailRoute;
17
+ declare function resolveEmailRouteFromNotifiable(notifiable: unknown): NotificationEmailRoute;
18
+ declare function normalizeDatabaseRouteFromValue(value: unknown): NotificationDatabaseRoute;
19
+ declare function resolveDatabaseRouteFromNotifiable(notifiable: unknown): NotificationDatabaseRoute;
20
+ declare function normalizeBroadcastRouteFromValue(value: unknown): NotificationBroadcastRoute;
21
+ declare function resolveBroadcastRouteFromNotifiable(notifiable: unknown): NotificationBroadcastRoute;
22
+ declare function normalizeNotificationRecord(route: NotificationDatabaseRoute, payload: NotificationDatabaseMessage, notificationType: string | undefined): NotificationRecord;
23
+ declare function normalizeNotificationRecordIds(ids: readonly string[]): readonly string[];
24
+
25
+ declare function normalizeOptionalString(value: string, label: string): string;
26
+ declare function normalizeOptionalString(value: string | undefined, label: string): string | undefined;
27
+ declare function normalizeDelayValue(value: NotificationDelayValue, label: string): NotificationDelayValue;
10
28
  declare function getRuntimeBindings(): NotificationRuntimeBindings;
11
29
  type RuntimeState = {
12
30
  bindings?: NotificationRuntimeBindings;
@@ -31,7 +49,6 @@ type ResolvedTarget = {
31
49
  readonly notifiable: unknown;
32
50
  readonly routes?: Record<string, unknown>;
33
51
  };
34
- type RouteResolver = (notifiable: unknown) => unknown;
35
52
  type ResolvedChannelPlan = {
36
53
  readonly channel: string;
37
54
  readonly queued: boolean;
@@ -80,26 +97,11 @@ type QueuedNotificationDeliveryPayload = Readonly<{
80
97
  readonly payload: unknown;
81
98
  readonly targetIndex: number;
82
99
  }>;
83
- type BuiltInChannelDefinition = NotificationChannel & {
84
- readonly resolveRoute?: RouteResolver;
85
- };
86
- declare function createNotificationContext(anonymous: boolean): {
87
- readonly anonymous: boolean;
88
- };
89
- declare function createBuildContext<TChannel extends string>(channel: TChannel, anonymous: boolean): NotificationBuildContext<TChannel>;
90
- declare function normalizeEmailRouteFromValue(value: unknown): NotificationEmailRoute;
91
- declare function resolveEmailRouteFromNotifiable(notifiable: unknown): NotificationEmailRoute;
92
- declare function normalizeDatabaseRouteFromValue(value: unknown): NotificationDatabaseRoute;
93
- declare function resolveDatabaseRouteFromNotifiable(notifiable: unknown): NotificationDatabaseRoute;
94
- declare function normalizeBroadcastRouteFromValue(value: unknown): NotificationBroadcastRoute;
95
- declare function resolveBroadcastRouteFromNotifiable(notifiable: unknown): NotificationBroadcastRoute;
96
- declare function normalizeNotificationRecord(route: NotificationDatabaseRoute, payload: NotificationDatabaseMessage, notificationType: string | undefined): NotificationRecord;
97
- declare function normalizeNotificationRecordIds(ids: readonly string[]): readonly string[];
98
100
  declare function getNotificationChannel(name: string): NotificationChannel | BuiltInChannelDefinition | undefined;
99
101
  declare function resolveTargets(target: NotificationDispatchTarget): readonly ResolvedTarget[];
100
102
  declare function resolveChannels(notification: NotificationDefinition, target: ResolvedTarget): readonly string[];
101
103
  declare function resolvePayload(notification: NotificationDefinition, channel: string, target: ResolvedTarget): unknown;
102
- declare function resolveNotificationQueueOptions(notification: NotificationDefinition, target: ResolvedTarget, channel: string): boolean | NotificationDispatchOptions;
104
+ declare function resolveNotificationQueueOptions(notification: NotificationDefinition, target: ResolvedTarget, channel: string): boolean | NotificationQueueOptions;
103
105
  declare function resolveNotificationDelay(notification: NotificationDefinition, target: ResolvedTarget, channel: string): NotificationDelayValue | undefined;
104
106
  declare function resolveRoute(channel: string, target: ResolvedTarget): unknown;
105
107
  declare function resolveChannelDispatchPlan(notification: NotificationDefinition, target: ResolvedTarget, channel: string, options: NotificationDispatchOptions): ResolvedChannelPlan;
@@ -148,8 +150,9 @@ interface NotificationRuntimeFacade {
148
150
  declare function configureNotificationsRuntime(bindings?: NotificationRuntimeBindings): void;
149
151
  declare function getNotificationsRuntimeBindings(): NotificationRuntimeBindings;
150
152
  declare function resetNotificationsRuntime(): void;
151
- declare function notify<TNotification extends NotificationDefinition<any, any>>(notifiable: InferNotificationNotifiable<TNotification>, notification: TNotification): PendingNotificationDispatch<NotificationDispatchResult>;
152
- declare function notifyMany<TNotification extends NotificationDefinition<any, any>>(notifiables: readonly InferNotificationNotifiable<TNotification>[] | Iterable<InferNotificationNotifiable<TNotification>>, notification: TNotification): PendingNotificationDispatch<NotificationDispatchResult>;
153
+ type NotificationDefinitionLike<TNotification> = TNotification extends NotificationDefinition<infer _TNotifiable, infer _TBuild> ? TNotification : never;
154
+ declare function notify<TNotification>(notifiable: InferNotificationNotifiable<TNotification>, notification: NotificationDefinitionLike<TNotification>): PendingNotificationDispatch<NotificationDispatchResult>;
155
+ declare function notifyMany<TNotification>(notifiables: readonly InferNotificationNotifiable<TNotification>[] | Iterable<InferNotificationNotifiable<TNotification>>, notification: NotificationDefinitionLike<TNotification>): PendingNotificationDispatch<NotificationDispatchResult>;
153
156
  declare function notifyUsing(): PendingAnonymousNotification;
154
157
  declare function listNotifications(notifiable: NotificationDatabaseRoute | Record<string, unknown>): Promise<readonly NotificationRecord[]>;
155
158
  declare function unreadNotifications(notifiable: NotificationDatabaseRoute | Record<string, unknown>): Promise<readonly NotificationRecord[]>;
@@ -223,4 +226,4 @@ declare const notifications: Readonly<{
223
226
  unreadNotifications: typeof unreadNotifications;
224
227
  }>;
225
228
 
226
- export { AnonymousNotificationTarget, InferNotificationNotifiable, NotificationBroadcastRoute, NotificationBuildContext, NotificationChannel, NotificationChannelName, NotificationDatabaseMessage, NotificationDatabaseRoute, NotificationDefinition, NotificationDelayValue, NotificationDispatchInput, NotificationDispatchOptions, NotificationDispatchResult, NotificationDispatchTarget, NotificationEmailRoute, NotificationRecord, NotificationRouteFor, NotificationRuntimeBindings, NotificationSendContext, PendingAnonymousNotification, PendingNotificationDispatch, RegisterNotificationChannelOptions, RegisteredNotificationChannel, configureNotificationsRuntime, notifications as default, defineNotificationsConfig, deleteNotifications, getNotificationsRuntime, getNotificationsRuntimeBindings, getRegisteredNotificationChannel, listNotifications, listRegisteredNotificationChannels, markNotificationsAsRead, markNotificationsAsUnread, notificationRegistryInternals, notificationsRuntimeInternals, notify, notifyMany, notifyUsing, registerNotificationChannel, resetNotificationChannelRegistry, resetNotificationsRuntime, unreadNotifications };
229
+ export { AnonymousNotificationTarget, InferNotificationNotifiable, NotificationBroadcastRoute, NotificationBuildContext, NotificationChannel, NotificationChannelName, NotificationDatabaseMessage, NotificationDatabaseRoute, NotificationDefinition, NotificationDelayValue, NotificationDispatchInput, NotificationDispatchOptions, NotificationDispatchResult, NotificationDispatchTarget, NotificationEmailRoute, NotificationQueueOptions, NotificationRecord, NotificationRouteFor, NotificationRuntimeBindings, NotificationSendContext, PendingAnonymousNotification, PendingNotificationDispatch, RegisterNotificationChannelOptions, RegisteredNotificationChannel, configureNotificationsRuntime, notifications as default, defineNotificationsConfig, deleteNotifications, getNotificationsRuntime, getNotificationsRuntimeBindings, getRegisteredNotificationChannel, listNotifications, listRegisteredNotificationChannels, markNotificationsAsRead, markNotificationsAsUnread, notificationRegistryInternals, notificationsRuntimeInternals, notify, notifyMany, notifyUsing, registerNotificationChannel, resetNotificationChannelRegistry, resetNotificationsRuntime, unreadNotifications };
package/dist/index.mjs CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  } from "./chunk-INGM2JYY.mjs";
8
8
 
9
9
  // src/runtime.ts
10
- import { randomUUID } from "crypto";
11
10
  import { holoNotificationsDefaults } from "@holo-js/config";
12
11
 
13
12
  // src/registry.ts
@@ -51,90 +50,14 @@ var notificationRegistryInternals = {
51
50
  normalizeChannelName
52
51
  };
53
52
 
54
- // src/runtime.ts
55
- var HOLO_NOTIFICATIONS_DELIVER_JOB = "holo.notifications.deliver";
56
- function normalizeOptionalString(value, label) {
57
- const normalized = value.trim();
58
- if (!normalized) {
59
- throw new Error(`[@holo-js/notifications] ${label} must be a non-empty string.`);
60
- }
61
- return normalized;
62
- }
63
- function normalizeDelayValue(value, label) {
64
- if (typeof value === "number") {
65
- if (!Number.isFinite(value) || value < 0) {
66
- throw new Error(`[@holo-js/notifications] ${label} must be a finite number greater than or equal to 0.`);
67
- }
68
- return value;
69
- }
70
- if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
71
- throw new Error(`[@holo-js/notifications] ${label} dates must be valid Date instances.`);
72
- }
73
- return value;
74
- }
53
+ // src/runtime-channels.ts
54
+ import { randomUUID } from "crypto";
75
55
  function isObject(value) {
76
56
  return !!value && typeof value === "object" && !Array.isArray(value);
77
57
  }
78
58
  function isAnonymousTarget(value) {
79
59
  return isObject(value) && value.anonymous === true && isObject(value.routes);
80
60
  }
81
- function getRuntimeBindings() {
82
- return getRuntimeState().bindings ?? {};
83
- }
84
- function getRuntimeState() {
85
- const runtime = globalThis;
86
- runtime.__holoNotificationsRuntime__ ??= {};
87
- return runtime.__holoNotificationsRuntime__;
88
- }
89
- function getDispatchHandler() {
90
- const bindings = getRuntimeBindings();
91
- return bindings.dispatch ?? dispatchNotifications;
92
- }
93
- function dynamicImport(specifier) {
94
- return import(specifier);
95
- }
96
- async function loadQueueModule() {
97
- const override = getRuntimeState().loadQueueModule;
98
- if (override) {
99
- try {
100
- return await override();
101
- } catch (error) {
102
- if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
103
- throw new Error("[@holo-js/notifications] Queued or delayed notifications require @holo-js/queue to be installed.");
104
- }
105
- throw error;
106
- }
107
- }
108
- try {
109
- return await dynamicImport("@holo-js/queue");
110
- } catch (error) {
111
- if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
112
- throw new Error("[@holo-js/notifications] Queued or delayed notifications require @holo-js/queue to be installed.");
113
- }
114
- throw error;
115
- }
116
- }
117
- async function loadDbModule() {
118
- const override = getRuntimeState().loadDbModule;
119
- if (override) {
120
- try {
121
- return await override();
122
- } catch (error) {
123
- if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
124
- return null;
125
- }
126
- throw error;
127
- }
128
- }
129
- try {
130
- return await dynamicImport("@holo-js/db");
131
- } catch (error) {
132
- if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
133
- return null;
134
- }
135
- throw error;
136
- }
137
- }
138
61
  function createNotificationContext(anonymous) {
139
62
  return Object.freeze({ anonymous });
140
63
  }
@@ -286,47 +209,156 @@ function normalizeNotificationRecordIds(ids) {
286
209
  });
287
210
  return Object.freeze([...new Set(normalized)]);
288
211
  }
289
- var builtInChannels = Object.freeze({
290
- email: Object.freeze({
291
- resolveRoute: resolveEmailRouteFromNotifiable,
292
- async send(input) {
293
- await requireMailer(getRuntimeBindings()).send(
294
- input.payload,
295
- input
296
- );
297
- }
298
- }),
299
- database: Object.freeze({
300
- resolveRoute: resolveDatabaseRouteFromNotifiable,
301
- async send(input) {
302
- const route = input.route;
303
- if (!route) {
304
- throw new Error("[@holo-js/notifications] Database notifications require a resolved route.");
212
+ function createBuiltInChannels(getBindings) {
213
+ return Object.freeze({
214
+ email: Object.freeze({
215
+ resolveRoute: resolveEmailRouteFromNotifiable,
216
+ async send(input) {
217
+ await requireMailer(getBindings()).send(
218
+ input.payload,
219
+ input
220
+ );
305
221
  }
306
- await requireStore(getRuntimeBindings()).create(
307
- normalizeNotificationRecord(
308
- route,
222
+ }),
223
+ database: Object.freeze({
224
+ resolveRoute: resolveDatabaseRouteFromNotifiable,
225
+ async send(input) {
226
+ const route = input.route;
227
+ if (!route) {
228
+ throw new Error("[@holo-js/notifications] Database notifications require a resolved route.");
229
+ }
230
+ await requireStore(getBindings()).create(
231
+ normalizeNotificationRecord(
232
+ route,
233
+ input.payload,
234
+ input.notificationType
235
+ )
236
+ );
237
+ }
238
+ }),
239
+ broadcast: Object.freeze({
240
+ resolveRoute: resolveBroadcastRouteFromNotifiable,
241
+ async send(input) {
242
+ await requireBroadcaster(getBindings()).send(
309
243
  input.payload,
310
- input.notificationType
311
- )
312
- );
244
+ input
245
+ );
246
+ }
247
+ })
248
+ });
249
+ }
250
+
251
+ // src/runtime.ts
252
+ var HOLO_NOTIFICATIONS_DELIVER_JOB = "holo.notifications.deliver";
253
+ function normalizeOptionalString(value, label) {
254
+ if (typeof value === "undefined") {
255
+ return void 0;
256
+ }
257
+ const normalized = value.trim();
258
+ if (!normalized) {
259
+ throw new Error(`[@holo-js/notifications] ${label} must be a non-empty string.`);
260
+ }
261
+ return normalized;
262
+ }
263
+ function normalizeDelayValue(value, label) {
264
+ if (typeof value === "number") {
265
+ if (!Number.isFinite(value) || value < 0) {
266
+ throw new Error(`[@holo-js/notifications] ${label} must be a finite number greater than or equal to 0.`);
267
+ }
268
+ return value;
269
+ }
270
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
271
+ throw new Error(`[@holo-js/notifications] ${label} dates must be valid Date instances.`);
272
+ }
273
+ return value;
274
+ }
275
+ function normalizeOptionalBoolean(value, label) {
276
+ if (typeof value === "undefined") {
277
+ return void 0;
278
+ }
279
+ if (typeof value !== "boolean") {
280
+ throw new Error(`[@holo-js/notifications] ${label} must be a boolean.`);
281
+ }
282
+ return value;
283
+ }
284
+ function normalizeQueueOptions(value) {
285
+ if (typeof value === "undefined") {
286
+ return void 0;
287
+ }
288
+ return Object.freeze({
289
+ connection: normalizeOptionalString(value.connection, "Notification queue connection"),
290
+ queue: normalizeOptionalString(value.queue, "Notification queue name"),
291
+ ...typeof value.delay === "undefined" ? {} : { delay: normalizeDelayValue(value.delay, "Notification queue delay") },
292
+ ...typeof normalizeOptionalBoolean(value.afterCommit, "Notification queue afterCommit") === "undefined" ? {} : { afterCommit: value.afterCommit }
293
+ });
294
+ }
295
+ function getRuntimeBindings() {
296
+ return getRuntimeState().bindings ?? {};
297
+ }
298
+ function getRuntimeState() {
299
+ const runtime = globalThis;
300
+ runtime.__holoNotificationsRuntime__ ??= {};
301
+ return runtime.__holoNotificationsRuntime__;
302
+ }
303
+ function getDispatchHandler() {
304
+ const bindings = getRuntimeBindings();
305
+ return bindings.dispatch ?? dispatchNotifications;
306
+ }
307
+ function dynamicImport(specifier) {
308
+ return import(specifier);
309
+ }
310
+ async function loadQueueModule() {
311
+ const override = getRuntimeState().loadQueueModule;
312
+ if (override) {
313
+ try {
314
+ return await override();
315
+ } catch (error) {
316
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
317
+ throw new Error("[@holo-js/notifications] Queued or delayed notifications require @holo-js/queue to be installed.");
318
+ }
319
+ throw error;
313
320
  }
314
- }),
315
- broadcast: Object.freeze({
316
- resolveRoute: resolveBroadcastRouteFromNotifiable,
317
- async send(input) {
318
- await requireBroadcaster(getRuntimeBindings()).send(
319
- input.payload,
320
- input
321
- );
321
+ }
322
+ try {
323
+ return await dynamicImport("@holo-js/queue");
324
+ } catch (error) {
325
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
326
+ throw new Error("[@holo-js/notifications] Queued or delayed notifications require @holo-js/queue to be installed.");
322
327
  }
323
- })
324
- });
328
+ throw error;
329
+ }
330
+ }
331
+ async function loadDbModule() {
332
+ const override = getRuntimeState().loadDbModule;
333
+ if (override) {
334
+ try {
335
+ return await override();
336
+ } catch (error) {
337
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
338
+ return null;
339
+ }
340
+ throw error;
341
+ }
342
+ }
343
+ try {
344
+ return await dynamicImport("@holo-js/db");
345
+ } catch (error) {
346
+ if (error && typeof error === "object" && "code" in error && error.code === "ERR_MODULE_NOT_FOUND") {
347
+ return null;
348
+ }
349
+ throw error;
350
+ }
351
+ }
352
+ var builtInChannels = createBuiltInChannels(getRuntimeBindings);
325
353
  function getNotificationChannel(name) {
354
+ const registered = getRegisteredNotificationChannel(name)?.channel;
355
+ if (registered) {
356
+ return registered;
357
+ }
326
358
  if (name in builtInChannels) {
327
359
  return builtInChannels[name];
328
360
  }
329
- return getRegisteredNotificationChannel(name)?.channel;
361
+ return void 0;
330
362
  }
331
363
  function resolveTargets(target) {
332
364
  if (target.kind === "anonymous") {
@@ -383,22 +415,24 @@ function resolvePayload(notification, channel, target) {
383
415
  return factory(target.notifiable, createBuildContext(channel, target.anonymous));
384
416
  }
385
417
  function resolveNotificationQueueOptions(notification, target, channel) {
386
- if (typeof notification.queue === "function") {
387
- return notification.queue(
388
- target.notifiable,
389
- channel,
390
- createNotificationContext(target.anonymous)
391
- );
418
+ const queue = typeof notification.queue === "function" ? notification.queue(
419
+ target.notifiable,
420
+ channel,
421
+ createNotificationContext(target.anonymous)
422
+ ) : notification.queue ?? false;
423
+ if (typeof queue === "boolean") {
424
+ return queue;
392
425
  }
393
- return notification.queue ?? false;
426
+ return normalizeQueueOptions(queue) ?? false;
394
427
  }
395
428
  function resolveNotificationDelay(notification, target, channel) {
396
429
  if (typeof notification.delay === "function") {
397
- return notification.delay(
430
+ const delay = notification.delay(
398
431
  target.notifiable,
399
432
  channel,
400
433
  createNotificationContext(target.anonymous)
401
434
  );
435
+ return typeof delay === "undefined" ? void 0 : normalizeDelayValue(delay, "Notification delay");
402
436
  }
403
437
  if (typeof notification.delay === "undefined") {
404
438
  return void 0;
@@ -414,6 +448,10 @@ function resolveRoute(channel, target) {
414
448
  throw new Error(`[@holo-js/notifications] Anonymous notifications must define a route for channel "${channel}".`);
415
449
  }
416
450
  const route = target.routes?.[channel];
451
+ const registered2 = getRegisteredNotificationChannel(channel)?.channel;
452
+ if (registered2) {
453
+ return typeof registered2.validateRoute === "function" ? registered2.validateRoute(route) : route;
454
+ }
417
455
  if (channel === "email") {
418
456
  return normalizeEmailRouteFromValue(route);
419
457
  }
@@ -461,10 +499,10 @@ function resolveChannelDispatchPlan(notification, target, channel, options) {
461
499
  const notificationQueueOptions = notificationQueue && notificationQueue !== true ? notificationQueue : void 0;
462
500
  const config = getRuntimeBindings().config ?? holoNotificationsDefaults;
463
501
  const resolvedDelay = options.delayByChannel?.[channel] ?? options.delay ?? resolveNotificationDelay(notification, target, channel);
464
- const resolvedConnection = options.connection ?? notificationQueueOptions?.connection ?? config.queue.connection;
465
- const resolvedQueue = options.queue ?? notificationQueueOptions?.queue ?? config.queue.queue;
466
- const afterCommit = options.afterCommit ?? notificationQueueOptions?.afterCommit ?? config.queue.afterCommit;
467
- const queued = notificationQueue === true || !!notificationQueueOptions || typeof resolvedDelay !== "undefined" || typeof resolvedConnection !== "undefined" || typeof resolvedQueue !== "undefined";
502
+ const queued = notificationQueue === true || !!notificationQueueOptions || typeof options.connection !== "undefined" || typeof options.queue !== "undefined" || typeof resolvedDelay !== "undefined";
503
+ const resolvedConnection = queued ? options.connection ?? notificationQueueOptions?.connection ?? config.queue.connection : void 0;
504
+ const resolvedQueue = queued ? options.queue ?? notificationQueueOptions?.queue ?? config.queue.queue : void 0;
505
+ const afterCommit = options.afterCommit ?? notificationQueueOptions?.afterCommit ?? (queued ? config.queue.afterCommit : false);
468
506
  return Object.freeze({
469
507
  channel,
470
508
  queued,
@@ -573,7 +611,11 @@ function shouldDeferDispatchAfterCommit(notification, targets, options) {
573
611
  return true;
574
612
  }
575
613
  return targets.some((target) => resolveChannels(notification, target).some((channel) => {
576
- return resolveChannelDispatchPlan(notification, target, channel, options).afterCommit;
614
+ try {
615
+ return resolveChannelDispatchPlan(notification, target, channel, options).afterCommit;
616
+ } catch {
617
+ return false;
618
+ }
577
619
  }));
578
620
  }
579
621
  async function dispatchNotifications(input, execution = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holo-js/notifications",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Holo-JS Framework - notification contracts, channels, and fluent dispatch helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,12 +28,16 @@
28
28
  "test": "vitest --run"
29
29
  },
30
30
  "dependencies": {
31
- "@holo-js/config": "^0.1.4"
31
+ "@holo-js/config": "^0.1.6"
32
32
  },
33
33
  "peerDependencies": {
34
- "@holo-js/queue": "^0.1.4"
34
+ "@holo-js/db": "^0.1.6",
35
+ "@holo-js/queue": "^0.1.6"
35
36
  },
36
37
  "peerDependenciesMeta": {
38
+ "@holo-js/db": {
39
+ "optional": true
40
+ },
37
41
  "@holo-js/queue": {
38
42
  "optional": true
39
43
  }
@@ -42,6 +46,6 @@
42
46
  "@types/node": "^22.10.2",
43
47
  "tsup": "^8.3.5",
44
48
  "typescript": "^5.7.2",
45
- "vitest": "^2.1.8"
49
+ "vitest": "^4.1.5"
46
50
  }
47
51
  }