@voltro/plugin-notifications 0.52.0 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +424 -0
- package/THIRD-PARTY-NOTICES.md +169 -1
- package/dist/index.d.ts +202 -4
- package/dist/index.js +626 -227
- package/dist/rpc.d.ts +27 -0
- package/dist/rpc.js +60 -2
- package/dist/web.d.ts +22 -0
- package/dist/web.js +81 -22
- package/package.json +8 -6
- package/sw.js +50 -0
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -5,7 +5,7 @@ property of its respective copyright holders and is used under the terms of
|
|
|
5
5
|
its license. This file is provided for attribution; it grants no rights in
|
|
6
6
|
@voltro/plugin-notifications itself, which is proprietary (see LICENSE).
|
|
7
7
|
|
|
8
|
-
Generated from the resolved runtime dependency closure (
|
|
8
|
+
Generated from the resolved runtime dependency closure (12 packages).
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -37,6 +37,146 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
37
37
|
SOFTWARE.
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
## @tanstack/devtools-event-client@0.4.4
|
|
41
|
+
|
|
42
|
+
License: MIT
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
MIT License
|
|
46
|
+
|
|
47
|
+
Copyright (c) 2025 TanStack
|
|
48
|
+
|
|
49
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
50
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
51
|
+
in the Software without restriction, including without limitation the rights
|
|
52
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
53
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
54
|
+
furnished to do so, subject to the following conditions:
|
|
55
|
+
|
|
56
|
+
The above copyright notice and this permission notice shall be included in all
|
|
57
|
+
copies or substantial portions of the Software.
|
|
58
|
+
|
|
59
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
60
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
61
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
62
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
63
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
64
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
65
|
+
SOFTWARE.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## @tanstack/form-core@1.33.5
|
|
69
|
+
|
|
70
|
+
License: MIT
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
MIT License
|
|
74
|
+
|
|
75
|
+
Copyright (c) 2021-present Tanner Linsley
|
|
76
|
+
|
|
77
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
78
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
79
|
+
in the Software without restriction, including without limitation the rights
|
|
80
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
81
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
82
|
+
furnished to do so, subject to the following conditions:
|
|
83
|
+
|
|
84
|
+
The above copyright notice and this permission notice shall be included in all
|
|
85
|
+
copies or substantial portions of the Software.
|
|
86
|
+
|
|
87
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
88
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
89
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
90
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
91
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
92
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
93
|
+
SOFTWARE.
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## @tanstack/pacer-lite@0.1.1
|
|
97
|
+
|
|
98
|
+
License: MIT
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
MIT License
|
|
102
|
+
|
|
103
|
+
Copyright (c) 2025 Tanner Linsley
|
|
104
|
+
|
|
105
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
106
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
107
|
+
in the Software without restriction, including without limitation the rights
|
|
108
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
109
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
110
|
+
furnished to do so, subject to the following conditions:
|
|
111
|
+
|
|
112
|
+
The above copyright notice and this permission notice shall be included in all
|
|
113
|
+
copies or substantial portions of the Software.
|
|
114
|
+
|
|
115
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
116
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
117
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
118
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
119
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
120
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
121
|
+
SOFTWARE.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## @tanstack/react-store@0.11.1
|
|
125
|
+
|
|
126
|
+
License: MIT
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
MIT License
|
|
130
|
+
|
|
131
|
+
Copyright (c) 2021 Tanner Linsley
|
|
132
|
+
|
|
133
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
134
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
135
|
+
in the Software without restriction, including without limitation the rights
|
|
136
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
137
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
138
|
+
furnished to do so, subject to the following conditions:
|
|
139
|
+
|
|
140
|
+
The above copyright notice and this permission notice shall be included in all
|
|
141
|
+
copies or substantial portions of the Software.
|
|
142
|
+
|
|
143
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
144
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
145
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
146
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
147
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
148
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
149
|
+
SOFTWARE.
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## @tanstack/store@0.11.1
|
|
153
|
+
|
|
154
|
+
License: MIT
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
MIT License
|
|
158
|
+
|
|
159
|
+
Copyright (c) 2021 Tanner Linsley
|
|
160
|
+
|
|
161
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
162
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
163
|
+
in the Software without restriction, including without limitation the rights
|
|
164
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
165
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
166
|
+
furnished to do so, subject to the following conditions:
|
|
167
|
+
|
|
168
|
+
The above copyright notice and this permission notice shall be included in all
|
|
169
|
+
copies or substantial portions of the Software.
|
|
170
|
+
|
|
171
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
172
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
173
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
174
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
175
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
176
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
177
|
+
SOFTWARE.
|
|
178
|
+
```
|
|
179
|
+
|
|
40
180
|
## jose@6.2.8
|
|
41
181
|
|
|
42
182
|
License: MIT
|
|
@@ -329,6 +469,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
329
469
|
SOFTWARE.
|
|
330
470
|
```
|
|
331
471
|
|
|
472
|
+
## use-sync-external-store@1.6.0
|
|
473
|
+
|
|
474
|
+
License: MIT
|
|
475
|
+
|
|
476
|
+
```
|
|
477
|
+
MIT License
|
|
478
|
+
|
|
479
|
+
Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
480
|
+
|
|
481
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
482
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
483
|
+
in the Software without restriction, including without limitation the rights
|
|
484
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
485
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
486
|
+
furnished to do so, subject to the following conditions:
|
|
487
|
+
|
|
488
|
+
The above copyright notice and this permission notice shall be included in all
|
|
489
|
+
copies or substantial portions of the Software.
|
|
490
|
+
|
|
491
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
492
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
493
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
494
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
495
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
496
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
497
|
+
SOFTWARE.
|
|
498
|
+
```
|
|
499
|
+
|
|
332
500
|
## uuid@11.1.1
|
|
333
501
|
|
|
334
502
|
License: MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -24,10 +24,17 @@ export declare const buildNotificationService: (deps: {
|
|
|
24
24
|
}) => NotificationServiceShape;
|
|
25
25
|
|
|
26
26
|
/** A delivery channel. `id` is the channel key prefs target (e.g. 'email',
|
|
27
|
-
* 'slack', 'sms', 'push', 'inApp').
|
|
27
|
+
* 'slack', 'sms', 'push', 'inApp').
|
|
28
|
+
*
|
|
29
|
+
* A MULTI-ENDPOINT channel (web push, mobile push) additionally implements
|
|
30
|
+
* `deliverDetailed`: per-endpoint outcomes, each endpoint isolated — one dead
|
|
31
|
+
* registration never aborts the others, and the delivery log says per
|
|
32
|
+
* endpoint what happened (the per-CHANNEL record the old push loop wrote
|
|
33
|
+
* could not). The service prefers it over `deliver` when present. */
|
|
28
34
|
export declare interface Channel {
|
|
29
35
|
readonly id: string;
|
|
30
36
|
readonly deliver: (msg: ChannelMessage) => Promise<void>;
|
|
37
|
+
readonly deliverDetailed?: (msg: ChannelMessage) => Promise<ReadonlyArray<EndpointDelivery>>;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
/** A built notification to deliver on one channel. */
|
|
@@ -65,6 +72,19 @@ export declare const customChannel: (id: string, deliver: (msg: ChannelMessage)
|
|
|
65
72
|
*/
|
|
66
73
|
export declare const dataStoreNotificationStore: (store: DataStore) => NotificationStore;
|
|
67
74
|
|
|
75
|
+
export declare const dataStorePushSubscriptionStore: (store: DataStore) => PushSubscriptionStore;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The receiver side — what a browser does with the body above. Shipped (not
|
|
79
|
+
* test-only) because the mock-push-endpoint test IS the product's proof: a test
|
|
80
|
+
* that asserts "some bytes arrived" proves transport, this proves the bytes
|
|
81
|
+
* decrypt with the subscriber's keys and nothing else.
|
|
82
|
+
*/
|
|
83
|
+
export declare const decryptWebPushPayload: (body: Buffer, receiver: {
|
|
84
|
+
readonly uaPrivate: Buffer;
|
|
85
|
+
readonly authSecret: Buffer;
|
|
86
|
+
}) => Buffer;
|
|
87
|
+
|
|
68
88
|
export declare const deliveriesTable: Table<"_voltro_notification_deliveries", FieldDefinitions<{
|
|
69
89
|
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
70
90
|
readonly recipient: ColumnBuilder<string, "text", boolean>;
|
|
@@ -73,9 +93,14 @@ export declare const deliveriesTable: Table<"_voltro_notification_deliveries", F
|
|
|
73
93
|
readonly status: ColumnBuilder<string, "text", boolean>;
|
|
74
94
|
readonly error: ColumnBuilder<string | null, "text", boolean>;
|
|
75
95
|
readonly at: ColumnBuilder<Date, "timestamp", true>;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
96
|
+
readonly endpoint: ColumnBuilder<string | null, "text", boolean>;
|
|
97
|
+
readonly clickToken: ColumnBuilder<string | null, "text", boolean>;
|
|
98
|
+
readonly clickedAt: ColumnBuilder<Date | null, "timestamp", boolean>;
|
|
99
|
+
}>, true, "byDeliveriesAt" | "byDeliveriesClickToken">;
|
|
100
|
+
|
|
101
|
+
/** Per-channel delivery outcome (for the delivery log). A multi-endpoint
|
|
102
|
+
* channel writes ONE record per endpoint (`endpoint` set); single-target
|
|
103
|
+
* channels write one per channel as before. */
|
|
79
104
|
export declare interface DeliveryRecord {
|
|
80
105
|
readonly id: string;
|
|
81
106
|
readonly to: string;
|
|
@@ -84,6 +109,13 @@ export declare interface DeliveryRecord {
|
|
|
84
109
|
readonly status: 'sent' | 'failed' | 'skipped';
|
|
85
110
|
readonly error?: string;
|
|
86
111
|
readonly at: string;
|
|
112
|
+
/** The device token / push endpoint this record is about, on multi-endpoint
|
|
113
|
+
* channels. Absent on single-target channels. */
|
|
114
|
+
readonly endpoint?: string | null;
|
|
115
|
+
/** Click-report capability (web push). Never sent to a dashboard reader. */
|
|
116
|
+
readonly clickToken?: string | null;
|
|
117
|
+
/** Set when the recipient clicked the delivered notification. */
|
|
118
|
+
readonly clickedAt?: string | null;
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
/** Email channel — the app supplies the sender (compose with
|
|
@@ -95,6 +127,39 @@ export declare const emailChannel: (send: (msg: {
|
|
|
95
127
|
text: string;
|
|
96
128
|
}) => Promise<void>) => Channel;
|
|
97
129
|
|
|
130
|
+
/** Test seam: fixed ephemeral key + salt reproduce RFC 8291 Appendix A. */
|
|
131
|
+
declare interface EncryptOverrides {
|
|
132
|
+
readonly asPrivate?: Buffer;
|
|
133
|
+
readonly salt?: Buffer;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Encrypt one payload for one subscription — the complete `aes128gcm` body
|
|
138
|
+
* (header ‖ ciphertext) a push service accepts, per RFC 8291. Single record
|
|
139
|
+
* (web-push payloads are capped ~4 KB by every push service, so multi-record
|
|
140
|
+
* framing buys nothing).
|
|
141
|
+
*/
|
|
142
|
+
export declare const encryptWebPushPayload: (keys: PushSubscriptionKeys, payload: Buffer, overrides?: EncryptOverrides) => Buffer;
|
|
143
|
+
|
|
144
|
+
/** One endpoint's (device token's / browser subscription's) outcome inside a
|
|
145
|
+
* multi-endpoint channel delivery. `pruned` marks a registration the channel
|
|
146
|
+
* itself removed (the push service said it is gone). */
|
|
147
|
+
declare interface EndpointDelivery {
|
|
148
|
+
readonly endpoint: string;
|
|
149
|
+
readonly status: 'sent' | 'failed';
|
|
150
|
+
readonly error?: string;
|
|
151
|
+
readonly pruned?: boolean;
|
|
152
|
+
/** Capability for the notification-click report — stored on the delivery
|
|
153
|
+
* record, carried in the push payload, matched constant-time. */
|
|
154
|
+
readonly clickToken?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export declare const endpointHashOf: (endpoint: string) => string;
|
|
158
|
+
|
|
159
|
+
/** Mint a fresh P-256 private scalar, base64url — what the dev secret mint and
|
|
160
|
+
* `voltro secret generate` produce for `VOLTRO_VAPID_PRIVATE_KEY`. */
|
|
161
|
+
export declare const generateVapidPrivateKey: () => string;
|
|
162
|
+
|
|
98
163
|
/** One notification held for later delivery — a digest window rollup or a
|
|
99
164
|
* quiet-hours deferral. `flushAt` is the ISO instant it becomes deliverable
|
|
100
165
|
* (the window boundary / the digest flush tick). `kind` marks WHY it was held
|
|
@@ -205,6 +270,8 @@ export declare const makeSubjectId: (resolve: NotificationsPluginOptions["resolv
|
|
|
205
270
|
|
|
206
271
|
export declare const memoryNotificationStore: () => NotificationStore;
|
|
207
272
|
|
|
273
|
+
export declare const memoryPushSubscriptionStore: () => PushSubscriptionStore;
|
|
274
|
+
|
|
208
275
|
/** The minute-of-day (0–1439) `at` falls on in the IANA `tz`. Uses `Intl` so no
|
|
209
276
|
* date-math library is needed; an unknown zone falls back to UTC. */
|
|
210
277
|
export declare const minuteOfDayInZone: (at: Date, tz: string) => number;
|
|
@@ -374,6 +441,10 @@ export declare interface NotificationStore {
|
|
|
374
441
|
readonly listDeliveries: (opts?: {
|
|
375
442
|
limit?: number;
|
|
376
443
|
}) => Promise<ReadonlyArray<DeliveryRecord>>;
|
|
444
|
+
/** Stamp `clickedAt` on the delivery record carrying this click token.
|
|
445
|
+
* Constant-time token match happens in the caller; this is the lookup +
|
|
446
|
+
* write. Returns false when no record carries the token. */
|
|
447
|
+
readonly markClicked: (clickToken: string) => Promise<boolean>;
|
|
377
448
|
readonly subscribeTopic: (sub: TopicSubscription) => Promise<void>;
|
|
378
449
|
readonly unsubscribeTopic: (topic: string, subjectId: string) => Promise<void>;
|
|
379
450
|
readonly listTopicSubscribers: (topic: string) => Promise<ReadonlyArray<TopicSubscription>>;
|
|
@@ -418,6 +489,10 @@ export declare const pushChannel: (opts: {
|
|
|
418
489
|
readonly id?: string;
|
|
419
490
|
readonly tokensFor: (subjectId: string) => Promise<ReadonlyArray<string>>;
|
|
420
491
|
readonly transport: (payload: PushPayload) => Promise<void>;
|
|
492
|
+
/** Called when the transport threw `PushTokenRejected` for one token — the
|
|
493
|
+
* app's prune hook (this channel's registry is the APP's table, so the app
|
|
494
|
+
* deletes; web push, whose registry is the plugin's own, prunes itself). */
|
|
495
|
+
readonly onTokenRejected?: (token: string, reason: string) => Promise<void>;
|
|
421
496
|
}) => Channel;
|
|
422
497
|
|
|
423
498
|
/** The provider-agnostic push payload `pushChannel` builds per device token —
|
|
@@ -432,6 +507,54 @@ export declare interface PushPayload {
|
|
|
432
507
|
readonly data: Record<string, unknown>;
|
|
433
508
|
}
|
|
434
509
|
|
|
510
|
+
/** A browser push subscription's crypto material, as `PushSubscription.toJSON()`
|
|
511
|
+
* hands it to the page: base64url `p256dh` (the UA public key) + `auth`. */
|
|
512
|
+
export declare interface PushSubscriptionKeys {
|
|
513
|
+
readonly p256dh: string;
|
|
514
|
+
readonly auth: string;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export declare interface PushSubscriptionRow {
|
|
518
|
+
readonly subjectId: string;
|
|
519
|
+
readonly endpoint: string;
|
|
520
|
+
readonly endpointHash: string;
|
|
521
|
+
readonly p256dh: string;
|
|
522
|
+
readonly auth: string;
|
|
523
|
+
readonly ua: string | null;
|
|
524
|
+
readonly tenantId: string | null;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export declare const pushSubscriptionsTable: Table<"_voltro_notification_push_subscriptions", FieldDefinitions<{
|
|
528
|
+
readonly id: ColumnBuilder<string, "id", boolean>;
|
|
529
|
+
readonly subjectId: ColumnBuilder<string, "text", boolean>;
|
|
530
|
+
/** sha256 hex of `endpoint` — the unique key (endpoint URLs can exceed the
|
|
531
|
+
* unique-index byte ceiling on mssql/mysql, so the hash carries the
|
|
532
|
+
* constraint and the URL rides as plain text). */
|
|
533
|
+
readonly endpointHash: ColumnBuilder<string, "text", boolean>;
|
|
534
|
+
readonly endpoint: ColumnBuilder<string, "text", boolean>;
|
|
535
|
+
/** Browser subscription crypto material (`PushSubscription.toJSON().keys`). */
|
|
536
|
+
readonly p256dh: ColumnBuilder<string, "text", boolean>;
|
|
537
|
+
readonly auth: ColumnBuilder<string, "text", boolean>;
|
|
538
|
+
readonly ua: ColumnBuilder<string | null, "text", boolean>;
|
|
539
|
+
readonly createdAt: ColumnBuilder<Date, "timestamp", true>;
|
|
540
|
+
readonly tenantId: ColumnBuilder<string | null, "text", boolean>;
|
|
541
|
+
}>, true, "byPushSubject">;
|
|
542
|
+
|
|
543
|
+
export declare interface PushSubscriptionStore {
|
|
544
|
+
/** Idempotent per endpoint: a re-subscribe (same browser, possibly a new
|
|
545
|
+
* logged-in subject) takes the row over — the endpoint belongs to the
|
|
546
|
+
* browser profile, and the latest authenticated subject is its owner. */
|
|
547
|
+
readonly upsert: (row: PushSubscriptionRow) => Promise<void>;
|
|
548
|
+
readonly listForSubject: (subjectId: string) => Promise<ReadonlyArray<PushSubscriptionRow>>;
|
|
549
|
+
/** Unsubscribe — scoped to the CALLING subject so one subject cannot detach
|
|
550
|
+
* another's browser. */
|
|
551
|
+
readonly removeForSubject: (endpointHash: string, subjectId: string) => Promise<boolean>;
|
|
552
|
+
/** Prune — unconditional by hash: the push service said the endpoint is gone,
|
|
553
|
+
* whoever it belonged to. */
|
|
554
|
+
readonly removeByHash: (endpointHash: string) => Promise<void>;
|
|
555
|
+
readonly countForSubject: (subjectId: string) => Promise<number>;
|
|
556
|
+
}
|
|
557
|
+
|
|
435
558
|
/** A push transport rejected a device token (unregistered / invalid / expired —
|
|
436
559
|
* the APNs `Unregistered` / FCM `UNREGISTERED` class). Thrown by `pushChannel`'s
|
|
437
560
|
* `deliver` so the fan-out records the delivery `failed` with the token that was
|
|
@@ -478,6 +601,9 @@ export declare const quietHoursTable: Table<"_voltro_notification_quiet_hours",
|
|
|
478
601
|
readonly policy: ColumnBuilder<string, "text", true>;
|
|
479
602
|
}>, true, never>;
|
|
480
603
|
|
|
604
|
+
/** Test seam. */
|
|
605
|
+
export declare const resetWebPushStoreForTest: () => void;
|
|
606
|
+
|
|
481
607
|
/**
|
|
482
608
|
* Resolve which channel ids to deliver on. Order: caller-requested `channels`
|
|
483
609
|
* (intersected with configured) ELSE all configured; then drop any the
|
|
@@ -509,6 +635,9 @@ export declare interface SendResult {
|
|
|
509
635
|
readonly held?: 'digest' | 'quiet-held' | 'quiet-drop';
|
|
510
636
|
}
|
|
511
637
|
|
|
638
|
+
/** POST one encrypted payload to one subscription's push service. */
|
|
639
|
+
export declare const sendWebPush: (target: WebPushTarget, payload: Buffer, options: WebPushSendOptions) => Promise<WebPushOutcome>;
|
|
640
|
+
|
|
512
641
|
/** SMS via a generic REST sender (Twilio etc.) — app supplies the send fn. */
|
|
513
642
|
export declare const smsChannel: (send: (to: string, body: string) => Promise<void>) => Channel;
|
|
514
643
|
|
|
@@ -527,6 +656,19 @@ export declare const topicSubscriptionsTable: Table<"_voltro_notification_topic_
|
|
|
527
656
|
readonly tenantId: ColumnBuilder<string | null, "text", boolean>;
|
|
528
657
|
}>, true, "byTopic">;
|
|
529
658
|
|
|
659
|
+
/**
|
|
660
|
+
* The `Authorization: vapid t=<jwt>, k=<pub>` header value for one push-service
|
|
661
|
+
* origin (RFC 8292). `aud` is the ENDPOINT's origin (scheme+host), `exp` 12h
|
|
662
|
+
* out (the spec ceiling is 24h), `sub` the operator contact when configured —
|
|
663
|
+
* push services use it to reach whoever is sending, so it is worth setting.
|
|
664
|
+
*/
|
|
665
|
+
export declare const vapidAuthorization: (endpoint: string, privateKeyB64url: string, contact?: string, nowMs?: number) => string;
|
|
666
|
+
|
|
667
|
+
/** The browser-facing application server key (65-byte uncompressed point,
|
|
668
|
+
* base64url) derived from the private scalar. Throws on a malformed key —
|
|
669
|
+
* a wrong VAPID key must fail the boot, not the first delivery. */
|
|
670
|
+
export declare const vapidPublicKeyFor: (privateKeyB64url: string) => string;
|
|
671
|
+
|
|
530
672
|
/** POST the message to an incoming webhook (Slack / Discord / Teams / generic).
|
|
531
673
|
* `format` shapes the JSON body; default Slack-style `{ text }`. */
|
|
532
674
|
export declare const webhookChannel: (opts: {
|
|
@@ -536,4 +678,60 @@ export declare const webhookChannel: (opts: {
|
|
|
536
678
|
readonly headers?: Record<string, string>;
|
|
537
679
|
}) => Channel;
|
|
538
680
|
|
|
681
|
+
/**
|
|
682
|
+
* The Web Push channel. Delivery is per endpoint and isolated — the fan-out
|
|
683
|
+
* records one `EndpointDelivery` per registered endpoint, prunes on `gone`,
|
|
684
|
+
* and never lets one endpoint's failure abort another's send.
|
|
685
|
+
*/
|
|
686
|
+
export declare const webPushChannel: (options?: WebPushChannelOptions) => Channel;
|
|
687
|
+
|
|
688
|
+
export declare interface WebPushChannelOptions {
|
|
689
|
+
/** Override the channel id (prefs key). Default `webPush`. */
|
|
690
|
+
readonly id?: string;
|
|
691
|
+
/** The VAPID private scalar. Default: `VOLTRO_VAPID_PRIVATE_KEY` from the
|
|
692
|
+
* environment, read at delivery time (env files load after config). */
|
|
693
|
+
readonly privateKey?: string;
|
|
694
|
+
/** VAPID `sub` contact (`mailto:`/https) push services may use to reach the
|
|
695
|
+
* operator. Strongly recommended for production. */
|
|
696
|
+
readonly contact?: string;
|
|
697
|
+
/** Push-service retention (TTL header), seconds. Default 24h. */
|
|
698
|
+
readonly ttl?: number;
|
|
699
|
+
/** Test seam — injected into `sendWebPush`. */
|
|
700
|
+
readonly fetchImpl?: typeof fetch;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
export declare type WebPushOutcome = {
|
|
704
|
+
readonly kind: 'delivered';
|
|
705
|
+
readonly status: number;
|
|
706
|
+
}
|
|
707
|
+
/** 404/410 — the subscription no longer exists at the push service. The ONE
|
|
708
|
+
* signal a registry may prune on. */
|
|
709
|
+
| {
|
|
710
|
+
readonly kind: 'gone';
|
|
711
|
+
readonly status: number;
|
|
712
|
+
} | {
|
|
713
|
+
readonly kind: 'failed';
|
|
714
|
+
readonly status: number;
|
|
715
|
+
readonly detail: string;
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
/** The browser-facing application server key for the configured private key. */
|
|
719
|
+
export declare const webPushPublicKey: (explicit?: string) => string;
|
|
720
|
+
|
|
721
|
+
export declare interface WebPushSendOptions {
|
|
722
|
+
readonly privateKey: string;
|
|
723
|
+
/** VAPID `sub` claim — `mailto:` or https URL the push service can reach the
|
|
724
|
+
* operator at. */
|
|
725
|
+
readonly contact?: string;
|
|
726
|
+
/** Push-service retention for an undeliverable message, seconds. Default 24h. */
|
|
727
|
+
readonly ttl?: number;
|
|
728
|
+
/** Injectable for the mock-endpoint tests. */
|
|
729
|
+
readonly fetchImpl?: typeof fetch;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
export declare interface WebPushTarget {
|
|
733
|
+
readonly endpoint: string;
|
|
734
|
+
readonly keys: PushSubscriptionKeys;
|
|
735
|
+
}
|
|
736
|
+
|
|
539
737
|
export { }
|