@house-rules/notifications 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,188 @@
1
+ import { Notifications } from "./facade.js";
2
+ import { bindNotifications } from "./notification/bind-notifications.js";
3
+ export declare const NotificationsMount: {
4
+ map: {
5
+ name: string;
6
+ kind: "package";
7
+ package: string;
8
+ };
9
+ service: typeof Notifications;
10
+ operations: (import("@house-rules/domain").Operation<"Notifications.dueDeliveries", import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").Never>, import("effect/Schema").$Array<import("effect/Schema").Struct<{
11
+ readonly recipient: import("effect/Schema").Struct<{
12
+ readonly kind: import("effect/Schema").NonEmptyString;
13
+ readonly id: import("effect/Schema").NonEmptyString;
14
+ }>;
15
+ readonly channel: import("effect/Schema").Literals<readonly ["inapp", "push", "email"]>;
16
+ readonly mode: import("effect/Schema").Literals<readonly ["immediate", "hourly", "daily"]>;
17
+ readonly deliveries: import("effect/Schema").$Array<import("effect/Schema").Struct<{
18
+ readonly delivery: import("effect/Schema").Struct<{
19
+ readonly id: import("effect/Schema").NonEmptyString;
20
+ readonly notificationId: import("effect/Schema").NonEmptyString;
21
+ readonly recipient: import("effect/Schema").Struct<{
22
+ readonly kind: import("effect/Schema").NonEmptyString;
23
+ readonly id: import("effect/Schema").NonEmptyString;
24
+ }>;
25
+ readonly channel: import("effect/Schema").Literals<readonly ["inapp", "push", "email"]>;
26
+ readonly mode: import("effect/Schema").Literals<readonly ["immediate", "hourly", "daily"]>;
27
+ readonly state: import("effect/Schema").Literals<readonly ["pending", "held", "sent", "failed", "cancelled"]>;
28
+ readonly dueAt: import("effect/Schema").Int;
29
+ readonly retryAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
30
+ readonly sentAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
31
+ readonly attempts: import("effect/Schema").Int;
32
+ readonly lastError: import("effect/Schema").NullOr<import("effect/Schema").String>;
33
+ }>;
34
+ readonly notification: import("effect/Schema").Struct<{
35
+ readonly id: import("effect/Schema").NonEmptyString;
36
+ readonly recipient: import("effect/Schema").Struct<{
37
+ readonly kind: import("effect/Schema").NonEmptyString;
38
+ readonly id: import("effect/Schema").NonEmptyString;
39
+ }>;
40
+ readonly category: import("effect/Schema").NonEmptyString;
41
+ readonly title: import("effect/Schema").NonEmptyString;
42
+ readonly body: import("effect/Schema").String;
43
+ readonly data: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
44
+ readonly createdAt: import("effect/Schema").Int;
45
+ readonly readAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
46
+ }>;
47
+ }>>;
48
+ }>>, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, true, false, false, false> | import("@house-rules/domain").Operation<"Notifications.getMyPreferences", import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").Never>, import("effect/Schema").Struct<{
49
+ readonly preferences: import("effect/Schema").$Array<import("effect/Schema").Struct<{
50
+ readonly category: import("effect/Schema").NonEmptyString;
51
+ readonly channel: import("effect/Schema").Literals<readonly ["inapp", "push", "email"]>;
52
+ readonly mode: import("effect/Schema").Literals<readonly ["immediate", "hourly", "daily", "off"]>;
53
+ }>>;
54
+ readonly quietHours: import("effect/Schema").NullOr<import("effect/Schema").Struct<{
55
+ readonly start: import("effect/Schema").String;
56
+ readonly end: import("effect/Schema").String;
57
+ }>>;
58
+ readonly timeZone: import("effect/Schema").NullOr<import("effect/Schema").NonEmptyString>;
59
+ }>, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, true, false, false, false> | import("@house-rules/domain").Operation<"Notifications.listMine", import("effect/Schema").Struct<{
60
+ readonly page: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
61
+ readonly size: import("effect/Schema").optionalKey<import("effect/Schema").Int>;
62
+ readonly cursor: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
63
+ readonly createdAt: import("effect/Schema").Int;
64
+ readonly id: import("effect/Schema").NonEmptyString;
65
+ }>>;
66
+ }>>;
67
+ }>, import("effect/Schema").Struct<{
68
+ readonly notifications: import("effect/Schema").$Array<import("effect/Schema").Struct<{
69
+ readonly id: import("effect/Schema").NonEmptyString;
70
+ readonly recipient: import("effect/Schema").Struct<{
71
+ readonly kind: import("effect/Schema").NonEmptyString;
72
+ readonly id: import("effect/Schema").NonEmptyString;
73
+ }>;
74
+ readonly category: import("effect/Schema").NonEmptyString;
75
+ readonly title: import("effect/Schema").NonEmptyString;
76
+ readonly body: import("effect/Schema").String;
77
+ readonly data: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
78
+ readonly createdAt: import("effect/Schema").Int;
79
+ readonly readAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
80
+ }>>;
81
+ readonly next: import("effect/Schema").optionalKey<import("effect/Schema").Struct<{
82
+ readonly createdAt: import("effect/Schema").Int;
83
+ readonly id: import("effect/Schema").NonEmptyString;
84
+ }>>;
85
+ }>, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, true, false, false, false> | import("@house-rules/domain").Operation<"Notifications.markAllRead", import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").Never>, import("effect/Schema").Struct<{
86
+ readonly notifications: import("effect/Schema").$Array<import("effect/Schema").Struct<{
87
+ readonly id: import("effect/Schema").NonEmptyString;
88
+ readonly recipient: import("effect/Schema").Struct<{
89
+ readonly kind: import("effect/Schema").NonEmptyString;
90
+ readonly id: import("effect/Schema").NonEmptyString;
91
+ }>;
92
+ readonly category: import("effect/Schema").NonEmptyString;
93
+ readonly title: import("effect/Schema").NonEmptyString;
94
+ readonly body: import("effect/Schema").String;
95
+ readonly data: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
96
+ readonly createdAt: import("effect/Schema").Int;
97
+ readonly readAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
98
+ }>>;
99
+ readonly events: import("effect/Schema").$Array<import("effect/Schema").Struct<{
100
+ readonly kind: import("effect/Schema").Literal<"NotificationRead">;
101
+ readonly notificationId: import("effect/Schema").NonEmptyString;
102
+ readonly recipient: import("effect/Schema").Struct<{
103
+ readonly kind: import("effect/Schema").NonEmptyString;
104
+ readonly id: import("effect/Schema").NonEmptyString;
105
+ }>;
106
+ }>>;
107
+ }>, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.markFailed", import("effect/Schema").Struct<{
108
+ readonly deliveryId: import("effect/Schema").NonEmptyString;
109
+ readonly error: import("effect/Schema").String;
110
+ }>, import("effect/Schema").Void, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.markRead", import("effect/Schema").Struct<{
111
+ readonly notificationId: import("effect/Schema").NonEmptyString;
112
+ }>, import("effect/Schema").Struct<{
113
+ readonly notifications: import("effect/Schema").$Array<import("effect/Schema").Struct<{
114
+ readonly id: import("effect/Schema").NonEmptyString;
115
+ readonly recipient: import("effect/Schema").Struct<{
116
+ readonly kind: import("effect/Schema").NonEmptyString;
117
+ readonly id: import("effect/Schema").NonEmptyString;
118
+ }>;
119
+ readonly category: import("effect/Schema").NonEmptyString;
120
+ readonly title: import("effect/Schema").NonEmptyString;
121
+ readonly body: import("effect/Schema").String;
122
+ readonly data: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
123
+ readonly createdAt: import("effect/Schema").Int;
124
+ readonly readAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
125
+ }>>;
126
+ readonly events: import("effect/Schema").$Array<import("effect/Schema").Struct<{
127
+ readonly kind: import("effect/Schema").Literal<"NotificationRead">;
128
+ readonly notificationId: import("effect/Schema").NonEmptyString;
129
+ readonly recipient: import("effect/Schema").Struct<{
130
+ readonly kind: import("effect/Schema").NonEmptyString;
131
+ readonly id: import("effect/Schema").NonEmptyString;
132
+ }>;
133
+ }>>;
134
+ }>, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.markSent", import("effect/Schema").Struct<{
135
+ readonly deliveryIds: import("effect/Schema").$Array<import("effect/Schema").NonEmptyString>;
136
+ }>, import("effect/Schema").Void, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.notify", import("effect/Schema").Struct<{
137
+ readonly recipients: import("effect/Schema").$Array<import("effect/Schema").Struct<{
138
+ readonly kind: import("effect/Schema").NonEmptyString;
139
+ readonly id: import("effect/Schema").NonEmptyString;
140
+ }>>;
141
+ readonly category: import("effect/Schema").NonEmptyString;
142
+ readonly title: import("effect/Schema").NonEmptyString;
143
+ readonly body: import("effect/Schema").String;
144
+ readonly data: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
145
+ }>, import("effect/Schema").Struct<{
146
+ readonly notifications: import("effect/Schema").$Array<import("effect/Schema").Struct<{
147
+ readonly id: import("effect/Schema").NonEmptyString;
148
+ readonly recipient: import("effect/Schema").Struct<{
149
+ readonly kind: import("effect/Schema").NonEmptyString;
150
+ readonly id: import("effect/Schema").NonEmptyString;
151
+ }>;
152
+ readonly category: import("effect/Schema").NonEmptyString;
153
+ readonly title: import("effect/Schema").NonEmptyString;
154
+ readonly body: import("effect/Schema").String;
155
+ readonly data: import("effect/Schema").Codec<import("effect/Schema").Json, import("effect/Schema").Json, never, never>;
156
+ readonly createdAt: import("effect/Schema").Int;
157
+ readonly readAt: import("effect/Schema").NullOr<import("effect/Schema").Int>;
158
+ }>>;
159
+ readonly events: import("effect/Schema").$Array<import("effect/Schema").Struct<{
160
+ readonly kind: import("effect/Schema").Literal<"NotificationCreated">;
161
+ readonly notificationId: import("effect/Schema").NonEmptyString;
162
+ readonly recipient: import("effect/Schema").Struct<{
163
+ readonly kind: import("effect/Schema").NonEmptyString;
164
+ readonly id: import("effect/Schema").NonEmptyString;
165
+ }>;
166
+ readonly channels: import("effect/Schema").$Array<import("effect/Schema").Literals<readonly ["inapp", "push", "email"]>>;
167
+ }>>;
168
+ }>, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.setMyPreference", import("effect/Schema").Struct<{
169
+ readonly category: import("effect/Schema").NonEmptyString;
170
+ readonly channel: import("effect/Schema").Literals<readonly ["inapp", "push", "email"]>;
171
+ readonly mode: import("effect/Schema").Literals<readonly ["immediate", "hourly", "daily", "off"]>;
172
+ }>, import("effect/Schema").Void, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.setMyQuietHours", import("effect/Schema").Struct<{
173
+ readonly quietHours: import("effect/Schema").NullOr<import("effect/Schema").Struct<{
174
+ readonly start: import("effect/Schema").String;
175
+ readonly end: import("effect/Schema").String;
176
+ }>>;
177
+ }>, import("effect/Schema").Void, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.setMyTimeZone", import("effect/Schema").Struct<{
178
+ readonly timeZone: import("effect/Schema").NullOr<import("effect/Schema").NonEmptyString>;
179
+ }>, import("effect/Schema").Void, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, false, false, true, false> | import("@house-rules/domain").Operation<"Notifications.unreadCount", import("effect/Schema").$Record<import("effect/Schema").String, import("effect/Schema").Never>, import("effect/Schema").Int, import("effect/Schema").Union<readonly [typeof import("./contract.js").NotificationForbidden, typeof import("./contract.js").NotificationInputInvalid, typeof import("./contract.js").NotificationStoreUnavailable]>, NoInfer<readonly []>, true, false, false, false>)[];
180
+ bind: typeof bindNotifications;
181
+ };
182
+ export { Notifications } from "./facade.js";
183
+ export { bindNotifications } from "./notification/bind-notifications.js";
184
+ export { NotificationCategory, NotificationsOptions } from "./notification/options.js";
185
+ export { NotificationStore } from "./storage.js";
186
+ export { memoryNotificationStore } from "./adapters/memory/notification.js";
187
+ export { postgresNotificationStore } from "./adapters/postgres/notification.js";
188
+ export { Channel, Delivery, DeliveryItem, DeliveryMode, DueDelivery, LocalTime, MyPreferences, Notification, NotificationCreated, NotificationCursor, NotificationEvents, NotificationForbidden, NotificationInputInvalid, NotificationList, NotificationNotFound, NotificationPage, type NotificationPermission, NotificationPermissions, NotificationRead, NotificationStoreUnavailable, NotificationsOperations, NotifyInput, NotifyResult, Preference, QuietHours, ReadResult, Recipient, TimeZone, } from "./contract.js";
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ import { Notifications } from "./facade.js";
2
+ import { NotificationsOperations } from "./contract.js";
3
+ import { bindNotifications } from "./notification/bind-notifications.js";
4
+ export const NotificationsMount = {
5
+ map: { name: "notifications", kind: "package", package: "@house-rules/notifications" },
6
+ service: Notifications,
7
+ operations: Object.values(NotificationsOperations),
8
+ bind: bindNotifications,
9
+ };
10
+ export { Notifications } from "./facade.js";
11
+ export { bindNotifications } from "./notification/bind-notifications.js";
12
+ export { NotificationCategory, NotificationsOptions } from "./notification/options.js";
13
+ export { NotificationStore } from "./storage.js";
14
+ export { memoryNotificationStore } from "./adapters/memory/notification.js";
15
+ export { postgresNotificationStore } from "./adapters/postgres/notification.js";
16
+ export { Channel, Delivery, DeliveryItem, DeliveryMode, DueDelivery, LocalTime, MyPreferences, Notification, NotificationCreated, NotificationCursor, NotificationEvents, NotificationForbidden, NotificationInputInvalid, NotificationList, NotificationNotFound, NotificationPage, NotificationPermissions, NotificationRead, NotificationStoreUnavailable, NotificationsOperations, NotifyInput, NotifyResult, Preference, QuietHours, ReadResult, Recipient, TimeZone, } from "./contract.js";
@@ -0,0 +1,3 @@
1
+ import { type Binding } from "@house-rules/application";
2
+ import type { NotificationsService } from "./notifications.js";
3
+ export declare const bindNotifications: (service: NotificationsService) => readonly Binding[];
@@ -0,0 +1,19 @@
1
+ import { bindOperation } from "@house-rules/application";
2
+ import { Effect } from "effect";
3
+ import { NotificationForbidden } from "./notification.js";
4
+ import { NotificationsOperations as operations } from "./operations.js";
5
+ const forbidden = () => Effect.fail(new NotificationForbidden({ message: "This notification action is not allowed." }));
6
+ export const bindNotifications = (service) => [
7
+ bindOperation(operations.notify, (input, { caller }) => caller.kind === "system" ? service.notify(input) : forbidden()),
8
+ bindOperation(operations.listMine, ({ page }, { caller }) => caller.kind === "person" ? service.listMine(caller.actor, page) : forbidden()),
9
+ bindOperation(operations.unreadCount, (_, { caller }) => caller.kind === "person" ? service.unreadCount(caller.actor) : forbidden()),
10
+ bindOperation(operations.markRead, ({ notificationId }, { caller }) => caller.kind === "person" ? service.markRead(caller.actor, notificationId) : forbidden()),
11
+ bindOperation(operations.markAllRead, (_, { caller }) => caller.kind === "person" ? service.markAllRead(caller.actor) : forbidden()),
12
+ bindOperation(operations.getMyPreferences, (_, { caller }) => caller.kind === "person" ? service.getMyPreferences(caller.actor) : forbidden()),
13
+ bindOperation(operations.setMyPreference, (input, { caller }) => caller.kind === "person" ? service.setMyPreference(caller.actor, input) : forbidden()),
14
+ bindOperation(operations.setMyQuietHours, ({ quietHours }, { caller }) => caller.kind === "person" ? service.setMyQuietHours(caller.actor, quietHours) : forbidden()),
15
+ bindOperation(operations.setMyTimeZone, ({ timeZone }, { caller }) => caller.kind === "person" ? service.setMyTimeZone(caller.actor, timeZone) : forbidden()),
16
+ bindOperation(operations.dueDeliveries, (_, { caller }) => caller.kind === "system" ? service.dueDeliveries() : forbidden()),
17
+ bindOperation(operations.markSent, ({ deliveryIds }, { caller }) => caller.kind === "system" ? service.markSent(deliveryIds) : forbidden()),
18
+ bindOperation(operations.markFailed, ({ deliveryId, error }, { caller }) => caller.kind === "system" ? service.markFailed(deliveryId, error) : forbidden()),
19
+ ];
@@ -0,0 +1,277 @@
1
+ import { Schema } from "effect";
2
+ export declare const Recipient: Schema.Struct<{
3
+ readonly kind: Schema.NonEmptyString;
4
+ readonly id: Schema.NonEmptyString;
5
+ }>;
6
+ export type Recipient = typeof Recipient.Type;
7
+ export declare const Channel: Schema.Literals<readonly ["inapp", "push", "email"]>;
8
+ export type Channel = typeof Channel.Type;
9
+ export declare const DeliveryMode: Schema.Literals<readonly ["immediate", "hourly", "daily", "off"]>;
10
+ export type DeliveryMode = typeof DeliveryMode.Type;
11
+ export declare const LocalTime: Schema.String;
12
+ export declare const TimeZone: Schema.NonEmptyString;
13
+ export declare const QuietHours: Schema.Struct<{
14
+ readonly start: Schema.String;
15
+ readonly end: Schema.String;
16
+ }>;
17
+ export type QuietHours = typeof QuietHours.Type;
18
+ export declare const Notification: Schema.Struct<{
19
+ readonly id: Schema.NonEmptyString;
20
+ readonly recipient: Schema.Struct<{
21
+ readonly kind: Schema.NonEmptyString;
22
+ readonly id: Schema.NonEmptyString;
23
+ }>;
24
+ readonly category: Schema.NonEmptyString;
25
+ readonly title: Schema.NonEmptyString;
26
+ readonly body: Schema.String;
27
+ readonly data: Schema.Codec<Schema.Json, Schema.Json, never, never>;
28
+ readonly createdAt: Schema.Int;
29
+ readonly readAt: Schema.NullOr<Schema.Int>;
30
+ }>;
31
+ export type Notification = typeof Notification.Type;
32
+ export declare const NotifyInput: Schema.Struct<{
33
+ readonly recipients: Schema.$Array<Schema.Struct<{
34
+ readonly kind: Schema.NonEmptyString;
35
+ readonly id: Schema.NonEmptyString;
36
+ }>>;
37
+ readonly category: Schema.NonEmptyString;
38
+ readonly title: Schema.NonEmptyString;
39
+ readonly body: Schema.String;
40
+ readonly data: Schema.Codec<Schema.Json, Schema.Json, never, never>;
41
+ }>;
42
+ export type NotifyInput = typeof NotifyInput.Type;
43
+ export declare const NotificationCreated: Schema.Struct<{
44
+ readonly kind: Schema.Literal<"NotificationCreated">;
45
+ readonly notificationId: Schema.NonEmptyString;
46
+ readonly recipient: Schema.Struct<{
47
+ readonly kind: Schema.NonEmptyString;
48
+ readonly id: Schema.NonEmptyString;
49
+ }>;
50
+ readonly channels: Schema.$Array<Schema.Literals<readonly ["inapp", "push", "email"]>>;
51
+ }>;
52
+ export type NotificationCreated = typeof NotificationCreated.Type;
53
+ export declare const NotificationRead: Schema.Struct<{
54
+ readonly kind: Schema.Literal<"NotificationRead">;
55
+ readonly notificationId: Schema.NonEmptyString;
56
+ readonly recipient: Schema.Struct<{
57
+ readonly kind: Schema.NonEmptyString;
58
+ readonly id: Schema.NonEmptyString;
59
+ }>;
60
+ }>;
61
+ export type NotificationRead = typeof NotificationRead.Type;
62
+ export declare const NotifyResult: Schema.Struct<{
63
+ readonly notifications: Schema.$Array<Schema.Struct<{
64
+ readonly id: Schema.NonEmptyString;
65
+ readonly recipient: Schema.Struct<{
66
+ readonly kind: Schema.NonEmptyString;
67
+ readonly id: Schema.NonEmptyString;
68
+ }>;
69
+ readonly category: Schema.NonEmptyString;
70
+ readonly title: Schema.NonEmptyString;
71
+ readonly body: Schema.String;
72
+ readonly data: Schema.Codec<Schema.Json, Schema.Json, never, never>;
73
+ readonly createdAt: Schema.Int;
74
+ readonly readAt: Schema.NullOr<Schema.Int>;
75
+ }>>;
76
+ readonly events: Schema.$Array<Schema.Struct<{
77
+ readonly kind: Schema.Literal<"NotificationCreated">;
78
+ readonly notificationId: Schema.NonEmptyString;
79
+ readonly recipient: Schema.Struct<{
80
+ readonly kind: Schema.NonEmptyString;
81
+ readonly id: Schema.NonEmptyString;
82
+ }>;
83
+ readonly channels: Schema.$Array<Schema.Literals<readonly ["inapp", "push", "email"]>>;
84
+ }>>;
85
+ }>;
86
+ export type NotifyResult = typeof NotifyResult.Type;
87
+ export declare const ReadResult: Schema.Struct<{
88
+ readonly notifications: Schema.$Array<Schema.Struct<{
89
+ readonly id: Schema.NonEmptyString;
90
+ readonly recipient: Schema.Struct<{
91
+ readonly kind: Schema.NonEmptyString;
92
+ readonly id: Schema.NonEmptyString;
93
+ }>;
94
+ readonly category: Schema.NonEmptyString;
95
+ readonly title: Schema.NonEmptyString;
96
+ readonly body: Schema.String;
97
+ readonly data: Schema.Codec<Schema.Json, Schema.Json, never, never>;
98
+ readonly createdAt: Schema.Int;
99
+ readonly readAt: Schema.NullOr<Schema.Int>;
100
+ }>>;
101
+ readonly events: Schema.$Array<Schema.Struct<{
102
+ readonly kind: Schema.Literal<"NotificationRead">;
103
+ readonly notificationId: Schema.NonEmptyString;
104
+ readonly recipient: Schema.Struct<{
105
+ readonly kind: Schema.NonEmptyString;
106
+ readonly id: Schema.NonEmptyString;
107
+ }>;
108
+ }>>;
109
+ }>;
110
+ export type ReadResult = typeof ReadResult.Type;
111
+ export declare const NotificationCursor: Schema.Struct<{
112
+ readonly createdAt: Schema.Int;
113
+ readonly id: Schema.NonEmptyString;
114
+ }>;
115
+ export type NotificationCursor = typeof NotificationCursor.Type;
116
+ export declare const NotificationPage: Schema.Struct<{
117
+ readonly size: Schema.optionalKey<Schema.Int>;
118
+ readonly cursor: Schema.optionalKey<Schema.Struct<{
119
+ readonly createdAt: Schema.Int;
120
+ readonly id: Schema.NonEmptyString;
121
+ }>>;
122
+ }>;
123
+ export type NotificationPage = typeof NotificationPage.Type;
124
+ export declare const NotificationList: Schema.Struct<{
125
+ readonly notifications: Schema.$Array<Schema.Struct<{
126
+ readonly id: Schema.NonEmptyString;
127
+ readonly recipient: Schema.Struct<{
128
+ readonly kind: Schema.NonEmptyString;
129
+ readonly id: Schema.NonEmptyString;
130
+ }>;
131
+ readonly category: Schema.NonEmptyString;
132
+ readonly title: Schema.NonEmptyString;
133
+ readonly body: Schema.String;
134
+ readonly data: Schema.Codec<Schema.Json, Schema.Json, never, never>;
135
+ readonly createdAt: Schema.Int;
136
+ readonly readAt: Schema.NullOr<Schema.Int>;
137
+ }>>;
138
+ readonly next: Schema.optionalKey<Schema.Struct<{
139
+ readonly createdAt: Schema.Int;
140
+ readonly id: Schema.NonEmptyString;
141
+ }>>;
142
+ }>;
143
+ export type NotificationList = typeof NotificationList.Type;
144
+ export declare const Preference: Schema.Struct<{
145
+ readonly category: Schema.NonEmptyString;
146
+ readonly channel: Schema.Literals<readonly ["inapp", "push", "email"]>;
147
+ readonly mode: Schema.Literals<readonly ["immediate", "hourly", "daily", "off"]>;
148
+ }>;
149
+ export type Preference = typeof Preference.Type;
150
+ export declare const MyPreferences: Schema.Struct<{
151
+ readonly preferences: Schema.$Array<Schema.Struct<{
152
+ readonly category: Schema.NonEmptyString;
153
+ readonly channel: Schema.Literals<readonly ["inapp", "push", "email"]>;
154
+ readonly mode: Schema.Literals<readonly ["immediate", "hourly", "daily", "off"]>;
155
+ }>>;
156
+ readonly quietHours: Schema.NullOr<Schema.Struct<{
157
+ readonly start: Schema.String;
158
+ readonly end: Schema.String;
159
+ }>>;
160
+ readonly timeZone: Schema.NullOr<Schema.NonEmptyString>;
161
+ }>;
162
+ export type MyPreferences = typeof MyPreferences.Type;
163
+ export declare const Delivery: Schema.Struct<{
164
+ readonly id: Schema.NonEmptyString;
165
+ readonly notificationId: Schema.NonEmptyString;
166
+ readonly recipient: Schema.Struct<{
167
+ readonly kind: Schema.NonEmptyString;
168
+ readonly id: Schema.NonEmptyString;
169
+ }>;
170
+ readonly channel: Schema.Literals<readonly ["inapp", "push", "email"]>;
171
+ readonly mode: Schema.Literals<readonly ["immediate", "hourly", "daily"]>;
172
+ readonly state: Schema.Literals<readonly ["pending", "held", "sent", "failed", "cancelled"]>;
173
+ readonly dueAt: Schema.Int;
174
+ readonly retryAt: Schema.NullOr<Schema.Int>;
175
+ readonly sentAt: Schema.NullOr<Schema.Int>;
176
+ readonly attempts: Schema.Int;
177
+ readonly lastError: Schema.NullOr<Schema.String>;
178
+ }>;
179
+ export type Delivery = typeof Delivery.Type;
180
+ export declare const DeliveryItem: Schema.Struct<{
181
+ readonly delivery: Schema.Struct<{
182
+ readonly id: Schema.NonEmptyString;
183
+ readonly notificationId: Schema.NonEmptyString;
184
+ readonly recipient: Schema.Struct<{
185
+ readonly kind: Schema.NonEmptyString;
186
+ readonly id: Schema.NonEmptyString;
187
+ }>;
188
+ readonly channel: Schema.Literals<readonly ["inapp", "push", "email"]>;
189
+ readonly mode: Schema.Literals<readonly ["immediate", "hourly", "daily"]>;
190
+ readonly state: Schema.Literals<readonly ["pending", "held", "sent", "failed", "cancelled"]>;
191
+ readonly dueAt: Schema.Int;
192
+ readonly retryAt: Schema.NullOr<Schema.Int>;
193
+ readonly sentAt: Schema.NullOr<Schema.Int>;
194
+ readonly attempts: Schema.Int;
195
+ readonly lastError: Schema.NullOr<Schema.String>;
196
+ }>;
197
+ readonly notification: Schema.Struct<{
198
+ readonly id: Schema.NonEmptyString;
199
+ readonly recipient: Schema.Struct<{
200
+ readonly kind: Schema.NonEmptyString;
201
+ readonly id: Schema.NonEmptyString;
202
+ }>;
203
+ readonly category: Schema.NonEmptyString;
204
+ readonly title: Schema.NonEmptyString;
205
+ readonly body: Schema.String;
206
+ readonly data: Schema.Codec<Schema.Json, Schema.Json, never, never>;
207
+ readonly createdAt: Schema.Int;
208
+ readonly readAt: Schema.NullOr<Schema.Int>;
209
+ }>;
210
+ }>;
211
+ export type DeliveryItem = typeof DeliveryItem.Type;
212
+ export declare const DueDelivery: Schema.Struct<{
213
+ readonly recipient: Schema.Struct<{
214
+ readonly kind: Schema.NonEmptyString;
215
+ readonly id: Schema.NonEmptyString;
216
+ }>;
217
+ readonly channel: Schema.Literals<readonly ["inapp", "push", "email"]>;
218
+ readonly mode: Schema.Literals<readonly ["immediate", "hourly", "daily"]>;
219
+ readonly deliveries: Schema.$Array<Schema.Struct<{
220
+ readonly delivery: Schema.Struct<{
221
+ readonly id: Schema.NonEmptyString;
222
+ readonly notificationId: Schema.NonEmptyString;
223
+ readonly recipient: Schema.Struct<{
224
+ readonly kind: Schema.NonEmptyString;
225
+ readonly id: Schema.NonEmptyString;
226
+ }>;
227
+ readonly channel: Schema.Literals<readonly ["inapp", "push", "email"]>;
228
+ readonly mode: Schema.Literals<readonly ["immediate", "hourly", "daily"]>;
229
+ readonly state: Schema.Literals<readonly ["pending", "held", "sent", "failed", "cancelled"]>;
230
+ readonly dueAt: Schema.Int;
231
+ readonly retryAt: Schema.NullOr<Schema.Int>;
232
+ readonly sentAt: Schema.NullOr<Schema.Int>;
233
+ readonly attempts: Schema.Int;
234
+ readonly lastError: Schema.NullOr<Schema.String>;
235
+ }>;
236
+ readonly notification: Schema.Struct<{
237
+ readonly id: Schema.NonEmptyString;
238
+ readonly recipient: Schema.Struct<{
239
+ readonly kind: Schema.NonEmptyString;
240
+ readonly id: Schema.NonEmptyString;
241
+ }>;
242
+ readonly category: Schema.NonEmptyString;
243
+ readonly title: Schema.NonEmptyString;
244
+ readonly body: Schema.String;
245
+ readonly data: Schema.Codec<Schema.Json, Schema.Json, never, never>;
246
+ readonly createdAt: Schema.Int;
247
+ readonly readAt: Schema.NullOr<Schema.Int>;
248
+ }>;
249
+ }>>;
250
+ }>;
251
+ export type DueDelivery = typeof DueDelivery.Type;
252
+ declare const NotificationInputInvalid_base: Schema.Class<NotificationInputInvalid, Schema.TaggedStruct<"NotificationInputInvalid", {
253
+ readonly kind: Schema.tag<"invalid">;
254
+ readonly message: Schema.String;
255
+ }>, import("effect/Cause").YieldableError>;
256
+ export declare class NotificationInputInvalid extends NotificationInputInvalid_base {
257
+ }
258
+ declare const NotificationNotFound_base: Schema.Class<NotificationNotFound, Schema.TaggedStruct<"NotificationNotFound", {
259
+ readonly kind: Schema.tag<"not_found">;
260
+ readonly message: Schema.String;
261
+ }>, import("effect/Cause").YieldableError>;
262
+ export declare class NotificationNotFound extends NotificationNotFound_base {
263
+ }
264
+ declare const NotificationForbidden_base: Schema.Class<NotificationForbidden, Schema.TaggedStruct<"NotificationForbidden", {
265
+ readonly kind: Schema.tag<"forbidden">;
266
+ readonly message: Schema.String;
267
+ }>, import("effect/Cause").YieldableError>;
268
+ export declare class NotificationForbidden extends NotificationForbidden_base {
269
+ }
270
+ declare const NotificationStoreUnavailable_base: Schema.Class<NotificationStoreUnavailable, Schema.TaggedStruct<"NotificationStoreUnavailable", {
271
+ readonly kind: Schema.tag<"unavailable">;
272
+ readonly cause: Schema.Defect;
273
+ }>, import("effect/Cause").YieldableError>;
274
+ export declare class NotificationStoreUnavailable extends NotificationStoreUnavailable_base {
275
+ get message(): string;
276
+ }
277
+ export {};
@@ -0,0 +1,108 @@
1
+ import { errorKind } from "@house-rules/domain/contract";
2
+ import { Schema } from "effect";
3
+ export const Recipient = Schema.Struct({ kind: Schema.NonEmptyString, id: Schema.NonEmptyString });
4
+ export const Channel = Schema.Literals(["inapp", "push", "email"]);
5
+ export const DeliveryMode = Schema.Literals(["immediate", "hourly", "daily", "off"]);
6
+ export const LocalTime = Schema.String.check(Schema.isPattern(/^([01]\d|2[0-3]):[0-5]\d$/));
7
+ export const TimeZone = Schema.NonEmptyString.check(Schema.makeFilter((zone) => {
8
+ try {
9
+ new Intl.DateTimeFormat("en", { timeZone: zone });
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }));
16
+ export const QuietHours = Schema.Struct({
17
+ start: LocalTime,
18
+ end: LocalTime,
19
+ }).check(Schema.makeFilter((hours) => hours.start !== hours.end));
20
+ export const Notification = Schema.Struct({
21
+ id: Schema.NonEmptyString,
22
+ recipient: Recipient,
23
+ category: Schema.NonEmptyString,
24
+ title: Schema.NonEmptyString,
25
+ body: Schema.String,
26
+ data: Schema.Json,
27
+ createdAt: Schema.Int,
28
+ readAt: Schema.NullOr(Schema.Int),
29
+ });
30
+ export const NotifyInput = Schema.Struct({
31
+ recipients: Schema.Array(Recipient).check(Schema.isMinLength(1)),
32
+ category: Schema.NonEmptyString,
33
+ title: Schema.NonEmptyString,
34
+ body: Schema.String,
35
+ data: Schema.Json,
36
+ });
37
+ export const NotificationCreated = Schema.Struct({
38
+ kind: Schema.Literal("NotificationCreated"),
39
+ notificationId: Schema.NonEmptyString,
40
+ recipient: Recipient,
41
+ channels: Schema.Array(Channel),
42
+ });
43
+ export const NotificationRead = Schema.Struct({
44
+ kind: Schema.Literal("NotificationRead"),
45
+ notificationId: Schema.NonEmptyString,
46
+ recipient: Recipient,
47
+ });
48
+ export const NotifyResult = Schema.Struct({
49
+ notifications: Schema.Array(Notification),
50
+ events: Schema.Array(NotificationCreated),
51
+ });
52
+ export const ReadResult = Schema.Struct({
53
+ notifications: Schema.Array(Notification),
54
+ events: Schema.Array(NotificationRead),
55
+ });
56
+ export const NotificationCursor = Schema.Struct({
57
+ createdAt: Schema.Int,
58
+ id: Schema.NonEmptyString,
59
+ });
60
+ export const NotificationPage = Schema.Struct({
61
+ size: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThan(0))),
62
+ cursor: Schema.optionalKey(NotificationCursor),
63
+ });
64
+ export const NotificationList = Schema.Struct({
65
+ notifications: Schema.Array(Notification),
66
+ next: Schema.optionalKey(NotificationCursor),
67
+ });
68
+ export const Preference = Schema.Struct({
69
+ category: Schema.NonEmptyString,
70
+ channel: Channel,
71
+ mode: DeliveryMode,
72
+ });
73
+ export const MyPreferences = Schema.Struct({
74
+ preferences: Schema.Array(Preference),
75
+ quietHours: Schema.NullOr(QuietHours),
76
+ timeZone: Schema.NullOr(TimeZone),
77
+ });
78
+ export const Delivery = Schema.Struct({
79
+ id: Schema.NonEmptyString,
80
+ notificationId: Schema.NonEmptyString,
81
+ recipient: Recipient,
82
+ channel: Channel,
83
+ mode: Schema.Literals(["immediate", "hourly", "daily"]),
84
+ state: Schema.Literals(["pending", "held", "sent", "failed", "cancelled"]),
85
+ dueAt: Schema.Int,
86
+ retryAt: Schema.NullOr(Schema.Int),
87
+ sentAt: Schema.NullOr(Schema.Int),
88
+ attempts: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
89
+ lastError: Schema.NullOr(Schema.String),
90
+ });
91
+ export const DeliveryItem = Schema.Struct({ delivery: Delivery, notification: Notification });
92
+ export const DueDelivery = Schema.Struct({
93
+ recipient: Recipient,
94
+ channel: Channel,
95
+ mode: Delivery.fields.mode,
96
+ deliveries: Schema.Array(DeliveryItem),
97
+ });
98
+ export class NotificationInputInvalid extends Schema.TaggedError()("NotificationInputInvalid", { kind: errorKind("invalid"), message: Schema.String }) {
99
+ }
100
+ export class NotificationNotFound extends Schema.TaggedError()("NotificationNotFound", { kind: errorKind("not_found"), message: Schema.String }) {
101
+ }
102
+ export class NotificationForbidden extends Schema.TaggedError()("NotificationForbidden", { kind: errorKind("forbidden"), message: Schema.String }) {
103
+ }
104
+ export class NotificationStoreUnavailable extends Schema.TaggedError()("NotificationStoreUnavailable", { kind: errorKind("unavailable"), cause: Schema.Defect() }) {
105
+ get message() {
106
+ return "Notifications are unavailable. Try again shortly.";
107
+ }
108
+ }