@orieljs/notifications 0.1.0-alpha.6 → 0.1.0-alpha.8

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 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 a default
60
- * HTML+text template with no View involved. line() appends to the intro before action() is called, and to the
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
- /** Renders the default notification mail template. No ViewFactory involved. */
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
85
+ var attribute = (value) => text(value).replace(/"/g, "&quot;");
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(text) {
286
+ line(text2) {
94
287
  ;
95
- (this.actionData ? this.outroLines : this.introLines).push(text);
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
- action(text, url) {
103
- this.actionData = { text, url };
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
- /** Renders the default notification mail template. No ViewFactory involved. */
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
- const greeting = this.greetingLine ?? "Hello!";
129
- const html = [`<p>${escapeHtml(greeting)}</p>`];
130
- const text = [greeting];
131
- for (const line of this.introLines) {
132
- html.push(`<p>${escapeHtml(line)}</p>`);
133
- text.push(line);
134
- }
135
- if (this.actionData) {
136
- const { text: actionText, url } = this.actionData;
137
- html.push(`<p><a href="${escapeHtml(url)}" class="button" target="_blank">${escapeHtml(actionText)}</a></p>`);
138
- text.push(`${actionText}: ${url}`);
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 appName() {
357
+ function appConfig(key, fallback) {
152
358
  const container = currentContainer();
153
- const name = container?.bound(ConfigRepository) ? container.make(ConfigRepository).get("app.name") : void 0;
154
- return typeof name === "string" && name !== "" ? name : "Oriel";
155
- }
156
- function escapeHtml(value) {
157
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
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${Str.studly(channel)}`];
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n\n/** An attribute value: the text characters and the double quote. */\nconst attribute = (value: string): string => text(value).replace(/\"/g, '&quot;')\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.6",
3
+ "version": "0.1.0-alpha.8",
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.6",
34
- "@orieljs/container": "0.1.0-alpha.6",
35
- "@orieljs/support": "0.1.0-alpha.6"
34
+ "@orieljs/contracts": "0.1.0-alpha.8",
35
+ "@orieljs/support": "0.1.0-alpha.8",
36
+ "@orieljs/container": "0.1.0-alpha.8"
36
37
  },
37
38
  "scripts": {
38
39
  "build": "tsup && node ../../../scripts/copy-license.mjs"