unified-notification-core 0.1.0 → 0.1.2
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 +95 -0
- package/README.md +157 -5
- package/dist/core.d.ts +2 -2
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +967 -297
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +88 -4
- package/dist/errors.js.map +1 -1
- package/dist/json.d.ts +8 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +207 -0
- package/dist/json.js.map +1 -0
- package/dist/keys.d.ts +10 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +68 -0
- package/dist/keys.js.map +1 -0
- package/dist/preferences.d.ts +9 -0
- package/dist/preferences.d.ts.map +1 -1
- package/dist/preferences.js +59 -14
- package/dist/preferences.js.map +1 -1
- package/dist/runtime.d.ts +4 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +92 -0
- package/dist/runtime.js.map +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +13 -3
- package/dist/schema.js.map +1 -1
- package/dist/strings.d.ts +5 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +63 -0
- package/dist/strings.js.map +1 -0
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/adoption-notes.md +76 -0
- package/docs/architecture.md +126 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,100 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2
|
|
4
|
+
|
|
5
|
+
- Validate runtime `preferencePolicy` values and dispatch limits before any
|
|
6
|
+
publication, claim, or provider invocation.
|
|
7
|
+
- Validate every caller-provided JSON payload recursively before persistence,
|
|
8
|
+
with an iterative 256-depth/10,000-node budget that rejects cycles,
|
|
9
|
+
unsupported values, accessors, array extras, non-finite numbers, and
|
|
10
|
+
non-plain objects without invoking getters.
|
|
11
|
+
- Keep resolved provider calls truthfully `sent` when optional adapter output is
|
|
12
|
+
invalid: discard the optional result, persist diagnostic error details, and
|
|
13
|
+
never retry the provider for result-shape failures.
|
|
14
|
+
- Isolate retry-delay and logger callbacks from delivery state. Invalid or
|
|
15
|
+
synchronously/asynchronously throwing retry policies and native Promises
|
|
16
|
+
now use the deterministic built-in backoff without unhandled rejections.
|
|
17
|
+
- Cap persisted, logged, and returned delivery diagnostic messages at 10,000
|
|
18
|
+
characters through one non-throwing normalization boundary.
|
|
19
|
+
- Reject every present runtime `channelData` value unless it is a plain own-key
|
|
20
|
+
JSON object; falsy non-objects can no longer bypass validation.
|
|
21
|
+
- Replace per-recipient publication queries with bulk inserts/selects while
|
|
22
|
+
preserving recipient order, channel uniqueness, preferences, custom text
|
|
23
|
+
identifiers, and per-recipient idempotency under concurrent publication.
|
|
24
|
+
- Avoid repeatedly normalizing immutable preference defaults during a batch.
|
|
25
|
+
- Reject PostgreSQL-unsafe NUL and unpaired-surrogate strings in public input
|
|
26
|
+
and JSON keys/values before mutation. Invalid optional adapter output follows
|
|
27
|
+
the existing sent-safe diagnostic path, while valid Unicode pairs are kept.
|
|
28
|
+
- Capture the durable finalization timestamp before provider invocation so an
|
|
29
|
+
application clock callback cannot fail after provider acceptance.
|
|
30
|
+
- Consume exact native logger Promise rejections and time out exact native retry
|
|
31
|
+
Promises after 100 ms with deterministic fallback and late-rejection handling.
|
|
32
|
+
- Validate exact runtime booleans, configured adapter functions, finite integer
|
|
33
|
+
limits, identifiers, dates, and complete preference batches before writes.
|
|
34
|
+
- Reserve JSON child-node capacity immediately after `ownKeys`, before child
|
|
35
|
+
descriptor inspection, so oversized Proxy objects remain bounded.
|
|
36
|
+
- Restrict async logger/retry results to exact same-realm native Node Promises,
|
|
37
|
+
detected with a hook-free brand check and captured intrinsic verification.
|
|
38
|
+
Arbitrary thenables are rejected without reading or invoking `then`.
|
|
39
|
+
- Harden `NotificationDeliveryError` options: only omitted or
|
|
40
|
+
plain/null-prototype records are accepted, a present `retryable` is an exact
|
|
41
|
+
boolean, and code/message strings are bounded and PostgreSQL-safe.
|
|
42
|
+
- Descriptor-capture constructor configuration, public method wrappers, public
|
|
43
|
+
arrays, schema-factory options, and preference-resolver input once before any
|
|
44
|
+
transaction, claim, or provider call, preventing mutable Proxy values from
|
|
45
|
+
changing after validation.
|
|
46
|
+
- Require configuration/API wrappers and channel-keyed maps to be plain or
|
|
47
|
+
null-prototype records, rejecting maps, sets, dates, arrays, functions, class
|
|
48
|
+
instances, accessors, and symbol keys while keeping Drizzle database/schema
|
|
49
|
+
dependencies opaque.
|
|
50
|
+
- Start the monotonic 100 ms retry-policy deadline before invoking application
|
|
51
|
+
code and reject values produced after synchronous policy stalls.
|
|
52
|
+
- Observe genuine logger Promises immediately without awaiting them. Ignore
|
|
53
|
+
invalid logger return values without traversal; synchronous logger callback
|
|
54
|
+
work remains application-owned.
|
|
55
|
+
- Reject Promise subclasses, cross-realm Promises, custom `PromiseLike` values,
|
|
56
|
+
and native Promises with own `then`/`constructor` hooks at runtime. Invalid
|
|
57
|
+
retry results use deterministic fallback.
|
|
58
|
+
- Require exact `Array.prototype` public/JSON arrays with canonical in-range
|
|
59
|
+
indices only; reject subclasses, exotic prototypes, accessors, index
|
|
60
|
+
`4294967295`, and every other extra property before mutation.
|
|
61
|
+
- Convert revoked-Proxy inspection failures, including `Array.isArray`, into
|
|
62
|
+
public `NotificationConfigurationError` values.
|
|
63
|
+
- Capture and bind every retry-isolation intrinsic at module initialization,
|
|
64
|
+
install native deadline settlement before policy invocation, attach accepted
|
|
65
|
+
Promise rejection handling before checking post-callback lateness, and remove
|
|
66
|
+
reliance on mutable public `Promise.race`.
|
|
67
|
+
- Enforce one PostgreSQL-safe `maxAttempts` range of 1 through 2,147,483,647 for
|
|
68
|
+
core defaults, scalar/per-channel publication, requeue, and claim overflow
|
|
69
|
+
guards.
|
|
70
|
+
- Classify native retry Promise lateness inside its actual fulfillment or
|
|
71
|
+
rejection callback using the captured monotonic clock, so event-loop ordering
|
|
72
|
+
cannot let a settlement after 100 ms beat a starved deadline timer.
|
|
73
|
+
|
|
74
|
+
## 0.1.1
|
|
75
|
+
|
|
76
|
+
- Made delivery claims unique per row and attempt, with durable attempt counts
|
|
77
|
+
incremented before adapter invocation and exhausted stale claims closed.
|
|
78
|
+
- Made cancellation races truthful: current-claim provider success is `sent`,
|
|
79
|
+
failure remains `canceled`, and stale callbacks report durable state without
|
|
80
|
+
mutating it.
|
|
81
|
+
- Restricted requeueing to failed deliveries on active notifications and made
|
|
82
|
+
attempt-budget/reset semantics explicit.
|
|
83
|
+
- Normalized recipient/topic/channel keys across publish, preference APIs, the
|
|
84
|
+
public resolver, and configured defaults; canonical default collisions now
|
|
85
|
+
fail fast. Oversized delivery error codes are rejected before persistence.
|
|
86
|
+
- Preserved `0.1.0` whitespace preference rows without a migration, with
|
|
87
|
+
deterministic opt-out-wins conflict handling and atomic consolidation on
|
|
88
|
+
writes/clears.
|
|
89
|
+
- Matched legacy SQL key normalization to the complete ECMAScript trim set,
|
|
90
|
+
batched preference reads into exact plus one legacy fallback query, and added
|
|
91
|
+
the corresponding functional recipient index to the schema factory.
|
|
92
|
+
- Hardened adapters, defaults, attempt limits, channel payloads, and inbox keys
|
|
93
|
+
against prototype-name collisions and runtime-invalid values.
|
|
94
|
+
- Prevented provider dispatch through transaction-scoped cores. Transactional
|
|
95
|
+
`publish()` remains supported, while `publishAndDispatch()` and `dispatchDue()`
|
|
96
|
+
fail before publication, claiming, or adapter invocation.
|
|
97
|
+
|
|
3
98
|
## 0.1.0
|
|
4
99
|
|
|
5
100
|
- Established the UNC (Unified Notification Core) package identity.
|
package/README.md
CHANGED
|
@@ -100,6 +100,12 @@ createNotificationSchema({ tablePrefix: "app_notification_" });
|
|
|
100
100
|
Run the application's normal Drizzle schema workflow after exporting the
|
|
101
101
|
tables. The package never connects to or mutates a database by itself.
|
|
102
102
|
|
|
103
|
+
Upgrading an existing `0.1.0` database to `0.1.1` adds a functional recipient
|
|
104
|
+
preference index used by legacy-key fallback reads. Generate and apply that
|
|
105
|
+
schema diff through the application's normal reviewed migration workflow. UNC
|
|
106
|
+
does not create the index at runtime; correctness is preserved before the index
|
|
107
|
+
is applied, but legacy fallback reads can be slower.
|
|
108
|
+
|
|
103
109
|
## Define channels
|
|
104
110
|
|
|
105
111
|
Channel names are plain text, not a PostgreSQL enum. Adding a future channel
|
|
@@ -144,6 +150,50 @@ const result = await notificationCore.publishAndDispatch({
|
|
|
144
150
|
`publish()` only persists. `publishAndDispatch()` persists and immediately
|
|
145
151
|
attempts due deliveries. Both use the same durable outbox records.
|
|
146
152
|
|
|
153
|
+
Publication validates the complete JSON payload before opening its transaction.
|
|
154
|
+
`data`, channel-specific values, and other persisted payload properties must be
|
|
155
|
+
recursive JSON values: cycles, `undefined`, functions, symbols, bigints,
|
|
156
|
+
non-finite numbers, sparse arrays, accessors, and non-plain objects are rejected
|
|
157
|
+
with `NotificationConfigurationError`. TypeScript's `JsonValue` cannot express
|
|
158
|
+
the finite-number restriction, so this is also enforced at runtime. Invalid
|
|
159
|
+
publish input creates no notification or delivery rows.
|
|
160
|
+
|
|
161
|
+
Every persisted public string and JSON key/value must also be PostgreSQL-safe
|
|
162
|
+
UTF-8 text: NUL and unpaired UTF-16 surrogates are rejected, while valid
|
|
163
|
+
surrogate pairs such as emoji remain supported. Optional actor/entity ids must
|
|
164
|
+
be non-empty strings when present. Runtime booleans are exact booleans; truthy
|
|
165
|
+
or falsy substitutes are not accepted.
|
|
166
|
+
|
|
167
|
+
Configuration objects, public method option/input wrappers, and channel-keyed
|
|
168
|
+
maps must be plain or null-prototype records. Maps, Sets, Dates, arrays,
|
|
169
|
+
functions, class instances, symbols, and accessor properties are rejected at
|
|
170
|
+
those record boundaries. UNC captures every accepted field and array element
|
|
171
|
+
through its descriptor into an immutable snapshot before opening a transaction
|
|
172
|
+
or invoking a callback, so caller-side getters and later mutations cannot
|
|
173
|
+
change one operation midway through execution. Drizzle database and schema
|
|
174
|
+
objects are opaque values and are not subjected to the plain-record rule.
|
|
175
|
+
Public arrays must use `Array.prototype` directly and contain only canonical
|
|
176
|
+
indices from zero through `2^32 - 2` below their captured length. Subclasses,
|
|
177
|
+
exotic prototypes, accessors, sparse entries, and all extra properties are
|
|
178
|
+
rejected without invoking getters.
|
|
179
|
+
|
|
180
|
+
Validation is iterative and bounded to 256 nesting levels and 10,000 total JSON
|
|
181
|
+
nodes per checked value. It inspects own property descriptors without invoking
|
|
182
|
+
getters, rejects symbol/non-canonical/extra array properties, and copies accepted
|
|
183
|
+
values through descriptors before persistence. A present `channelData` must be
|
|
184
|
+
a plain own-key JSON object; `null`, `false`, `0`, and empty strings are invalid
|
|
185
|
+
rather than being treated as absence.
|
|
186
|
+
|
|
187
|
+
Recipients and deliveries are inserted and read in bulk. The number of database
|
|
188
|
+
query waves stays constant as recipient/channel count grows, while returned
|
|
189
|
+
recipients preserve first-seen input order and channels remain unique.
|
|
190
|
+
|
|
191
|
+
A core constructed with a Drizzle `PgTransaction` is publish-only. This lets an
|
|
192
|
+
application commit its own row and `publish()` atomically. Do not call
|
|
193
|
+
`publishAndDispatch()` or `dispatchDue()` on that core: both fail before writing
|
|
194
|
+
or invoking an adapter. After the outer transaction commits, dispatch with a
|
|
195
|
+
core constructed from the top-level `PgDatabase`.
|
|
196
|
+
|
|
147
197
|
For scheduled delivery or retries, call `dispatchDue()` from infrastructure the
|
|
148
198
|
application already owns:
|
|
149
199
|
|
|
@@ -155,10 +205,18 @@ await notificationCore.dispatchDue({
|
|
|
155
205
|
});
|
|
156
206
|
```
|
|
157
207
|
|
|
208
|
+
`dispatchDue()` accepts `limit` from 1 through 1000 and `concurrency` from 1
|
|
209
|
+
through 100. `publishAndDispatch()` accepts the same concurrency range. Runtime
|
|
210
|
+
values must be finite positive integers; invalid values fail before publishing,
|
|
211
|
+
claiming, or calling a provider.
|
|
212
|
+
|
|
158
213
|
This can run from a cron handler, queue consumer, scheduled function, or an
|
|
159
214
|
application-owned interval. The package starts no timer and exposes no port.
|
|
160
215
|
Concurrent workers claim rows with `FOR UPDATE SKIP LOCKED`; abandoned
|
|
161
216
|
`processing` rows become eligible again after the configured lock timeout.
|
|
217
|
+
`workerId` is diagnostic (maximum 154 characters). Every claimed delivery gets
|
|
218
|
+
a separate claim token, so two attempts from the same worker cannot finalize
|
|
219
|
+
each other's rows.
|
|
162
220
|
|
|
163
221
|
## Preferences
|
|
164
222
|
|
|
@@ -215,6 +273,23 @@ const notificationCore = createNotificationCore<AppChannel>({
|
|
|
215
273
|
});
|
|
216
274
|
```
|
|
217
275
|
|
|
276
|
+
Recipient, topic, and channel keys are trimmed at the public boundary, including
|
|
277
|
+
`resolvePreference()` and configured defaults. Keys that become empty are
|
|
278
|
+
rejected. Configuration also fails fast when two raw default keys normalize to
|
|
279
|
+
the same topic or channel, rather than silently choosing one.
|
|
280
|
+
|
|
281
|
+
Reads remain compatible with `0.1.0` preference rows whose stored keys contain
|
|
282
|
+
surrounding whitespace. If multiple stored rows collapse to one canonical
|
|
283
|
+
override, opt-out wins: any `enabled: false` row disables that topic/channel.
|
|
284
|
+
Writing or clearing that override atomically consolidates its legacy duplicates.
|
|
285
|
+
Channel-keyed configuration is copied into own-key registries, so names such as
|
|
286
|
+
`toString`, `constructor`, and `__proto__` have no inherited behavior and work
|
|
287
|
+
only when explicitly configured.
|
|
288
|
+
|
|
289
|
+
Batch publication loads preferences in one exact indexed query plus at most one
|
|
290
|
+
legacy canonical fallback query for the whole recipient batch. It does not scan
|
|
291
|
+
preferences once per recipient.
|
|
292
|
+
|
|
218
293
|
Use `preferencePolicy: "ignore"` only for notifications that legally or
|
|
219
294
|
operationally must be delivered regardless of opt-out:
|
|
220
295
|
|
|
@@ -228,6 +303,11 @@ await notificationCore.publish({
|
|
|
228
303
|
});
|
|
229
304
|
```
|
|
230
305
|
|
|
306
|
+
The only accepted runtime policy values are `"respect"` and `"ignore"`.
|
|
307
|
+
Omitting the property defaults to `"respect"`; an explicitly supplied `null`,
|
|
308
|
+
`undefined`, or any other value is a `NotificationConfigurationError` before
|
|
309
|
+
the publication transaction starts.
|
|
310
|
+
|
|
231
311
|
## In-app inbox
|
|
232
312
|
|
|
233
313
|
```ts
|
|
@@ -257,7 +337,8 @@ const unread = await notificationCore.unreadCount(userId);
|
|
|
257
337
|
```
|
|
258
338
|
|
|
259
339
|
Inbox mutations always require the recipient id as well as the notification id,
|
|
260
|
-
so a route can scope changes to its authenticated user.
|
|
340
|
+
so a route can scope changes to its authenticated user. Inbox recipient ids use
|
|
341
|
+
the same trim-and-reject-empty normalization as publication and preferences.
|
|
261
342
|
|
|
262
343
|
## Retry behavior
|
|
263
344
|
|
|
@@ -274,23 +355,93 @@ throw new NotificationDeliveryError(
|
|
|
274
355
|
);
|
|
275
356
|
```
|
|
276
357
|
|
|
358
|
+
`NotificationDeliveryError` accepts omitted options or a plain/null-prototype
|
|
359
|
+
options record. If `retryable` is present it must be an exact boolean; only
|
|
360
|
+
omission defaults it to `true`. The code is trimmed, non-empty, PostgreSQL-safe,
|
|
361
|
+
and at most 191 characters. The message is non-empty, PostgreSQL-safe, and at
|
|
362
|
+
most 10,000 characters. Options are descriptor-captured once before the Error
|
|
363
|
+
is constructed.
|
|
364
|
+
|
|
277
365
|
Unknown errors are retryable. The default backoff is 1 minute, 5 minutes,
|
|
278
366
|
15 minutes, 1 hour, then 6 hours. Configure `retryDelayMs` and
|
|
279
367
|
`defaultMaxAttempts` for application policy. A failed delivery can be explicitly
|
|
280
|
-
requeued with `requeueDelivery()`.
|
|
368
|
+
requeued with `requeueDelivery()`. Attempts are durably incremented when a row
|
|
369
|
+
is claimed, before the adapter runs. `requeueDelivery()` preserves that counter
|
|
370
|
+
by default and therefore requires remaining attempt capacity; pass a larger
|
|
371
|
+
`maxAttempts`, or set `resetAttempts: true` to start a new attempt budget.
|
|
372
|
+
Only a `failed` delivery whose notification is not canceled can be requeued.
|
|
373
|
+
Constructor defaults, scalar or per-channel publish overrides, and requeue
|
|
374
|
+
overrides all accept `maxAttempts` from 1 through 2,147,483,647, matching the
|
|
375
|
+
positive PostgreSQL `integer` range. Larger values fail before a transaction or
|
|
376
|
+
claim, and claim predicates prevent `attempts` from overflowing that range.
|
|
377
|
+
|
|
378
|
+
`retryDelayMs` may return a number or an exact same-realm native Node
|
|
379
|
+
`Promise<number>`. UNC installs the deadline before invoking the policy and
|
|
380
|
+
awaits it inside that boundary for at most 100 ms measured with a trusted
|
|
381
|
+
monotonic clock. Synchronous policy or Promise handling that crosses the
|
|
382
|
+
deadline cannot submit a late value. The resolved value must be finite,
|
|
383
|
+
non-negative, and produce a valid retry timestamp. Synchronous throws,
|
|
384
|
+
asynchronous rejections, never-settling Promises, timeouts, and
|
|
385
|
+
invalid values are consumed and logged before the deterministic built-in
|
|
386
|
+
backoff is used; the provider failure is still durably finalized and never left
|
|
387
|
+
stuck in `processing`. Late policy rejections are consumed. Logger callbacks may
|
|
388
|
+
return `void` or an exact native `Promise<void>`. UNC observes that Promise
|
|
389
|
+
immediately without awaiting it, so rejection is consumed while dispatch remains
|
|
390
|
+
independent. The logger callback itself is synchronous application code and must
|
|
391
|
+
return promptly.
|
|
392
|
+
|
|
393
|
+
The monotonic clock, timer functions, Promise constructor/prototype/`then`,
|
|
394
|
+
Promise brand check, and required Object/Reflect inspection operations are
|
|
395
|
+
captured and bound when the module initializes. Deadline settlement is installed
|
|
396
|
+
before application policy code runs. For an accepted native Promise, rejection
|
|
397
|
+
handling is attached before UNC checks whether synchronous policy work already
|
|
398
|
+
crossed the deadline. Fulfillment and rejection callbacks read the captured
|
|
399
|
+
monotonic clock before settling the deadline, so a timer starved by blocking
|
|
400
|
+
event-loop work cannot admit a value produced after 100 ms. UNC does not depend
|
|
401
|
+
on mutable public `Promise.race`.
|
|
402
|
+
|
|
403
|
+
Arbitrary `PromiseLike` values, custom thenables, cross-realm Promises, Promise
|
|
404
|
+
subclasses, and genuine Promises with own `then`/`constructor` hooks are invalid.
|
|
405
|
+
UNC uses Node's hook-free Promise brand check plus the captured exact native
|
|
406
|
+
prototype/intrinsics and never reads or calls an invalid value's `then` property.
|
|
407
|
+
An invalid retry result uses deterministic fallback; an invalid logger result is
|
|
408
|
+
ignored. The caller remains responsible for any rejected Promise hidden inside
|
|
409
|
+
an invalid object.
|
|
281
410
|
|
|
282
411
|
If a provider callback already has its own durable queue, returning after a
|
|
283
412
|
successful enqueue is correct. UNC then tracks acceptance by that
|
|
284
413
|
queue; provider delivery events may be stored in the adapter's own tables.
|
|
285
414
|
|
|
415
|
+
After an adapter resolves, provider success is final even if its optional return
|
|
416
|
+
value is malformed. `providerMessageId`, when present, must be a non-empty
|
|
417
|
+
string of at most 10,000 characters. `metadata`, when present, must be a finite
|
|
418
|
+
recursive JSON object under the same rules as publish input. If either field is
|
|
419
|
+
invalid, UNC discards the entire optional adapter result, durably marks the
|
|
420
|
+
delivery `sent`, stores `invalid_adapter_result` diagnostics, and logs the
|
|
421
|
+
problem without invoking the provider again.
|
|
422
|
+
|
|
423
|
+
Optional adapter strings and metadata must obey the same PostgreSQL-safe string
|
|
424
|
+
contract. Invalid NUL or unpaired-surrogate output is discarded through the
|
|
425
|
+
same sent-safe path. UNC captures the finalization timestamp with the claim,
|
|
426
|
+
before calling the provider, so a later failure in an application-supplied
|
|
427
|
+
`now()` callback cannot turn provider acceptance into a resend risk.
|
|
428
|
+
|
|
429
|
+
Diagnostic extraction never invokes error getters, `toString`, or coercion
|
|
430
|
+
hooks. Persisted `errorMessage`, logger context, and returned `DispatchResult`
|
|
431
|
+
share one 10,000-character cap, including malformed adapter-result diagnostics;
|
|
432
|
+
unsafe PostgreSQL characters are replaced before the diagnostic is persisted.
|
|
433
|
+
|
|
286
434
|
## Idempotency and scheduling
|
|
287
435
|
|
|
288
436
|
An `idempotencyKey` is unique per recipient. Publishing the same key again
|
|
289
437
|
returns the original notification and deliveries with `created: false`.
|
|
290
438
|
|
|
291
439
|
`scheduledFor` delays all selected channels. `cancel(notificationId)` cancels
|
|
292
|
-
pending/retrying/processing deliveries.
|
|
293
|
-
|
|
440
|
+
pending/retrying/processing deliveries. A provider callback already in flight
|
|
441
|
+
cannot be recalled: success from its current claim is persisted as `sent`
|
|
442
|
+
(`sent` is immutable audit history), while failure stays `canceled` and is not
|
|
443
|
+
retried. A stale callback never overwrites the state written by a newer claim,
|
|
444
|
+
and its `DispatchResult` reports the durable state it observed.
|
|
294
445
|
|
|
295
446
|
## Package guarantees
|
|
296
447
|
|
|
@@ -300,7 +451,8 @@ history.
|
|
|
300
451
|
- No background timers or service runtime.
|
|
301
452
|
- No assumption that recipient ids are UUIDs.
|
|
302
453
|
- No assumption that every notification is in-app.
|
|
303
|
-
- Provider callbacks are invoked
|
|
454
|
+
- Provider callbacks are invoked only by a top-level-database core, after the
|
|
455
|
+
claim transaction commits.
|
|
304
456
|
- Preferences disabled at publication are recorded as `skipped` deliveries for
|
|
305
457
|
auditability.
|
|
306
458
|
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PgDatabase } from "drizzle-orm/pg-core";
|
|
2
2
|
import { type NotificationSchema } from "./schema.js";
|
|
3
3
|
import { PREFERENCE_WILDCARD, type DeliveryAdapters, type DispatchResult, type NotificationLogger, type InboxCursor, type InboxPage, type JsonObject, type PreferenceDefaults, type PreferenceOverride, type PublishInput, type PublishedNotification, type ResolvedPreference } from "./types.js";
|
|
4
4
|
type AnyPgDatabase = PgDatabase<any, any, any>;
|
|
@@ -10,7 +10,7 @@ export type NotificationCoreConfig<TChannel extends string, TData extends JsonOb
|
|
|
10
10
|
preferenceDefaults?: PreferenceDefaults<TChannel>;
|
|
11
11
|
defaultPreferenceEnabled?: boolean;
|
|
12
12
|
defaultMaxAttempts?: number;
|
|
13
|
-
retryDelayMs?: (attempt: number, error: unknown) => number
|
|
13
|
+
retryDelayMs?: (attempt: number, error: unknown) => number | Promise<number>;
|
|
14
14
|
lockTimeoutMs?: number;
|
|
15
15
|
now?: () => Date;
|
|
16
16
|
logger?: NotificationLogger;
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
|