@orieljs/notifications 0.1.0-alpha.6 → 0.1.0-alpha.7
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 +4 -0
- package/README.md +38 -0
- package/TRADEMARKS.md +33 -0
- package/dist/index.d.ts +52 -7
- package/dist/index.js +292 -38
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/NOTICE
CHANGED
|
@@ -10,3 +10,7 @@ The Oriel name and logo are not covered by that license; see TRADEMARKS.md
|
|
|
10
10
|
|
|
11
11
|
@orieljs/support (and @orieljs/framework, which bundles it) includes transliteration data from
|
|
12
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, Notification as Notification$1, NotificationDispatcher, Container, Job } from '@orieljs/contracts';
|
|
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,16 +51,40 @@ 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[];
|
|
@@ -71,19 +95,40 @@ declare class MailMessage {
|
|
|
71
95
|
toAddresses: MailRecipient[];
|
|
72
96
|
ccAddresses: MailRecipient[];
|
|
73
97
|
bccAddresses: MailRecipient[];
|
|
98
|
+
replyToAddresses: MailRecipient[];
|
|
74
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;
|
|
75
106
|
subject(subject: string): this;
|
|
76
107
|
greeting(greeting: string): this;
|
|
77
108
|
/** Before action(), appends to the intro; after action(), to the outro. */
|
|
78
109
|
line(text: string): this;
|
|
79
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;
|
|
80
115
|
action(text: string, url: string): this;
|
|
81
116
|
salutation(salutation: string): this;
|
|
82
117
|
from(address: MailRecipient): this;
|
|
83
118
|
to(recipients: MailRecipient | MailRecipient[]): this;
|
|
84
119
|
cc(recipients: MailRecipient | MailRecipient[]): this;
|
|
85
120
|
bcc(recipients: MailRecipient | MailRecipient[]): this;
|
|
86
|
-
|
|
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. */
|
|
87
132
|
render(): {
|
|
88
133
|
html: string;
|
|
89
134
|
text: string;
|
|
@@ -170,7 +215,7 @@ declare class Dispatcher implements NotificationDispatcher {
|
|
|
170
215
|
* re-decides queued-or-now on whatever job it's handed — actually enqueues it instead of running it inline; the
|
|
171
216
|
* notification's own queueability (checked by Dispatcher, above) only decides whether to wrap it at all. Like
|
|
172
217
|
* Laravel's SendQueuedNotifications, it forwards failed(), retryUntil(), middleware() and displayName() to the
|
|
173
|
-
* notification.
|
|
218
|
+
* notification. Unlike Laravel's, it is unique when the notification is (Laravel queues it past the unique lock).
|
|
174
219
|
*/
|
|
175
220
|
declare class SendQueuedNotifications implements Job {
|
|
176
221
|
readonly notifiables: Notifiable[];
|
|
@@ -244,4 +289,4 @@ declare class NotificationsServiceProvider extends ServiceProvider {
|
|
|
244
289
|
boot(): void;
|
|
245
290
|
}
|
|
246
291
|
|
|
247
|
-
export { AnonymousNotifiable, DatabaseNotificationChannel, DatabaseUnavailableError, Dispatcher, 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 };
|
|
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
|
@@ -69,7 +69,186 @@ var NotificationDispatcherUnavailableError = class extends Error {
|
|
|
69
69
|
// MailMessage.ts
|
|
70
70
|
import { ConfigRepository } from "@orieljs/contracts";
|
|
71
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
|
+
|
|
248
|
+
// MailMessage.ts
|
|
72
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";
|
|
73
252
|
subjectLine;
|
|
74
253
|
greetingLine;
|
|
75
254
|
introLines = [];
|
|
@@ -80,7 +259,21 @@ var MailMessage = class {
|
|
|
80
259
|
toAddresses = [];
|
|
81
260
|
ccAddresses = [];
|
|
82
261
|
bccAddresses = [];
|
|
262
|
+
replyToAddresses = [];
|
|
83
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
|
+
}
|
|
84
277
|
subject(subject) {
|
|
85
278
|
this.subjectLine = subject;
|
|
86
279
|
return this;
|
|
@@ -90,17 +283,25 @@ var MailMessage = class {
|
|
|
90
283
|
return this;
|
|
91
284
|
}
|
|
92
285
|
/** Before action(), appends to the intro; after action(), to the outro. */
|
|
93
|
-
line(
|
|
286
|
+
line(text2) {
|
|
94
287
|
;
|
|
95
|
-
(this.actionData ? this.outroLines : this.introLines).push(
|
|
288
|
+
(this.actionData ? this.outroLines : this.introLines).push(text2);
|
|
96
289
|
return this;
|
|
97
290
|
}
|
|
98
291
|
lines(lines) {
|
|
99
292
|
for (const line of lines) this.line(line);
|
|
100
293
|
return this;
|
|
101
294
|
}
|
|
102
|
-
|
|
103
|
-
|
|
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 };
|
|
104
305
|
return this;
|
|
105
306
|
}
|
|
106
307
|
salutation(salutation) {
|
|
@@ -123,38 +324,40 @@ var MailMessage = class {
|
|
|
123
324
|
this.bccAddresses.push(...Array.isArray(recipients) ? recipients : [recipients]);
|
|
124
325
|
return this;
|
|
125
326
|
}
|
|
126
|
-
|
|
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. */
|
|
127
342
|
render() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
for (const line of this.outroLines) {
|
|
141
|
-
html.push(`<p>${escapeHtml(line)}</p>`);
|
|
142
|
-
text.push(line);
|
|
143
|
-
}
|
|
144
|
-
const salutation = this.salutationLine ?? `Regards,
|
|
145
|
-
${appName()}`;
|
|
146
|
-
html.push(`<p>${escapeHtml(salutation).replace(/\n/g, "<br>")}</p>`);
|
|
147
|
-
text.push(salutation);
|
|
148
|
-
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
|
+
});
|
|
149
355
|
}
|
|
150
356
|
};
|
|
151
|
-
function
|
|
357
|
+
function appConfig(key, fallback) {
|
|
152
358
|
const container = currentContainer();
|
|
153
|
-
const
|
|
154
|
-
return typeof
|
|
155
|
-
}
|
|
156
|
-
function escapeHtml(value) {
|
|
157
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
359
|
+
const value = container?.bound(ConfigRepository) ? container.make(ConfigRepository).get(key) : void 0;
|
|
360
|
+
return typeof value === "string" && value !== "" ? value : fallback;
|
|
158
361
|
}
|
|
159
362
|
|
|
160
363
|
// channels/MailNotificationChannel.ts
|
|
@@ -162,6 +365,7 @@ import {
|
|
|
162
365
|
MailManager as MailManagerToken
|
|
163
366
|
} from "@orieljs/contracts";
|
|
164
367
|
import { currentContainer as currentContainer2 } from "@orieljs/container";
|
|
368
|
+
import { Str } from "@orieljs/support";
|
|
165
369
|
function isMailable(value) {
|
|
166
370
|
return typeof value === "object" && value !== null && typeof value.envelope === "function" && typeof value.content === "function";
|
|
167
371
|
}
|
|
@@ -180,19 +384,21 @@ function toMailMessage(result, notificationName) {
|
|
|
180
384
|
if (result.salutation) message.salutation(result.salutation);
|
|
181
385
|
return message;
|
|
182
386
|
}
|
|
183
|
-
function mailableFromMessage(message, fallbackTo) {
|
|
387
|
+
function mailableFromMessage(message, fallbackTo, notificationName) {
|
|
184
388
|
return {
|
|
185
389
|
envelope: async () => ({
|
|
186
|
-
subject: message.subjectLine
|
|
390
|
+
subject: message.subjectLine || Str.title(Str.snake(notificationName, " ")),
|
|
187
391
|
to: message.toAddresses.length > 0 ? message.toAddresses : fallbackTo,
|
|
188
392
|
cc: message.ccAddresses,
|
|
189
393
|
bcc: message.bccAddresses,
|
|
394
|
+
replyTo: message.replyToAddresses,
|
|
190
395
|
from: message.fromAddress
|
|
191
396
|
}),
|
|
192
397
|
content: async () => {
|
|
193
398
|
const rendered = message.render();
|
|
194
399
|
return { htmlString: rendered.html, textString: rendered.text };
|
|
195
|
-
}
|
|
400
|
+
},
|
|
401
|
+
attachments: async () => message.attachments
|
|
196
402
|
};
|
|
197
403
|
}
|
|
198
404
|
function normalizeRoute(route) {
|
|
@@ -205,7 +411,7 @@ var MailNotificationChannel = class {
|
|
|
205
411
|
const result = await notification.toMail(notifiable);
|
|
206
412
|
const to = normalizeRoute(notifiable.routeNotificationFor("mail", notification));
|
|
207
413
|
if (to.length === 0 && !isMailable(result)) return;
|
|
208
|
-
const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to);
|
|
414
|
+
const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to, notification.constructor.name);
|
|
209
415
|
const container = currentContainer2();
|
|
210
416
|
if (!container?.bound(MailManagerToken)) throw new MailUnavailableError();
|
|
211
417
|
await container.make(MailManagerToken).sendNow(mailable);
|
|
@@ -270,6 +476,20 @@ export default class extends Migration {
|
|
|
270
476
|
|
|
271
477
|
// symbols.ts
|
|
272
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
|
+
}
|
|
273
493
|
|
|
274
494
|
// Dispatcher.ts
|
|
275
495
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -282,7 +502,7 @@ import {
|
|
|
282
502
|
NotificationDispatcher as NotificationDispatcherToken
|
|
283
503
|
} from "@orieljs/contracts";
|
|
284
504
|
import { currentContainer as currentContainer4 } from "@orieljs/container";
|
|
285
|
-
import { Str } from "@orieljs/support";
|
|
505
|
+
import { Str as Str2 } from "@orieljs/support";
|
|
286
506
|
function resolveDispatcher() {
|
|
287
507
|
const container = currentContainer4();
|
|
288
508
|
if (!container?.bound(NotificationDispatcherToken)) throw new NotificationDispatcherUnavailableError();
|
|
@@ -291,7 +511,7 @@ function resolveDispatcher() {
|
|
|
291
511
|
function RoutesNotifications(Base) {
|
|
292
512
|
return class RoutesNotificationsMixin extends Base {
|
|
293
513
|
routeNotificationFor(channel, notification) {
|
|
294
|
-
const custom = this[`routeNotificationFor${
|
|
514
|
+
const custom = this[`routeNotificationFor${Str2.studly(channel)}`];
|
|
295
515
|
if (typeof custom === "function") return custom.call(this, notification);
|
|
296
516
|
if (channel === "mail") {
|
|
297
517
|
const withAttrs = this;
|
|
@@ -329,6 +549,7 @@ var AnonymousNotifiable = class {
|
|
|
329
549
|
};
|
|
330
550
|
|
|
331
551
|
// Jobs/SendQueuedNotifications.ts
|
|
552
|
+
import { createHash } from "crypto";
|
|
332
553
|
import { NotificationDispatcher as NotificationDispatcherToken2 } from "@orieljs/contracts";
|
|
333
554
|
import { inject } from "@orieljs/container";
|
|
334
555
|
var QUEUE_OPTIONS = [
|
|
@@ -347,12 +568,25 @@ function copyShouldQueueFields(target, source) {
|
|
|
347
568
|
if (fields[option] !== void 0) target[option] = fields[option];
|
|
348
569
|
}
|
|
349
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
|
+
}
|
|
350
578
|
var SendQueuedNotifications = class {
|
|
351
579
|
constructor(notifiables, notification, channels) {
|
|
352
580
|
this.notifiables = notifiables;
|
|
353
581
|
this.notification = notification;
|
|
354
582
|
this.channels = channels;
|
|
355
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
|
+
}
|
|
356
590
|
}
|
|
357
591
|
notifiables;
|
|
358
592
|
notification;
|
|
@@ -525,4 +759,24 @@ export {
|
|
|
525
759
|
UnrenderableNotificationMailError,
|
|
526
760
|
toNotifiableList
|
|
527
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
|
+
*/
|
|
528
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","../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 { ConfigRepository, type MailRecipient } from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\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 /** Unset: \"Regards,\" and config('app.name'), as Laravel's template. */\n salutationLine?: string\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 const salutation = this.salutationLine ?? `Regards,\\n${appName()}`\n html.push(`<p>${escapeHtml(salutation).replace(/\\n/g, '<br>')}</p>`)\n text.push(salutation)\n\n return { html: `<!doctype html><html><body>${html.join('\\n')}</body></html>`, text: text.join('\\n\\n') }\n }\n}\n\n/** config('app.name') when there is a container with config, else 'Oriel'. */\nfunction appName(): string {\n const container = currentContainer()\n const name = container?.bound(ConfigRepository) ? container.make(ConfigRepository).get<unknown>('app.name') : undefined\n return typeof name === 'string' && name !== '' ? name : 'Oriel'\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.sendNow() 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 // 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","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 { 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 './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":["currentContainer","currentContainer","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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orieljs/notifications",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"description": "The Oriel Notifications component.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -27,12 +27,13 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
29
|
"LICENSE",
|
|
30
|
-
"NOTICE"
|
|
30
|
+
"NOTICE",
|
|
31
|
+
"TRADEMARKS.md"
|
|
31
32
|
],
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@orieljs/contracts": "0.1.0-alpha.
|
|
34
|
-
"@orieljs/container": "0.1.0-alpha.
|
|
35
|
-
"@orieljs/support": "0.1.0-alpha.
|
|
34
|
+
"@orieljs/contracts": "0.1.0-alpha.7",
|
|
35
|
+
"@orieljs/container": "0.1.0-alpha.7",
|
|
36
|
+
"@orieljs/support": "0.1.0-alpha.7"
|
|
36
37
|
},
|
|
37
38
|
"scripts": {
|
|
38
39
|
"build": "tsup && node ../../../scripts/copy-license.mjs"
|