@holo-js/notifications 0.1.4 → 0.1.5
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/contracts.d.ts +6 -3
- package/dist/index.d.ts +27 -24
- package/dist/index.mjs +167 -125
- package/package.json +8 -4
package/dist/contracts.d.ts
CHANGED
|
@@ -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:
|
|
194
|
-
declare function normalizeNotificationDefinition<TNotifiable, TBuild extends NotificationBuildFactories<TNotifiable>>(definition:
|
|
195
|
-
declare function defineNotification<TNotifiable, TBuild extends NotificationBuildFactories<TNotifiable>>(definition:
|
|
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 {
|
|
2
|
-
export { BuiltInNotificationChannelRegistry, HoloNotificationChannelRegistry, InferNotificationChannels, NotificationBroadcastMessage, NotificationBroadcaster, NotificationBuildFactories, NotificationChannelDispatchResult, NotificationContext, NotificationDelayResolver, NotificationJsonValue, NotificationMailMessage, NotificationMailSender, NotificationPayloadFor,
|
|
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
|
-
|
|
7
|
-
|
|
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 |
|
|
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
|
-
|
|
152
|
-
declare function
|
|
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
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
|
426
|
+
return normalizeQueueOptions(queue) ?? false;
|
|
394
427
|
}
|
|
395
428
|
function resolveNotificationDelay(notification, target, channel) {
|
|
396
429
|
if (typeof notification.delay === "function") {
|
|
397
|
-
|
|
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
|
|
465
|
-
const
|
|
466
|
-
const
|
|
467
|
-
const
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.1.5",
|
|
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.
|
|
31
|
+
"@holo-js/config": "^0.1.5"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@holo-js/
|
|
34
|
+
"@holo-js/db": "^0.1.5",
|
|
35
|
+
"@holo-js/queue": "^0.1.5"
|
|
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": "^
|
|
49
|
+
"vitest": "^4.1.5"
|
|
46
50
|
}
|
|
47
51
|
}
|