@house-rules/notifications 0.0.0-stage → 0.1.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.
@@ -0,0 +1,452 @@
1
+ import { Effect, Layer, Schema } from "effect";
2
+ import { NotificationInputInvalid } from "./notification/notification.js";
3
+ import { NotificationsOptions } from "./notification/options.js";
4
+ import { NotificationStore } from "./storage.js";
5
+ declare const Notifications_base: import("effect/Context").ServiceClass<Notifications, "notifications", {
6
+ notify: (input: {
7
+ readonly recipients: readonly {
8
+ readonly kind: string;
9
+ readonly id: string;
10
+ }[];
11
+ readonly category: string;
12
+ readonly title: string;
13
+ readonly body: string;
14
+ readonly data: Schema.Json;
15
+ }) => Effect.Effect<{
16
+ notifications: {
17
+ readonly id: string;
18
+ readonly recipient: {
19
+ readonly kind: string;
20
+ readonly id: string;
21
+ };
22
+ readonly category: string;
23
+ readonly title: string;
24
+ readonly body: string;
25
+ readonly data: Schema.Json;
26
+ readonly createdAt: number;
27
+ readonly readAt: number | null;
28
+ }[];
29
+ events: {
30
+ kind: "NotificationCreated";
31
+ notificationId: string;
32
+ recipient: import("./notification/notification.js").Recipient;
33
+ channels: import("./notification/notification.js").Channel[];
34
+ }[];
35
+ }, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
36
+ listMine: (actor: {
37
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
38
+ }, page?: {
39
+ readonly size?: number;
40
+ readonly cursor?: {
41
+ readonly createdAt: number;
42
+ readonly id: string;
43
+ };
44
+ } | undefined) => Effect.Effect<{
45
+ notifications: {
46
+ readonly id: string;
47
+ readonly recipient: {
48
+ readonly kind: string;
49
+ readonly id: string;
50
+ };
51
+ readonly category: string;
52
+ readonly title: string;
53
+ readonly body: string;
54
+ readonly data: Schema.Json;
55
+ readonly createdAt: number;
56
+ readonly readAt: number | null;
57
+ }[];
58
+ next: {
59
+ createdAt: number;
60
+ id: string;
61
+ };
62
+ } | {
63
+ next?: never;
64
+ notifications: {
65
+ readonly id: string;
66
+ readonly recipient: {
67
+ readonly kind: string;
68
+ readonly id: string;
69
+ };
70
+ readonly category: string;
71
+ readonly title: string;
72
+ readonly body: string;
73
+ readonly data: Schema.Json;
74
+ readonly createdAt: number;
75
+ readonly readAt: number | null;
76
+ }[];
77
+ }, NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
78
+ unreadCount: (actor: import("@house-rules/domain").Actor) => Effect.Effect<number, import("./notification/notification.js").NotificationStoreUnavailable, never>;
79
+ markRead: (actor: {
80
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
81
+ }, notificationId: string) => Effect.Effect<{
82
+ readonly notifications: readonly {
83
+ readonly id: string;
84
+ readonly recipient: {
85
+ readonly kind: string;
86
+ readonly id: string;
87
+ };
88
+ readonly category: string;
89
+ readonly title: string;
90
+ readonly body: string;
91
+ readonly data: Schema.Json;
92
+ readonly createdAt: number;
93
+ readonly readAt: number | null;
94
+ }[];
95
+ readonly events: readonly {
96
+ readonly kind: "NotificationRead";
97
+ readonly notificationId: string;
98
+ readonly recipient: {
99
+ readonly kind: string;
100
+ readonly id: string;
101
+ };
102
+ }[];
103
+ }, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
104
+ markAllRead: (actor: {
105
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
106
+ }) => Effect.Effect<{
107
+ readonly notifications: readonly {
108
+ readonly id: string;
109
+ readonly recipient: {
110
+ readonly kind: string;
111
+ readonly id: string;
112
+ };
113
+ readonly category: string;
114
+ readonly title: string;
115
+ readonly body: string;
116
+ readonly data: Schema.Json;
117
+ readonly createdAt: number;
118
+ readonly readAt: number | null;
119
+ }[];
120
+ readonly events: readonly {
121
+ readonly kind: "NotificationRead";
122
+ readonly notificationId: string;
123
+ readonly recipient: {
124
+ readonly kind: string;
125
+ readonly id: string;
126
+ };
127
+ }[];
128
+ }, import("@house-rules/domain").NoOpenUnit | import("./notification/notification.js").NotificationStoreUnavailable, never>;
129
+ getMyPreferences: (actor: {
130
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
131
+ }) => Effect.Effect<{
132
+ preferences: {
133
+ category: string;
134
+ channel: "email" | "inapp" | "push";
135
+ mode: "daily" | "hourly" | "immediate" | "off";
136
+ }[];
137
+ quietHours: {
138
+ readonly start: string;
139
+ readonly end: string;
140
+ } | null;
141
+ timeZone: string | null;
142
+ }, import("./notification/notification.js").NotificationStoreUnavailable, never>;
143
+ setMyPreference: (actor: {
144
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
145
+ }, preference: {
146
+ readonly category: string;
147
+ readonly channel: "email" | "inapp" | "push";
148
+ readonly mode: "daily" | "hourly" | "immediate" | "off";
149
+ }) => Effect.Effect<undefined, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
150
+ setMyQuietHours: (actor: {
151
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
152
+ }, hours: {
153
+ readonly start: string;
154
+ readonly end: string;
155
+ } | null) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
156
+ setMyTimeZone: (actor: {
157
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
158
+ }, timeZone: string | null) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
159
+ dueDeliveries: () => Effect.Effect<{
160
+ readonly recipient: {
161
+ readonly kind: string;
162
+ readonly id: string;
163
+ };
164
+ readonly channel: "email" | "inapp" | "push";
165
+ readonly mode: "daily" | "hourly" | "immediate";
166
+ readonly deliveries: readonly {
167
+ readonly delivery: {
168
+ readonly id: string;
169
+ readonly notificationId: string;
170
+ readonly recipient: {
171
+ readonly kind: string;
172
+ readonly id: string;
173
+ };
174
+ readonly channel: "email" | "inapp" | "push";
175
+ readonly mode: "daily" | "hourly" | "immediate";
176
+ readonly state: "cancelled" | "failed" | "held" | "pending" | "sent";
177
+ readonly dueAt: number;
178
+ readonly retryAt: number | null;
179
+ readonly sentAt: number | null;
180
+ readonly attempts: number;
181
+ readonly lastError: string | null;
182
+ };
183
+ readonly notification: {
184
+ readonly id: string;
185
+ readonly recipient: {
186
+ readonly kind: string;
187
+ readonly id: string;
188
+ };
189
+ readonly category: string;
190
+ readonly title: string;
191
+ readonly body: string;
192
+ readonly data: Schema.Json;
193
+ readonly createdAt: number;
194
+ readonly readAt: number | null;
195
+ };
196
+ }[];
197
+ }[], import("./notification/notification.js").NotificationStoreUnavailable, never>;
198
+ markSent: (deliveryIds: readonly string[]) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
199
+ markFailed: (deliveryId: string, error: string) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
200
+ erase: (recipient: {
201
+ readonly kind: string;
202
+ readonly id: string;
203
+ }) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
204
+ }> & {
205
+ readonly id: "notifications";
206
+ } & {
207
+ readonly make: (options: {
208
+ readonly recipientKind: string;
209
+ readonly channels: readonly ("email" | "inapp" | "push")[];
210
+ readonly categories: readonly {
211
+ readonly name: string;
212
+ readonly defaultChannels: readonly ("email" | "inapp" | "push")[];
213
+ readonly mandatory: boolean;
214
+ }[];
215
+ readonly dailyDigestTime?: string;
216
+ readonly maxPageSize: number;
217
+ readonly maxDueBatch?: number;
218
+ readonly maxAttempts?: number;
219
+ readonly retryBaseMs?: number;
220
+ readonly retryCapMs?: number;
221
+ }) => Effect.Effect<{
222
+ notify: (input: {
223
+ readonly recipients: readonly {
224
+ readonly kind: string;
225
+ readonly id: string;
226
+ }[];
227
+ readonly category: string;
228
+ readonly title: string;
229
+ readonly body: string;
230
+ readonly data: Schema.Json;
231
+ }) => Effect.Effect<{
232
+ notifications: {
233
+ readonly id: string;
234
+ readonly recipient: {
235
+ readonly kind: string;
236
+ readonly id: string;
237
+ };
238
+ readonly category: string;
239
+ readonly title: string;
240
+ readonly body: string;
241
+ readonly data: Schema.Json;
242
+ readonly createdAt: number;
243
+ readonly readAt: number | null;
244
+ }[];
245
+ events: {
246
+ kind: "NotificationCreated";
247
+ notificationId: string;
248
+ recipient: import("./notification/notification.js").Recipient;
249
+ channels: import("./notification/notification.js").Channel[];
250
+ }[];
251
+ }, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
252
+ listMine: (actor: {
253
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
254
+ }, page?: {
255
+ readonly size?: number;
256
+ readonly cursor?: {
257
+ readonly createdAt: number;
258
+ readonly id: string;
259
+ };
260
+ } | undefined) => Effect.Effect<{
261
+ notifications: {
262
+ readonly id: string;
263
+ readonly recipient: {
264
+ readonly kind: string;
265
+ readonly id: string;
266
+ };
267
+ readonly category: string;
268
+ readonly title: string;
269
+ readonly body: string;
270
+ readonly data: Schema.Json;
271
+ readonly createdAt: number;
272
+ readonly readAt: number | null;
273
+ }[];
274
+ next: {
275
+ createdAt: number;
276
+ id: string;
277
+ };
278
+ } | {
279
+ next?: never;
280
+ notifications: {
281
+ readonly id: string;
282
+ readonly recipient: {
283
+ readonly kind: string;
284
+ readonly id: string;
285
+ };
286
+ readonly category: string;
287
+ readonly title: string;
288
+ readonly body: string;
289
+ readonly data: Schema.Json;
290
+ readonly createdAt: number;
291
+ readonly readAt: number | null;
292
+ }[];
293
+ }, NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
294
+ unreadCount: (actor: import("@house-rules/domain").Actor) => Effect.Effect<number, import("./notification/notification.js").NotificationStoreUnavailable, never>;
295
+ markRead: (actor: {
296
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
297
+ }, notificationId: string) => Effect.Effect<{
298
+ readonly notifications: readonly {
299
+ readonly id: string;
300
+ readonly recipient: {
301
+ readonly kind: string;
302
+ readonly id: string;
303
+ };
304
+ readonly category: string;
305
+ readonly title: string;
306
+ readonly body: string;
307
+ readonly data: Schema.Json;
308
+ readonly createdAt: number;
309
+ readonly readAt: number | null;
310
+ }[];
311
+ readonly events: readonly {
312
+ readonly kind: "NotificationRead";
313
+ readonly notificationId: string;
314
+ readonly recipient: {
315
+ readonly kind: string;
316
+ readonly id: string;
317
+ };
318
+ }[];
319
+ }, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
320
+ markAllRead: (actor: {
321
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
322
+ }) => Effect.Effect<{
323
+ readonly notifications: readonly {
324
+ readonly id: string;
325
+ readonly recipient: {
326
+ readonly kind: string;
327
+ readonly id: string;
328
+ };
329
+ readonly category: string;
330
+ readonly title: string;
331
+ readonly body: string;
332
+ readonly data: Schema.Json;
333
+ readonly createdAt: number;
334
+ readonly readAt: number | null;
335
+ }[];
336
+ readonly events: readonly {
337
+ readonly kind: "NotificationRead";
338
+ readonly notificationId: string;
339
+ readonly recipient: {
340
+ readonly kind: string;
341
+ readonly id: string;
342
+ };
343
+ }[];
344
+ }, import("@house-rules/domain").NoOpenUnit | import("./notification/notification.js").NotificationStoreUnavailable, never>;
345
+ getMyPreferences: (actor: {
346
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
347
+ }) => Effect.Effect<{
348
+ preferences: {
349
+ category: string;
350
+ channel: "email" | "inapp" | "push";
351
+ mode: "daily" | "hourly" | "immediate" | "off";
352
+ }[];
353
+ quietHours: {
354
+ readonly start: string;
355
+ readonly end: string;
356
+ } | null;
357
+ timeZone: string | null;
358
+ }, import("./notification/notification.js").NotificationStoreUnavailable, never>;
359
+ setMyPreference: (actor: {
360
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
361
+ }, preference: {
362
+ readonly category: string;
363
+ readonly channel: "email" | "inapp" | "push";
364
+ readonly mode: "daily" | "hourly" | "immediate" | "off";
365
+ }) => Effect.Effect<undefined, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
366
+ setMyQuietHours: (actor: {
367
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
368
+ }, hours: {
369
+ readonly start: string;
370
+ readonly end: string;
371
+ } | null) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
372
+ setMyTimeZone: (actor: {
373
+ readonly id: string & import("effect/Brand").Brand<"ActorId">;
374
+ }, timeZone: string | null) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
375
+ dueDeliveries: () => Effect.Effect<{
376
+ readonly recipient: {
377
+ readonly kind: string;
378
+ readonly id: string;
379
+ };
380
+ readonly channel: "email" | "inapp" | "push";
381
+ readonly mode: "daily" | "hourly" | "immediate";
382
+ readonly deliveries: readonly {
383
+ readonly delivery: {
384
+ readonly id: string;
385
+ readonly notificationId: string;
386
+ readonly recipient: {
387
+ readonly kind: string;
388
+ readonly id: string;
389
+ };
390
+ readonly channel: "email" | "inapp" | "push";
391
+ readonly mode: "daily" | "hourly" | "immediate";
392
+ readonly state: "cancelled" | "failed" | "held" | "pending" | "sent";
393
+ readonly dueAt: number;
394
+ readonly retryAt: number | null;
395
+ readonly sentAt: number | null;
396
+ readonly attempts: number;
397
+ readonly lastError: string | null;
398
+ };
399
+ readonly notification: {
400
+ readonly id: string;
401
+ readonly recipient: {
402
+ readonly kind: string;
403
+ readonly id: string;
404
+ };
405
+ readonly category: string;
406
+ readonly title: string;
407
+ readonly body: string;
408
+ readonly data: Schema.Json;
409
+ readonly createdAt: number;
410
+ readonly readAt: number | null;
411
+ };
412
+ }[];
413
+ }[], import("./notification/notification.js").NotificationStoreUnavailable, never>;
414
+ markSent: (deliveryIds: readonly string[]) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
415
+ markFailed: (deliveryId: string, error: string) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
416
+ erase: (recipient: {
417
+ readonly kind: string;
418
+ readonly id: string;
419
+ }) => Effect.Effect<void, import("@house-rules/domain").NoOpenUnit | NotificationInputInvalid | import("./notification/notification.js").NotificationStoreUnavailable, never>;
420
+ }, NotificationInputInvalid, NotificationStore>;
421
+ readonly layer: (options: {
422
+ readonly recipientKind: string;
423
+ readonly channels: readonly ("email" | "inapp" | "push")[];
424
+ readonly categories: readonly {
425
+ readonly name: string;
426
+ readonly defaultChannels: readonly ("email" | "inapp" | "push")[];
427
+ readonly mandatory: boolean;
428
+ }[];
429
+ readonly dailyDigestTime?: string;
430
+ readonly maxPageSize: number;
431
+ readonly maxDueBatch?: number;
432
+ readonly maxAttempts?: number;
433
+ readonly retryBaseMs?: number;
434
+ readonly retryCapMs?: number;
435
+ }) => import("@house-rules/domain").ModuleLayer<Notifications, NotificationInputInvalid, NotificationStore>;
436
+ };
437
+ export declare class Notifications extends Notifications_base {
438
+ static readonly presets: {
439
+ readonly default: {
440
+ readonly channels: readonly ["inapp", "push", "email"];
441
+ readonly dailyDigestTime: "08:00";
442
+ readonly maxPageSize: 100;
443
+ readonly maxDueBatch: 100;
444
+ readonly maxAttempts: 5;
445
+ readonly retryBaseMs: 1000;
446
+ readonly retryCapMs: 3600000;
447
+ };
448
+ };
449
+ static readonly layerMemory: (options: NotificationsOptions) => Layer.Layer<Notifications, NotificationInputInvalid, never>;
450
+ static readonly layerPostgres: (options: NotificationsOptions) => Layer.Layer<Notifications, NotificationInputInvalid, import("effect/sql/SqlClient").SqlClient>;
451
+ }
452
+ export {};
package/dist/facade.js ADDED
@@ -0,0 +1,28 @@
1
+ import { defineModule } from "@house-rules/domain";
2
+ import { Effect, Layer, Schema } from "effect";
3
+ import { memoryNotificationStore } from "./adapters/memory/notification.js";
4
+ import { postgresNotificationStore } from "./adapters/postgres/notification.js";
5
+ import { NotificationInputInvalid } from "./notification/notification.js";
6
+ import { makeNotifications } from "./notification/notifications.js";
7
+ import { NotificationsOptions } from "./notification/options.js";
8
+ import { NotificationStore } from "./storage.js";
9
+ export class Notifications extends defineModule()("notifications", {
10
+ make: (options) => Effect.gen(function* () {
11
+ const settings = yield* Schema.decodeEffect(NotificationsOptions)(options).pipe(Effect.mapError(() => new NotificationInputInvalid({ message: "Invalid notifications options." })));
12
+ return makeNotifications(yield* NotificationStore, settings);
13
+ }),
14
+ }) {
15
+ static presets = {
16
+ default: {
17
+ channels: ["inapp", "push", "email"],
18
+ dailyDigestTime: "08:00",
19
+ maxPageSize: 100,
20
+ maxDueBatch: 100,
21
+ maxAttempts: 5,
22
+ retryBaseMs: 1000,
23
+ retryCapMs: 3_600_000,
24
+ },
25
+ };
26
+ static layerMemory = (options) => this.layer(options).pipe(Layer.provide(memoryNotificationStore()));
27
+ static layerPostgres = (options) => this.layer(options).pipe(Layer.provide(postgresNotificationStore));
28
+ }