@orieljs/notifications 0.1.0-alpha.1 → 0.1.0-alpha.11
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/NOTICE +7 -0
- package/README.md +38 -0
- package/TRADEMARKS.md +33 -0
- package/dist/index.d.ts +75 -43
- package/dist/index.js +313 -102
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
package/NOTICE
CHANGED
|
@@ -7,3 +7,10 @@ This product includes software developed by the Oriel project
|
|
|
7
7
|
Oriel is licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
8
8
|
The Oriel name and logo are not covered by that license; see TRADEMARKS.md
|
|
9
9
|
(https://github.com/orieljs/framework/blob/main/TRADEMARKS.md).
|
|
10
|
+
|
|
11
|
+
@orieljs/support (and @orieljs/framework, which bundles it) includes transliteration data from
|
|
12
|
+
voku/portable-ascii (https://github.com/voku/portable-ascii), Copyright (C) 2019 Lars Moelleken, MIT License.
|
|
13
|
+
|
|
14
|
+
@orieljs/notifications (and @orieljs/framework, which bundles it) includes the HTML and CSS of
|
|
15
|
+
Laravel's default notification mail (laravel/framework, https://github.com/laravel/framework),
|
|
16
|
+
Copyright (c) Taylor Otwell, MIT License.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @orieljs/notifications
|
|
2
|
+
|
|
3
|
+
Notifications for Oriel: short messages to your users by mail or stored in the database, as Laravel's notifications. It is part of `@orieljs/framework`, which every Oriel app already installs, and is re-exported from it. In an app, import it from `@orieljs/framework`; `@orieljs/notifications` is also published on its own.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @orieljs/framework
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts title="app/Notifications/PostPublished.ts"
|
|
10
|
+
import { MailMessage, Notification, url } from '@orieljs/framework'
|
|
11
|
+
import type { Post } from '../Models/Post'
|
|
12
|
+
|
|
13
|
+
export class PostPublished extends Notification {
|
|
14
|
+
constructor(public post: Post) {
|
|
15
|
+
super()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
via(): string[] {
|
|
19
|
+
return ['mail']
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
toMail(): MailMessage {
|
|
23
|
+
return new MailMessage().line(`"${this.post.title}" is published.`).action('View post', url(`/posts/${this.post.slug}`))
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`via()` names the channels (`mail`, `database`), and `toMail()` or `toDatabase()` builds the message for each. Send it with `await user.notify(new PostPublished(post))` (models get `notify()` from the `Notifiable` mixin), to several users with `await Notification.send(users, notification)`, or to an address with `await Notification.route('mail', address).notify(notification)`. A mail's links need full URLs: `url()` makes one from a path. A notification with `readonly shouldQueue = true` is sent by the queue worker.
|
|
29
|
+
|
|
30
|
+
Documentation: https://orieljs.vercel.app/docs/notifications
|
|
31
|
+
|
|
32
|
+
Status: early alpha. APIs will change.
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
Oriel is open-source software licensed under the Apache License 2.0 (Apache-2.0; see `LICENSE` and `NOTICE`). The Oriel name and logo are trademarks; see `TRADEMARKS.md`.
|
|
37
|
+
|
|
38
|
+
Website: https://orieljs.vercel.app
|
package/TRADEMARKS.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Oriel trademark policy
|
|
2
|
+
|
|
3
|
+
The Oriel code is open source under the Apache License 2.0. The **Oriel name and logo** are not: the license covers
|
|
4
|
+
the code only (section 6 of the license says so), and the name and logo stay with the project. This page explains
|
|
5
|
+
what you can and can't do with them. The goal is simple: people should always be able to tell what is the real Oriel
|
|
6
|
+
and what isn't.
|
|
7
|
+
|
|
8
|
+
## You can, without asking
|
|
9
|
+
|
|
10
|
+
- Use, change, ship and sell the code, as the license allows.
|
|
11
|
+
- Say that your app or package is "built with Oriel", "for Oriel" or "compatible with Oriel".
|
|
12
|
+
- Name a package `oriel-<something>` or `<something>-for-oriel` when it works with Oriel, as long as it is clear that
|
|
13
|
+
it is not official (for example, don't publish it under the `@orieljs` npm scope).
|
|
14
|
+
- Use the name and logo to write about Oriel, teach it, or link to it, in articles, talks, videos and courses.
|
|
15
|
+
- Use the unmodified logo to point to the project, as it is (no recolouring, cropping or
|
|
16
|
+
distortion).
|
|
17
|
+
|
|
18
|
+
## You can't, without written permission
|
|
19
|
+
|
|
20
|
+
- Call a fork, a modified copy or a competing product "Oriel", or anything likely to be confused with it.
|
|
21
|
+
- Use the name or logo as the name, logo or domain of your own product, company or service.
|
|
22
|
+
- Suggest that the Oriel project made, endorses or supports your work when it doesn't.
|
|
23
|
+
- Sell merchandise with the name or logo.
|
|
24
|
+
|
|
25
|
+
## Forks
|
|
26
|
+
|
|
27
|
+
Forks are welcome. Please give yours a different name and logo, keep the `LICENSE` and `NOTICE` files, and say that
|
|
28
|
+
it is based on Oriel.
|
|
29
|
+
|
|
30
|
+
## Questions
|
|
31
|
+
|
|
32
|
+
Open an issue at https://github.com/orieljs/framework or email meeeet.dev@gmail.com. If you're unsure whether a use
|
|
33
|
+
is fine, ask; the answer is usually yes.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OrielError, MailRecipient, NotificationChannel, Notifiable
|
|
1
|
+
import { OrielError, MailRecipient, Attachment, NotificationChannel, Notifiable, Notification as Notification$1, NotificationDispatcher, Container, Job } from '@orieljs/contracts';
|
|
2
2
|
import * as _orieljs_support from '@orieljs/support';
|
|
3
3
|
import { ServiceProvider } from '@orieljs/support';
|
|
4
4
|
|
|
@@ -51,38 +51,84 @@ declare class NotificationDispatcherUnavailableError extends Error implements Or
|
|
|
51
51
|
constructor();
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/*!
|
|
55
|
+
* Laravel's default notification mail (the `notifications::email` view in the `mail::` markdown components with the
|
|
56
|
+
* `default` theme), as Laravel 12 sends it: the same HTML, with the theme's CSS inlined as CssToInlineStyles inlines
|
|
57
|
+
* it, and the same plain-text part. The lines are plain text (escaped), not Markdown.
|
|
58
|
+
*
|
|
59
|
+
* laravel/framework: Copyright (c) Taylor Otwell
|
|
60
|
+
*
|
|
61
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
62
|
+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
63
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
64
|
+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
65
|
+
*
|
|
66
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
67
|
+
* Software.
|
|
68
|
+
*
|
|
69
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
70
|
+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
71
|
+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
72
|
+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
73
|
+
*/
|
|
74
|
+
type MailLevel = 'info' | 'success' | 'error';
|
|
75
|
+
|
|
54
76
|
interface MailMessageAction {
|
|
55
77
|
text: string;
|
|
56
78
|
url: string;
|
|
57
79
|
}
|
|
58
80
|
/**
|
|
59
|
-
* Laravel's Illuminate\Notifications\Messages\MailMessage: greeting/line/action/salutation, rendered to
|
|
60
|
-
* HTML
|
|
61
|
-
* outro afterwards — exactly like Laravel's SimpleMessage.
|
|
81
|
+
* Laravel's Illuminate\Notifications\Messages\MailMessage: greeting/line/action/salutation, rendered to Laravel's
|
|
82
|
+
* default notification mail (HTML and text) with no View involved. line() appends to the intro before action() is
|
|
83
|
+
* called, and to the outro afterwards — exactly like Laravel's SimpleMessage.
|
|
62
84
|
*/
|
|
63
85
|
declare class MailMessage {
|
|
86
|
+
/** info, success or error: the colour of the button, and error() greets with "Whoops!". Laravel's `$level`. */
|
|
87
|
+
levelName: MailLevel;
|
|
64
88
|
subjectLine?: string;
|
|
65
89
|
greetingLine?: string;
|
|
66
90
|
introLines: string[];
|
|
67
91
|
actionData?: MailMessageAction;
|
|
68
92
|
outroLines: string[];
|
|
69
|
-
|
|
93
|
+
/** Unset: "Regards," and config('app.name'), as Laravel's template. */
|
|
94
|
+
salutationLine?: string;
|
|
70
95
|
toAddresses: MailRecipient[];
|
|
71
96
|
ccAddresses: MailRecipient[];
|
|
72
97
|
bccAddresses: MailRecipient[];
|
|
98
|
+
replyToAddresses: MailRecipient[];
|
|
73
99
|
fromAddress?: MailRecipient;
|
|
100
|
+
attachments: Attachment[];
|
|
101
|
+
/** A green button: something went well. */
|
|
102
|
+
success(): this;
|
|
103
|
+
/** A red button, and "Whoops!" when there is no greeting: something went wrong. */
|
|
104
|
+
error(): this;
|
|
105
|
+
level(level: MailLevel): this;
|
|
74
106
|
subject(subject: string): this;
|
|
75
107
|
greeting(greeting: string): this;
|
|
76
108
|
/** Before action(), appends to the intro; after action(), to the outro. */
|
|
77
109
|
line(text: string): this;
|
|
78
110
|
lines(lines: string[]): this;
|
|
111
|
+
/** line(), when the condition holds. */
|
|
112
|
+
lineIf(condition: boolean, text: string): this;
|
|
113
|
+
/** lines(), when the condition holds. */
|
|
114
|
+
linesIf(condition: boolean, lines: string[]): this;
|
|
79
115
|
action(text: string, url: string): this;
|
|
80
116
|
salutation(salutation: string): this;
|
|
81
117
|
from(address: MailRecipient): this;
|
|
82
118
|
to(recipients: MailRecipient | MailRecipient[]): this;
|
|
83
119
|
cc(recipients: MailRecipient | MailRecipient[]): this;
|
|
84
120
|
bcc(recipients: MailRecipient | MailRecipient[]): this;
|
|
85
|
-
|
|
121
|
+
replyTo(recipients: MailRecipient | MailRecipient[]): this;
|
|
122
|
+
/** Attaches a file by its path: `as` names it in the mail (its file name by default), `mime` gives its type. */
|
|
123
|
+
attach(path: string, options?: {
|
|
124
|
+
as?: string;
|
|
125
|
+
mime?: string;
|
|
126
|
+
}): this;
|
|
127
|
+
/** Attaches bytes or text under a file name. */
|
|
128
|
+
attachData(data: Uint8Array | string, name: string, options?: {
|
|
129
|
+
mime?: string;
|
|
130
|
+
}): this;
|
|
131
|
+
/** Renders Laravel's default notification mail, HTML and text. No ViewFactory involved. */
|
|
86
132
|
render(): {
|
|
87
133
|
html: string;
|
|
88
134
|
text: string;
|
|
@@ -104,29 +150,17 @@ interface PlainNotificationMail {
|
|
|
104
150
|
salutation?: string;
|
|
105
151
|
}
|
|
106
152
|
declare class MailNotificationChannel implements NotificationChannel {
|
|
107
|
-
send(notifiable: Notifiable
|
|
153
|
+
send(notifiable: Notifiable, notification: Notification$1): Promise<void>;
|
|
108
154
|
}
|
|
109
155
|
|
|
110
156
|
declare const NOTIFICATIONS_TABLE = "notifications";
|
|
111
|
-
interface DatabaseNotificationRecord {
|
|
112
|
-
readonly id: string;
|
|
113
|
-
readonly type: string;
|
|
114
|
-
readonly data: Record<string, unknown>;
|
|
115
|
-
readonly createdAt: Date;
|
|
116
|
-
readonly updatedAt: Date;
|
|
117
|
-
readAt: Date | undefined;
|
|
118
|
-
markAsRead(): Promise<void>;
|
|
119
|
-
markAsUnread(): Promise<void>;
|
|
120
|
-
}
|
|
121
157
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
158
|
+
* Laravel's DatabaseChannel: one row in `notifications` per notifiable, with what toDatabase() (or toArray())
|
|
159
|
+
* returns as JSON. The row is keyed as `user.notifications()` reads it: `notifiable_type` is the model's morph class
|
|
160
|
+
* (its morph map alias, else its class name) and it goes to the model's own connection.
|
|
124
161
|
*/
|
|
125
|
-
declare function parseStoredDate(value: unknown): Date;
|
|
126
|
-
/** notifiable_type + notifiable_id together, never id alone. */
|
|
127
|
-
declare function fetchNotifications(notifiableType: string, notifiableId: unknown, filter?: 'all' | 'read' | 'unread'): Promise<DatabaseNotificationRecord[]>;
|
|
128
162
|
declare class DatabaseNotificationChannel implements NotificationChannel {
|
|
129
|
-
send(notifiable: Notifiable
|
|
163
|
+
send(notifiable: Notifiable, notification: Notification$1): Promise<void>;
|
|
130
164
|
}
|
|
131
165
|
|
|
132
166
|
declare const NOTIFICATIONS_TABLE_MIGRATION = "import { Migration, Schema } from '@orieljs/framework'\n\nexport default class extends Migration {\n async up() {\n await Schema.create('notifications', (t) => {\n t.uuid('id').primary()\n t.string('type')\n t.morphs('notifiable')\n t.text('data')\n t.timestamp('read_at').nullable()\n t.timestamps()\n })\n }\n\n async down() {\n await Schema.dropIfExists('notifications')\n }\n}\n";
|
|
@@ -144,7 +178,7 @@ declare const SHOULD_QUEUE: unique symbol;
|
|
|
144
178
|
* survives Queue's serializer (which rejects functions in own fields) and is restored on the worker as this class
|
|
145
179
|
* (registered as `'Notifications.AnonymousNotifiable'` in ClassRegistry).
|
|
146
180
|
*/
|
|
147
|
-
declare class AnonymousNotifiable implements Notifiable
|
|
181
|
+
declare class AnonymousNotifiable implements Notifiable {
|
|
148
182
|
routes: Record<string, unknown>;
|
|
149
183
|
/** Adds a route; chains, so one on-demand notifiable can go to several channels. */
|
|
150
184
|
route(channel: string, route: unknown): this;
|
|
@@ -158,7 +192,7 @@ declare class AnonymousNotifiable implements Notifiable$1 {
|
|
|
158
192
|
* a Set, ...), as Laravel's `Notification::send($users, ...)` accepts. A notifiable is recognised by its
|
|
159
193
|
* routeNotificationFor() method first, so an iterable model is still treated as one notifiable.
|
|
160
194
|
*/
|
|
161
|
-
declare function toNotifiableList(notifiables: Notifiable
|
|
195
|
+
declare function toNotifiableList(notifiables: Notifiable | Iterable<Notifiable>): Notifiable[];
|
|
162
196
|
/** Resolves channels by name from a factory map — mirrors MailManager's driver resolution. */
|
|
163
197
|
declare class Dispatcher implements NotificationDispatcher {
|
|
164
198
|
private readonly container;
|
|
@@ -167,8 +201,8 @@ declare class Dispatcher implements NotificationDispatcher {
|
|
|
167
201
|
constructor(container: Container);
|
|
168
202
|
extend(channel: string, factory: () => NotificationChannel): void;
|
|
169
203
|
private resolve;
|
|
170
|
-
send(notifiables: Notifiable
|
|
171
|
-
sendNow(notifiables: Notifiable
|
|
204
|
+
send(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification$1): Promise<void>;
|
|
205
|
+
sendNow(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification$1, channels?: string[]): Promise<void>;
|
|
172
206
|
/** On-demand: an AnonymousNotifiable, whose notify() goes through the bound NotificationDispatcher. */
|
|
173
207
|
route(channel: string, route: unknown): AnonymousNotifiable;
|
|
174
208
|
}
|
|
@@ -181,10 +215,10 @@ declare class Dispatcher implements NotificationDispatcher {
|
|
|
181
215
|
* re-decides queued-or-now on whatever job it's handed — actually enqueues it instead of running it inline; the
|
|
182
216
|
* notification's own queueability (checked by Dispatcher, above) only decides whether to wrap it at all. Like
|
|
183
217
|
* Laravel's SendQueuedNotifications, it forwards failed(), retryUntil(), middleware() and displayName() to the
|
|
184
|
-
* notification.
|
|
218
|
+
* notification. Unlike Laravel's, it is unique when the notification is (Laravel queues it past the unique lock).
|
|
185
219
|
*/
|
|
186
220
|
declare class SendQueuedNotifications implements Job {
|
|
187
|
-
readonly notifiables: Notifiable
|
|
221
|
+
readonly notifiables: Notifiable[];
|
|
188
222
|
readonly notification: Notification$1;
|
|
189
223
|
readonly channels?: string[] | undefined;
|
|
190
224
|
connection?: string;
|
|
@@ -195,7 +229,7 @@ declare class SendQueuedNotifications implements Job {
|
|
|
195
229
|
timeout?: number;
|
|
196
230
|
failOnTimeout?: boolean;
|
|
197
231
|
deleteWhenMissingModels?: boolean;
|
|
198
|
-
constructor(notifiables: Notifiable
|
|
232
|
+
constructor(notifiables: Notifiable[], notification: Notification$1, channels?: string[] | undefined);
|
|
199
233
|
handle(): Promise<void>;
|
|
200
234
|
/** Called by Queue after the final failed attempt. */
|
|
201
235
|
failed(error: unknown): Promise<void>;
|
|
@@ -207,19 +241,17 @@ declare class SendQueuedNotifications implements Job {
|
|
|
207
241
|
|
|
208
242
|
type NotifiableConstructor = new (...args: any[]) => object;
|
|
209
243
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
* (
|
|
244
|
+
* Laravel's RoutesNotifications trait: `notify()`, `notifyNow()` and `routeNotificationFor()`. Apps use
|
|
245
|
+
* `Notifiable` from '@orieljs/framework', which adds the database notifications relations. Custom routing per
|
|
246
|
+
* channel follows Laravel's order: a `routeNotificationFor<Channel>()` method (Studly name) wins if present, else
|
|
247
|
+
* 'mail' reads `email`/`getAttribute('email')`, 'database' returns the instance itself (the database channel derives
|
|
248
|
+
* the row from it), and anything else is unrouted (`undefined`).
|
|
214
249
|
*/
|
|
215
|
-
declare function
|
|
250
|
+
declare function RoutesNotifications<TBase extends NotifiableConstructor>(Base: TBase): {
|
|
216
251
|
new (...args: any[]): {
|
|
217
252
|
routeNotificationFor(channel: string, notification: Notification$1): unknown;
|
|
218
253
|
notify(notification: Notification$1): Promise<void>;
|
|
219
254
|
notifyNow(notification: Notification$1, channels?: string[]): Promise<void>;
|
|
220
|
-
notifications(): Promise<DatabaseNotificationRecord[]>;
|
|
221
|
-
unreadNotifications(): Promise<DatabaseNotificationRecord[]>;
|
|
222
|
-
readNotifications(): Promise<DatabaseNotificationRecord[]>;
|
|
223
255
|
};
|
|
224
256
|
} & TBase;
|
|
225
257
|
|
|
@@ -232,12 +264,12 @@ declare function Notifiable<TBase extends NotifiableConstructor>(Base: TBase): {
|
|
|
232
264
|
*/
|
|
233
265
|
declare abstract class Notification implements Notification$1 {
|
|
234
266
|
id?: string;
|
|
235
|
-
abstract via(notifiable: Notifiable
|
|
267
|
+
abstract via(notifiable: Notifiable): string[];
|
|
236
268
|
/** Queues it when the notification is queueable, otherwise sends now. Accepts one notifiable, an array or any iterable. */
|
|
237
|
-
static send(notifiables: Notifiable
|
|
238
|
-
static sendNow(notifiables: Notifiable
|
|
269
|
+
static send(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification$1): Promise<void>;
|
|
270
|
+
static sendNow(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification$1, channels?: string[]): Promise<void>;
|
|
239
271
|
/** On-demand: `Notification.route('mail', 'a@b.c').notify(new InvoicePaid())`. */
|
|
240
|
-
static route(channel: string, route: unknown): Notifiable
|
|
272
|
+
static route(channel: string, route: unknown): Notifiable & {
|
|
241
273
|
notify(notification: Notification$1): Promise<void>;
|
|
242
274
|
};
|
|
243
275
|
static extend(channel: string, factory: () => NotificationChannel): void;
|
|
@@ -257,4 +289,4 @@ declare class NotificationsServiceProvider extends ServiceProvider {
|
|
|
257
289
|
boot(): void;
|
|
258
290
|
}
|
|
259
291
|
|
|
260
|
-
export { AnonymousNotifiable, DatabaseNotificationChannel,
|
|
292
|
+
export { AnonymousNotifiable, DatabaseNotificationChannel, DatabaseUnavailableError, Dispatcher, type MailLevel, MailMessage, type MailMessageAction, MailNotificationChannel, MailUnavailableError, NOTIFICATIONS_TABLE, NOTIFICATIONS_TABLE_MIGRATION, NotDatabaseNotifiableError, type NotifiableConstructor, Notification, NotificationDispatcherUnavailableError, NotificationFacade, NotificationQueueUnavailableError, NotificationsServiceProvider, type PlainNotificationMail, RoutesNotifications, SHOULD_QUEUE, SendQueuedNotifications, UnknownNotificationChannelError, UnrenderableNotificationDataError, UnrenderableNotificationMailError, toNotifiableList };
|
package/dist/index.js
CHANGED
|
@@ -66,18 +66,214 @@ var NotificationDispatcherUnavailableError = class extends Error {
|
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
+
// MailMessage.ts
|
|
70
|
+
import { ConfigRepository } from "@orieljs/contracts";
|
|
71
|
+
import { currentContainer } from "@orieljs/container";
|
|
72
|
+
|
|
73
|
+
// mail/template.ts
|
|
74
|
+
var BASE = "box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; position: relative;";
|
|
75
|
+
var style = (rules = "") => `style="${BASE}${rules === "" ? "" : ` ${rules}`}"`;
|
|
76
|
+
var PREMAILER_FULL = "-premailer-cellpadding: 0; -premailer-cellspacing: 0; -premailer-width: 100%;";
|
|
77
|
+
var PREMAILER_570 = "-premailer-cellpadding: 0; -premailer-cellspacing: 0; -premailer-width: 570px;";
|
|
78
|
+
var PARAGRAPH = style("font-size: 16px; line-height: 1.5em; margin-top: 0; text-align: left;");
|
|
79
|
+
var BUTTON_COLORS = {
|
|
80
|
+
info: { name: "primary", color: "#2d3748" },
|
|
81
|
+
success: { name: "success", color: "#48bb78" },
|
|
82
|
+
error: { name: "error", color: "#e53e3e" }
|
|
83
|
+
};
|
|
84
|
+
var text = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
85
|
+
var attribute = (value) => text(value).replace(/"/g, """);
|
|
86
|
+
var displayable = (url) => url.replace(/mailto:|tel:/g, "");
|
|
87
|
+
var isUnsafeLink = (url) => {
|
|
88
|
+
const destination = url.replace(/^[\x00-\x20]+/, "").replace(/[\t\n\r]/g, "");
|
|
89
|
+
return /^javascript:|vbscript:|file:|data:/i.test(destination) && !/^data:image\/(?:png|gif|jpeg|webp)/i.test(destination);
|
|
90
|
+
};
|
|
91
|
+
var HEAD = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
92
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
93
|
+
<head>
|
|
94
|
+
<title>{{title}}</title>
|
|
95
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
96
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
97
|
+
<meta name="color-scheme" content="light">
|
|
98
|
+
<meta name="supported-color-schemes" content="light">
|
|
99
|
+
<style>
|
|
100
|
+
@media only screen and (max-width: 600px) {
|
|
101
|
+
.inner-body {
|
|
102
|
+
width: 100% !important;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.footer {
|
|
106
|
+
width: 100% !important;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@media only screen and (max-width: 500px) {
|
|
111
|
+
.button {
|
|
112
|
+
width: 100% !important;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
</style>
|
|
116
|
+
|
|
117
|
+
</head>`;
|
|
118
|
+
function button(label, url, level) {
|
|
119
|
+
const { name, color } = BUTTON_COLORS[level];
|
|
120
|
+
const look = `-webkit-text-size-adjust: none; border-radius: 4px; color: #fff; display: inline-block; overflow: hidden; text-decoration: none; background-color: ${color}; border-bottom: 8px solid ${color}; border-left: 18px solid ${color}; border-right: 18px solid ${color}; border-top: 8px solid ${color}; word-break: break-all;`;
|
|
121
|
+
return [
|
|
122
|
+
`<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation" ${style(`${PREMAILER_FULL} margin: 30px auto; padding: 0; text-align: center; width: 100%; float: unset;`)}>`,
|
|
123
|
+
"<tr>",
|
|
124
|
+
`<td align="center" ${style()}>`,
|
|
125
|
+
`<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation" ${style()}>`,
|
|
126
|
+
"<tr>",
|
|
127
|
+
`<td align="center" ${style()}>`,
|
|
128
|
+
`<table border="0" cellpadding="0" cellspacing="0" role="presentation" ${style()}>`,
|
|
129
|
+
"<tr>",
|
|
130
|
+
`<td ${style()}>`,
|
|
131
|
+
`<a href="${attribute(url)}" class="button button-${name}" target="_blank" rel="noopener" ${style(look)}>${text(label)}</a>`,
|
|
132
|
+
"</td>",
|
|
133
|
+
"</tr>",
|
|
134
|
+
"</table>",
|
|
135
|
+
"</td>",
|
|
136
|
+
"</tr>",
|
|
137
|
+
"</table>",
|
|
138
|
+
"</td>",
|
|
139
|
+
"</tr>",
|
|
140
|
+
"</table>"
|
|
141
|
+
].join("\n");
|
|
142
|
+
}
|
|
143
|
+
function subcopy(label, url) {
|
|
144
|
+
const link = `<a ${isUnsafeLink(url) ? "" : `href="${attribute(url)}" `}${style("color: #3869d4; word-break: break-all;")}>${text(displayable(url))}</a>`;
|
|
145
|
+
return [
|
|
146
|
+
`<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation" ${style("border-top: 1px solid #e8e5ef; margin-top: 25px; padding-top: 25px;")}>`,
|
|
147
|
+
"<tr>",
|
|
148
|
+
`<td ${style()}>`,
|
|
149
|
+
`<p ${style("line-height: 1.5em; margin-top: 0; text-align: left; font-size: 14px;")}>If you're having trouble clicking the "${text(label)}" button, copy and paste the URL below`,
|
|
150
|
+
`into your web browser: <span class="break-all" ${style("word-break: break-all;")}>${link}</span></p>`,
|
|
151
|
+
"",
|
|
152
|
+
"</td>",
|
|
153
|
+
"</tr>",
|
|
154
|
+
"</table>"
|
|
155
|
+
].join("\n");
|
|
156
|
+
}
|
|
157
|
+
function greetingOf(content) {
|
|
158
|
+
if (content.greeting !== void 0 && content.greeting !== "") return content.greeting;
|
|
159
|
+
return content.level === "error" ? "Whoops!" : "Hello!";
|
|
160
|
+
}
|
|
161
|
+
function hasAction(content) {
|
|
162
|
+
return content.actionText !== void 0 && content.actionUrl !== void 0;
|
|
163
|
+
}
|
|
164
|
+
function html(content) {
|
|
165
|
+
const salutation = content.salutation === void 0 || content.salutation === "" ? `Regards,<br>
|
|
166
|
+
${text(content.appName)}` : text(content.salutation).replace(/\r?\n/g, "<br>\n");
|
|
167
|
+
const body = [
|
|
168
|
+
`<h1 ${style("color: #3d4852; font-size: 18px; font-weight: bold; margin-top: 0; text-align: left;")}>${text(greetingOf(content))}</h1>`,
|
|
169
|
+
...content.introLines.map((line) => `<p ${PARAGRAPH}>${text(line)}</p>`),
|
|
170
|
+
...hasAction(content) ? [button(content.actionText, content.actionUrl, content.level)] : [],
|
|
171
|
+
...content.outroLines.map((line) => `<p ${PARAGRAPH}>${text(line)}</p>`),
|
|
172
|
+
`<p ${PARAGRAPH}>${salutation}</p>`
|
|
173
|
+
].join("\n");
|
|
174
|
+
const after = hasAction(content) ? `${subcopy(content.actionText, content.actionUrl)}
|
|
175
|
+
` : "\n";
|
|
176
|
+
return [
|
|
177
|
+
HEAD.replace("{{title}}", () => text(content.appName)),
|
|
178
|
+
`<body ${style("-webkit-text-size-adjust: none; background-color: #ffffff; color: #718096; height: 100%; line-height: 1.4; margin: 0; padding: 0; width: 100% !important;")}>`,
|
|
179
|
+
"",
|
|
180
|
+
`<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation" ${style(`${PREMAILER_FULL} background-color: #edf2f7; margin: 0; padding: 0; width: 100%;`)}>`,
|
|
181
|
+
"<tr>",
|
|
182
|
+
`<td align="center" ${style()}>`,
|
|
183
|
+
`<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation" ${style(`${PREMAILER_FULL} margin: 0; padding: 0; width: 100%;`)}>`,
|
|
184
|
+
"<tr>",
|
|
185
|
+
`<td class="header" ${style("padding: 25px 0; text-align: center;")}>`,
|
|
186
|
+
`<a href="${attribute(content.appUrl)}" ${style("color: #3d4852; font-size: 19px; font-weight: bold; text-decoration: none; display: inline-block;")}>`,
|
|
187
|
+
text(content.appName),
|
|
188
|
+
"</a>",
|
|
189
|
+
"</td>",
|
|
190
|
+
"</tr>",
|
|
191
|
+
"",
|
|
192
|
+
"<!-- Email Body -->",
|
|
193
|
+
"<tr>",
|
|
194
|
+
`<td class="body" width="100%" cellpadding="0" cellspacing="0" ${style(`${PREMAILER_FULL} background-color: #edf2f7; border-bottom: 1px solid #edf2f7; border-top: 1px solid #edf2f7; margin: 0; padding: 0; width: 100%; border: hidden !important;`)}>`,
|
|
195
|
+
`<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation" ${style(`${PREMAILER_570} background-color: #ffffff; border-color: #e8e5ef; border-radius: 2px; border-width: 1px; box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015); margin: 0 auto; padding: 0; width: 570px;`)}>`,
|
|
196
|
+
"<!-- Body content -->",
|
|
197
|
+
"<tr>",
|
|
198
|
+
`<td class="content-cell" ${style("max-width: 100vw; padding: 32px;")}>`,
|
|
199
|
+
`${body}
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
${after}</td>`,
|
|
203
|
+
"</tr>",
|
|
204
|
+
"</table>",
|
|
205
|
+
"</td>",
|
|
206
|
+
"</tr>",
|
|
207
|
+
"",
|
|
208
|
+
"<tr>",
|
|
209
|
+
`<td ${style()}>`,
|
|
210
|
+
`<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation" ${style(`${PREMAILER_570} margin: 0 auto; padding: 0; text-align: center; width: 570px;`)}>`,
|
|
211
|
+
"<tr>",
|
|
212
|
+
`<td class="content-cell" align="center" ${style("max-width: 100vw; padding: 32px;")}>`,
|
|
213
|
+
`<p ${style("line-height: 1.5em; margin-top: 0; color: #b0adc5; font-size: 12px; text-align: center;")}>\xA9 ${content.year} ${text(content.appName)}. All rights reserved.</p>`,
|
|
214
|
+
"",
|
|
215
|
+
"</td>",
|
|
216
|
+
"</tr>",
|
|
217
|
+
"</table>",
|
|
218
|
+
"</td>",
|
|
219
|
+
"</tr>",
|
|
220
|
+
"</table>",
|
|
221
|
+
"</td>",
|
|
222
|
+
"</tr>",
|
|
223
|
+
"</table>",
|
|
224
|
+
"</body>",
|
|
225
|
+
"</html>"
|
|
226
|
+
].join("\n");
|
|
227
|
+
}
|
|
228
|
+
function plain(content) {
|
|
229
|
+
const parts = [`${content.appName}: ${content.appUrl}`, `# ${greetingOf(content)}`, ...content.introLines];
|
|
230
|
+
if (hasAction(content)) parts.push(`${content.actionText}: ${content.actionUrl}`);
|
|
231
|
+
parts.push(...content.outroLines);
|
|
232
|
+
parts.push(content.salutation === void 0 || content.salutation === "" ? `Regards,
|
|
233
|
+
${content.appName}` : content.salutation);
|
|
234
|
+
if (hasAction(content)) {
|
|
235
|
+
parts.push(
|
|
236
|
+
`If you're having trouble clicking the "${content.actionText}" button, copy and paste the URL below
|
|
237
|
+
into your web browser: [${displayable(content.actionUrl)}](${content.actionUrl})`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
parts.push(`\xA9 ${content.year} ${content.appName}. All rights reserved.`);
|
|
241
|
+
return `${parts.join("\n\n")}
|
|
242
|
+
`;
|
|
243
|
+
}
|
|
244
|
+
function renderNotificationMail(content) {
|
|
245
|
+
return { html: html(content), text: plain(content) };
|
|
246
|
+
}
|
|
247
|
+
|
|
69
248
|
// MailMessage.ts
|
|
70
249
|
var MailMessage = class {
|
|
250
|
+
/** info, success or error: the colour of the button, and error() greets with "Whoops!". Laravel's `$level`. */
|
|
251
|
+
levelName = "info";
|
|
71
252
|
subjectLine;
|
|
72
253
|
greetingLine;
|
|
73
254
|
introLines = [];
|
|
74
255
|
actionData;
|
|
75
256
|
outroLines = [];
|
|
76
|
-
|
|
257
|
+
/** Unset: "Regards," and config('app.name'), as Laravel's template. */
|
|
258
|
+
salutationLine;
|
|
77
259
|
toAddresses = [];
|
|
78
260
|
ccAddresses = [];
|
|
79
261
|
bccAddresses = [];
|
|
262
|
+
replyToAddresses = [];
|
|
80
263
|
fromAddress;
|
|
264
|
+
attachments = [];
|
|
265
|
+
/** A green button: something went well. */
|
|
266
|
+
success() {
|
|
267
|
+
return this.level("success");
|
|
268
|
+
}
|
|
269
|
+
/** A red button, and "Whoops!" when there is no greeting: something went wrong. */
|
|
270
|
+
error() {
|
|
271
|
+
return this.level("error");
|
|
272
|
+
}
|
|
273
|
+
level(level) {
|
|
274
|
+
this.levelName = level;
|
|
275
|
+
return this;
|
|
276
|
+
}
|
|
81
277
|
subject(subject) {
|
|
82
278
|
this.subjectLine = subject;
|
|
83
279
|
return this;
|
|
@@ -87,17 +283,25 @@ var MailMessage = class {
|
|
|
87
283
|
return this;
|
|
88
284
|
}
|
|
89
285
|
/** Before action(), appends to the intro; after action(), to the outro. */
|
|
90
|
-
line(
|
|
286
|
+
line(text2) {
|
|
91
287
|
;
|
|
92
|
-
(this.actionData ? this.outroLines : this.introLines).push(
|
|
288
|
+
(this.actionData ? this.outroLines : this.introLines).push(text2);
|
|
93
289
|
return this;
|
|
94
290
|
}
|
|
95
291
|
lines(lines) {
|
|
96
292
|
for (const line of lines) this.line(line);
|
|
97
293
|
return this;
|
|
98
294
|
}
|
|
99
|
-
|
|
100
|
-
|
|
295
|
+
/** line(), when the condition holds. */
|
|
296
|
+
lineIf(condition, text2) {
|
|
297
|
+
return condition ? this.line(text2) : this;
|
|
298
|
+
}
|
|
299
|
+
/** lines(), when the condition holds. */
|
|
300
|
+
linesIf(condition, lines) {
|
|
301
|
+
return condition ? this.lines(lines) : this;
|
|
302
|
+
}
|
|
303
|
+
action(text2, url) {
|
|
304
|
+
this.actionData = { text: text2, url };
|
|
101
305
|
return this;
|
|
102
306
|
}
|
|
103
307
|
salutation(salutation) {
|
|
@@ -120,38 +324,48 @@ var MailMessage = class {
|
|
|
120
324
|
this.bccAddresses.push(...Array.isArray(recipients) ? recipients : [recipients]);
|
|
121
325
|
return this;
|
|
122
326
|
}
|
|
123
|
-
|
|
327
|
+
replyTo(recipients) {
|
|
328
|
+
this.replyToAddresses.push(...Array.isArray(recipients) ? recipients : [recipients]);
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
/** Attaches a file by its path: `as` names it in the mail (its file name by default), `mime` gives its type. */
|
|
332
|
+
attach(path, options = {}) {
|
|
333
|
+
this.attachments.push({ name: options.as ?? path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1), path, ...options.mime === void 0 ? {} : { mime: options.mime } });
|
|
334
|
+
return this;
|
|
335
|
+
}
|
|
336
|
+
/** Attaches bytes or text under a file name. */
|
|
337
|
+
attachData(data, name, options = {}) {
|
|
338
|
+
this.attachments.push({ name, data, ...options.mime === void 0 ? {} : { mime: options.mime } });
|
|
339
|
+
return this;
|
|
340
|
+
}
|
|
341
|
+
/** Renders Laravel's default notification mail, HTML and text. No ViewFactory involved. */
|
|
124
342
|
render() {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
for (const line of this.outroLines) {
|
|
138
|
-
html.push(`<p>${escapeHtml(line)}</p>`);
|
|
139
|
-
text.push(line);
|
|
140
|
-
}
|
|
141
|
-
html.push(`<p>${escapeHtml(this.salutationLine).replace(/\n/g, "<br>")}</p>`);
|
|
142
|
-
text.push(this.salutationLine);
|
|
143
|
-
return { html: `<!doctype html><html><body>${html.join("\n")}</body></html>`, text: text.join("\n\n") };
|
|
343
|
+
return renderNotificationMail({
|
|
344
|
+
level: this.levelName,
|
|
345
|
+
greeting: this.greetingLine,
|
|
346
|
+
introLines: this.introLines,
|
|
347
|
+
actionText: this.actionData?.text,
|
|
348
|
+
actionUrl: this.actionData?.url,
|
|
349
|
+
outroLines: this.outroLines,
|
|
350
|
+
salutation: this.salutationLine,
|
|
351
|
+
appName: appConfig("app.name", "Oriel"),
|
|
352
|
+
appUrl: appConfig("app.url", "http://localhost"),
|
|
353
|
+
year: (/* @__PURE__ */ new Date()).getFullYear()
|
|
354
|
+
});
|
|
144
355
|
}
|
|
145
356
|
};
|
|
146
|
-
function
|
|
147
|
-
|
|
357
|
+
function appConfig(key, fallback) {
|
|
358
|
+
const container = currentContainer();
|
|
359
|
+
const value = container?.bound(ConfigRepository) ? container.make(ConfigRepository).get(key) : void 0;
|
|
360
|
+
return typeof value === "string" && value !== "" ? value : fallback;
|
|
148
361
|
}
|
|
149
362
|
|
|
150
363
|
// channels/MailNotificationChannel.ts
|
|
151
364
|
import {
|
|
152
365
|
MailManager as MailManagerToken
|
|
153
366
|
} from "@orieljs/contracts";
|
|
154
|
-
import { currentContainer } from "@orieljs/container";
|
|
367
|
+
import { currentContainer as currentContainer2 } from "@orieljs/container";
|
|
368
|
+
import { Str } from "@orieljs/support";
|
|
155
369
|
function isMailable(value) {
|
|
156
370
|
return typeof value === "object" && value !== null && typeof value.envelope === "function" && typeof value.content === "function";
|
|
157
371
|
}
|
|
@@ -170,19 +384,21 @@ function toMailMessage(result, notificationName) {
|
|
|
170
384
|
if (result.salutation) message.salutation(result.salutation);
|
|
171
385
|
return message;
|
|
172
386
|
}
|
|
173
|
-
function mailableFromMessage(message, fallbackTo) {
|
|
387
|
+
function mailableFromMessage(message, fallbackTo, notificationName) {
|
|
174
388
|
return {
|
|
175
389
|
envelope: async () => ({
|
|
176
|
-
subject: message.subjectLine
|
|
390
|
+
subject: message.subjectLine || Str.title(Str.snake(notificationName, " ")),
|
|
177
391
|
to: message.toAddresses.length > 0 ? message.toAddresses : fallbackTo,
|
|
178
392
|
cc: message.ccAddresses,
|
|
179
393
|
bcc: message.bccAddresses,
|
|
394
|
+
replyTo: message.replyToAddresses,
|
|
180
395
|
from: message.fromAddress
|
|
181
396
|
}),
|
|
182
397
|
content: async () => {
|
|
183
398
|
const rendered = message.render();
|
|
184
399
|
return { htmlString: rendered.html, textString: rendered.text };
|
|
185
|
-
}
|
|
400
|
+
},
|
|
401
|
+
attachments: async () => message.attachments
|
|
186
402
|
};
|
|
187
403
|
}
|
|
188
404
|
function normalizeRoute(route) {
|
|
@@ -195,10 +411,10 @@ var MailNotificationChannel = class {
|
|
|
195
411
|
const result = await notification.toMail(notifiable);
|
|
196
412
|
const to = normalizeRoute(notifiable.routeNotificationFor("mail", notification));
|
|
197
413
|
if (to.length === 0 && !isMailable(result)) return;
|
|
198
|
-
const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to);
|
|
199
|
-
const container =
|
|
414
|
+
const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to, notification.constructor.name);
|
|
415
|
+
const container = currentContainer2();
|
|
200
416
|
if (!container?.bound(MailManagerToken)) throw new MailUnavailableError();
|
|
201
|
-
await container.make(MailManagerToken).
|
|
417
|
+
await container.make(MailManagerToken).sendNow(mailable);
|
|
202
418
|
}
|
|
203
419
|
};
|
|
204
420
|
|
|
@@ -207,49 +423,12 @@ import { randomUUID } from "crypto";
|
|
|
207
423
|
import {
|
|
208
424
|
DatabaseManager as DatabaseManagerToken
|
|
209
425
|
} from "@orieljs/contracts";
|
|
210
|
-
import { currentContainer as
|
|
426
|
+
import { currentContainer as currentContainer3 } from "@orieljs/container";
|
|
211
427
|
var NOTIFICATIONS_TABLE = "notifications";
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (value instanceof Date) return value;
|
|
215
|
-
if (typeof value === "number") return new Date(value);
|
|
216
|
-
const text = String(value);
|
|
217
|
-
return ZONELESS_TIMESTAMP.test(text) ? /* @__PURE__ */ new Date(`${text.replace(" ", "T")}Z`) : new Date(text);
|
|
218
|
-
}
|
|
219
|
-
function wrapRow(row, db) {
|
|
220
|
-
const record = {
|
|
221
|
-
id: String(row.id),
|
|
222
|
-
type: String(row.type),
|
|
223
|
-
data: JSON.parse(String(row.data)),
|
|
224
|
-
createdAt: parseStoredDate(row.created_at),
|
|
225
|
-
updatedAt: parseStoredDate(row.updated_at),
|
|
226
|
-
readAt: row.read_at ? parseStoredDate(row.read_at) : void 0,
|
|
227
|
-
async markAsRead() {
|
|
228
|
-
if (record.readAt) return;
|
|
229
|
-
const now = /* @__PURE__ */ new Date();
|
|
230
|
-
await db.table(NOTIFICATIONS_TABLE).where("id", record.id).update({ read_at: now, updated_at: now });
|
|
231
|
-
record.readAt = now;
|
|
232
|
-
},
|
|
233
|
-
async markAsUnread() {
|
|
234
|
-
if (!record.readAt) return;
|
|
235
|
-
await db.table(NOTIFICATIONS_TABLE).where("id", record.id).update({ read_at: null, updated_at: /* @__PURE__ */ new Date() });
|
|
236
|
-
record.readAt = void 0;
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
return record;
|
|
240
|
-
}
|
|
241
|
-
function requireDatabase() {
|
|
242
|
-
const container = currentContainer2();
|
|
428
|
+
function requireDatabase(connection) {
|
|
429
|
+
const container = currentContainer3();
|
|
243
430
|
if (!container?.bound(DatabaseManagerToken)) throw new DatabaseUnavailableError();
|
|
244
|
-
return container.make(DatabaseManagerToken).connection();
|
|
245
|
-
}
|
|
246
|
-
async function fetchNotifications(notifiableType, notifiableId, filter = "all") {
|
|
247
|
-
const db = requireDatabase();
|
|
248
|
-
let query = db.table(NOTIFICATIONS_TABLE).where("notifiable_type", notifiableType).where("notifiable_id", notifiableId).orderBy("created_at", "desc");
|
|
249
|
-
if (filter === "unread") query = query.whereNull("read_at");
|
|
250
|
-
if (filter === "read") query = query.whereNotNull("read_at");
|
|
251
|
-
const rows = await query.get();
|
|
252
|
-
return rows.all().map((row) => wrapRow(row, db));
|
|
431
|
+
return container.make(DatabaseManagerToken).connection(connection);
|
|
253
432
|
}
|
|
254
433
|
var DatabaseNotificationChannel = class {
|
|
255
434
|
async send(notifiable, notification) {
|
|
@@ -259,12 +438,12 @@ var DatabaseNotificationChannel = class {
|
|
|
259
438
|
const data = notification.toDatabase ? notification.toDatabase(notifiable) : notification.toArray(notifiable);
|
|
260
439
|
const model = notifiable;
|
|
261
440
|
if (typeof model.getKey !== "function") throw new NotDatabaseNotifiableError(notifiable.constructor.name);
|
|
262
|
-
const db = requireDatabase();
|
|
441
|
+
const db = requireDatabase(typeof model.getConnectionName === "function" ? model.getConnectionName() : void 0);
|
|
263
442
|
const now = /* @__PURE__ */ new Date();
|
|
264
443
|
await db.table(NOTIFICATIONS_TABLE).insert({
|
|
265
444
|
id: notification.id ?? randomUUID(),
|
|
266
445
|
type: notification.constructor.name,
|
|
267
|
-
notifiable_type: notifiable.constructor.name,
|
|
446
|
+
notifiable_type: typeof model.getMorphClass === "function" ? model.getMorphClass() : notifiable.constructor.name,
|
|
268
447
|
notifiable_id: model.getKey(),
|
|
269
448
|
data: JSON.stringify(data),
|
|
270
449
|
read_at: null,
|
|
@@ -297,6 +476,20 @@ export default class extends Migration {
|
|
|
297
476
|
|
|
298
477
|
// symbols.ts
|
|
299
478
|
var SHOULD_QUEUE = /* @__PURE__ */ Symbol.for("oriel:bus.shouldQueue");
|
|
479
|
+
var UNIQUE = /* @__PURE__ */ Symbol.for("oriel:bus.unique");
|
|
480
|
+
function uniqueSpecOf(target, name) {
|
|
481
|
+
const members = target;
|
|
482
|
+
const declared = typeof members.uniqueId === "function" || members.uniqueFor !== void 0 || members.uniqueUntilProcessing === true || typeof members.uniqueVia === "function";
|
|
483
|
+
if (!declared) return void 0;
|
|
484
|
+
const id = typeof members.uniqueId === "function" ? members.uniqueId.call(target) : void 0;
|
|
485
|
+
const spec = { name, id: id === void 0 || id === null ? "" : String(id) };
|
|
486
|
+
const uniqueFor = typeof members.uniqueFor === "function" ? members.uniqueFor.call(target) : members.uniqueFor;
|
|
487
|
+
if (typeof uniqueFor === "number") spec.for = uniqueFor;
|
|
488
|
+
if (members.uniqueUntilProcessing === true) spec.untilProcessing = true;
|
|
489
|
+
const via = typeof members.uniqueVia === "function" ? members.uniqueVia.call(target) : void 0;
|
|
490
|
+
if (typeof via === "string") spec.via = via;
|
|
491
|
+
return spec;
|
|
492
|
+
}
|
|
300
493
|
|
|
301
494
|
// Dispatcher.ts
|
|
302
495
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -304,26 +497,21 @@ import {
|
|
|
304
497
|
BusDispatcher as BusDispatcherToken
|
|
305
498
|
} from "@orieljs/contracts";
|
|
306
499
|
|
|
307
|
-
//
|
|
500
|
+
// RoutesNotifications.ts
|
|
308
501
|
import {
|
|
309
502
|
NotificationDispatcher as NotificationDispatcherToken
|
|
310
503
|
} from "@orieljs/contracts";
|
|
311
|
-
import { currentContainer as
|
|
312
|
-
import { Str } from "@orieljs/support";
|
|
504
|
+
import { currentContainer as currentContainer4 } from "@orieljs/container";
|
|
505
|
+
import { Str as Str2 } from "@orieljs/support";
|
|
313
506
|
function resolveDispatcher() {
|
|
314
|
-
const container =
|
|
507
|
+
const container = currentContainer4();
|
|
315
508
|
if (!container?.bound(NotificationDispatcherToken)) throw new NotificationDispatcherUnavailableError();
|
|
316
509
|
return container.make(NotificationDispatcherToken);
|
|
317
510
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
320
|
-
if (typeof model.getKey !== "function") throw new NotDatabaseNotifiableError(instance.constructor.name);
|
|
321
|
-
return model.getKey();
|
|
322
|
-
}
|
|
323
|
-
function Notifiable(Base) {
|
|
324
|
-
return class NotifiableMixin extends Base {
|
|
511
|
+
function RoutesNotifications(Base) {
|
|
512
|
+
return class RoutesNotificationsMixin extends Base {
|
|
325
513
|
routeNotificationFor(channel, notification) {
|
|
326
|
-
const custom = this[`routeNotificationFor${
|
|
514
|
+
const custom = this[`routeNotificationFor${Str2.studly(channel)}`];
|
|
327
515
|
if (typeof custom === "function") return custom.call(this, notification);
|
|
328
516
|
if (channel === "mail") {
|
|
329
517
|
const withAttrs = this;
|
|
@@ -338,15 +526,6 @@ function Notifiable(Base) {
|
|
|
338
526
|
async notifyNow(notification, channels) {
|
|
339
527
|
await resolveDispatcher().sendNow(this, notification, channels);
|
|
340
528
|
}
|
|
341
|
-
notifications() {
|
|
342
|
-
return fetchNotifications(this.constructor.name, requireKey(this));
|
|
343
|
-
}
|
|
344
|
-
unreadNotifications() {
|
|
345
|
-
return fetchNotifications(this.constructor.name, requireKey(this), "unread");
|
|
346
|
-
}
|
|
347
|
-
readNotifications() {
|
|
348
|
-
return fetchNotifications(this.constructor.name, requireKey(this), "read");
|
|
349
|
-
}
|
|
350
529
|
};
|
|
351
530
|
}
|
|
352
531
|
|
|
@@ -370,6 +549,7 @@ var AnonymousNotifiable = class {
|
|
|
370
549
|
};
|
|
371
550
|
|
|
372
551
|
// Jobs/SendQueuedNotifications.ts
|
|
552
|
+
import { createHash } from "crypto";
|
|
373
553
|
import { NotificationDispatcher as NotificationDispatcherToken2 } from "@orieljs/contracts";
|
|
374
554
|
import { inject } from "@orieljs/container";
|
|
375
555
|
var QUEUE_OPTIONS = [
|
|
@@ -388,12 +568,25 @@ function copyShouldQueueFields(target, source) {
|
|
|
388
568
|
if (fields[option] !== void 0) target[option] = fields[option];
|
|
389
569
|
}
|
|
390
570
|
}
|
|
571
|
+
function notifiableIdentity(notifiable, channel, notification) {
|
|
572
|
+
const model = notifiable;
|
|
573
|
+
if (typeof model.getKey === "function") return `${model.getMorphClass?.() ?? notifiable.constructor.name}:${String(model.getKey())}`;
|
|
574
|
+
const route = channel === void 0 ? void 0 : notifiable.routeNotificationFor(channel, notification);
|
|
575
|
+
if (route === void 0 || route === null) return notifiable.constructor.name;
|
|
576
|
+
return createHash("sha1").update(typeof route === "string" ? route : JSON.stringify(route)).digest("hex");
|
|
577
|
+
}
|
|
391
578
|
var SendQueuedNotifications = class {
|
|
392
579
|
constructor(notifiables, notification, channels) {
|
|
393
580
|
this.notifiables = notifiables;
|
|
394
581
|
this.notification = notification;
|
|
395
582
|
this.channels = channels;
|
|
396
583
|
copyShouldQueueFields(this, notification);
|
|
584
|
+
const unique = uniqueSpecOf(notification, notification.constructor.name);
|
|
585
|
+
if (unique) {
|
|
586
|
+
const recipients = notifiables.map((notifiable) => notifiableIdentity(notifiable, channels?.[0], notification));
|
|
587
|
+
unique.id = [unique.id, channels?.join(",") ?? "", recipients.join(",")].join(":");
|
|
588
|
+
this[UNIQUE] = unique;
|
|
589
|
+
}
|
|
397
590
|
}
|
|
398
591
|
notifiables;
|
|
399
592
|
notification;
|
|
@@ -424,7 +617,7 @@ SendQueuedNotifications.prototype[SHOULD_QUEUE] = true;
|
|
|
424
617
|
// Dispatcher.ts
|
|
425
618
|
function isQueueableNotification(notification) {
|
|
426
619
|
const record = notification;
|
|
427
|
-
if (record[SHOULD_QUEUE] === true) return true;
|
|
620
|
+
if (record.shouldQueue === true || record[SHOULD_QUEUE] === true) return true;
|
|
428
621
|
return record.connection !== void 0 || record.queue !== void 0 || record.delay !== void 0 || record.tries !== void 0 || record.backoff !== void 0 || record.timeout !== void 0 || typeof record.uniqueId === "function" || typeof record.retryUntil === "function";
|
|
429
622
|
}
|
|
430
623
|
function forNotifiable(notification) {
|
|
@@ -553,19 +746,37 @@ export {
|
|
|
553
746
|
NOTIFICATIONS_TABLE,
|
|
554
747
|
NOTIFICATIONS_TABLE_MIGRATION,
|
|
555
748
|
NotDatabaseNotifiableError,
|
|
556
|
-
Notifiable,
|
|
557
749
|
Notification,
|
|
558
750
|
NotificationDispatcherUnavailableError,
|
|
559
751
|
NotificationFacade,
|
|
560
752
|
NotificationQueueUnavailableError,
|
|
561
753
|
NotificationsServiceProvider,
|
|
754
|
+
RoutesNotifications,
|
|
562
755
|
SHOULD_QUEUE,
|
|
563
756
|
SendQueuedNotifications,
|
|
564
757
|
UnknownNotificationChannelError,
|
|
565
758
|
UnrenderableNotificationDataError,
|
|
566
759
|
UnrenderableNotificationMailError,
|
|
567
|
-
fetchNotifications,
|
|
568
|
-
parseStoredDate,
|
|
569
760
|
toNotifiableList
|
|
570
761
|
};
|
|
762
|
+
/*!
|
|
763
|
+
* Laravel's default notification mail (the `notifications::email` view in the `mail::` markdown components with the
|
|
764
|
+
* `default` theme), as Laravel 12 sends it: the same HTML, with the theme's CSS inlined as CssToInlineStyles inlines
|
|
765
|
+
* it, and the same plain-text part. The lines are plain text (escaped), not Markdown.
|
|
766
|
+
*
|
|
767
|
+
* laravel/framework: Copyright (c) Taylor Otwell
|
|
768
|
+
*
|
|
769
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
770
|
+
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
771
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
772
|
+
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
773
|
+
*
|
|
774
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
775
|
+
* Software.
|
|
776
|
+
*
|
|
777
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
778
|
+
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
779
|
+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
780
|
+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
781
|
+
*/
|
|
571
782
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../errors.ts","../MailMessage.ts","../channels/MailNotificationChannel.ts","../channels/DatabaseNotificationChannel.ts","../migrations.ts","../symbols.ts","../Dispatcher.ts","../Notifiable.ts","../AnonymousNotifiable.ts","../Jobs/SendQueuedNotifications.ts","../Notification.ts","../facade.ts","../NotificationsServiceProvider.ts"],"sourcesContent":["import type { OrielError } from '@orieljs/contracts'\n\nexport class UnrenderableNotificationMailError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnrenderableNotificationMailError'\n constructor(notification: string) {\n super(\n `[${notification}].toMail() must return a Mailable, a MailMessage, or ` +\n '{ subject?, greeting?, lines, actionText?, actionUrl?, outroLines?, salutation? }.',\n )\n }\n}\n\nexport class MailUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'MailUnavailableError'\n constructor() {\n super(\"Sending a notification's mail requires the Mail component. Register MailManager first.\")\n }\n}\n\nexport class DatabaseUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'DatabaseUnavailableError'\n constructor() {\n super('The database notification channel requires the Database component. Register DatabaseManager first.')\n }\n}\n\nexport class NotDatabaseNotifiableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotDatabaseNotifiableError'\n constructor(notifiable: string) {\n super(`[${notifiable}] has no getKey() — the database channel and notification-listing methods need a model.`)\n }\n}\n\nexport class UnrenderableNotificationDataError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnrenderableNotificationDataError'\n constructor(notification: string) {\n super(`[${notification}] needs a toDatabase() or toArray() method to use the database channel.`)\n }\n}\n\nexport class UnknownNotificationChannelError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnknownNotificationChannelError'\n constructor(channel: string) {\n super(`Notification channel [${channel}] is not registered. Register it first with NotificationDispatcher.extend().`)\n }\n}\n\nexport class NotificationQueueUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotificationQueueUnavailableError'\n constructor() {\n super('Queueing a notification requires the Bus component. Register BusDispatcher, or call sendNow() instead.')\n }\n}\n\nexport class NotificationDispatcherUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotificationDispatcherUnavailableError'\n constructor() {\n super('notify()/notifyNow() require the Notifications component. Register NotificationDispatcher first.')\n }\n}\n","import type { MailRecipient } from '@orieljs/contracts'\n\nexport interface MailMessageAction {\n text: string\n url: string\n}\n\n/**\n * Laravel's Illuminate\\Notifications\\Messages\\MailMessage: greeting/line/action/salutation, rendered to a default\n * HTML+text template with no View involved. line() appends to the intro before action() is called, and to the\n * outro afterwards — exactly like Laravel's SimpleMessage.\n */\nexport class MailMessage {\n subjectLine?: string\n greetingLine?: string\n introLines: string[] = []\n actionData?: MailMessageAction\n outroLines: string[] = []\n salutationLine = 'Regards,\\nApplication'\n toAddresses: MailRecipient[] = []\n ccAddresses: MailRecipient[] = []\n bccAddresses: MailRecipient[] = []\n fromAddress?: MailRecipient\n\n subject(subject: string): this {\n this.subjectLine = subject\n return this\n }\n\n greeting(greeting: string): this {\n this.greetingLine = greeting\n return this\n }\n\n /** Before action(), appends to the intro; after action(), to the outro. */\n line(text: string): this {\n ;(this.actionData ? this.outroLines : this.introLines).push(text)\n return this\n }\n\n lines(lines: string[]): this {\n for (const line of lines) this.line(line)\n return this\n }\n\n action(text: string, url: string): this {\n this.actionData = { text, url }\n return this\n }\n\n salutation(salutation: string): this {\n this.salutationLine = salutation\n return this\n }\n\n from(address: MailRecipient): this {\n this.fromAddress = address\n return this\n }\n\n to(recipients: MailRecipient | MailRecipient[]): this {\n this.toAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n cc(recipients: MailRecipient | MailRecipient[]): this {\n this.ccAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n bcc(recipients: MailRecipient | MailRecipient[]): this {\n this.bccAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n /** Renders the default notification mail template. No ViewFactory involved. */\n render(): { html: string; text: string } {\n const greeting = this.greetingLine ?? 'Hello!'\n const html: string[] = [`<p>${escapeHtml(greeting)}</p>`]\n const text: string[] = [greeting]\n\n for (const line of this.introLines) {\n html.push(`<p>${escapeHtml(line)}</p>`)\n text.push(line)\n }\n if (this.actionData) {\n const { text: actionText, url } = this.actionData\n html.push(`<p><a href=\"${escapeHtml(url)}\" class=\"button\" target=\"_blank\">${escapeHtml(actionText)}</a></p>`)\n text.push(`${actionText}: ${url}`)\n }\n for (const line of this.outroLines) {\n html.push(`<p>${escapeHtml(line)}</p>`)\n text.push(line)\n }\n html.push(`<p>${escapeHtml(this.salutationLine).replace(/\\n/g, '<br>')}</p>`)\n text.push(this.salutationLine)\n\n return { html: `<!doctype html><html><body>${html.join('\\n')}</body></html>`, text: text.join('\\n\\n') }\n }\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n}\n","import {\n MailManager as MailManagerToken,\n type Mailable,\n type MailRecipient,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { MailUnavailableError, UnrenderableNotificationMailError } from '../errors'\nimport { MailMessage } from '../MailMessage'\n\n/**\n * What Auth's default ResetPassword notification's toMail() returns: plain data, since Auth can't import\n * Notifications. `{ subject, lines, actionText, actionUrl, outroLines }` exactly; `greeting`/`salutation` are\n * optional extensions any other notification's plain toMail() may also use.\n */\nexport interface PlainNotificationMail {\n subject?: string\n greeting?: string\n lines: string[]\n actionText?: string\n actionUrl?: string\n outroLines?: string[]\n salutation?: string\n}\n\nfunction isMailable(value: unknown): value is Mailable {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Mailable).envelope === 'function' &&\n typeof (value as Mailable).content === 'function'\n )\n}\n\nfunction isPlainNotificationMail(value: unknown): value is PlainNotificationMail {\n return typeof value === 'object' && value !== null && Array.isArray((value as PlainNotificationMail).lines)\n}\n\nfunction toMailMessage(result: unknown, notificationName: string): MailMessage {\n if (result instanceof MailMessage) return result\n if (!isPlainNotificationMail(result)) throw new UnrenderableNotificationMailError(notificationName)\n const message = new MailMessage()\n if (result.subject) message.subject(result.subject)\n if (result.greeting) message.greeting(result.greeting)\n message.lines(result.lines)\n if (result.actionText && result.actionUrl) message.action(result.actionText, result.actionUrl)\n for (const line of result.outroLines ?? []) message.line(line)\n if (result.salutation) message.salutation(result.salutation)\n return message\n}\n\n/**\n * Wraps a MailMessage as a Mailable, so it can go through MailManager.send() without a View: the default template's\n * HTML goes in `htmlString` and its plain-text twin in `textString`.\n */\nfunction mailableFromMessage(message: MailMessage, fallbackTo: MailRecipient[]): Mailable {\n return {\n envelope: async () => ({\n subject: message.subjectLine ?? '',\n to: message.toAddresses.length > 0 ? message.toAddresses : fallbackTo,\n cc: message.ccAddresses,\n bcc: message.bccAddresses,\n from: message.fromAddress,\n }),\n content: async () => {\n const rendered = message.render()\n return { htmlString: rendered.html, textString: rendered.text }\n },\n }\n}\n\nfunction normalizeRoute(route: unknown): MailRecipient[] {\n if (route === undefined || route === null) return []\n return Array.isArray(route) ? (route as MailRecipient[]) : [route as MailRecipient]\n}\n\nexport class MailNotificationChannel implements NotificationChannel {\n async send(notifiable: Notifiable, notification: Notification): Promise<void> {\n if (!notification.toMail) throw new UnrenderableNotificationMailError(notification.constructor.name)\n const result = await notification.toMail(notifiable)\n const to = normalizeRoute(notifiable.routeNotificationFor('mail', notification))\n\n // Matches Laravel's MailChannel: with no route and no Mailable to supply its own recipients, there's nothing to send.\n if (to.length === 0 && !isMailable(result)) return\n\n const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to)\n const container = currentContainer()\n if (!container?.bound(MailManagerToken)) throw new MailUnavailableError()\n // MailManager.send() is the only send path the contract offers, so a queueable Mailable returned from toMail()\n // is queued again from here (even when this notification is itself running on a queue worker).\n await container.make(MailManagerToken).send(mailable)\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n DatabaseManager as DatabaseManagerToken,\n type DatabaseConnection,\n type ModelInstance,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n type Row,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { DatabaseUnavailableError, NotDatabaseNotifiableError, UnrenderableNotificationDataError } from '../errors'\n\nexport const NOTIFICATIONS_TABLE = 'notifications'\n\nexport interface DatabaseNotificationRecord {\n readonly id: string\n readonly type: string\n readonly data: Record<string, unknown>\n readonly createdAt: Date\n readonly updatedAt: Date\n readAt: Date | undefined\n markAsRead(): Promise<void>\n markAsUnread(): Promise<void>\n}\n\nconst ZONELESS_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?$/\n\n/**\n * Database writes Dates as zoneless UTC (`'2026-09-25 04:30:00'`) and hands them back as strings, so\n * `new Date()` would read such a string as local time; it is parsed as UTC here instead.\n */\nexport function parseStoredDate(value: unknown): Date {\n if (value instanceof Date) return value\n if (typeof value === 'number') return new Date(value)\n const text = String(value)\n return ZONELESS_TIMESTAMP.test(text) ? new Date(`${text.replace(' ', 'T')}Z`) : new Date(text)\n}\n\nfunction wrapRow(row: Row, db: DatabaseConnection): DatabaseNotificationRecord {\n const record: DatabaseNotificationRecord = {\n id: String(row.id),\n type: String(row.type),\n data: JSON.parse(String(row.data)) as Record<string, unknown>,\n createdAt: parseStoredDate(row.created_at),\n updatedAt: parseStoredDate(row.updated_at),\n readAt: row.read_at ? parseStoredDate(row.read_at) : undefined,\n async markAsRead() {\n if (record.readAt) return\n const now = new Date()\n await db.table(NOTIFICATIONS_TABLE).where('id', record.id).update({ read_at: now, updated_at: now })\n record.readAt = now\n },\n async markAsUnread() {\n if (!record.readAt) return\n await db.table(NOTIFICATIONS_TABLE).where('id', record.id).update({ read_at: null, updated_at: new Date() })\n record.readAt = undefined\n },\n }\n return record\n}\n\nfunction requireDatabase(): DatabaseConnection {\n const container = currentContainer()\n if (!container?.bound(DatabaseManagerToken)) throw new DatabaseUnavailableError()\n return container.make(DatabaseManagerToken).connection()\n}\n\n/** notifiable_type + notifiable_id together, never id alone. */\nexport async function fetchNotifications(\n notifiableType: string,\n notifiableId: unknown,\n filter: 'all' | 'read' | 'unread' = 'all',\n): Promise<DatabaseNotificationRecord[]> {\n const db = requireDatabase()\n let query = db\n .table(NOTIFICATIONS_TABLE)\n .where('notifiable_type', notifiableType)\n .where('notifiable_id', notifiableId)\n .orderBy('created_at', 'desc')\n if (filter === 'unread') query = query.whereNull('read_at')\n if (filter === 'read') query = query.whereNotNull('read_at')\n const rows = await query.get()\n return rows.all().map((row) => wrapRow(row, db))\n}\n\nexport class DatabaseNotificationChannel implements NotificationChannel {\n async send(notifiable: Notifiable, notification: Notification): Promise<void> {\n if (!notification.toDatabase && !notification.toArray) {\n throw new UnrenderableNotificationDataError(notification.constructor.name)\n }\n const data = notification.toDatabase ? notification.toDatabase(notifiable) : notification.toArray!(notifiable)\n const model = notifiable as Partial<ModelInstance>\n if (typeof model.getKey !== 'function') throw new NotDatabaseNotifiableError(notifiable.constructor.name)\n\n const db = requireDatabase()\n const now = new Date()\n await db.table(NOTIFICATIONS_TABLE).insert({\n id: notification.id ?? randomUUID(),\n type: notification.constructor.name,\n notifiable_type: notifiable.constructor.name,\n notifiable_id: model.getKey(),\n data: JSON.stringify(data),\n read_at: null,\n created_at: now,\n updated_at: now,\n })\n }\n}\n","export const NOTIFICATIONS_TABLE_MIGRATION = `import { Migration, Schema } from '@orieljs/framework'\n\nexport default class extends Migration {\n async up() {\n await Schema.create('notifications', (t) => {\n t.uuid('id').primary()\n t.string('type')\n t.morphs('notifiable')\n t.text('data')\n t.timestamp('read_at').nullable()\n t.timestamps()\n })\n }\n\n async down() {\n await Schema.dropIfExists('notifications')\n }\n}\n`\n","/**\n * The Queue/Bus protocol's shared marker (the \"Queue/Bus protocol\" section of the Queue/Bus design notes) —\n * identical to Mail's `symbols.ts`. `Symbol.for` is a *global* registry key, so this is the literal same runtime\n * symbol Bus's `Queueable` base class and `isQueueable()` use, with no import needed (and none allowed).\n */\nexport const SHOULD_QUEUE: unique symbol = Symbol.for('oriel:bus.shouldQueue')\n","import { randomUUID } from 'node:crypto'\nimport {\n BusDispatcher as BusDispatcherToken,\n type Container,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n type NotificationDispatcher as NotificationDispatcherContract,\n} from '@orieljs/contracts'\nimport { AnonymousNotifiable } from './AnonymousNotifiable'\nimport { NotificationQueueUnavailableError, UnknownNotificationChannelError } from './errors'\nimport { SendQueuedNotifications } from './Jobs/SendQueuedNotifications'\nimport { SHOULD_QUEUE } from './symbols'\n\n/**\n * Matches the Queue/Bus protocol's `isQueueable()` exactly: true when the notification carries\n * `SHOULD_QUEUE === true`, or has a defined value for any of ShouldQueue's fields, or declares a\n * `uniqueId()`/`retryUntil()` method (same rule as Mail's `isQueueableMailable`). `implements ShouldQueue` itself\n * leaves no runtime trace. Call sendNow() explicitly to bypass this for a notification with none of these.\n */\nfunction isQueueableNotification(notification: Notification): boolean {\n const record = notification as unknown as Record<PropertyKey, unknown>\n if (record[SHOULD_QUEUE] === true) return true\n return (\n record.connection !== undefined ||\n record.queue !== undefined ||\n record.delay !== undefined ||\n record.tries !== undefined ||\n record.backoff !== undefined ||\n record.timeout !== undefined ||\n typeof record.uniqueId === 'function' ||\n typeof record.retryUntil === 'function'\n )\n}\n\n/**\n * The notification one notifiable receives. With no id set, a shallow copy (same prototype, own fields copied)\n * carrying a fresh id, so every notifiable gets its own id (the database channel's primary key) and the caller's\n * instance is left untouched; a caller-preset id is kept as is. The copy keeps its class, so Queue's serializer\n * restores it as that class on the worker.\n */\nfunction forNotifiable(notification: Notification): Notification {\n if (notification.id) return notification\n const copy = Object.assign(Object.create(Object.getPrototypeOf(notification) as object) as Notification, notification)\n copy.id = randomUUID()\n return copy\n}\n\n/**\n * One notifiable, an array, or any other iterable of them (a Database `Collection` from `await User.where(...).get()`,\n * a Set, ...), as Laravel's `Notification::send($users, ...)` accepts. A notifiable is recognised by its\n * routeNotificationFor() method first, so an iterable model is still treated as one notifiable.\n */\nexport function toNotifiableList(notifiables: Notifiable | Iterable<Notifiable>): Notifiable[] {\n if (Array.isArray(notifiables)) return notifiables as Notifiable[]\n if (typeof (notifiables as Partial<Notifiable>).routeNotificationFor === 'function') return [notifiables as Notifiable]\n if (typeof notifiables === 'object' && notifiables !== null && Symbol.iterator in notifiables) {\n return [...(notifiables as Iterable<Notifiable>)]\n }\n return [notifiables as Notifiable]\n}\n\n/** Resolves channels by name from a factory map — mirrors MailManager's driver resolution. */\nexport class Dispatcher implements NotificationDispatcherContract {\n private readonly factories = new Map<string, () => NotificationChannel>()\n private readonly channels = new Map<string, NotificationChannel>()\n\n constructor(private readonly container: Container) {}\n\n extend(channel: string, factory: () => NotificationChannel): void {\n this.factories.set(channel, factory)\n this.channels.delete(channel)\n }\n\n private resolve(name: string): NotificationChannel {\n const cached = this.channels.get(name)\n if (cached) return cached\n const factory = this.factories.get(name)\n if (!factory) throw new UnknownNotificationChannelError(name)\n const channel = factory()\n this.channels.set(name, channel)\n return channel\n }\n\n async send(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification): Promise<void> {\n const list = toNotifiableList(notifiables)\n if (list.length === 0) return\n if (isQueueableNotification(notification)) {\n if (!this.container.bound(BusDispatcherToken)) throw new NotificationQueueUnavailableError()\n const bus = this.container.make(BusDispatcherToken)\n // One job per notifiable per channel, as Laravel does, so a failing channel or recipient retries alone.\n for (const notifiable of list) {\n const own = forNotifiable(notification)\n for (const channel of notification.via(notifiable)) {\n await bus.dispatch(new SendQueuedNotifications([notifiable], own, [channel]))\n }\n }\n return\n }\n await this.sendNow(list, notification)\n }\n\n async sendNow(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification, channels?: string[]): Promise<void> {\n const list = toNotifiableList(notifiables)\n for (const notifiable of list) {\n const own = forNotifiable(notification)\n const names = channels ?? notification.via(notifiable)\n for (const name of names) {\n // An on-demand notifiable has no model to store a row against, so it skips 'database' as in Laravel.\n if (name === 'database' && notifiable instanceof AnonymousNotifiable) continue\n await this.resolve(name).send(notifiable, own)\n }\n }\n }\n\n /** On-demand: an AnonymousNotifiable, whose notify() goes through the bound NotificationDispatcher. */\n route(channel: string, route: unknown): AnonymousNotifiable {\n return new AnonymousNotifiable().route(channel, route)\n }\n}\n","import {\n NotificationDispatcher as NotificationDispatcherToken,\n type ModelInstance,\n type Notifiable as NotifiableContract,\n type Notification,\n type NotificationDispatcher,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { Str } from '@orieljs/support'\nimport { fetchNotifications, type DatabaseNotificationRecord } from './channels/DatabaseNotificationChannel'\nimport { NotDatabaseNotifiableError, NotificationDispatcherUnavailableError } from './errors'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type NotifiableConstructor = new (...args: any[]) => object\n\n/** The bound NotificationDispatcher, or a clear error when the Notifications component isn't registered. */\nexport function resolveDispatcher(): NotificationDispatcher {\n const container = currentContainer()\n if (!container?.bound(NotificationDispatcherToken)) throw new NotificationDispatcherUnavailableError()\n return container.make(NotificationDispatcherToken)\n}\n\nfunction requireKey(instance: object): unknown {\n const model = instance as Partial<ModelInstance>\n if (typeof model.getKey !== 'function') throw new NotDatabaseNotifiableError(instance.constructor.name)\n return model.getKey()\n}\n\n/**\n * Mirrors Laravel's Illuminate\\Notifications\\Notifiable trait: `class User extends Notifiable(Model) {}`. Custom\n * routing per channel follows Laravel's own resolution order: a `routeNotificationFor<Channel>()` method (Studly\n * name) wins if present, else 'mail' reads `email`/`getAttribute('email')`, 'database' returns the instance itself\n * (the database channel derives the table row from it directly), and anything else is unrouted (`undefined`).\n */\nexport function Notifiable<TBase extends NotifiableConstructor>(Base: TBase) {\n return class NotifiableMixin extends Base implements NotifiableContract {\n routeNotificationFor(channel: string, notification: Notification): unknown {\n const custom = (this as unknown as Record<string, unknown>)[`routeNotificationFor${Str.studly(channel)}`]\n if (typeof custom === 'function') return (custom as (n: Notification) => unknown).call(this, notification)\n if (channel === 'mail') {\n const withAttrs = this as unknown as { email?: string; getAttribute?: (key: string) => unknown }\n return typeof withAttrs.getAttribute === 'function' ? withAttrs.getAttribute('email') : withAttrs.email\n }\n if (channel === 'database') return this\n return undefined\n }\n\n async notify(notification: Notification): Promise<void> {\n await resolveDispatcher().send(this as unknown as NotifiableContract, notification)\n }\n\n async notifyNow(notification: Notification, channels?: string[]): Promise<void> {\n await resolveDispatcher().sendNow(this as unknown as NotifiableContract, notification, channels)\n }\n\n notifications(): Promise<DatabaseNotificationRecord[]> {\n return fetchNotifications(this.constructor.name, requireKey(this))\n }\n\n unreadNotifications(): Promise<DatabaseNotificationRecord[]> {\n return fetchNotifications(this.constructor.name, requireKey(this), 'unread')\n }\n\n readNotifications(): Promise<DatabaseNotificationRecord[]> {\n return fetchNotifications(this.constructor.name, requireKey(this), 'read')\n }\n }\n}\n","import type { Notifiable, Notification } from '@orieljs/contracts'\nimport { resolveDispatcher } from './Notifiable'\n\n/**\n * Returned by `Notification.route(channel, address)` for sending to an address with no model behind it. The\n * routes are an own plain record and every method sits on the prototype, so a queued on-demand notification\n * survives Queue's serializer (which rejects functions in own fields) and is restored on the worker as this class\n * (registered as `'Notifications.AnonymousNotifiable'` in ClassRegistry).\n */\nexport class AnonymousNotifiable implements Notifiable {\n routes: Record<string, unknown> = {}\n\n /** Adds a route; chains, so one on-demand notifiable can go to several channels. */\n route(channel: string, route: unknown): this {\n this.routes[channel] = route\n return this\n }\n\n routeNotificationFor(channel: string): unknown {\n return this.routes[channel]\n }\n\n async notify(notification: Notification): Promise<void> {\n await resolveDispatcher().send(this, notification)\n }\n\n async notifyNow(notification: Notification, channels?: string[]): Promise<void> {\n await resolveDispatcher().sendNow(this, notification, channels)\n }\n}\n","import { NotificationDispatcher as NotificationDispatcherToken, type Job, type Notifiable, type Notification } from '@orieljs/contracts'\nimport { inject } from '@orieljs/container'\nimport { SHOULD_QUEUE } from '../symbols'\n\n/** The ShouldQueue options copied from the notification onto the wrapper job. */\nconst QUEUE_OPTIONS = [\n 'connection',\n 'queue',\n 'delay',\n 'tries',\n 'backoff',\n 'timeout',\n 'failOnTimeout',\n 'deleteWhenMissingModels',\n] as const\n\n/** The job hooks Queue reads off a job instance, when the wrapped notification defines them. */\ninterface JobHooks {\n failed?: (error: unknown) => unknown\n retryUntil?: () => unknown\n middleware?: unknown[] | (() => unknown[])\n}\n\n/**\n * Copies the notification's own ShouldQueue fields onto the wrapper job (when set) — same reasoning as Mail's\n * SendQueuedMailable. Uses `declare` fields on the class (no runtime auto-initialization to `undefined`), so a\n * field is a genuinely own property only when the notification actually set it.\n */\nfunction copyShouldQueueFields(target: Record<PropertyKey, unknown>, source: object): void {\n const fields = source as Record<string, unknown>\n for (const option of QUEUE_OPTIONS) {\n if (fields[option] !== undefined) target[option] = fields[option]\n }\n}\n\n/**\n * Dispatched by Dispatcher.send() when the notification is queueable. Runs on the worker: calls sendNow()\n * directly (never send()), so it can't re-detect itself as queueable and loop.\n *\n * Carries `SHOULD_QUEUE` on its own prototype, per the Queue/Bus protocol, so `BusDispatcher.dispatch()` — which\n * re-decides queued-or-now on whatever job it's handed — actually enqueues it instead of running it inline; the\n * notification's own queueability (checked by Dispatcher, above) only decides whether to wrap it at all. Like\n * Laravel's SendQueuedNotifications, it forwards failed(), retryUntil(), middleware() and displayName() to the\n * notification.\n */\nexport class SendQueuedNotifications implements Job {\n declare connection?: string\n declare queue?: string\n declare delay?: number\n declare tries?: number\n declare backoff?: number | number[]\n declare timeout?: number\n declare failOnTimeout?: boolean\n declare deleteWhenMissingModels?: boolean\n\n constructor(\n readonly notifiables: Notifiable[],\n readonly notification: Notification,\n readonly channels?: string[],\n ) {\n copyShouldQueueFields(this as unknown as Record<PropertyKey, unknown>, notification)\n }\n\n async handle(): Promise<void> {\n await inject(NotificationDispatcherToken).sendNow(this.notifiables, this.notification, this.channels)\n }\n\n /** Called by Queue after the final failed attempt. */\n async failed(error: unknown): Promise<void> {\n const hooks = this.notification as unknown as JobHooks\n if (typeof hooks.failed === 'function') await hooks.failed.call(this.notification, error)\n }\n\n /** `failed_jobs` and `queue:failed` show the notification's class, not the wrapper's. */\n displayName(): string {\n return this.notification.constructor.name\n }\n\n retryUntil(): unknown {\n const hooks = this.notification as unknown as JobHooks\n return typeof hooks.retryUntil === 'function' ? hooks.retryUntil.call(this.notification) : undefined\n }\n\n middleware(): unknown[] {\n const declared = (this.notification as unknown as JobHooks).middleware\n return (typeof declared === 'function' ? declared.call(this.notification) : declared) ?? []\n }\n}\n;(SendQueuedNotifications.prototype as unknown as Record<PropertyKey, unknown>)[SHOULD_QUEUE] = true\n","import {\n NotificationDispatcher as NotificationDispatcherToken,\n type Notifiable,\n type Notification as NotificationContract,\n type NotificationChannel,\n type NotificationDispatcher,\n} from '@orieljs/contracts'\nimport { facade } from '@orieljs/support'\nimport { toNotifiableList } from './Dispatcher'\nimport { resolveDispatcher } from './Notifiable'\n\n/**\n * Mirrors Laravel's Illuminate\\Notifications\\Notification: override via(), and whichever toXxx() the channels\n * need.\n *\n * The statics are the `Notification` facade, so one export serves both `class InvoicePaid extends Notification`\n * and `Notification.send(users, new InvoicePaid())`.\n */\nexport abstract class Notification implements NotificationContract {\n id?: string\n abstract via(notifiable: Notifiable): string[]\n\n /** Queues it when the notification is queueable, otherwise sends now. Accepts one notifiable, an array or any iterable. */\n static send(notifiables: Notifiable | Iterable<Notifiable>, notification: NotificationContract): Promise<void> {\n return resolveDispatcher().send(toNotifiableList(notifiables), notification)\n }\n\n static sendNow(\n notifiables: Notifiable | Iterable<Notifiable>,\n notification: NotificationContract,\n channels?: string[],\n ): Promise<void> {\n return resolveDispatcher().sendNow(toNotifiableList(notifiables), notification, channels)\n }\n\n /** On-demand: `Notification.route('mail', 'a@b.c').notify(new InvoicePaid())`. */\n static route(channel: string, route: unknown): Notifiable & { notify(notification: NotificationContract): Promise<void> } {\n return resolveDispatcher().route(channel, route)\n }\n\n static extend(channel: string, factory: () => NotificationChannel): void {\n resolveDispatcher().extend(channel, factory)\n }\n\n /** Replace the dispatcher in the current container (what a NotificationFake uses). */\n static swap(fake: NotificationDispatcher): void {\n facade<NotificationDispatcher>(NotificationDispatcherToken).swap(fake)\n }\n}\n","import type { NotificationDispatcher } from '@orieljs/contracts'\nimport { NotificationDispatcher as NotificationDispatcherToken } from '@orieljs/contracts'\nimport { facade } from '@orieljs/support'\n\n/**\n * The plain facade over NotificationDispatcher. App code normally uses the `Notification` base class's statics\n * instead (one `Notification` export is both the base class and the facade); this stays exported for code that\n * wants the contract-typed proxy (getFacadeRoot(), swap()).\n */\nexport const NotificationFacade = facade<NotificationDispatcher>(NotificationDispatcherToken)\n","import { ClassRegistry as ClassRegistryToken, NotificationDispatcher as NotificationDispatcherToken } from '@orieljs/contracts'\nimport { ServiceProvider } from '@orieljs/support'\nimport { AnonymousNotifiable } from './AnonymousNotifiable'\nimport { DatabaseNotificationChannel } from './channels/DatabaseNotificationChannel'\nimport { MailNotificationChannel } from './channels/MailNotificationChannel'\nimport { Dispatcher } from './Dispatcher'\nimport { SendQueuedNotifications } from './Jobs/SendQueuedNotifications'\n\nexport class NotificationsServiceProvider extends ServiceProvider {\n override register(): void {\n this.app.singleton(NotificationDispatcherToken, (container) => new Dispatcher(container))\n // Namespaced names, registered in register() (not boot()) — the Queue/Bus protocol rule for wrapper classes.\n // ClassRegistry is safe to use here: Application's own constructor binds it before any provider registers.\n const registry = this.app.make(ClassRegistryToken)\n registry.register(SendQueuedNotifications, 'Notifications.SendQueuedNotifications')\n registry.register(AnonymousNotifiable, 'Notifications.AnonymousNotifiable')\n }\n\n override boot(): void {\n const dispatcher = this.app.make(NotificationDispatcherToken)\n dispatcher.extend('mail', () => new MailNotificationChannel())\n dispatcher.extend('database', () => new DatabaseNotificationChannel())\n }\n}\n"],"mappings":"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","names":["currentContainer","currentContainer","randomUUID","currentContainer","currentContainer","NotificationDispatcherToken","NotificationDispatcherToken","randomUUID","NotificationDispatcherToken","NotificationDispatcherToken","NotificationDispatcherToken","facade","NotificationDispatcherToken","NotificationDispatcherToken"]}
|
|
1
|
+
{"version":3,"sources":["../errors.ts","../MailMessage.ts","../mail/template.ts","../channels/MailNotificationChannel.ts","../channels/DatabaseNotificationChannel.ts","../migrations.ts","../symbols.ts","../Dispatcher.ts","../RoutesNotifications.ts","../AnonymousNotifiable.ts","../Jobs/SendQueuedNotifications.ts","../Notification.ts","../facade.ts","../NotificationsServiceProvider.ts"],"sourcesContent":["import type { OrielError } from '@orieljs/contracts'\n\nexport class UnrenderableNotificationMailError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnrenderableNotificationMailError'\n constructor(notification: string) {\n super(\n `[${notification}].toMail() must return a Mailable, a MailMessage, or ` +\n '{ subject?, greeting?, lines, actionText?, actionUrl?, outroLines?, salutation? }.',\n )\n }\n}\n\nexport class MailUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'MailUnavailableError'\n constructor() {\n super(\"Sending a notification's mail requires the Mail component. Register MailManager first.\")\n }\n}\n\nexport class DatabaseUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'DatabaseUnavailableError'\n constructor() {\n super('The database notification channel requires the Database component. Register DatabaseManager first.')\n }\n}\n\nexport class NotDatabaseNotifiableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotDatabaseNotifiableError'\n constructor(notifiable: string) {\n super(`[${notifiable}] has no getKey() — the database channel and notification-listing methods need a model.`)\n }\n}\n\nexport class UnrenderableNotificationDataError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnrenderableNotificationDataError'\n constructor(notification: string) {\n super(`[${notification}] needs a toDatabase() or toArray() method to use the database channel.`)\n }\n}\n\nexport class UnknownNotificationChannelError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnknownNotificationChannelError'\n constructor(channel: string) {\n super(`Notification channel [${channel}] is not registered. Register it first with NotificationDispatcher.extend().`)\n }\n}\n\nexport class NotificationQueueUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotificationQueueUnavailableError'\n constructor() {\n super('Queueing a notification requires the Bus component. Register BusDispatcher, or call sendNow() instead.')\n }\n}\n\nexport class NotificationDispatcherUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotificationDispatcherUnavailableError'\n constructor() {\n super('notify()/notifyNow() require the Notifications component. Register NotificationDispatcher first.')\n }\n}\n","import { type Attachment, ConfigRepository, type MailRecipient } from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { type MailLevel, renderNotificationMail } from './mail/template'\n\nexport interface MailMessageAction {\n text: string\n url: string\n}\n\n/**\n * Laravel's Illuminate\\Notifications\\Messages\\MailMessage: greeting/line/action/salutation, rendered to Laravel's\n * default notification mail (HTML and text) with no View involved. line() appends to the intro before action() is\n * called, and to the outro afterwards — exactly like Laravel's SimpleMessage.\n */\nexport class MailMessage {\n /** info, success or error: the colour of the button, and error() greets with \"Whoops!\". Laravel's `$level`. */\n levelName: MailLevel = 'info'\n subjectLine?: string\n greetingLine?: string\n introLines: string[] = []\n actionData?: MailMessageAction\n outroLines: string[] = []\n /** Unset: \"Regards,\" and config('app.name'), as Laravel's template. */\n salutationLine?: string\n toAddresses: MailRecipient[] = []\n ccAddresses: MailRecipient[] = []\n bccAddresses: MailRecipient[] = []\n replyToAddresses: MailRecipient[] = []\n fromAddress?: MailRecipient\n attachments: Attachment[] = []\n\n /** A green button: something went well. */\n success(): this {\n return this.level('success')\n }\n\n /** A red button, and \"Whoops!\" when there is no greeting: something went wrong. */\n error(): this {\n return this.level('error')\n }\n\n level(level: MailLevel): this {\n this.levelName = level\n return this\n }\n\n subject(subject: string): this {\n this.subjectLine = subject\n return this\n }\n\n greeting(greeting: string): this {\n this.greetingLine = greeting\n return this\n }\n\n /** Before action(), appends to the intro; after action(), to the outro. */\n line(text: string): this {\n ;(this.actionData ? this.outroLines : this.introLines).push(text)\n return this\n }\n\n lines(lines: string[]): this {\n for (const line of lines) this.line(line)\n return this\n }\n\n /** line(), when the condition holds. */\n lineIf(condition: boolean, text: string): this {\n return condition ? this.line(text) : this\n }\n\n /** lines(), when the condition holds. */\n linesIf(condition: boolean, lines: string[]): this {\n return condition ? this.lines(lines) : this\n }\n\n action(text: string, url: string): this {\n this.actionData = { text, url }\n return this\n }\n\n salutation(salutation: string): this {\n this.salutationLine = salutation\n return this\n }\n\n from(address: MailRecipient): this {\n this.fromAddress = address\n return this\n }\n\n to(recipients: MailRecipient | MailRecipient[]): this {\n this.toAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n cc(recipients: MailRecipient | MailRecipient[]): this {\n this.ccAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n bcc(recipients: MailRecipient | MailRecipient[]): this {\n this.bccAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n replyTo(recipients: MailRecipient | MailRecipient[]): this {\n this.replyToAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n /** Attaches a file by its path: `as` names it in the mail (its file name by default), `mime` gives its type. */\n attach(path: string, options: { as?: string; mime?: string } = {}): this {\n this.attachments.push({ name: options.as ?? path.slice(Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\\\')) + 1), path, ...(options.mime === undefined ? {} : { mime: options.mime }) })\n return this\n }\n\n /** Attaches bytes or text under a file name. */\n attachData(data: Uint8Array | string, name: string, options: { mime?: string } = {}): this {\n this.attachments.push({ name, data, ...(options.mime === undefined ? {} : { mime: options.mime }) })\n return this\n }\n\n /** Renders Laravel's default notification mail, HTML and text. No ViewFactory involved. */\n render(): { html: string; text: string } {\n return renderNotificationMail({\n level: this.levelName,\n greeting: this.greetingLine,\n introLines: this.introLines,\n actionText: this.actionData?.text,\n actionUrl: this.actionData?.url,\n outroLines: this.outroLines,\n salutation: this.salutationLine,\n appName: appConfig('app.name', 'Oriel'),\n appUrl: appConfig('app.url', 'http://localhost'),\n year: new Date().getFullYear(),\n })\n }\n}\n\n/** A config value (`app.name`, `app.url`) when there is a container with config and the value is set, else `fallback`. */\nfunction appConfig(key: string, fallback: string): string {\n const container = currentContainer()\n const value = container?.bound(ConfigRepository) ? container.make(ConfigRepository).get<unknown>(key) : undefined\n return typeof value === 'string' && value !== '' ? value : fallback\n}\n","/*!\n * Laravel's default notification mail (the `notifications::email` view in the `mail::` markdown components with the\n * `default` theme), as Laravel 12 sends it: the same HTML, with the theme's CSS inlined as CssToInlineStyles inlines\n * it, and the same plain-text part. The lines are plain text (escaped), not Markdown.\n *\n * laravel/framework: Copyright (c) Taylor Otwell\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n * documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n * Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE\n * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\n * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\nexport type MailLevel = 'info' | 'success' | 'error'\n\nexport interface NotificationMailContent {\n level: MailLevel\n greeting: string | undefined\n introLines: readonly string[]\n actionText: string | undefined\n actionUrl: string | undefined\n outroLines: readonly string[]\n salutation: string | undefined\n appName: string\n appUrl: string\n year: number\n}\n\n/** The base rule of the theme (`body *`), which every element gets first. */\nconst BASE =\n \"box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; position: relative;\"\n\nconst style = (rules = ''): string => `style=\"${BASE}${rules === '' ? '' : ` ${rules}`}\"`\n\nconst PREMAILER_FULL = '-premailer-cellpadding: 0; -premailer-cellspacing: 0; -premailer-width: 100%;'\nconst PREMAILER_570 = '-premailer-cellpadding: 0; -premailer-cellspacing: 0; -premailer-width: 570px;'\nconst PARAGRAPH = style('font-size: 16px; line-height: 1.5em; margin-top: 0; text-align: left;')\n\n/** The theme's button colours: `.button-primary`, `.button-success`, `.button-error`. */\nconst BUTTON_COLORS: Record<MailLevel, { name: string; color: string }> = {\n info: { name: 'primary', color: '#2d3748' },\n success: { name: 'success', color: '#48bb78' },\n error: { name: 'error', color: '#e53e3e' },\n}\n\n/** Text content: &, < and > (quotes stay as written, as Laravel's output has them). */\nconst text = (value: string): string => value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')\n\n/** An attribute value: the text characters and the double quote. */\nconst attribute = (value: string): string => text(value).replace(/\"/g, '"')\n\n/** Laravel's displayableActionUrl: the URL without a mailto: or tel: scheme. */\nconst displayable = (url: string): string => url.replace(/mailto:|tel:/g, '')\n\n/**\n * CommonMark's isLinkPotentiallyUnsafe, which drops the subcopy link's href (Laravel's Markdown sets\n * `allow_unsafe_links` off). It tests the destination CommonMark parsed, so the leading spaces and line break the\n * parser skips are skipped here too. A URL with other leading control characters or space, or a tab or line break\n * inside, is not a link destination at all (Laravel shows no link), and a browser would still run it, so those are\n * skipped as well rather than kept as a live href.\n */\nconst isUnsafeLink = (url: string): boolean => {\n const destination = url.replace(/^[\\x00-\\x20]+/, '').replace(/[\\t\\n\\r]/g, '')\n return /^javascript:|vbscript:|file:|data:/i.test(destination) && !/^data:image\\/(?:png|gif|jpeg|webp)/i.test(destination)\n}\n\nconst HEAD = `<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<title>{{title}}</title>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n<meta name=\"color-scheme\" content=\"light\">\n<meta name=\"supported-color-schemes\" content=\"light\">\n<style>\n@media only screen and (max-width: 600px) {\n.inner-body {\nwidth: 100% !important;\n}\n\n.footer {\nwidth: 100% !important;\n}\n}\n\n@media only screen and (max-width: 500px) {\n.button {\nwidth: 100% !important;\n}\n}\n</style>\n\n</head>`\n\nfunction button(label: string, url: string, level: MailLevel): string {\n const { name, color } = BUTTON_COLORS[level]\n const look = `-webkit-text-size-adjust: none; border-radius: 4px; color: #fff; display: inline-block; overflow: hidden; text-decoration: none; background-color: ${color}; border-bottom: 8px solid ${color}; border-left: 18px solid ${color}; border-right: 18px solid ${color}; border-top: 8px solid ${color}; word-break: break-all;`\n return [\n `<table class=\"action\" align=\"center\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style(`${PREMAILER_FULL} margin: 30px auto; padding: 0; text-align: center; width: 100%; float: unset;`)}>`,\n '<tr>',\n `<td align=\"center\" ${style()}>`,\n `<table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style()}>`,\n '<tr>',\n `<td align=\"center\" ${style()}>`,\n `<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style()}>`,\n '<tr>',\n `<td ${style()}>`,\n `<a href=\"${attribute(url)}\" class=\"button button-${name}\" target=\"_blank\" rel=\"noopener\" ${style(look)}>${text(label)}</a>`,\n '</td>',\n '</tr>',\n '</table>',\n '</td>',\n '</tr>',\n '</table>',\n '</td>',\n '</tr>',\n '</table>',\n ].join('\\n')\n}\n\nfunction subcopy(label: string, url: string): string {\n const link = `<a ${isUnsafeLink(url) ? '' : `href=\"${attribute(url)}\" `}${style('color: #3869d4; word-break: break-all;')}>${text(displayable(url))}</a>`\n return [\n `<table class=\"subcopy\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style('border-top: 1px solid #e8e5ef; margin-top: 25px; padding-top: 25px;')}>`,\n '<tr>',\n `<td ${style()}>`,\n `<p ${style('line-height: 1.5em; margin-top: 0; text-align: left; font-size: 14px;')}>If you're having trouble clicking the \"${text(label)}\" button, copy and paste the URL below`,\n `into your web browser: <span class=\"break-all\" ${style('word-break: break-all;')}>${link}</span></p>`,\n '',\n '</td>',\n '</tr>',\n '</table>',\n ].join('\\n')\n}\n\nfunction greetingOf(content: NotificationMailContent): string {\n if (content.greeting !== undefined && content.greeting !== '') return content.greeting\n return content.level === 'error' ? 'Whoops!' : 'Hello!'\n}\n\nfunction hasAction(content: NotificationMailContent): content is NotificationMailContent & { actionText: string; actionUrl: string } {\n return content.actionText !== undefined && content.actionUrl !== undefined\n}\n\n/** The HTML part, byte for byte the markup Laravel 12 sends for the same message. */\nfunction html(content: NotificationMailContent): string {\n const salutation =\n content.salutation === undefined || content.salutation === ''\n ? `Regards,<br>\\n${text(content.appName)}`\n : text(content.salutation).replace(/\\r?\\n/g, '<br>\\n')\n const body = [\n `<h1 ${style('color: #3d4852; font-size: 18px; font-weight: bold; margin-top: 0; text-align: left;')}>${text(greetingOf(content))}</h1>`,\n ...content.introLines.map((line) => `<p ${PARAGRAPH}>${text(line)}</p>`),\n ...(hasAction(content) ? [button(content.actionText, content.actionUrl, content.level)] : []),\n ...content.outroLines.map((line) => `<p ${PARAGRAPH}>${text(line)}</p>`),\n `<p ${PARAGRAPH}>${salutation}</p>`,\n ].join('\\n')\n const after = hasAction(content) ? `${subcopy(content.actionText, content.actionUrl)}\\n` : '\\n'\n return [\n HEAD.replace('{{title}}', () => text(content.appName)),\n `<body ${style('-webkit-text-size-adjust: none; background-color: #ffffff; color: #718096; height: 100%; line-height: 1.4; margin: 0; padding: 0; width: 100% !important;')}>`,\n '',\n `<table class=\"wrapper\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style(`${PREMAILER_FULL} background-color: #edf2f7; margin: 0; padding: 0; width: 100%;`)}>`,\n '<tr>',\n `<td align=\"center\" ${style()}>`,\n `<table class=\"content\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style(`${PREMAILER_FULL} margin: 0; padding: 0; width: 100%;`)}>`,\n '<tr>',\n `<td class=\"header\" ${style('padding: 25px 0; text-align: center;')}>`,\n `<a href=\"${attribute(content.appUrl)}\" ${style('color: #3d4852; font-size: 19px; font-weight: bold; text-decoration: none; display: inline-block;')}>`,\n text(content.appName),\n '</a>',\n '</td>',\n '</tr>',\n '',\n '<!-- Email Body -->',\n '<tr>',\n `<td class=\"body\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" ${style(`${PREMAILER_FULL} background-color: #edf2f7; border-bottom: 1px solid #edf2f7; border-top: 1px solid #edf2f7; margin: 0; padding: 0; width: 100%; border: hidden !important;`)}>`,\n `<table class=\"inner-body\" align=\"center\" width=\"570\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style(`${PREMAILER_570} background-color: #ffffff; border-color: #e8e5ef; border-radius: 2px; border-width: 1px; box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015); margin: 0 auto; padding: 0; width: 570px;`)}>`,\n '<!-- Body content -->',\n '<tr>',\n `<td class=\"content-cell\" ${style('max-width: 100vw; padding: 32px;')}>`,\n `${body}\\n\\n\\n${after}</td>`,\n '</tr>',\n '</table>',\n '</td>',\n '</tr>',\n '',\n '<tr>',\n `<td ${style()}>`,\n `<table class=\"footer\" align=\"center\" width=\"570\" cellpadding=\"0\" cellspacing=\"0\" role=\"presentation\" ${style(`${PREMAILER_570} margin: 0 auto; padding: 0; text-align: center; width: 570px;`)}>`,\n '<tr>',\n `<td class=\"content-cell\" align=\"center\" ${style('max-width: 100vw; padding: 32px;')}>`,\n `<p ${style('line-height: 1.5em; margin-top: 0; color: #b0adc5; font-size: 12px; text-align: center;')}>© ${content.year} ${text(content.appName)}. All rights reserved.</p>`,\n '',\n '</td>',\n '</tr>',\n '</table>',\n '</td>',\n '</tr>',\n '</table>',\n '</td>',\n '</tr>',\n '</table>',\n '</body>',\n '</html>',\n ].join('\\n')\n}\n\n/** The plain-text part, as Laravel's text components write it. */\nfunction plain(content: NotificationMailContent): string {\n const parts = [`${content.appName}: ${content.appUrl}`, `# ${greetingOf(content)}`, ...content.introLines]\n if (hasAction(content)) parts.push(`${content.actionText}: ${content.actionUrl}`)\n parts.push(...content.outroLines)\n parts.push(content.salutation === undefined || content.salutation === '' ? `Regards,\\n${content.appName}` : content.salutation)\n if (hasAction(content)) {\n parts.push(\n `If you're having trouble clicking the \"${content.actionText}\" button, copy and paste the URL below\\ninto your web browser: [${displayable(content.actionUrl)}](${content.actionUrl})`,\n )\n }\n parts.push(`© ${content.year} ${content.appName}. All rights reserved.`)\n return `${parts.join('\\n\\n')}\\n`\n}\n\nexport function renderNotificationMail(content: NotificationMailContent): { html: string; text: string } {\n return { html: html(content), text: plain(content) }\n}\n","import {\n MailManager as MailManagerToken,\n type Mailable,\n type MailRecipient,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { Str } from '@orieljs/support'\nimport { MailUnavailableError, UnrenderableNotificationMailError } from '../errors'\nimport { MailMessage } from '../MailMessage'\n\n/**\n * What Auth's default ResetPassword notification's toMail() returns: plain data, since Auth can't import\n * Notifications. `{ subject, lines, actionText, actionUrl, outroLines }` exactly; `greeting`/`salutation` are\n * optional extensions any other notification's plain toMail() may also use.\n */\nexport interface PlainNotificationMail {\n subject?: string\n greeting?: string\n lines: string[]\n actionText?: string\n actionUrl?: string\n outroLines?: string[]\n salutation?: string\n}\n\nfunction isMailable(value: unknown): value is Mailable {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Mailable).envelope === 'function' &&\n typeof (value as Mailable).content === 'function'\n )\n}\n\nfunction isPlainNotificationMail(value: unknown): value is PlainNotificationMail {\n return typeof value === 'object' && value !== null && Array.isArray((value as PlainNotificationMail).lines)\n}\n\nfunction toMailMessage(result: unknown, notificationName: string): MailMessage {\n if (result instanceof MailMessage) return result\n if (!isPlainNotificationMail(result)) throw new UnrenderableNotificationMailError(notificationName)\n const message = new MailMessage()\n if (result.subject) message.subject(result.subject)\n if (result.greeting) message.greeting(result.greeting)\n message.lines(result.lines)\n if (result.actionText && result.actionUrl) message.action(result.actionText, result.actionUrl)\n for (const line of result.outroLines ?? []) message.line(line)\n if (result.salutation) message.salutation(result.salutation)\n return message\n}\n\n/**\n * Wraps a MailMessage as a Mailable, so it can go through MailManager.sendNow() without a View: the default template's\n * HTML goes in `htmlString` and its plain-text twin in `textString`. Without subject(), the subject is the\n * notification's class name in words (`InvoicePaid` → \"Invoice Paid\"), as Laravel's MailChannel names it.\n */\nfunction mailableFromMessage(message: MailMessage, fallbackTo: MailRecipient[], notificationName: string): Mailable {\n return {\n envelope: async () => ({\n subject: message.subjectLine || Str.title(Str.snake(notificationName, ' ')),\n to: message.toAddresses.length > 0 ? message.toAddresses : fallbackTo,\n cc: message.ccAddresses,\n bcc: message.bccAddresses,\n replyTo: message.replyToAddresses,\n from: message.fromAddress,\n }),\n content: async () => {\n const rendered = message.render()\n return { htmlString: rendered.html, textString: rendered.text }\n },\n attachments: async () => message.attachments,\n }\n}\n\nfunction normalizeRoute(route: unknown): MailRecipient[] {\n if (route === undefined || route === null) return []\n return Array.isArray(route) ? (route as MailRecipient[]) : [route as MailRecipient]\n}\n\nexport class MailNotificationChannel implements NotificationChannel {\n async send(notifiable: Notifiable, notification: Notification): Promise<void> {\n if (!notification.toMail) throw new UnrenderableNotificationMailError(notification.constructor.name)\n const result = await notification.toMail(notifiable)\n const to = normalizeRoute(notifiable.routeNotificationFor('mail', notification))\n\n // Matches Laravel's MailChannel: with no route and no Mailable to supply its own recipients, there's nothing to send.\n if (to.length === 0 && !isMailable(result)) return\n\n const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to, notification.constructor.name)\n const container = currentContainer()\n if (!container?.bound(MailManagerToken)) throw new MailUnavailableError()\n // sendNow(), as Laravel's MailChannel sends: a queueable Mailable returned from toMail() is delivered here, not\n // queued a second time (a queued notification already runs on the worker).\n await container.make(MailManagerToken).sendNow(mailable)\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n DatabaseManager as DatabaseManagerToken,\n type DatabaseConnection,\n type ModelInstance,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { DatabaseUnavailableError, NotDatabaseNotifiableError, UnrenderableNotificationDataError } from '../errors'\n\nexport const NOTIFICATIONS_TABLE = 'notifications'\n\n/** What the channel reads from a model notifiable: ModelInstance, plus Database's connection name when it has one. */\ntype StoredNotifiable = Partial<ModelInstance> & { getConnectionName?(): string | undefined }\n\nfunction requireDatabase(connection: string | undefined): DatabaseConnection {\n const container = currentContainer()\n if (!container?.bound(DatabaseManagerToken)) throw new DatabaseUnavailableError()\n return container.make(DatabaseManagerToken).connection(connection)\n}\n\n/**\n * Laravel's DatabaseChannel: one row in `notifications` per notifiable, with what toDatabase() (or toArray())\n * returns as JSON. The row is keyed as `user.notifications()` reads it: `notifiable_type` is the model's morph class\n * (its morph map alias, else its class name) and it goes to the model's own connection.\n */\nexport class DatabaseNotificationChannel implements NotificationChannel {\n async send(notifiable: Notifiable, notification: Notification): Promise<void> {\n if (!notification.toDatabase && !notification.toArray) {\n throw new UnrenderableNotificationDataError(notification.constructor.name)\n }\n const data = notification.toDatabase ? notification.toDatabase(notifiable) : notification.toArray!(notifiable)\n const model = notifiable as StoredNotifiable\n if (typeof model.getKey !== 'function') throw new NotDatabaseNotifiableError(notifiable.constructor.name)\n\n const db = requireDatabase(typeof model.getConnectionName === 'function' ? model.getConnectionName() : undefined)\n const now = new Date()\n await db.table(NOTIFICATIONS_TABLE).insert({\n id: notification.id ?? randomUUID(),\n type: notification.constructor.name,\n notifiable_type: typeof model.getMorphClass === 'function' ? model.getMorphClass() : notifiable.constructor.name,\n notifiable_id: model.getKey(),\n data: JSON.stringify(data),\n read_at: null,\n created_at: now,\n updated_at: now,\n })\n }\n}\n","export const NOTIFICATIONS_TABLE_MIGRATION = `import { Migration, Schema } from '@orieljs/framework'\n\nexport default class extends Migration {\n async up() {\n await Schema.create('notifications', (t) => {\n t.uuid('id').primary()\n t.string('type')\n t.morphs('notifiable')\n t.text('data')\n t.timestamp('read_at').nullable()\n t.timestamps()\n })\n }\n\n async down() {\n await Schema.dropIfExists('notifications')\n }\n}\n`\n","/**\n * The Queue/Bus protocol's shared marker (the \"Queue/Bus protocol\" section of the Queue/Bus design notes) —\n * identical to Mail's `symbols.ts`. `Symbol.for` is a *global* registry key, so this is the literal same runtime\n * symbol Bus's `Queueable` base class and `isQueueable()` use, with no import needed (and none allowed).\n */\nexport const SHOULD_QUEUE: unique symbol = Symbol.for('oriel:bus.shouldQueue')\n\n/**\n * Bus's UNIQUE key, the same `Symbol.for`: the uniqueness a SendQueuedNotifications carries for its notification,\n * read by Bus's isUnique() and UniqueLock. Symbol-keyed, so the payload never carries it.\n */\nexport const UNIQUE: unique symbol = Symbol.for('oriel:bus.unique')\n\n/** Bus's UniqueSpec, declared again here (Notifications can't import Bus): the lock is `oriel_unique_job:<name>:<id>`. */\nexport interface UniqueSpec {\n name: string\n id: string\n for?: number\n untilProcessing?: boolean\n via?: string\n}\n\ninterface UniqueMembers {\n uniqueId?: () => unknown\n uniqueFor?: number | (() => unknown)\n uniqueUntilProcessing?: boolean\n uniqueVia?: () => unknown\n}\n\n/**\n * Laravel's ShouldBeUnique read off a notification, by Bus's isUnique() rule (it defines uniqueId(), uniqueFor,\n * uniqueUntilProcessing or uniqueVia()), each value resolved now, at dispatch. undefined when it declares none.\n */\nexport function uniqueSpecOf(target: object, name: string): UniqueSpec | undefined {\n const members = target as UniqueMembers\n const declared =\n typeof members.uniqueId === 'function' ||\n members.uniqueFor !== undefined ||\n members.uniqueUntilProcessing === true ||\n typeof members.uniqueVia === 'function'\n if (!declared) return undefined\n const id = typeof members.uniqueId === 'function' ? members.uniqueId.call(target) : undefined\n const spec: UniqueSpec = { name, id: id === undefined || id === null ? '' : String(id) }\n const uniqueFor = typeof members.uniqueFor === 'function' ? members.uniqueFor.call(target) : members.uniqueFor\n if (typeof uniqueFor === 'number') spec.for = uniqueFor\n if (members.uniqueUntilProcessing === true) spec.untilProcessing = true\n const via = typeof members.uniqueVia === 'function' ? members.uniqueVia.call(target) : undefined\n if (typeof via === 'string') spec.via = via\n return spec\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n BusDispatcher as BusDispatcherToken,\n type Container,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n type NotificationDispatcher as NotificationDispatcherContract,\n} from '@orieljs/contracts'\nimport { AnonymousNotifiable } from './AnonymousNotifiable'\nimport { NotificationQueueUnavailableError, UnknownNotificationChannelError } from './errors'\nimport { SendQueuedNotifications } from './Jobs/SendQueuedNotifications'\nimport { SHOULD_QUEUE } from './symbols'\n\n/**\n * Matches the Queue/Bus protocol's `isQueueable()` exactly: true when the notification carries ShouldQueue's\n * `shouldQueue: true` or `SHOULD_QUEUE === true`, or has a defined value for any of ShouldQueue's fields, or\n * declares a `uniqueId()`/`retryUntil()` method (same rule as Mail's `isQueueableMailable`). Call sendNow() to send\n * a queueable notification right away.\n */\nfunction isQueueableNotification(notification: Notification): boolean {\n const record = notification as unknown as Record<PropertyKey, unknown>\n if (record.shouldQueue === true || record[SHOULD_QUEUE] === true) return true\n return (\n record.connection !== undefined ||\n record.queue !== undefined ||\n record.delay !== undefined ||\n record.tries !== undefined ||\n record.backoff !== undefined ||\n record.timeout !== undefined ||\n typeof record.uniqueId === 'function' ||\n typeof record.retryUntil === 'function'\n )\n}\n\n/**\n * The notification one notifiable receives. With no id set, a shallow copy (same prototype, own fields copied)\n * carrying a fresh id, so every notifiable gets its own id (the database channel's primary key) and the caller's\n * instance is left untouched; a caller-preset id is kept as is. The copy keeps its class, so Queue's serializer\n * restores it as that class on the worker.\n */\nfunction forNotifiable(notification: Notification): Notification {\n if (notification.id) return notification\n const copy = Object.assign(Object.create(Object.getPrototypeOf(notification) as object) as Notification, notification)\n copy.id = randomUUID()\n return copy\n}\n\n/**\n * One notifiable, an array, or any other iterable of them (a Database `Collection` from `await User.where(...).get()`,\n * a Set, ...), as Laravel's `Notification::send($users, ...)` accepts. A notifiable is recognised by its\n * routeNotificationFor() method first, so an iterable model is still treated as one notifiable.\n */\nexport function toNotifiableList(notifiables: Notifiable | Iterable<Notifiable>): Notifiable[] {\n if (Array.isArray(notifiables)) return notifiables as Notifiable[]\n if (typeof (notifiables as Partial<Notifiable>).routeNotificationFor === 'function') return [notifiables as Notifiable]\n if (typeof notifiables === 'object' && notifiables !== null && Symbol.iterator in notifiables) {\n return [...(notifiables as Iterable<Notifiable>)]\n }\n return [notifiables as Notifiable]\n}\n\n/** Resolves channels by name from a factory map — mirrors MailManager's driver resolution. */\nexport class Dispatcher implements NotificationDispatcherContract {\n private readonly factories = new Map<string, () => NotificationChannel>()\n private readonly channels = new Map<string, NotificationChannel>()\n\n constructor(private readonly container: Container) {}\n\n extend(channel: string, factory: () => NotificationChannel): void {\n this.factories.set(channel, factory)\n this.channels.delete(channel)\n }\n\n private resolve(name: string): NotificationChannel {\n const cached = this.channels.get(name)\n if (cached) return cached\n const factory = this.factories.get(name)\n if (!factory) throw new UnknownNotificationChannelError(name)\n const channel = factory()\n this.channels.set(name, channel)\n return channel\n }\n\n async send(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification): Promise<void> {\n const list = toNotifiableList(notifiables)\n if (list.length === 0) return\n if (isQueueableNotification(notification)) {\n if (!this.container.bound(BusDispatcherToken)) throw new NotificationQueueUnavailableError()\n const bus = this.container.make(BusDispatcherToken)\n // One job per notifiable per channel, as Laravel does, so a failing channel or recipient retries alone.\n for (const notifiable of list) {\n const own = forNotifiable(notification)\n for (const channel of notification.via(notifiable)) {\n await bus.dispatch(new SendQueuedNotifications([notifiable], own, [channel]))\n }\n }\n return\n }\n await this.sendNow(list, notification)\n }\n\n async sendNow(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification, channels?: string[]): Promise<void> {\n const list = toNotifiableList(notifiables)\n for (const notifiable of list) {\n const own = forNotifiable(notification)\n const names = channels ?? notification.via(notifiable)\n for (const name of names) {\n // An on-demand notifiable has no model to store a row against, so it skips 'database' as in Laravel.\n if (name === 'database' && notifiable instanceof AnonymousNotifiable) continue\n await this.resolve(name).send(notifiable, own)\n }\n }\n }\n\n /** On-demand: an AnonymousNotifiable, whose notify() goes through the bound NotificationDispatcher. */\n route(channel: string, route: unknown): AnonymousNotifiable {\n return new AnonymousNotifiable().route(channel, route)\n }\n}\n","import {\n NotificationDispatcher as NotificationDispatcherToken,\n type Notifiable as NotifiableContract,\n type Notification,\n type NotificationDispatcher,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { Str } from '@orieljs/support'\nimport { NotificationDispatcherUnavailableError } from './errors'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type NotifiableConstructor = new (...args: any[]) => object\n\n/** The bound NotificationDispatcher, or a clear error when the Notifications component isn't registered. */\nexport function resolveDispatcher(): NotificationDispatcher {\n const container = currentContainer()\n if (!container?.bound(NotificationDispatcherToken)) throw new NotificationDispatcherUnavailableError()\n return container.make(NotificationDispatcherToken)\n}\n\n/**\n * Laravel's RoutesNotifications trait: `notify()`, `notifyNow()` and `routeNotificationFor()`. Apps use\n * `Notifiable` from '@orieljs/framework', which adds the database notifications relations. Custom routing per\n * channel follows Laravel's order: a `routeNotificationFor<Channel>()` method (Studly name) wins if present, else\n * 'mail' reads `email`/`getAttribute('email')`, 'database' returns the instance itself (the database channel derives\n * the row from it), and anything else is unrouted (`undefined`).\n */\nexport function RoutesNotifications<TBase extends NotifiableConstructor>(Base: TBase) {\n return class RoutesNotificationsMixin extends Base implements NotifiableContract {\n routeNotificationFor(channel: string, notification: Notification): unknown {\n const custom = (this as unknown as Record<string, unknown>)[`routeNotificationFor${Str.studly(channel)}`]\n if (typeof custom === 'function') return (custom as (n: Notification) => unknown).call(this, notification)\n if (channel === 'mail') {\n const withAttrs = this as unknown as { email?: string; getAttribute?: (key: string) => unknown }\n return typeof withAttrs.getAttribute === 'function' ? withAttrs.getAttribute('email') : withAttrs.email\n }\n if (channel === 'database') return this\n return undefined\n }\n\n async notify(notification: Notification): Promise<void> {\n await resolveDispatcher().send(this as unknown as NotifiableContract, notification)\n }\n\n async notifyNow(notification: Notification, channels?: string[]): Promise<void> {\n await resolveDispatcher().sendNow(this as unknown as NotifiableContract, notification, channels)\n }\n }\n}\n","import type { Notifiable, Notification } from '@orieljs/contracts'\nimport { resolveDispatcher } from './RoutesNotifications'\n\n/**\n * Returned by `Notification.route(channel, address)` for sending to an address with no model behind it. The\n * routes are an own plain record and every method sits on the prototype, so a queued on-demand notification\n * survives Queue's serializer (which rejects functions in own fields) and is restored on the worker as this class\n * (registered as `'Notifications.AnonymousNotifiable'` in ClassRegistry).\n */\nexport class AnonymousNotifiable implements Notifiable {\n routes: Record<string, unknown> = {}\n\n /** Adds a route; chains, so one on-demand notifiable can go to several channels. */\n route(channel: string, route: unknown): this {\n this.routes[channel] = route\n return this\n }\n\n routeNotificationFor(channel: string): unknown {\n return this.routes[channel]\n }\n\n async notify(notification: Notification): Promise<void> {\n await resolveDispatcher().send(this, notification)\n }\n\n async notifyNow(notification: Notification, channels?: string[]): Promise<void> {\n await resolveDispatcher().sendNow(this, notification, channels)\n }\n}\n","import { createHash } from 'node:crypto'\nimport { NotificationDispatcher as NotificationDispatcherToken, type Job, type Notifiable, type Notification } from '@orieljs/contracts'\nimport { inject } from '@orieljs/container'\nimport { SHOULD_QUEUE, UNIQUE, uniqueSpecOf } from '../symbols'\n\n/** The ShouldQueue options copied from the notification onto the wrapper job. */\nconst QUEUE_OPTIONS = [\n 'connection',\n 'queue',\n 'delay',\n 'tries',\n 'backoff',\n 'timeout',\n 'failOnTimeout',\n 'deleteWhenMissingModels',\n] as const\n\n/** The job hooks Queue reads off a job instance, when the wrapped notification defines them. */\ninterface JobHooks {\n failed?: (error: unknown) => unknown\n retryUntil?: () => unknown\n middleware?: unknown[] | (() => unknown[])\n}\n\n/**\n * Copies the notification's own ShouldQueue fields onto the wrapper job (when set) — same reasoning as Mail's\n * SendQueuedMailable. Uses `declare` fields on the class (no runtime auto-initialization to `undefined`), so a\n * field is a genuinely own property only when the notification actually set it.\n */\nfunction copyShouldQueueFields(target: Record<PropertyKey, unknown>, source: object): void {\n const fields = source as Record<string, unknown>\n for (const option of QUEUE_OPTIONS) {\n if (fields[option] !== undefined) target[option] = fields[option]\n }\n}\n\n/**\n * Who a notifiable is, for a unique notification's lock: a model by its morph class and key, anything else by a sha1\n * of its route for the channel (an on-demand notifiable's address: the lock key stays short and holds no address),\n * else by its class.\n */\nfunction notifiableIdentity(notifiable: Notifiable, channel: string | undefined, notification: Notification): string {\n const model = notifiable as Notifiable & { getKey?: () => unknown; getMorphClass?: () => string }\n if (typeof model.getKey === 'function') return `${model.getMorphClass?.() ?? notifiable.constructor.name}:${String(model.getKey())}`\n const route = channel === undefined ? undefined : notifiable.routeNotificationFor(channel, notification)\n if (route === undefined || route === null) return notifiable.constructor.name\n return createHash('sha1').update(typeof route === 'string' ? route : JSON.stringify(route)).digest('hex')\n}\n\n/**\n * Dispatched by Dispatcher.send() when the notification is queueable. Runs on the worker: calls sendNow()\n * directly (never send()), so it can't re-detect itself as queueable and loop.\n *\n * Carries `SHOULD_QUEUE` on its own prototype, per the Queue/Bus protocol, so `BusDispatcher.dispatch()` — which\n * re-decides queued-or-now on whatever job it's handed — actually enqueues it instead of running it inline; the\n * notification's own queueability (checked by Dispatcher, above) only decides whether to wrap it at all. Like\n * Laravel's SendQueuedNotifications, it forwards failed(), retryUntil(), middleware() and displayName() to the\n * notification. Unlike Laravel's, it is unique when the notification is (Laravel queues it past the unique lock).\n */\nexport class SendQueuedNotifications implements Job {\n declare connection?: string\n declare queue?: string\n declare delay?: number\n declare tries?: number\n declare backoff?: number | number[]\n declare timeout?: number\n declare failOnTimeout?: boolean\n declare deleteWhenMissingModels?: boolean\n\n constructor(\n readonly notifiables: Notifiable[],\n readonly notification: Notification,\n readonly channels?: string[],\n ) {\n copyShouldQueueFields(this as unknown as Record<PropertyKey, unknown>, notification)\n // A unique notification (uniqueId(), uniqueFor, ...) makes this job unique, under the notification's class. The\n // channel and the notifiable are part of its id: Dispatcher queues one job per notifiable per channel, and a\n // duplicate is the same notification to the same notifiable on the same channel.\n const unique = uniqueSpecOf(notification, notification.constructor.name)\n if (unique) {\n const recipients = notifiables.map((notifiable) => notifiableIdentity(notifiable, channels?.[0], notification))\n unique.id = [unique.id, channels?.join(',') ?? '', recipients.join(',')].join(':')\n ;(this as unknown as Record<PropertyKey, unknown>)[UNIQUE] = unique\n }\n }\n\n async handle(): Promise<void> {\n await inject(NotificationDispatcherToken).sendNow(this.notifiables, this.notification, this.channels)\n }\n\n /** Called by Queue after the final failed attempt. */\n async failed(error: unknown): Promise<void> {\n const hooks = this.notification as unknown as JobHooks\n if (typeof hooks.failed === 'function') await hooks.failed.call(this.notification, error)\n }\n\n /** `failed_jobs` and `queue:failed` show the notification's class, not the wrapper's. */\n displayName(): string {\n return this.notification.constructor.name\n }\n\n retryUntil(): unknown {\n const hooks = this.notification as unknown as JobHooks\n return typeof hooks.retryUntil === 'function' ? hooks.retryUntil.call(this.notification) : undefined\n }\n\n middleware(): unknown[] {\n const declared = (this.notification as unknown as JobHooks).middleware\n return (typeof declared === 'function' ? declared.call(this.notification) : declared) ?? []\n }\n}\n;(SendQueuedNotifications.prototype as unknown as Record<PropertyKey, unknown>)[SHOULD_QUEUE] = true\n","import {\n NotificationDispatcher as NotificationDispatcherToken,\n type Notifiable,\n type Notification as NotificationContract,\n type NotificationChannel,\n type NotificationDispatcher,\n} from '@orieljs/contracts'\nimport { facade } from '@orieljs/support'\nimport { toNotifiableList } from './Dispatcher'\nimport { resolveDispatcher } from './RoutesNotifications'\n\n/**\n * Mirrors Laravel's Illuminate\\Notifications\\Notification: override via(), and whichever toXxx() the channels\n * need.\n *\n * The statics are the `Notification` facade, so one export serves both `class InvoicePaid extends Notification`\n * and `Notification.send(users, new InvoicePaid())`.\n */\nexport abstract class Notification implements NotificationContract {\n id?: string\n abstract via(notifiable: Notifiable): string[]\n\n /** Queues it when the notification is queueable, otherwise sends now. Accepts one notifiable, an array or any iterable. */\n static send(notifiables: Notifiable | Iterable<Notifiable>, notification: NotificationContract): Promise<void> {\n return resolveDispatcher().send(toNotifiableList(notifiables), notification)\n }\n\n static sendNow(\n notifiables: Notifiable | Iterable<Notifiable>,\n notification: NotificationContract,\n channels?: string[],\n ): Promise<void> {\n return resolveDispatcher().sendNow(toNotifiableList(notifiables), notification, channels)\n }\n\n /** On-demand: `Notification.route('mail', 'a@b.c').notify(new InvoicePaid())`. */\n static route(channel: string, route: unknown): Notifiable & { notify(notification: NotificationContract): Promise<void> } {\n return resolveDispatcher().route(channel, route)\n }\n\n static extend(channel: string, factory: () => NotificationChannel): void {\n resolveDispatcher().extend(channel, factory)\n }\n\n /** Replace the dispatcher in the current container (what a NotificationFake uses). */\n static swap(fake: NotificationDispatcher): void {\n facade<NotificationDispatcher>(NotificationDispatcherToken).swap(fake)\n }\n}\n","import type { NotificationDispatcher } from '@orieljs/contracts'\nimport { NotificationDispatcher as NotificationDispatcherToken } from '@orieljs/contracts'\nimport { facade } from '@orieljs/support'\n\n/**\n * The plain facade over NotificationDispatcher. App code normally uses the `Notification` base class's statics\n * instead (one `Notification` export is both the base class and the facade); this stays exported for code that\n * wants the contract-typed proxy (getFacadeRoot(), swap()).\n */\nexport const NotificationFacade = facade<NotificationDispatcher>(NotificationDispatcherToken)\n","import { ClassRegistry as ClassRegistryToken, NotificationDispatcher as NotificationDispatcherToken } from '@orieljs/contracts'\nimport { ServiceProvider } from '@orieljs/support'\nimport { AnonymousNotifiable } from './AnonymousNotifiable'\nimport { DatabaseNotificationChannel } from './channels/DatabaseNotificationChannel'\nimport { MailNotificationChannel } from './channels/MailNotificationChannel'\nimport { Dispatcher } from './Dispatcher'\nimport { SendQueuedNotifications } from './Jobs/SendQueuedNotifications'\n\nexport class NotificationsServiceProvider extends ServiceProvider {\n override register(): void {\n this.app.singleton(NotificationDispatcherToken, (container) => new Dispatcher(container))\n // Namespaced names, registered in register() (not boot()) — the Queue/Bus protocol rule for wrapper classes.\n // ClassRegistry is safe to use here: Application's own constructor binds it before any provider registers.\n const registry = this.app.make(ClassRegistryToken)\n registry.register(SendQueuedNotifications, 'Notifications.SendQueuedNotifications')\n registry.register(AnonymousNotifiable, 'Notifications.AnonymousNotifiable')\n }\n\n override boot(): void {\n const dispatcher = this.app.make(NotificationDispatcherToken)\n dispatcher.extend('mail', () => new MailNotificationChannel())\n dispatcher.extend('database', () => new DatabaseNotificationChannel())\n }\n}\n"],"mappings":"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","names":["text","currentContainer","currentContainer","currentContainer","currentContainer","randomUUID","currentContainer","Str","currentContainer","Str","NotificationDispatcherToken","NotificationDispatcherToken","randomUUID","NotificationDispatcherToken","NotificationDispatcherToken","NotificationDispatcherToken","facade","NotificationDispatcherToken","NotificationDispatcherToken"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orieljs/notifications",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.11",
|
|
4
4
|
"description": "The Oriel Notifications component.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
+
"homepage": "https://orieljs.vercel.app",
|
|
7
8
|
"sideEffects": false,
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
@@ -26,12 +27,13 @@
|
|
|
26
27
|
"files": [
|
|
27
28
|
"dist",
|
|
28
29
|
"LICENSE",
|
|
29
|
-
"NOTICE"
|
|
30
|
+
"NOTICE",
|
|
31
|
+
"TRADEMARKS.md"
|
|
30
32
|
],
|
|
31
33
|
"dependencies": {
|
|
32
|
-
"@orieljs/
|
|
33
|
-
"@orieljs/container": "0.1.0-alpha.
|
|
34
|
-
"@orieljs/
|
|
34
|
+
"@orieljs/support": "0.1.0-alpha.11",
|
|
35
|
+
"@orieljs/container": "0.1.0-alpha.11",
|
|
36
|
+
"@orieljs/contracts": "0.1.0-alpha.11"
|
|
35
37
|
},
|
|
36
38
|
"scripts": {
|
|
37
39
|
"build": "tsup && node ../../../scripts/copy-license.mjs"
|