@orieljs/notifications 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/NOTICE +9 -0
- package/dist/index.d.ts +260 -0
- package/dist/index.js +571 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Oriel
|
|
2
|
+
Copyright 2026 meeeet-dev and Oriel contributors
|
|
3
|
+
|
|
4
|
+
This product includes software developed by the Oriel project
|
|
5
|
+
(https://orieljs.vercel.app, https://github.com/orieljs).
|
|
6
|
+
|
|
7
|
+
Oriel is licensed under the Apache License, Version 2.0 (see LICENSE).
|
|
8
|
+
The Oriel name and logo are not covered by that license; see TRADEMARKS.md
|
|
9
|
+
(https://github.com/orieljs/framework/blob/main/TRADEMARKS.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { OrielError, MailRecipient, NotificationChannel, Notifiable as Notifiable$1, Notification as Notification$1, NotificationDispatcher, Container, Job } from '@orieljs/contracts';
|
|
2
|
+
import * as _orieljs_support from '@orieljs/support';
|
|
3
|
+
import { ServiceProvider } from '@orieljs/support';
|
|
4
|
+
|
|
5
|
+
declare class UnrenderableNotificationMailError extends Error implements OrielError {
|
|
6
|
+
readonly kind: "Server";
|
|
7
|
+
readonly status = 500;
|
|
8
|
+
readonly name = "UnrenderableNotificationMailError";
|
|
9
|
+
constructor(notification: string);
|
|
10
|
+
}
|
|
11
|
+
declare class MailUnavailableError extends Error implements OrielError {
|
|
12
|
+
readonly kind: "Server";
|
|
13
|
+
readonly status = 500;
|
|
14
|
+
readonly name = "MailUnavailableError";
|
|
15
|
+
constructor();
|
|
16
|
+
}
|
|
17
|
+
declare class DatabaseUnavailableError extends Error implements OrielError {
|
|
18
|
+
readonly kind: "Server";
|
|
19
|
+
readonly status = 500;
|
|
20
|
+
readonly name = "DatabaseUnavailableError";
|
|
21
|
+
constructor();
|
|
22
|
+
}
|
|
23
|
+
declare class NotDatabaseNotifiableError extends Error implements OrielError {
|
|
24
|
+
readonly kind: "Server";
|
|
25
|
+
readonly status = 500;
|
|
26
|
+
readonly name = "NotDatabaseNotifiableError";
|
|
27
|
+
constructor(notifiable: string);
|
|
28
|
+
}
|
|
29
|
+
declare class UnrenderableNotificationDataError extends Error implements OrielError {
|
|
30
|
+
readonly kind: "Server";
|
|
31
|
+
readonly status = 500;
|
|
32
|
+
readonly name = "UnrenderableNotificationDataError";
|
|
33
|
+
constructor(notification: string);
|
|
34
|
+
}
|
|
35
|
+
declare class UnknownNotificationChannelError extends Error implements OrielError {
|
|
36
|
+
readonly kind: "Server";
|
|
37
|
+
readonly status = 500;
|
|
38
|
+
readonly name = "UnknownNotificationChannelError";
|
|
39
|
+
constructor(channel: string);
|
|
40
|
+
}
|
|
41
|
+
declare class NotificationQueueUnavailableError extends Error implements OrielError {
|
|
42
|
+
readonly kind: "Server";
|
|
43
|
+
readonly status = 500;
|
|
44
|
+
readonly name = "NotificationQueueUnavailableError";
|
|
45
|
+
constructor();
|
|
46
|
+
}
|
|
47
|
+
declare class NotificationDispatcherUnavailableError extends Error implements OrielError {
|
|
48
|
+
readonly kind: "Server";
|
|
49
|
+
readonly status = 500;
|
|
50
|
+
readonly name = "NotificationDispatcherUnavailableError";
|
|
51
|
+
constructor();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface MailMessageAction {
|
|
55
|
+
text: string;
|
|
56
|
+
url: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
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.
|
|
62
|
+
*/
|
|
63
|
+
declare class MailMessage {
|
|
64
|
+
subjectLine?: string;
|
|
65
|
+
greetingLine?: string;
|
|
66
|
+
introLines: string[];
|
|
67
|
+
actionData?: MailMessageAction;
|
|
68
|
+
outroLines: string[];
|
|
69
|
+
salutationLine: string;
|
|
70
|
+
toAddresses: MailRecipient[];
|
|
71
|
+
ccAddresses: MailRecipient[];
|
|
72
|
+
bccAddresses: MailRecipient[];
|
|
73
|
+
fromAddress?: MailRecipient;
|
|
74
|
+
subject(subject: string): this;
|
|
75
|
+
greeting(greeting: string): this;
|
|
76
|
+
/** Before action(), appends to the intro; after action(), to the outro. */
|
|
77
|
+
line(text: string): this;
|
|
78
|
+
lines(lines: string[]): this;
|
|
79
|
+
action(text: string, url: string): this;
|
|
80
|
+
salutation(salutation: string): this;
|
|
81
|
+
from(address: MailRecipient): this;
|
|
82
|
+
to(recipients: MailRecipient | MailRecipient[]): this;
|
|
83
|
+
cc(recipients: MailRecipient | MailRecipient[]): this;
|
|
84
|
+
bcc(recipients: MailRecipient | MailRecipient[]): this;
|
|
85
|
+
/** Renders the default notification mail template. No ViewFactory involved. */
|
|
86
|
+
render(): {
|
|
87
|
+
html: string;
|
|
88
|
+
text: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* What Auth's default ResetPassword notification's toMail() returns: plain data, since Auth can't import
|
|
94
|
+
* Notifications. `{ subject, lines, actionText, actionUrl, outroLines }` exactly; `greeting`/`salutation` are
|
|
95
|
+
* optional extensions any other notification's plain toMail() may also use.
|
|
96
|
+
*/
|
|
97
|
+
interface PlainNotificationMail {
|
|
98
|
+
subject?: string;
|
|
99
|
+
greeting?: string;
|
|
100
|
+
lines: string[];
|
|
101
|
+
actionText?: string;
|
|
102
|
+
actionUrl?: string;
|
|
103
|
+
outroLines?: string[];
|
|
104
|
+
salutation?: string;
|
|
105
|
+
}
|
|
106
|
+
declare class MailNotificationChannel implements NotificationChannel {
|
|
107
|
+
send(notifiable: Notifiable$1, notification: Notification$1): Promise<void>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
declare const NOTIFICATIONS_TABLE = "notifications";
|
|
111
|
+
interface DatabaseNotificationRecord {
|
|
112
|
+
readonly id: string;
|
|
113
|
+
readonly type: string;
|
|
114
|
+
readonly data: Record<string, unknown>;
|
|
115
|
+
readonly createdAt: Date;
|
|
116
|
+
readonly updatedAt: Date;
|
|
117
|
+
readAt: Date | undefined;
|
|
118
|
+
markAsRead(): Promise<void>;
|
|
119
|
+
markAsUnread(): Promise<void>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Database writes Dates as zoneless UTC (`'2026-09-25 04:30:00'`) and hands them back as strings, so
|
|
123
|
+
* `new Date()` would read such a string as local time; it is parsed as UTC here instead.
|
|
124
|
+
*/
|
|
125
|
+
declare function parseStoredDate(value: unknown): Date;
|
|
126
|
+
/** notifiable_type + notifiable_id together, never id alone. */
|
|
127
|
+
declare function fetchNotifications(notifiableType: string, notifiableId: unknown, filter?: 'all' | 'read' | 'unread'): Promise<DatabaseNotificationRecord[]>;
|
|
128
|
+
declare class DatabaseNotificationChannel implements NotificationChannel {
|
|
129
|
+
send(notifiable: Notifiable$1, notification: Notification$1): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
declare const NOTIFICATIONS_TABLE_MIGRATION = "import { Migration, Schema } from '@orieljs/framework'\n\nexport default class extends Migration {\n async up() {\n await Schema.create('notifications', (t) => {\n t.uuid('id').primary()\n t.string('type')\n t.morphs('notifiable')\n t.text('data')\n t.timestamp('read_at').nullable()\n t.timestamps()\n })\n }\n\n async down() {\n await Schema.dropIfExists('notifications')\n }\n}\n";
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The Queue/Bus protocol's shared marker (the "Queue/Bus protocol" section of the Queue/Bus design notes) —
|
|
136
|
+
* identical to Mail's `symbols.ts`. `Symbol.for` is a *global* registry key, so this is the literal same runtime
|
|
137
|
+
* symbol Bus's `Queueable` base class and `isQueueable()` use, with no import needed (and none allowed).
|
|
138
|
+
*/
|
|
139
|
+
declare const SHOULD_QUEUE: unique symbol;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Returned by `Notification.route(channel, address)` for sending to an address with no model behind it. The
|
|
143
|
+
* routes are an own plain record and every method sits on the prototype, so a queued on-demand notification
|
|
144
|
+
* survives Queue's serializer (which rejects functions in own fields) and is restored on the worker as this class
|
|
145
|
+
* (registered as `'Notifications.AnonymousNotifiable'` in ClassRegistry).
|
|
146
|
+
*/
|
|
147
|
+
declare class AnonymousNotifiable implements Notifiable$1 {
|
|
148
|
+
routes: Record<string, unknown>;
|
|
149
|
+
/** Adds a route; chains, so one on-demand notifiable can go to several channels. */
|
|
150
|
+
route(channel: string, route: unknown): this;
|
|
151
|
+
routeNotificationFor(channel: string): unknown;
|
|
152
|
+
notify(notification: Notification$1): Promise<void>;
|
|
153
|
+
notifyNow(notification: Notification$1, channels?: string[]): Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* One notifiable, an array, or any other iterable of them (a Database `Collection` from `await User.where(...).get()`,
|
|
158
|
+
* a Set, ...), as Laravel's `Notification::send($users, ...)` accepts. A notifiable is recognised by its
|
|
159
|
+
* routeNotificationFor() method first, so an iterable model is still treated as one notifiable.
|
|
160
|
+
*/
|
|
161
|
+
declare function toNotifiableList(notifiables: Notifiable$1 | Iterable<Notifiable$1>): Notifiable$1[];
|
|
162
|
+
/** Resolves channels by name from a factory map — mirrors MailManager's driver resolution. */
|
|
163
|
+
declare class Dispatcher implements NotificationDispatcher {
|
|
164
|
+
private readonly container;
|
|
165
|
+
private readonly factories;
|
|
166
|
+
private readonly channels;
|
|
167
|
+
constructor(container: Container);
|
|
168
|
+
extend(channel: string, factory: () => NotificationChannel): void;
|
|
169
|
+
private resolve;
|
|
170
|
+
send(notifiables: Notifiable$1 | Iterable<Notifiable$1>, notification: Notification$1): Promise<void>;
|
|
171
|
+
sendNow(notifiables: Notifiable$1 | Iterable<Notifiable$1>, notification: Notification$1, channels?: string[]): Promise<void>;
|
|
172
|
+
/** On-demand: an AnonymousNotifiable, whose notify() goes through the bound NotificationDispatcher. */
|
|
173
|
+
route(channel: string, route: unknown): AnonymousNotifiable;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Dispatched by Dispatcher.send() when the notification is queueable. Runs on the worker: calls sendNow()
|
|
178
|
+
* directly (never send()), so it can't re-detect itself as queueable and loop.
|
|
179
|
+
*
|
|
180
|
+
* Carries `SHOULD_QUEUE` on its own prototype, per the Queue/Bus protocol, so `BusDispatcher.dispatch()` — which
|
|
181
|
+
* re-decides queued-or-now on whatever job it's handed — actually enqueues it instead of running it inline; the
|
|
182
|
+
* notification's own queueability (checked by Dispatcher, above) only decides whether to wrap it at all. Like
|
|
183
|
+
* Laravel's SendQueuedNotifications, it forwards failed(), retryUntil(), middleware() and displayName() to the
|
|
184
|
+
* notification.
|
|
185
|
+
*/
|
|
186
|
+
declare class SendQueuedNotifications implements Job {
|
|
187
|
+
readonly notifiables: Notifiable$1[];
|
|
188
|
+
readonly notification: Notification$1;
|
|
189
|
+
readonly channels?: string[] | undefined;
|
|
190
|
+
connection?: string;
|
|
191
|
+
queue?: string;
|
|
192
|
+
delay?: number;
|
|
193
|
+
tries?: number;
|
|
194
|
+
backoff?: number | number[];
|
|
195
|
+
timeout?: number;
|
|
196
|
+
failOnTimeout?: boolean;
|
|
197
|
+
deleteWhenMissingModels?: boolean;
|
|
198
|
+
constructor(notifiables: Notifiable$1[], notification: Notification$1, channels?: string[] | undefined);
|
|
199
|
+
handle(): Promise<void>;
|
|
200
|
+
/** Called by Queue after the final failed attempt. */
|
|
201
|
+
failed(error: unknown): Promise<void>;
|
|
202
|
+
/** `failed_jobs` and `queue:failed` show the notification's class, not the wrapper's. */
|
|
203
|
+
displayName(): string;
|
|
204
|
+
retryUntil(): unknown;
|
|
205
|
+
middleware(): unknown[];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
type NotifiableConstructor = new (...args: any[]) => object;
|
|
209
|
+
/**
|
|
210
|
+
* Mirrors Laravel's Illuminate\Notifications\Notifiable trait: `class User extends Notifiable(Model) {}`. Custom
|
|
211
|
+
* routing per channel follows Laravel's own resolution order: a `routeNotificationFor<Channel>()` method (Studly
|
|
212
|
+
* name) wins if present, else 'mail' reads `email`/`getAttribute('email')`, 'database' returns the instance itself
|
|
213
|
+
* (the database channel derives the table row from it directly), and anything else is unrouted (`undefined`).
|
|
214
|
+
*/
|
|
215
|
+
declare function Notifiable<TBase extends NotifiableConstructor>(Base: TBase): {
|
|
216
|
+
new (...args: any[]): {
|
|
217
|
+
routeNotificationFor(channel: string, notification: Notification$1): unknown;
|
|
218
|
+
notify(notification: Notification$1): Promise<void>;
|
|
219
|
+
notifyNow(notification: Notification$1, channels?: string[]): Promise<void>;
|
|
220
|
+
notifications(): Promise<DatabaseNotificationRecord[]>;
|
|
221
|
+
unreadNotifications(): Promise<DatabaseNotificationRecord[]>;
|
|
222
|
+
readNotifications(): Promise<DatabaseNotificationRecord[]>;
|
|
223
|
+
};
|
|
224
|
+
} & TBase;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Mirrors Laravel's Illuminate\Notifications\Notification: override via(), and whichever toXxx() the channels
|
|
228
|
+
* need.
|
|
229
|
+
*
|
|
230
|
+
* The statics are the `Notification` facade, so one export serves both `class InvoicePaid extends Notification`
|
|
231
|
+
* and `Notification.send(users, new InvoicePaid())`.
|
|
232
|
+
*/
|
|
233
|
+
declare abstract class Notification implements Notification$1 {
|
|
234
|
+
id?: string;
|
|
235
|
+
abstract via(notifiable: Notifiable$1): string[];
|
|
236
|
+
/** Queues it when the notification is queueable, otherwise sends now. Accepts one notifiable, an array or any iterable. */
|
|
237
|
+
static send(notifiables: Notifiable$1 | Iterable<Notifiable$1>, notification: Notification$1): Promise<void>;
|
|
238
|
+
static sendNow(notifiables: Notifiable$1 | Iterable<Notifiable$1>, notification: Notification$1, channels?: string[]): Promise<void>;
|
|
239
|
+
/** On-demand: `Notification.route('mail', 'a@b.c').notify(new InvoicePaid())`. */
|
|
240
|
+
static route(channel: string, route: unknown): Notifiable$1 & {
|
|
241
|
+
notify(notification: Notification$1): Promise<void>;
|
|
242
|
+
};
|
|
243
|
+
static extend(channel: string, factory: () => NotificationChannel): void;
|
|
244
|
+
/** Replace the dispatcher in the current container (what a NotificationFake uses). */
|
|
245
|
+
static swap(fake: NotificationDispatcher): void;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The plain facade over NotificationDispatcher. App code normally uses the `Notification` base class's statics
|
|
250
|
+
* instead (one `Notification` export is both the base class and the facade); this stays exported for code that
|
|
251
|
+
* wants the contract-typed proxy (getFacadeRoot(), swap()).
|
|
252
|
+
*/
|
|
253
|
+
declare const NotificationFacade: _orieljs_support.Facade<NotificationDispatcher, object>;
|
|
254
|
+
|
|
255
|
+
declare class NotificationsServiceProvider extends ServiceProvider {
|
|
256
|
+
register(): void;
|
|
257
|
+
boot(): void;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export { AnonymousNotifiable, DatabaseNotificationChannel, type DatabaseNotificationRecord, DatabaseUnavailableError, Dispatcher, MailMessage, type MailMessageAction, MailNotificationChannel, MailUnavailableError, NOTIFICATIONS_TABLE, NOTIFICATIONS_TABLE_MIGRATION, NotDatabaseNotifiableError, Notifiable, type NotifiableConstructor, Notification, NotificationDispatcherUnavailableError, NotificationFacade, NotificationQueueUnavailableError, NotificationsServiceProvider, type PlainNotificationMail, SHOULD_QUEUE, SendQueuedNotifications, UnknownNotificationChannelError, UnrenderableNotificationDataError, UnrenderableNotificationMailError, fetchNotifications, parseStoredDate, toNotifiableList };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
// errors.ts
|
|
2
|
+
var UnrenderableNotificationMailError = class extends Error {
|
|
3
|
+
kind = "Server";
|
|
4
|
+
status = 500;
|
|
5
|
+
name = "UnrenderableNotificationMailError";
|
|
6
|
+
constructor(notification) {
|
|
7
|
+
super(
|
|
8
|
+
`[${notification}].toMail() must return a Mailable, a MailMessage, or { subject?, greeting?, lines, actionText?, actionUrl?, outroLines?, salutation? }.`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var MailUnavailableError = class extends Error {
|
|
13
|
+
kind = "Server";
|
|
14
|
+
status = 500;
|
|
15
|
+
name = "MailUnavailableError";
|
|
16
|
+
constructor() {
|
|
17
|
+
super("Sending a notification's mail requires the Mail component. Register MailManager first.");
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var DatabaseUnavailableError = class extends Error {
|
|
21
|
+
kind = "Server";
|
|
22
|
+
status = 500;
|
|
23
|
+
name = "DatabaseUnavailableError";
|
|
24
|
+
constructor() {
|
|
25
|
+
super("The database notification channel requires the Database component. Register DatabaseManager first.");
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var NotDatabaseNotifiableError = class extends Error {
|
|
29
|
+
kind = "Server";
|
|
30
|
+
status = 500;
|
|
31
|
+
name = "NotDatabaseNotifiableError";
|
|
32
|
+
constructor(notifiable) {
|
|
33
|
+
super(`[${notifiable}] has no getKey() \u2014 the database channel and notification-listing methods need a model.`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var UnrenderableNotificationDataError = class extends Error {
|
|
37
|
+
kind = "Server";
|
|
38
|
+
status = 500;
|
|
39
|
+
name = "UnrenderableNotificationDataError";
|
|
40
|
+
constructor(notification) {
|
|
41
|
+
super(`[${notification}] needs a toDatabase() or toArray() method to use the database channel.`);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var UnknownNotificationChannelError = class extends Error {
|
|
45
|
+
kind = "Server";
|
|
46
|
+
status = 500;
|
|
47
|
+
name = "UnknownNotificationChannelError";
|
|
48
|
+
constructor(channel) {
|
|
49
|
+
super(`Notification channel [${channel}] is not registered. Register it first with NotificationDispatcher.extend().`);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var NotificationQueueUnavailableError = class extends Error {
|
|
53
|
+
kind = "Server";
|
|
54
|
+
status = 500;
|
|
55
|
+
name = "NotificationQueueUnavailableError";
|
|
56
|
+
constructor() {
|
|
57
|
+
super("Queueing a notification requires the Bus component. Register BusDispatcher, or call sendNow() instead.");
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var NotificationDispatcherUnavailableError = class extends Error {
|
|
61
|
+
kind = "Server";
|
|
62
|
+
status = 500;
|
|
63
|
+
name = "NotificationDispatcherUnavailableError";
|
|
64
|
+
constructor() {
|
|
65
|
+
super("notify()/notifyNow() require the Notifications component. Register NotificationDispatcher first.");
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// MailMessage.ts
|
|
70
|
+
var MailMessage = class {
|
|
71
|
+
subjectLine;
|
|
72
|
+
greetingLine;
|
|
73
|
+
introLines = [];
|
|
74
|
+
actionData;
|
|
75
|
+
outroLines = [];
|
|
76
|
+
salutationLine = "Regards,\nApplication";
|
|
77
|
+
toAddresses = [];
|
|
78
|
+
ccAddresses = [];
|
|
79
|
+
bccAddresses = [];
|
|
80
|
+
fromAddress;
|
|
81
|
+
subject(subject) {
|
|
82
|
+
this.subjectLine = subject;
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
greeting(greeting) {
|
|
86
|
+
this.greetingLine = greeting;
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/** Before action(), appends to the intro; after action(), to the outro. */
|
|
90
|
+
line(text) {
|
|
91
|
+
;
|
|
92
|
+
(this.actionData ? this.outroLines : this.introLines).push(text);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
lines(lines) {
|
|
96
|
+
for (const line of lines) this.line(line);
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
action(text, url) {
|
|
100
|
+
this.actionData = { text, url };
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
salutation(salutation) {
|
|
104
|
+
this.salutationLine = salutation;
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
from(address) {
|
|
108
|
+
this.fromAddress = address;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
to(recipients) {
|
|
112
|
+
this.toAddresses.push(...Array.isArray(recipients) ? recipients : [recipients]);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
cc(recipients) {
|
|
116
|
+
this.ccAddresses.push(...Array.isArray(recipients) ? recipients : [recipients]);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
bcc(recipients) {
|
|
120
|
+
this.bccAddresses.push(...Array.isArray(recipients) ? recipients : [recipients]);
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
/** Renders the default notification mail template. No ViewFactory involved. */
|
|
124
|
+
render() {
|
|
125
|
+
const greeting = this.greetingLine ?? "Hello!";
|
|
126
|
+
const html = [`<p>${escapeHtml(greeting)}</p>`];
|
|
127
|
+
const text = [greeting];
|
|
128
|
+
for (const line of this.introLines) {
|
|
129
|
+
html.push(`<p>${escapeHtml(line)}</p>`);
|
|
130
|
+
text.push(line);
|
|
131
|
+
}
|
|
132
|
+
if (this.actionData) {
|
|
133
|
+
const { text: actionText, url } = this.actionData;
|
|
134
|
+
html.push(`<p><a href="${escapeHtml(url)}" class="button" target="_blank">${escapeHtml(actionText)}</a></p>`);
|
|
135
|
+
text.push(`${actionText}: ${url}`);
|
|
136
|
+
}
|
|
137
|
+
for (const line of this.outroLines) {
|
|
138
|
+
html.push(`<p>${escapeHtml(line)}</p>`);
|
|
139
|
+
text.push(line);
|
|
140
|
+
}
|
|
141
|
+
html.push(`<p>${escapeHtml(this.salutationLine).replace(/\n/g, "<br>")}</p>`);
|
|
142
|
+
text.push(this.salutationLine);
|
|
143
|
+
return { html: `<!doctype html><html><body>${html.join("\n")}</body></html>`, text: text.join("\n\n") };
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
function escapeHtml(value) {
|
|
147
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// channels/MailNotificationChannel.ts
|
|
151
|
+
import {
|
|
152
|
+
MailManager as MailManagerToken
|
|
153
|
+
} from "@orieljs/contracts";
|
|
154
|
+
import { currentContainer } from "@orieljs/container";
|
|
155
|
+
function isMailable(value) {
|
|
156
|
+
return typeof value === "object" && value !== null && typeof value.envelope === "function" && typeof value.content === "function";
|
|
157
|
+
}
|
|
158
|
+
function isPlainNotificationMail(value) {
|
|
159
|
+
return typeof value === "object" && value !== null && Array.isArray(value.lines);
|
|
160
|
+
}
|
|
161
|
+
function toMailMessage(result, notificationName) {
|
|
162
|
+
if (result instanceof MailMessage) return result;
|
|
163
|
+
if (!isPlainNotificationMail(result)) throw new UnrenderableNotificationMailError(notificationName);
|
|
164
|
+
const message = new MailMessage();
|
|
165
|
+
if (result.subject) message.subject(result.subject);
|
|
166
|
+
if (result.greeting) message.greeting(result.greeting);
|
|
167
|
+
message.lines(result.lines);
|
|
168
|
+
if (result.actionText && result.actionUrl) message.action(result.actionText, result.actionUrl);
|
|
169
|
+
for (const line of result.outroLines ?? []) message.line(line);
|
|
170
|
+
if (result.salutation) message.salutation(result.salutation);
|
|
171
|
+
return message;
|
|
172
|
+
}
|
|
173
|
+
function mailableFromMessage(message, fallbackTo) {
|
|
174
|
+
return {
|
|
175
|
+
envelope: async () => ({
|
|
176
|
+
subject: message.subjectLine ?? "",
|
|
177
|
+
to: message.toAddresses.length > 0 ? message.toAddresses : fallbackTo,
|
|
178
|
+
cc: message.ccAddresses,
|
|
179
|
+
bcc: message.bccAddresses,
|
|
180
|
+
from: message.fromAddress
|
|
181
|
+
}),
|
|
182
|
+
content: async () => {
|
|
183
|
+
const rendered = message.render();
|
|
184
|
+
return { htmlString: rendered.html, textString: rendered.text };
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function normalizeRoute(route) {
|
|
189
|
+
if (route === void 0 || route === null) return [];
|
|
190
|
+
return Array.isArray(route) ? route : [route];
|
|
191
|
+
}
|
|
192
|
+
var MailNotificationChannel = class {
|
|
193
|
+
async send(notifiable, notification) {
|
|
194
|
+
if (!notification.toMail) throw new UnrenderableNotificationMailError(notification.constructor.name);
|
|
195
|
+
const result = await notification.toMail(notifiable);
|
|
196
|
+
const to = normalizeRoute(notifiable.routeNotificationFor("mail", notification));
|
|
197
|
+
if (to.length === 0 && !isMailable(result)) return;
|
|
198
|
+
const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to);
|
|
199
|
+
const container = currentContainer();
|
|
200
|
+
if (!container?.bound(MailManagerToken)) throw new MailUnavailableError();
|
|
201
|
+
await container.make(MailManagerToken).send(mailable);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// channels/DatabaseNotificationChannel.ts
|
|
206
|
+
import { randomUUID } from "crypto";
|
|
207
|
+
import {
|
|
208
|
+
DatabaseManager as DatabaseManagerToken
|
|
209
|
+
} from "@orieljs/contracts";
|
|
210
|
+
import { currentContainer as currentContainer2 } from "@orieljs/container";
|
|
211
|
+
var NOTIFICATIONS_TABLE = "notifications";
|
|
212
|
+
var ZONELESS_TIMESTAMP = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/;
|
|
213
|
+
function parseStoredDate(value) {
|
|
214
|
+
if (value instanceof Date) return value;
|
|
215
|
+
if (typeof value === "number") return new Date(value);
|
|
216
|
+
const text = String(value);
|
|
217
|
+
return ZONELESS_TIMESTAMP.test(text) ? /* @__PURE__ */ new Date(`${text.replace(" ", "T")}Z`) : new Date(text);
|
|
218
|
+
}
|
|
219
|
+
function wrapRow(row, db) {
|
|
220
|
+
const record = {
|
|
221
|
+
id: String(row.id),
|
|
222
|
+
type: String(row.type),
|
|
223
|
+
data: JSON.parse(String(row.data)),
|
|
224
|
+
createdAt: parseStoredDate(row.created_at),
|
|
225
|
+
updatedAt: parseStoredDate(row.updated_at),
|
|
226
|
+
readAt: row.read_at ? parseStoredDate(row.read_at) : void 0,
|
|
227
|
+
async markAsRead() {
|
|
228
|
+
if (record.readAt) return;
|
|
229
|
+
const now = /* @__PURE__ */ new Date();
|
|
230
|
+
await db.table(NOTIFICATIONS_TABLE).where("id", record.id).update({ read_at: now, updated_at: now });
|
|
231
|
+
record.readAt = now;
|
|
232
|
+
},
|
|
233
|
+
async markAsUnread() {
|
|
234
|
+
if (!record.readAt) return;
|
|
235
|
+
await db.table(NOTIFICATIONS_TABLE).where("id", record.id).update({ read_at: null, updated_at: /* @__PURE__ */ new Date() });
|
|
236
|
+
record.readAt = void 0;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
return record;
|
|
240
|
+
}
|
|
241
|
+
function requireDatabase() {
|
|
242
|
+
const container = currentContainer2();
|
|
243
|
+
if (!container?.bound(DatabaseManagerToken)) throw new DatabaseUnavailableError();
|
|
244
|
+
return container.make(DatabaseManagerToken).connection();
|
|
245
|
+
}
|
|
246
|
+
async function fetchNotifications(notifiableType, notifiableId, filter = "all") {
|
|
247
|
+
const db = requireDatabase();
|
|
248
|
+
let query = db.table(NOTIFICATIONS_TABLE).where("notifiable_type", notifiableType).where("notifiable_id", notifiableId).orderBy("created_at", "desc");
|
|
249
|
+
if (filter === "unread") query = query.whereNull("read_at");
|
|
250
|
+
if (filter === "read") query = query.whereNotNull("read_at");
|
|
251
|
+
const rows = await query.get();
|
|
252
|
+
return rows.all().map((row) => wrapRow(row, db));
|
|
253
|
+
}
|
|
254
|
+
var DatabaseNotificationChannel = class {
|
|
255
|
+
async send(notifiable, notification) {
|
|
256
|
+
if (!notification.toDatabase && !notification.toArray) {
|
|
257
|
+
throw new UnrenderableNotificationDataError(notification.constructor.name);
|
|
258
|
+
}
|
|
259
|
+
const data = notification.toDatabase ? notification.toDatabase(notifiable) : notification.toArray(notifiable);
|
|
260
|
+
const model = notifiable;
|
|
261
|
+
if (typeof model.getKey !== "function") throw new NotDatabaseNotifiableError(notifiable.constructor.name);
|
|
262
|
+
const db = requireDatabase();
|
|
263
|
+
const now = /* @__PURE__ */ new Date();
|
|
264
|
+
await db.table(NOTIFICATIONS_TABLE).insert({
|
|
265
|
+
id: notification.id ?? randomUUID(),
|
|
266
|
+
type: notification.constructor.name,
|
|
267
|
+
notifiable_type: notifiable.constructor.name,
|
|
268
|
+
notifiable_id: model.getKey(),
|
|
269
|
+
data: JSON.stringify(data),
|
|
270
|
+
read_at: null,
|
|
271
|
+
created_at: now,
|
|
272
|
+
updated_at: now
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// migrations.ts
|
|
278
|
+
var NOTIFICATIONS_TABLE_MIGRATION = `import { Migration, Schema } from '@orieljs/framework'
|
|
279
|
+
|
|
280
|
+
export default class extends Migration {
|
|
281
|
+
async up() {
|
|
282
|
+
await Schema.create('notifications', (t) => {
|
|
283
|
+
t.uuid('id').primary()
|
|
284
|
+
t.string('type')
|
|
285
|
+
t.morphs('notifiable')
|
|
286
|
+
t.text('data')
|
|
287
|
+
t.timestamp('read_at').nullable()
|
|
288
|
+
t.timestamps()
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async down() {
|
|
293
|
+
await Schema.dropIfExists('notifications')
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
`;
|
|
297
|
+
|
|
298
|
+
// symbols.ts
|
|
299
|
+
var SHOULD_QUEUE = /* @__PURE__ */ Symbol.for("oriel:bus.shouldQueue");
|
|
300
|
+
|
|
301
|
+
// Dispatcher.ts
|
|
302
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
303
|
+
import {
|
|
304
|
+
BusDispatcher as BusDispatcherToken
|
|
305
|
+
} from "@orieljs/contracts";
|
|
306
|
+
|
|
307
|
+
// Notifiable.ts
|
|
308
|
+
import {
|
|
309
|
+
NotificationDispatcher as NotificationDispatcherToken
|
|
310
|
+
} from "@orieljs/contracts";
|
|
311
|
+
import { currentContainer as currentContainer3 } from "@orieljs/container";
|
|
312
|
+
import { Str } from "@orieljs/support";
|
|
313
|
+
function resolveDispatcher() {
|
|
314
|
+
const container = currentContainer3();
|
|
315
|
+
if (!container?.bound(NotificationDispatcherToken)) throw new NotificationDispatcherUnavailableError();
|
|
316
|
+
return container.make(NotificationDispatcherToken);
|
|
317
|
+
}
|
|
318
|
+
function requireKey(instance) {
|
|
319
|
+
const model = instance;
|
|
320
|
+
if (typeof model.getKey !== "function") throw new NotDatabaseNotifiableError(instance.constructor.name);
|
|
321
|
+
return model.getKey();
|
|
322
|
+
}
|
|
323
|
+
function Notifiable(Base) {
|
|
324
|
+
return class NotifiableMixin extends Base {
|
|
325
|
+
routeNotificationFor(channel, notification) {
|
|
326
|
+
const custom = this[`routeNotificationFor${Str.studly(channel)}`];
|
|
327
|
+
if (typeof custom === "function") return custom.call(this, notification);
|
|
328
|
+
if (channel === "mail") {
|
|
329
|
+
const withAttrs = this;
|
|
330
|
+
return typeof withAttrs.getAttribute === "function" ? withAttrs.getAttribute("email") : withAttrs.email;
|
|
331
|
+
}
|
|
332
|
+
if (channel === "database") return this;
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
async notify(notification) {
|
|
336
|
+
await resolveDispatcher().send(this, notification);
|
|
337
|
+
}
|
|
338
|
+
async notifyNow(notification, channels) {
|
|
339
|
+
await resolveDispatcher().sendNow(this, notification, channels);
|
|
340
|
+
}
|
|
341
|
+
notifications() {
|
|
342
|
+
return fetchNotifications(this.constructor.name, requireKey(this));
|
|
343
|
+
}
|
|
344
|
+
unreadNotifications() {
|
|
345
|
+
return fetchNotifications(this.constructor.name, requireKey(this), "unread");
|
|
346
|
+
}
|
|
347
|
+
readNotifications() {
|
|
348
|
+
return fetchNotifications(this.constructor.name, requireKey(this), "read");
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// AnonymousNotifiable.ts
|
|
354
|
+
var AnonymousNotifiable = class {
|
|
355
|
+
routes = {};
|
|
356
|
+
/** Adds a route; chains, so one on-demand notifiable can go to several channels. */
|
|
357
|
+
route(channel, route) {
|
|
358
|
+
this.routes[channel] = route;
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
routeNotificationFor(channel) {
|
|
362
|
+
return this.routes[channel];
|
|
363
|
+
}
|
|
364
|
+
async notify(notification) {
|
|
365
|
+
await resolveDispatcher().send(this, notification);
|
|
366
|
+
}
|
|
367
|
+
async notifyNow(notification, channels) {
|
|
368
|
+
await resolveDispatcher().sendNow(this, notification, channels);
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// Jobs/SendQueuedNotifications.ts
|
|
373
|
+
import { NotificationDispatcher as NotificationDispatcherToken2 } from "@orieljs/contracts";
|
|
374
|
+
import { inject } from "@orieljs/container";
|
|
375
|
+
var QUEUE_OPTIONS = [
|
|
376
|
+
"connection",
|
|
377
|
+
"queue",
|
|
378
|
+
"delay",
|
|
379
|
+
"tries",
|
|
380
|
+
"backoff",
|
|
381
|
+
"timeout",
|
|
382
|
+
"failOnTimeout",
|
|
383
|
+
"deleteWhenMissingModels"
|
|
384
|
+
];
|
|
385
|
+
function copyShouldQueueFields(target, source) {
|
|
386
|
+
const fields = source;
|
|
387
|
+
for (const option of QUEUE_OPTIONS) {
|
|
388
|
+
if (fields[option] !== void 0) target[option] = fields[option];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
var SendQueuedNotifications = class {
|
|
392
|
+
constructor(notifiables, notification, channels) {
|
|
393
|
+
this.notifiables = notifiables;
|
|
394
|
+
this.notification = notification;
|
|
395
|
+
this.channels = channels;
|
|
396
|
+
copyShouldQueueFields(this, notification);
|
|
397
|
+
}
|
|
398
|
+
notifiables;
|
|
399
|
+
notification;
|
|
400
|
+
channels;
|
|
401
|
+
async handle() {
|
|
402
|
+
await inject(NotificationDispatcherToken2).sendNow(this.notifiables, this.notification, this.channels);
|
|
403
|
+
}
|
|
404
|
+
/** Called by Queue after the final failed attempt. */
|
|
405
|
+
async failed(error) {
|
|
406
|
+
const hooks = this.notification;
|
|
407
|
+
if (typeof hooks.failed === "function") await hooks.failed.call(this.notification, error);
|
|
408
|
+
}
|
|
409
|
+
/** `failed_jobs` and `queue:failed` show the notification's class, not the wrapper's. */
|
|
410
|
+
displayName() {
|
|
411
|
+
return this.notification.constructor.name;
|
|
412
|
+
}
|
|
413
|
+
retryUntil() {
|
|
414
|
+
const hooks = this.notification;
|
|
415
|
+
return typeof hooks.retryUntil === "function" ? hooks.retryUntil.call(this.notification) : void 0;
|
|
416
|
+
}
|
|
417
|
+
middleware() {
|
|
418
|
+
const declared = this.notification.middleware;
|
|
419
|
+
return (typeof declared === "function" ? declared.call(this.notification) : declared) ?? [];
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
SendQueuedNotifications.prototype[SHOULD_QUEUE] = true;
|
|
423
|
+
|
|
424
|
+
// Dispatcher.ts
|
|
425
|
+
function isQueueableNotification(notification) {
|
|
426
|
+
const record = notification;
|
|
427
|
+
if (record[SHOULD_QUEUE] === true) return true;
|
|
428
|
+
return record.connection !== void 0 || record.queue !== void 0 || record.delay !== void 0 || record.tries !== void 0 || record.backoff !== void 0 || record.timeout !== void 0 || typeof record.uniqueId === "function" || typeof record.retryUntil === "function";
|
|
429
|
+
}
|
|
430
|
+
function forNotifiable(notification) {
|
|
431
|
+
if (notification.id) return notification;
|
|
432
|
+
const copy = Object.assign(Object.create(Object.getPrototypeOf(notification)), notification);
|
|
433
|
+
copy.id = randomUUID2();
|
|
434
|
+
return copy;
|
|
435
|
+
}
|
|
436
|
+
function toNotifiableList(notifiables) {
|
|
437
|
+
if (Array.isArray(notifiables)) return notifiables;
|
|
438
|
+
if (typeof notifiables.routeNotificationFor === "function") return [notifiables];
|
|
439
|
+
if (typeof notifiables === "object" && notifiables !== null && Symbol.iterator in notifiables) {
|
|
440
|
+
return [...notifiables];
|
|
441
|
+
}
|
|
442
|
+
return [notifiables];
|
|
443
|
+
}
|
|
444
|
+
var Dispatcher = class {
|
|
445
|
+
constructor(container) {
|
|
446
|
+
this.container = container;
|
|
447
|
+
}
|
|
448
|
+
container;
|
|
449
|
+
factories = /* @__PURE__ */ new Map();
|
|
450
|
+
channels = /* @__PURE__ */ new Map();
|
|
451
|
+
extend(channel, factory) {
|
|
452
|
+
this.factories.set(channel, factory);
|
|
453
|
+
this.channels.delete(channel);
|
|
454
|
+
}
|
|
455
|
+
resolve(name) {
|
|
456
|
+
const cached = this.channels.get(name);
|
|
457
|
+
if (cached) return cached;
|
|
458
|
+
const factory = this.factories.get(name);
|
|
459
|
+
if (!factory) throw new UnknownNotificationChannelError(name);
|
|
460
|
+
const channel = factory();
|
|
461
|
+
this.channels.set(name, channel);
|
|
462
|
+
return channel;
|
|
463
|
+
}
|
|
464
|
+
async send(notifiables, notification) {
|
|
465
|
+
const list = toNotifiableList(notifiables);
|
|
466
|
+
if (list.length === 0) return;
|
|
467
|
+
if (isQueueableNotification(notification)) {
|
|
468
|
+
if (!this.container.bound(BusDispatcherToken)) throw new NotificationQueueUnavailableError();
|
|
469
|
+
const bus = this.container.make(BusDispatcherToken);
|
|
470
|
+
for (const notifiable of list) {
|
|
471
|
+
const own = forNotifiable(notification);
|
|
472
|
+
for (const channel of notification.via(notifiable)) {
|
|
473
|
+
await bus.dispatch(new SendQueuedNotifications([notifiable], own, [channel]));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
await this.sendNow(list, notification);
|
|
479
|
+
}
|
|
480
|
+
async sendNow(notifiables, notification, channels) {
|
|
481
|
+
const list = toNotifiableList(notifiables);
|
|
482
|
+
for (const notifiable of list) {
|
|
483
|
+
const own = forNotifiable(notification);
|
|
484
|
+
const names = channels ?? notification.via(notifiable);
|
|
485
|
+
for (const name of names) {
|
|
486
|
+
if (name === "database" && notifiable instanceof AnonymousNotifiable) continue;
|
|
487
|
+
await this.resolve(name).send(notifiable, own);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/** On-demand: an AnonymousNotifiable, whose notify() goes through the bound NotificationDispatcher. */
|
|
492
|
+
route(channel, route) {
|
|
493
|
+
return new AnonymousNotifiable().route(channel, route);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// Notification.ts
|
|
498
|
+
import {
|
|
499
|
+
NotificationDispatcher as NotificationDispatcherToken3
|
|
500
|
+
} from "@orieljs/contracts";
|
|
501
|
+
import { facade } from "@orieljs/support";
|
|
502
|
+
var Notification = class {
|
|
503
|
+
id;
|
|
504
|
+
/** Queues it when the notification is queueable, otherwise sends now. Accepts one notifiable, an array or any iterable. */
|
|
505
|
+
static send(notifiables, notification) {
|
|
506
|
+
return resolveDispatcher().send(toNotifiableList(notifiables), notification);
|
|
507
|
+
}
|
|
508
|
+
static sendNow(notifiables, notification, channels) {
|
|
509
|
+
return resolveDispatcher().sendNow(toNotifiableList(notifiables), notification, channels);
|
|
510
|
+
}
|
|
511
|
+
/** On-demand: `Notification.route('mail', 'a@b.c').notify(new InvoicePaid())`. */
|
|
512
|
+
static route(channel, route) {
|
|
513
|
+
return resolveDispatcher().route(channel, route);
|
|
514
|
+
}
|
|
515
|
+
static extend(channel, factory) {
|
|
516
|
+
resolveDispatcher().extend(channel, factory);
|
|
517
|
+
}
|
|
518
|
+
/** Replace the dispatcher in the current container (what a NotificationFake uses). */
|
|
519
|
+
static swap(fake) {
|
|
520
|
+
facade(NotificationDispatcherToken3).swap(fake);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
// facade.ts
|
|
525
|
+
import { NotificationDispatcher as NotificationDispatcherToken4 } from "@orieljs/contracts";
|
|
526
|
+
import { facade as facade2 } from "@orieljs/support";
|
|
527
|
+
var NotificationFacade = facade2(NotificationDispatcherToken4);
|
|
528
|
+
|
|
529
|
+
// NotificationsServiceProvider.ts
|
|
530
|
+
import { ClassRegistry as ClassRegistryToken, NotificationDispatcher as NotificationDispatcherToken5 } from "@orieljs/contracts";
|
|
531
|
+
import { ServiceProvider } from "@orieljs/support";
|
|
532
|
+
var NotificationsServiceProvider = class extends ServiceProvider {
|
|
533
|
+
register() {
|
|
534
|
+
this.app.singleton(NotificationDispatcherToken5, (container) => new Dispatcher(container));
|
|
535
|
+
const registry = this.app.make(ClassRegistryToken);
|
|
536
|
+
registry.register(SendQueuedNotifications, "Notifications.SendQueuedNotifications");
|
|
537
|
+
registry.register(AnonymousNotifiable, "Notifications.AnonymousNotifiable");
|
|
538
|
+
}
|
|
539
|
+
boot() {
|
|
540
|
+
const dispatcher = this.app.make(NotificationDispatcherToken5);
|
|
541
|
+
dispatcher.extend("mail", () => new MailNotificationChannel());
|
|
542
|
+
dispatcher.extend("database", () => new DatabaseNotificationChannel());
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
export {
|
|
546
|
+
AnonymousNotifiable,
|
|
547
|
+
DatabaseNotificationChannel,
|
|
548
|
+
DatabaseUnavailableError,
|
|
549
|
+
Dispatcher,
|
|
550
|
+
MailMessage,
|
|
551
|
+
MailNotificationChannel,
|
|
552
|
+
MailUnavailableError,
|
|
553
|
+
NOTIFICATIONS_TABLE,
|
|
554
|
+
NOTIFICATIONS_TABLE_MIGRATION,
|
|
555
|
+
NotDatabaseNotifiableError,
|
|
556
|
+
Notifiable,
|
|
557
|
+
Notification,
|
|
558
|
+
NotificationDispatcherUnavailableError,
|
|
559
|
+
NotificationFacade,
|
|
560
|
+
NotificationQueueUnavailableError,
|
|
561
|
+
NotificationsServiceProvider,
|
|
562
|
+
SHOULD_QUEUE,
|
|
563
|
+
SendQueuedNotifications,
|
|
564
|
+
UnknownNotificationChannelError,
|
|
565
|
+
UnrenderableNotificationDataError,
|
|
566
|
+
UnrenderableNotificationMailError,
|
|
567
|
+
fetchNotifications,
|
|
568
|
+
parseStoredDate,
|
|
569
|
+
toNotifiableList
|
|
570
|
+
};
|
|
571
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../errors.ts","../MailMessage.ts","../channels/MailNotificationChannel.ts","../channels/DatabaseNotificationChannel.ts","../migrations.ts","../symbols.ts","../Dispatcher.ts","../Notifiable.ts","../AnonymousNotifiable.ts","../Jobs/SendQueuedNotifications.ts","../Notification.ts","../facade.ts","../NotificationsServiceProvider.ts"],"sourcesContent":["import type { OrielError } from '@orieljs/contracts'\n\nexport class UnrenderableNotificationMailError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnrenderableNotificationMailError'\n constructor(notification: string) {\n super(\n `[${notification}].toMail() must return a Mailable, a MailMessage, or ` +\n '{ subject?, greeting?, lines, actionText?, actionUrl?, outroLines?, salutation? }.',\n )\n }\n}\n\nexport class MailUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'MailUnavailableError'\n constructor() {\n super(\"Sending a notification's mail requires the Mail component. Register MailManager first.\")\n }\n}\n\nexport class DatabaseUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'DatabaseUnavailableError'\n constructor() {\n super('The database notification channel requires the Database component. Register DatabaseManager first.')\n }\n}\n\nexport class NotDatabaseNotifiableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotDatabaseNotifiableError'\n constructor(notifiable: string) {\n super(`[${notifiable}] has no getKey() — the database channel and notification-listing methods need a model.`)\n }\n}\n\nexport class UnrenderableNotificationDataError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnrenderableNotificationDataError'\n constructor(notification: string) {\n super(`[${notification}] needs a toDatabase() or toArray() method to use the database channel.`)\n }\n}\n\nexport class UnknownNotificationChannelError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'UnknownNotificationChannelError'\n constructor(channel: string) {\n super(`Notification channel [${channel}] is not registered. Register it first with NotificationDispatcher.extend().`)\n }\n}\n\nexport class NotificationQueueUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotificationQueueUnavailableError'\n constructor() {\n super('Queueing a notification requires the Bus component. Register BusDispatcher, or call sendNow() instead.')\n }\n}\n\nexport class NotificationDispatcherUnavailableError extends Error implements OrielError {\n readonly kind = 'Server' as const\n readonly status = 500\n override readonly name = 'NotificationDispatcherUnavailableError'\n constructor() {\n super('notify()/notifyNow() require the Notifications component. Register NotificationDispatcher first.')\n }\n}\n","import type { MailRecipient } from '@orieljs/contracts'\n\nexport interface MailMessageAction {\n text: string\n url: string\n}\n\n/**\n * Laravel's Illuminate\\Notifications\\Messages\\MailMessage: greeting/line/action/salutation, rendered to a default\n * HTML+text template with no View involved. line() appends to the intro before action() is called, and to the\n * outro afterwards — exactly like Laravel's SimpleMessage.\n */\nexport class MailMessage {\n subjectLine?: string\n greetingLine?: string\n introLines: string[] = []\n actionData?: MailMessageAction\n outroLines: string[] = []\n salutationLine = 'Regards,\\nApplication'\n toAddresses: MailRecipient[] = []\n ccAddresses: MailRecipient[] = []\n bccAddresses: MailRecipient[] = []\n fromAddress?: MailRecipient\n\n subject(subject: string): this {\n this.subjectLine = subject\n return this\n }\n\n greeting(greeting: string): this {\n this.greetingLine = greeting\n return this\n }\n\n /** Before action(), appends to the intro; after action(), to the outro. */\n line(text: string): this {\n ;(this.actionData ? this.outroLines : this.introLines).push(text)\n return this\n }\n\n lines(lines: string[]): this {\n for (const line of lines) this.line(line)\n return this\n }\n\n action(text: string, url: string): this {\n this.actionData = { text, url }\n return this\n }\n\n salutation(salutation: string): this {\n this.salutationLine = salutation\n return this\n }\n\n from(address: MailRecipient): this {\n this.fromAddress = address\n return this\n }\n\n to(recipients: MailRecipient | MailRecipient[]): this {\n this.toAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n cc(recipients: MailRecipient | MailRecipient[]): this {\n this.ccAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n bcc(recipients: MailRecipient | MailRecipient[]): this {\n this.bccAddresses.push(...(Array.isArray(recipients) ? recipients : [recipients]))\n return this\n }\n\n /** Renders the default notification mail template. No ViewFactory involved. */\n render(): { html: string; text: string } {\n const greeting = this.greetingLine ?? 'Hello!'\n const html: string[] = [`<p>${escapeHtml(greeting)}</p>`]\n const text: string[] = [greeting]\n\n for (const line of this.introLines) {\n html.push(`<p>${escapeHtml(line)}</p>`)\n text.push(line)\n }\n if (this.actionData) {\n const { text: actionText, url } = this.actionData\n html.push(`<p><a href=\"${escapeHtml(url)}\" class=\"button\" target=\"_blank\">${escapeHtml(actionText)}</a></p>`)\n text.push(`${actionText}: ${url}`)\n }\n for (const line of this.outroLines) {\n html.push(`<p>${escapeHtml(line)}</p>`)\n text.push(line)\n }\n html.push(`<p>${escapeHtml(this.salutationLine).replace(/\\n/g, '<br>')}</p>`)\n text.push(this.salutationLine)\n\n return { html: `<!doctype html><html><body>${html.join('\\n')}</body></html>`, text: text.join('\\n\\n') }\n }\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n}\n","import {\n MailManager as MailManagerToken,\n type Mailable,\n type MailRecipient,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { MailUnavailableError, UnrenderableNotificationMailError } from '../errors'\nimport { MailMessage } from '../MailMessage'\n\n/**\n * What Auth's default ResetPassword notification's toMail() returns: plain data, since Auth can't import\n * Notifications. `{ subject, lines, actionText, actionUrl, outroLines }` exactly; `greeting`/`salutation` are\n * optional extensions any other notification's plain toMail() may also use.\n */\nexport interface PlainNotificationMail {\n subject?: string\n greeting?: string\n lines: string[]\n actionText?: string\n actionUrl?: string\n outroLines?: string[]\n salutation?: string\n}\n\nfunction isMailable(value: unknown): value is Mailable {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as Mailable).envelope === 'function' &&\n typeof (value as Mailable).content === 'function'\n )\n}\n\nfunction isPlainNotificationMail(value: unknown): value is PlainNotificationMail {\n return typeof value === 'object' && value !== null && Array.isArray((value as PlainNotificationMail).lines)\n}\n\nfunction toMailMessage(result: unknown, notificationName: string): MailMessage {\n if (result instanceof MailMessage) return result\n if (!isPlainNotificationMail(result)) throw new UnrenderableNotificationMailError(notificationName)\n const message = new MailMessage()\n if (result.subject) message.subject(result.subject)\n if (result.greeting) message.greeting(result.greeting)\n message.lines(result.lines)\n if (result.actionText && result.actionUrl) message.action(result.actionText, result.actionUrl)\n for (const line of result.outroLines ?? []) message.line(line)\n if (result.salutation) message.salutation(result.salutation)\n return message\n}\n\n/**\n * Wraps a MailMessage as a Mailable, so it can go through MailManager.send() without a View: the default template's\n * HTML goes in `htmlString` and its plain-text twin in `textString`.\n */\nfunction mailableFromMessage(message: MailMessage, fallbackTo: MailRecipient[]): Mailable {\n return {\n envelope: async () => ({\n subject: message.subjectLine ?? '',\n to: message.toAddresses.length > 0 ? message.toAddresses : fallbackTo,\n cc: message.ccAddresses,\n bcc: message.bccAddresses,\n from: message.fromAddress,\n }),\n content: async () => {\n const rendered = message.render()\n return { htmlString: rendered.html, textString: rendered.text }\n },\n }\n}\n\nfunction normalizeRoute(route: unknown): MailRecipient[] {\n if (route === undefined || route === null) return []\n return Array.isArray(route) ? (route as MailRecipient[]) : [route as MailRecipient]\n}\n\nexport class MailNotificationChannel implements NotificationChannel {\n async send(notifiable: Notifiable, notification: Notification): Promise<void> {\n if (!notification.toMail) throw new UnrenderableNotificationMailError(notification.constructor.name)\n const result = await notification.toMail(notifiable)\n const to = normalizeRoute(notifiable.routeNotificationFor('mail', notification))\n\n // Matches Laravel's MailChannel: with no route and no Mailable to supply its own recipients, there's nothing to send.\n if (to.length === 0 && !isMailable(result)) return\n\n const mailable = isMailable(result) ? result : mailableFromMessage(toMailMessage(result, notification.constructor.name), to)\n const container = currentContainer()\n if (!container?.bound(MailManagerToken)) throw new MailUnavailableError()\n // MailManager.send() is the only send path the contract offers, so a queueable Mailable returned from toMail()\n // is queued again from here (even when this notification is itself running on a queue worker).\n await container.make(MailManagerToken).send(mailable)\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport {\n DatabaseManager as DatabaseManagerToken,\n type DatabaseConnection,\n type ModelInstance,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n type Row,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { DatabaseUnavailableError, NotDatabaseNotifiableError, UnrenderableNotificationDataError } from '../errors'\n\nexport const NOTIFICATIONS_TABLE = 'notifications'\n\nexport interface DatabaseNotificationRecord {\n readonly id: string\n readonly type: string\n readonly data: Record<string, unknown>\n readonly createdAt: Date\n readonly updatedAt: Date\n readAt: Date | undefined\n markAsRead(): Promise<void>\n markAsUnread(): Promise<void>\n}\n\nconst ZONELESS_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?$/\n\n/**\n * Database writes Dates as zoneless UTC (`'2026-09-25 04:30:00'`) and hands them back as strings, so\n * `new Date()` would read such a string as local time; it is parsed as UTC here instead.\n */\nexport function parseStoredDate(value: unknown): Date {\n if (value instanceof Date) return value\n if (typeof value === 'number') return new Date(value)\n const text = String(value)\n return ZONELESS_TIMESTAMP.test(text) ? new Date(`${text.replace(' ', 'T')}Z`) : new Date(text)\n}\n\nfunction wrapRow(row: Row, db: DatabaseConnection): DatabaseNotificationRecord {\n const record: DatabaseNotificationRecord = {\n id: String(row.id),\n type: String(row.type),\n data: JSON.parse(String(row.data)) as Record<string, unknown>,\n createdAt: parseStoredDate(row.created_at),\n updatedAt: parseStoredDate(row.updated_at),\n readAt: row.read_at ? parseStoredDate(row.read_at) : undefined,\n async markAsRead() {\n if (record.readAt) return\n const now = new Date()\n await db.table(NOTIFICATIONS_TABLE).where('id', record.id).update({ read_at: now, updated_at: now })\n record.readAt = now\n },\n async markAsUnread() {\n if (!record.readAt) return\n await db.table(NOTIFICATIONS_TABLE).where('id', record.id).update({ read_at: null, updated_at: new Date() })\n record.readAt = undefined\n },\n }\n return record\n}\n\nfunction requireDatabase(): DatabaseConnection {\n const container = currentContainer()\n if (!container?.bound(DatabaseManagerToken)) throw new DatabaseUnavailableError()\n return container.make(DatabaseManagerToken).connection()\n}\n\n/** notifiable_type + notifiable_id together, never id alone. */\nexport async function fetchNotifications(\n notifiableType: string,\n notifiableId: unknown,\n filter: 'all' | 'read' | 'unread' = 'all',\n): Promise<DatabaseNotificationRecord[]> {\n const db = requireDatabase()\n let query = db\n .table(NOTIFICATIONS_TABLE)\n .where('notifiable_type', notifiableType)\n .where('notifiable_id', notifiableId)\n .orderBy('created_at', 'desc')\n if (filter === 'unread') query = query.whereNull('read_at')\n if (filter === 'read') query = query.whereNotNull('read_at')\n const rows = await query.get()\n return rows.all().map((row) => wrapRow(row, db))\n}\n\nexport class DatabaseNotificationChannel implements NotificationChannel {\n async send(notifiable: Notifiable, notification: Notification): Promise<void> {\n if (!notification.toDatabase && !notification.toArray) {\n throw new UnrenderableNotificationDataError(notification.constructor.name)\n }\n const data = notification.toDatabase ? notification.toDatabase(notifiable) : notification.toArray!(notifiable)\n const model = notifiable as Partial<ModelInstance>\n if (typeof model.getKey !== 'function') throw new NotDatabaseNotifiableError(notifiable.constructor.name)\n\n const db = requireDatabase()\n const now = new Date()\n await db.table(NOTIFICATIONS_TABLE).insert({\n id: notification.id ?? randomUUID(),\n type: notification.constructor.name,\n notifiable_type: notifiable.constructor.name,\n notifiable_id: model.getKey(),\n data: JSON.stringify(data),\n read_at: null,\n created_at: now,\n updated_at: now,\n })\n }\n}\n","export const NOTIFICATIONS_TABLE_MIGRATION = `import { Migration, Schema } from '@orieljs/framework'\n\nexport default class extends Migration {\n async up() {\n await Schema.create('notifications', (t) => {\n t.uuid('id').primary()\n t.string('type')\n t.morphs('notifiable')\n t.text('data')\n t.timestamp('read_at').nullable()\n t.timestamps()\n })\n }\n\n async down() {\n await Schema.dropIfExists('notifications')\n }\n}\n`\n","/**\n * The Queue/Bus protocol's shared marker (the \"Queue/Bus protocol\" section of the Queue/Bus design notes) —\n * identical to Mail's `symbols.ts`. `Symbol.for` is a *global* registry key, so this is the literal same runtime\n * symbol Bus's `Queueable` base class and `isQueueable()` use, with no import needed (and none allowed).\n */\nexport const SHOULD_QUEUE: unique symbol = Symbol.for('oriel:bus.shouldQueue')\n","import { randomUUID } from 'node:crypto'\nimport {\n BusDispatcher as BusDispatcherToken,\n type Container,\n type Notifiable,\n type Notification,\n type NotificationChannel,\n type NotificationDispatcher as NotificationDispatcherContract,\n} from '@orieljs/contracts'\nimport { AnonymousNotifiable } from './AnonymousNotifiable'\nimport { NotificationQueueUnavailableError, UnknownNotificationChannelError } from './errors'\nimport { SendQueuedNotifications } from './Jobs/SendQueuedNotifications'\nimport { SHOULD_QUEUE } from './symbols'\n\n/**\n * Matches the Queue/Bus protocol's `isQueueable()` exactly: true when the notification carries\n * `SHOULD_QUEUE === true`, or has a defined value for any of ShouldQueue's fields, or declares a\n * `uniqueId()`/`retryUntil()` method (same rule as Mail's `isQueueableMailable`). `implements ShouldQueue` itself\n * leaves no runtime trace. Call sendNow() explicitly to bypass this for a notification with none of these.\n */\nfunction isQueueableNotification(notification: Notification): boolean {\n const record = notification as unknown as Record<PropertyKey, unknown>\n if (record[SHOULD_QUEUE] === true) return true\n return (\n record.connection !== undefined ||\n record.queue !== undefined ||\n record.delay !== undefined ||\n record.tries !== undefined ||\n record.backoff !== undefined ||\n record.timeout !== undefined ||\n typeof record.uniqueId === 'function' ||\n typeof record.retryUntil === 'function'\n )\n}\n\n/**\n * The notification one notifiable receives. With no id set, a shallow copy (same prototype, own fields copied)\n * carrying a fresh id, so every notifiable gets its own id (the database channel's primary key) and the caller's\n * instance is left untouched; a caller-preset id is kept as is. The copy keeps its class, so Queue's serializer\n * restores it as that class on the worker.\n */\nfunction forNotifiable(notification: Notification): Notification {\n if (notification.id) return notification\n const copy = Object.assign(Object.create(Object.getPrototypeOf(notification) as object) as Notification, notification)\n copy.id = randomUUID()\n return copy\n}\n\n/**\n * One notifiable, an array, or any other iterable of them (a Database `Collection` from `await User.where(...).get()`,\n * a Set, ...), as Laravel's `Notification::send($users, ...)` accepts. A notifiable is recognised by its\n * routeNotificationFor() method first, so an iterable model is still treated as one notifiable.\n */\nexport function toNotifiableList(notifiables: Notifiable | Iterable<Notifiable>): Notifiable[] {\n if (Array.isArray(notifiables)) return notifiables as Notifiable[]\n if (typeof (notifiables as Partial<Notifiable>).routeNotificationFor === 'function') return [notifiables as Notifiable]\n if (typeof notifiables === 'object' && notifiables !== null && Symbol.iterator in notifiables) {\n return [...(notifiables as Iterable<Notifiable>)]\n }\n return [notifiables as Notifiable]\n}\n\n/** Resolves channels by name from a factory map — mirrors MailManager's driver resolution. */\nexport class Dispatcher implements NotificationDispatcherContract {\n private readonly factories = new Map<string, () => NotificationChannel>()\n private readonly channels = new Map<string, NotificationChannel>()\n\n constructor(private readonly container: Container) {}\n\n extend(channel: string, factory: () => NotificationChannel): void {\n this.factories.set(channel, factory)\n this.channels.delete(channel)\n }\n\n private resolve(name: string): NotificationChannel {\n const cached = this.channels.get(name)\n if (cached) return cached\n const factory = this.factories.get(name)\n if (!factory) throw new UnknownNotificationChannelError(name)\n const channel = factory()\n this.channels.set(name, channel)\n return channel\n }\n\n async send(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification): Promise<void> {\n const list = toNotifiableList(notifiables)\n if (list.length === 0) return\n if (isQueueableNotification(notification)) {\n if (!this.container.bound(BusDispatcherToken)) throw new NotificationQueueUnavailableError()\n const bus = this.container.make(BusDispatcherToken)\n // One job per notifiable per channel, as Laravel does, so a failing channel or recipient retries alone.\n for (const notifiable of list) {\n const own = forNotifiable(notification)\n for (const channel of notification.via(notifiable)) {\n await bus.dispatch(new SendQueuedNotifications([notifiable], own, [channel]))\n }\n }\n return\n }\n await this.sendNow(list, notification)\n }\n\n async sendNow(notifiables: Notifiable | Iterable<Notifiable>, notification: Notification, channels?: string[]): Promise<void> {\n const list = toNotifiableList(notifiables)\n for (const notifiable of list) {\n const own = forNotifiable(notification)\n const names = channels ?? notification.via(notifiable)\n for (const name of names) {\n // An on-demand notifiable has no model to store a row against, so it skips 'database' as in Laravel.\n if (name === 'database' && notifiable instanceof AnonymousNotifiable) continue\n await this.resolve(name).send(notifiable, own)\n }\n }\n }\n\n /** On-demand: an AnonymousNotifiable, whose notify() goes through the bound NotificationDispatcher. */\n route(channel: string, route: unknown): AnonymousNotifiable {\n return new AnonymousNotifiable().route(channel, route)\n }\n}\n","import {\n NotificationDispatcher as NotificationDispatcherToken,\n type ModelInstance,\n type Notifiable as NotifiableContract,\n type Notification,\n type NotificationDispatcher,\n} from '@orieljs/contracts'\nimport { currentContainer } from '@orieljs/container'\nimport { Str } from '@orieljs/support'\nimport { fetchNotifications, type DatabaseNotificationRecord } from './channels/DatabaseNotificationChannel'\nimport { NotDatabaseNotifiableError, NotificationDispatcherUnavailableError } from './errors'\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type NotifiableConstructor = new (...args: any[]) => object\n\n/** The bound NotificationDispatcher, or a clear error when the Notifications component isn't registered. */\nexport function resolveDispatcher(): NotificationDispatcher {\n const container = currentContainer()\n if (!container?.bound(NotificationDispatcherToken)) throw new NotificationDispatcherUnavailableError()\n return container.make(NotificationDispatcherToken)\n}\n\nfunction requireKey(instance: object): unknown {\n const model = instance as Partial<ModelInstance>\n if (typeof model.getKey !== 'function') throw new NotDatabaseNotifiableError(instance.constructor.name)\n return model.getKey()\n}\n\n/**\n * Mirrors Laravel's Illuminate\\Notifications\\Notifiable trait: `class User extends Notifiable(Model) {}`. Custom\n * routing per channel follows Laravel's own resolution order: a `routeNotificationFor<Channel>()` method (Studly\n * name) wins if present, else 'mail' reads `email`/`getAttribute('email')`, 'database' returns the instance itself\n * (the database channel derives the table row from it directly), and anything else is unrouted (`undefined`).\n */\nexport function Notifiable<TBase extends NotifiableConstructor>(Base: TBase) {\n return class NotifiableMixin extends Base implements NotifiableContract {\n routeNotificationFor(channel: string, notification: Notification): unknown {\n const custom = (this as unknown as Record<string, unknown>)[`routeNotificationFor${Str.studly(channel)}`]\n if (typeof custom === 'function') return (custom as (n: Notification) => unknown).call(this, notification)\n if (channel === 'mail') {\n const withAttrs = this as unknown as { email?: string; getAttribute?: (key: string) => unknown }\n return typeof withAttrs.getAttribute === 'function' ? withAttrs.getAttribute('email') : withAttrs.email\n }\n if (channel === 'database') return this\n return undefined\n }\n\n async notify(notification: Notification): Promise<void> {\n await resolveDispatcher().send(this as unknown as NotifiableContract, notification)\n }\n\n async notifyNow(notification: Notification, channels?: string[]): Promise<void> {\n await resolveDispatcher().sendNow(this as unknown as NotifiableContract, notification, channels)\n }\n\n notifications(): Promise<DatabaseNotificationRecord[]> {\n return fetchNotifications(this.constructor.name, requireKey(this))\n }\n\n unreadNotifications(): Promise<DatabaseNotificationRecord[]> {\n return fetchNotifications(this.constructor.name, requireKey(this), 'unread')\n }\n\n readNotifications(): Promise<DatabaseNotificationRecord[]> {\n return fetchNotifications(this.constructor.name, requireKey(this), 'read')\n }\n }\n}\n","import type { Notifiable, Notification } from '@orieljs/contracts'\nimport { resolveDispatcher } from './Notifiable'\n\n/**\n * Returned by `Notification.route(channel, address)` for sending to an address with no model behind it. The\n * routes are an own plain record and every method sits on the prototype, so a queued on-demand notification\n * survives Queue's serializer (which rejects functions in own fields) and is restored on the worker as this class\n * (registered as `'Notifications.AnonymousNotifiable'` in ClassRegistry).\n */\nexport class AnonymousNotifiable implements Notifiable {\n routes: Record<string, unknown> = {}\n\n /** Adds a route; chains, so one on-demand notifiable can go to several channels. */\n route(channel: string, route: unknown): this {\n this.routes[channel] = route\n return this\n }\n\n routeNotificationFor(channel: string): unknown {\n return this.routes[channel]\n }\n\n async notify(notification: Notification): Promise<void> {\n await resolveDispatcher().send(this, notification)\n }\n\n async notifyNow(notification: Notification, channels?: string[]): Promise<void> {\n await resolveDispatcher().sendNow(this, notification, channels)\n }\n}\n","import { NotificationDispatcher as NotificationDispatcherToken, type Job, type Notifiable, type Notification } from '@orieljs/contracts'\nimport { inject } from '@orieljs/container'\nimport { SHOULD_QUEUE } from '../symbols'\n\n/** The ShouldQueue options copied from the notification onto the wrapper job. */\nconst QUEUE_OPTIONS = [\n 'connection',\n 'queue',\n 'delay',\n 'tries',\n 'backoff',\n 'timeout',\n 'failOnTimeout',\n 'deleteWhenMissingModels',\n] as const\n\n/** The job hooks Queue reads off a job instance, when the wrapped notification defines them. */\ninterface JobHooks {\n failed?: (error: unknown) => unknown\n retryUntil?: () => unknown\n middleware?: unknown[] | (() => unknown[])\n}\n\n/**\n * Copies the notification's own ShouldQueue fields onto the wrapper job (when set) — same reasoning as Mail's\n * SendQueuedMailable. Uses `declare` fields on the class (no runtime auto-initialization to `undefined`), so a\n * field is a genuinely own property only when the notification actually set it.\n */\nfunction copyShouldQueueFields(target: Record<PropertyKey, unknown>, source: object): void {\n const fields = source as Record<string, unknown>\n for (const option of QUEUE_OPTIONS) {\n if (fields[option] !== undefined) target[option] = fields[option]\n }\n}\n\n/**\n * Dispatched by Dispatcher.send() when the notification is queueable. Runs on the worker: calls sendNow()\n * directly (never send()), so it can't re-detect itself as queueable and loop.\n *\n * Carries `SHOULD_QUEUE` on its own prototype, per the Queue/Bus protocol, so `BusDispatcher.dispatch()` — which\n * re-decides queued-or-now on whatever job it's handed — actually enqueues it instead of running it inline; the\n * notification's own queueability (checked by Dispatcher, above) only decides whether to wrap it at all. Like\n * Laravel's SendQueuedNotifications, it forwards failed(), retryUntil(), middleware() and displayName() to the\n * notification.\n */\nexport class SendQueuedNotifications implements Job {\n declare connection?: string\n declare queue?: string\n declare delay?: number\n declare tries?: number\n declare backoff?: number | number[]\n declare timeout?: number\n declare failOnTimeout?: boolean\n declare deleteWhenMissingModels?: boolean\n\n constructor(\n readonly notifiables: Notifiable[],\n readonly notification: Notification,\n readonly channels?: string[],\n ) {\n copyShouldQueueFields(this as unknown as Record<PropertyKey, unknown>, notification)\n }\n\n async handle(): Promise<void> {\n await inject(NotificationDispatcherToken).sendNow(this.notifiables, this.notification, this.channels)\n }\n\n /** Called by Queue after the final failed attempt. */\n async failed(error: unknown): Promise<void> {\n const hooks = this.notification as unknown as JobHooks\n if (typeof hooks.failed === 'function') await hooks.failed.call(this.notification, error)\n }\n\n /** `failed_jobs` and `queue:failed` show the notification's class, not the wrapper's. */\n displayName(): string {\n return this.notification.constructor.name\n }\n\n retryUntil(): unknown {\n const hooks = this.notification as unknown as JobHooks\n return typeof hooks.retryUntil === 'function' ? hooks.retryUntil.call(this.notification) : undefined\n }\n\n middleware(): unknown[] {\n const declared = (this.notification as unknown as JobHooks).middleware\n return (typeof declared === 'function' ? declared.call(this.notification) : declared) ?? []\n }\n}\n;(SendQueuedNotifications.prototype as unknown as Record<PropertyKey, unknown>)[SHOULD_QUEUE] = true\n","import {\n NotificationDispatcher as NotificationDispatcherToken,\n type Notifiable,\n type Notification as NotificationContract,\n type NotificationChannel,\n type NotificationDispatcher,\n} from '@orieljs/contracts'\nimport { facade } from '@orieljs/support'\nimport { toNotifiableList } from './Dispatcher'\nimport { resolveDispatcher } from './Notifiable'\n\n/**\n * Mirrors Laravel's Illuminate\\Notifications\\Notification: override via(), and whichever toXxx() the channels\n * need.\n *\n * The statics are the `Notification` facade, so one export serves both `class InvoicePaid extends Notification`\n * and `Notification.send(users, new InvoicePaid())`.\n */\nexport abstract class Notification implements NotificationContract {\n id?: string\n abstract via(notifiable: Notifiable): string[]\n\n /** Queues it when the notification is queueable, otherwise sends now. Accepts one notifiable, an array or any iterable. */\n static send(notifiables: Notifiable | Iterable<Notifiable>, notification: NotificationContract): Promise<void> {\n return resolveDispatcher().send(toNotifiableList(notifiables), notification)\n }\n\n static sendNow(\n notifiables: Notifiable | Iterable<Notifiable>,\n notification: NotificationContract,\n channels?: string[],\n ): Promise<void> {\n return resolveDispatcher().sendNow(toNotifiableList(notifiables), notification, channels)\n }\n\n /** On-demand: `Notification.route('mail', 'a@b.c').notify(new InvoicePaid())`. */\n static route(channel: string, route: unknown): Notifiable & { notify(notification: NotificationContract): Promise<void> } {\n return resolveDispatcher().route(channel, route)\n }\n\n static extend(channel: string, factory: () => NotificationChannel): void {\n resolveDispatcher().extend(channel, factory)\n }\n\n /** Replace the dispatcher in the current container (what a NotificationFake uses). */\n static swap(fake: NotificationDispatcher): void {\n facade<NotificationDispatcher>(NotificationDispatcherToken).swap(fake)\n }\n}\n","import type { NotificationDispatcher } from '@orieljs/contracts'\nimport { NotificationDispatcher as NotificationDispatcherToken } from '@orieljs/contracts'\nimport { facade } from '@orieljs/support'\n\n/**\n * The plain facade over NotificationDispatcher. App code normally uses the `Notification` base class's statics\n * instead (one `Notification` export is both the base class and the facade); this stays exported for code that\n * wants the contract-typed proxy (getFacadeRoot(), swap()).\n */\nexport const NotificationFacade = facade<NotificationDispatcher>(NotificationDispatcherToken)\n","import { ClassRegistry as ClassRegistryToken, NotificationDispatcher as NotificationDispatcherToken } from '@orieljs/contracts'\nimport { ServiceProvider } from '@orieljs/support'\nimport { AnonymousNotifiable } from './AnonymousNotifiable'\nimport { DatabaseNotificationChannel } from './channels/DatabaseNotificationChannel'\nimport { MailNotificationChannel } from './channels/MailNotificationChannel'\nimport { Dispatcher } from './Dispatcher'\nimport { SendQueuedNotifications } from './Jobs/SendQueuedNotifications'\n\nexport class NotificationsServiceProvider extends ServiceProvider {\n override register(): void {\n this.app.singleton(NotificationDispatcherToken, (container) => new Dispatcher(container))\n // Namespaced names, registered in register() (not boot()) — the Queue/Bus protocol rule for wrapper classes.\n // ClassRegistry is safe to use here: Application's own constructor binds it before any provider registers.\n const registry = this.app.make(ClassRegistryToken)\n registry.register(SendQueuedNotifications, 'Notifications.SendQueuedNotifications')\n registry.register(AnonymousNotifiable, 'Notifications.AnonymousNotifiable')\n }\n\n override boot(): void {\n const dispatcher = this.app.make(NotificationDispatcherToken)\n dispatcher.extend('mail', () => new MailNotificationChannel())\n dispatcher.extend('database', () => new DatabaseNotificationChannel())\n }\n}\n"],"mappings":"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","names":["currentContainer","currentContainer","randomUUID","currentContainer","currentContainer","NotificationDispatcherToken","NotificationDispatcherToken","randomUUID","NotificationDispatcherToken","NotificationDispatcherToken","NotificationDispatcherToken","facade","NotificationDispatcherToken","NotificationDispatcherToken"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@orieljs/notifications",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "The Oriel Notifications component.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/orieljs/framework.git",
|
|
11
|
+
"directory": "src/Oriel/Notifications"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=22"
|
|
15
|
+
},
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"NOTICE"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@orieljs/contracts": "0.1.0-alpha.1",
|
|
33
|
+
"@orieljs/container": "0.1.0-alpha.1",
|
|
34
|
+
"@orieljs/support": "0.1.0-alpha.1"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup && node ../../../scripts/copy-license.mjs"
|
|
38
|
+
}
|
|
39
|
+
}
|