unified-notification-core 0.1.2 → 0.1.6
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 +36 -0
- package/README.md +96 -14
- package/dist/core.d.ts +31 -0
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +516 -77
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts +2 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +24 -6
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +389 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +30 -1
- package/dist/schema.js.map +1 -1
- package/docs/adoption-notes.md +16 -5
- package/docs/architecture.md +36 -11
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.6
|
|
4
|
+
|
|
5
|
+
- Add CommonJS `require` export conditions for the package root and schema
|
|
6
|
+
entrypoints so bundled CommonJS consumers can resolve UNC without package
|
|
7
|
+
manager patches.
|
|
8
|
+
- Add a built-package CommonJS export smoke test before packing.
|
|
9
|
+
|
|
10
|
+
## 0.1.5
|
|
11
|
+
|
|
12
|
+
- Add provider-agnostic `channelPolicies` with durable per-channel rate windows
|
|
13
|
+
and circuit-breaker cooldowns backed by the new `unc_dispatch_state` schema
|
|
14
|
+
table.
|
|
15
|
+
- Add `NotificationDeliveryError({ retryAfterMs })` so adapters can pass
|
|
16
|
+
provider throttle hints without coupling UNC to provider SDKs; retry-after
|
|
17
|
+
schedules the delivery retry and opens a shared channel cooldown.
|
|
18
|
+
- Report `dispatchUntilIdle()` stop reason `rate-limited` with `deferredUntil`
|
|
19
|
+
when due rows exist but channel policy defers claiming.
|
|
20
|
+
- Keep channel-policy deferrals attempt-free: deferred rows remain pending or
|
|
21
|
+
retrying and other channels can still dispatch in the same worker call.
|
|
22
|
+
|
|
23
|
+
## 0.1.4
|
|
24
|
+
|
|
25
|
+
- Add `dispatchUntilIdle()` as an explicit bounded drain helper with stable
|
|
26
|
+
worker identity, batch/deadline limits, and high-failure stop defaults so
|
|
27
|
+
application workers do not need to hand-roll dispatch loops.
|
|
28
|
+
- Batch-finalize built-in `in_app` deliveries after claim instead of invoking
|
|
29
|
+
per-row provider-style finalization.
|
|
30
|
+
- Run test files serially to avoid PGlite setup contention in the default
|
|
31
|
+
package check.
|
|
32
|
+
|
|
33
|
+
## 0.1.3
|
|
34
|
+
|
|
35
|
+
- Dispatch every due delivery created or reused by `publishAndDispatch()` by
|
|
36
|
+
draining its own publication in bounded delivery-id claim pages. The public
|
|
37
|
+
`dispatchDue()` worker limit remains capped per call.
|
|
38
|
+
|
|
3
39
|
## 0.1.2
|
|
4
40
|
|
|
5
41
|
- Validate runtime `preferencePolicy` values and dispatch limits before any
|
package/README.md
CHANGED
|
@@ -60,6 +60,9 @@ npm install unified-notification-core drizzle-orm
|
|
|
60
60
|
The package supports Drizzle `0.45.x` with PostgreSQL-compatible drivers,
|
|
61
61
|
including `node-postgres`, `postgres.js`, and PGlite.
|
|
62
62
|
|
|
63
|
+
Both public entrypoints support ESM `import` and CommonJS `require`:
|
|
64
|
+
`unified-notification-core` and `unified-notification-core/schema`.
|
|
65
|
+
|
|
63
66
|
## Add the schema
|
|
64
67
|
|
|
65
68
|
Export the UNC tables from the schema entrypoint already read by Drizzle Kit:
|
|
@@ -74,6 +77,7 @@ export const {
|
|
|
74
77
|
notifications,
|
|
75
78
|
notificationDeliveries,
|
|
76
79
|
notificationPreferences,
|
|
80
|
+
notificationDispatchState,
|
|
77
81
|
} = notificationSchema;
|
|
78
82
|
```
|
|
79
83
|
|
|
@@ -106,6 +110,13 @@ schema diff through the application's normal reviewed migration workflow. UNC
|
|
|
106
110
|
does not create the index at runtime; correctness is preserved before the index
|
|
107
111
|
is applied, but legacy fallback reads can be slower.
|
|
108
112
|
|
|
113
|
+
Upgrading to `0.1.5` adds the `unc_dispatch_state` table, or the same suffix
|
|
114
|
+
under a custom prefix. That table stores provider-agnostic channel rate-limit
|
|
115
|
+
windows and cooldowns. Generate and apply the schema diff through the
|
|
116
|
+
application's normal reviewed migration workflow before enabling
|
|
117
|
+
`channelPolicies` or adapter `retryAfterMs` hints. UNC never creates the table
|
|
118
|
+
at runtime.
|
|
119
|
+
|
|
109
120
|
## Define channels
|
|
110
121
|
|
|
111
122
|
Channel names are plain text, not a PostgreSQL enum. Adding a future channel
|
|
@@ -148,7 +159,10 @@ const result = await notificationCore.publishAndDispatch({
|
|
|
148
159
|
```
|
|
149
160
|
|
|
150
161
|
`publish()` only persists. `publishAndDispatch()` persists and immediately
|
|
151
|
-
attempts due
|
|
162
|
+
attempts every due delivery from that publication. Internally it drains large
|
|
163
|
+
publications in bounded delivery-id claim pages, so broadcasts above 1,000
|
|
164
|
+
deliveries are not left pending just because a single claim wave is capped. Both
|
|
165
|
+
methods use the same durable outbox records.
|
|
152
166
|
|
|
153
167
|
Publication validates the complete JSON payload before opening its transaction.
|
|
154
168
|
`data`, channel-specific values, and other persisted payload properties must be
|
|
@@ -190,25 +204,38 @@ recipients preserve first-seen input order and channels remain unique.
|
|
|
190
204
|
|
|
191
205
|
A core constructed with a Drizzle `PgTransaction` is publish-only. This lets an
|
|
192
206
|
application commit its own row and `publish()` atomically. Do not call
|
|
193
|
-
`publishAndDispatch()` or `dispatchDue()` on that core:
|
|
194
|
-
or invoking an adapter. After the outer transaction
|
|
195
|
-
core constructed from the top-level `PgDatabase`.
|
|
207
|
+
`publishAndDispatch()`, `dispatchUntilIdle()`, or `dispatchDue()` on that core:
|
|
208
|
+
all fail before writing or invoking an adapter. After the outer transaction
|
|
209
|
+
commits, dispatch with a core constructed from the top-level `PgDatabase`.
|
|
196
210
|
|
|
197
|
-
For scheduled delivery
|
|
198
|
-
application already owns:
|
|
211
|
+
For scheduled delivery, retries, or queue-style workers, call
|
|
212
|
+
`dispatchUntilIdle()` from infrastructure the application already owns:
|
|
199
213
|
|
|
200
214
|
```ts
|
|
201
|
-
await notificationCore.
|
|
215
|
+
const result = await notificationCore.dispatchUntilIdle({
|
|
202
216
|
workerId: process.env.HOSTNAME,
|
|
203
217
|
limit: 100,
|
|
204
218
|
concurrency: 10,
|
|
219
|
+
maxBatches: 25,
|
|
220
|
+
deadlineMs: 30_000,
|
|
205
221
|
});
|
|
206
222
|
```
|
|
207
223
|
|
|
208
224
|
`dispatchDue()` accepts `limit` from 1 through 1000 and `concurrency` from 1
|
|
209
|
-
through 100
|
|
210
|
-
|
|
211
|
-
|
|
225
|
+
through 100; the limit is a per-call worker claim cap. `dispatchUntilIdle()`
|
|
226
|
+
uses the same limit and concurrency boundaries, keeps one stable worker id for
|
|
227
|
+
its drain, stops when no due rows remain, and also stops on `maxBatches`,
|
|
228
|
+
`deadlineMs`, a high-failure batch, or a channel policy deferral. By default a
|
|
229
|
+
batch stops the drain when at least 80% of attempted deliveries fail or
|
|
230
|
+
schedule a retry, which prevents a broken provider from being hammered in a
|
|
231
|
+
tight loop. When due rows exist but a channel rate limit or cooldown blocks
|
|
232
|
+
claiming them, the stop reason is `rate-limited` and `deferredUntil` reports
|
|
233
|
+
the earliest known channel wake-up time. `publishAndDispatch()` accepts the
|
|
234
|
+
same concurrency range and uses capped internal claim pages until every due
|
|
235
|
+
delivery from its own publication has been attempted once, unless a configured
|
|
236
|
+
channel policy defers some rows. Runtime values must be finite positive
|
|
237
|
+
integers; invalid values fail before publishing, claiming, or calling a
|
|
238
|
+
provider.
|
|
212
239
|
|
|
213
240
|
This can run from a cron handler, queue consumer, scheduled function, or an
|
|
214
241
|
application-owned interval. The package starts no timer and exposes no port.
|
|
@@ -218,6 +245,58 @@ Concurrent workers claim rows with `FOR UPDATE SKIP LOCKED`; abandoned
|
|
|
218
245
|
a separate claim token, so two attempts from the same worker cannot finalize
|
|
219
246
|
each other's rows.
|
|
220
247
|
|
|
248
|
+
## Channel dispatch policies
|
|
249
|
+
|
|
250
|
+
UNC can enforce provider-agnostic channel policy without knowing the provider
|
|
251
|
+
SDK. Configure static channel rate windows and circuit-breaker cooldowns when
|
|
252
|
+
creating the core:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
const notificationCore = createNotificationCore<AppChannel>({
|
|
256
|
+
db,
|
|
257
|
+
adapters,
|
|
258
|
+
channelPolicies: {
|
|
259
|
+
sms: {
|
|
260
|
+
rateLimit: { max: 30, windowMs: 60_000 },
|
|
261
|
+
circuitBreaker: {
|
|
262
|
+
failureRatio: 0.8,
|
|
263
|
+
minAttempts: 20,
|
|
264
|
+
cooldownMs: 5 * 60_000,
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
email: {
|
|
268
|
+
rateLimit: { max: 500, windowMs: 60_000 },
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Rate limits are durable per channel and shared by concurrent workers through
|
|
275
|
+
the `unc_dispatch_state` table. A row that is deferred by channel policy is not
|
|
276
|
+
claimed and does not consume an attempt. Other channels keep dispatching in the
|
|
277
|
+
same worker call.
|
|
278
|
+
|
|
279
|
+
Circuit breakers are also channel-scoped and durable. After a batch for one
|
|
280
|
+
channel reaches the configured failure ratio and minimum attempted deliveries,
|
|
281
|
+
UNC opens that channel's cooldown. Claimed rows still finalize normally as
|
|
282
|
+
`sent`, `retrying`, or `failed`; the cooldown only prevents another immediate
|
|
283
|
+
wave from hammering the same provider boundary.
|
|
284
|
+
|
|
285
|
+
Adapters can also return provider throttle information without coupling UNC to
|
|
286
|
+
the provider:
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
throw new NotificationDeliveryError("rate_limited", "Provider throttled", {
|
|
290
|
+
retryAfterMs: 120_000,
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
`retryAfterMs` schedules the failed delivery's next attempt and opens the same
|
|
295
|
+
durable channel cooldown for fresh due rows. It is a finite non-negative number
|
|
296
|
+
no greater than 30 days. It does not replace provider-specific credentials,
|
|
297
|
+
contact lookup, account management, or webhooks; those remain in the adapter and
|
|
298
|
+
application.
|
|
299
|
+
|
|
221
300
|
## Preferences
|
|
222
301
|
|
|
223
302
|
Preferences are overrides, not a copied matrix. This keeps storage small when
|
|
@@ -357,10 +436,11 @@ throw new NotificationDeliveryError(
|
|
|
357
436
|
|
|
358
437
|
`NotificationDeliveryError` accepts omitted options or a plain/null-prototype
|
|
359
438
|
options record. If `retryable` is present it must be an exact boolean; only
|
|
360
|
-
omission defaults it to `true`.
|
|
361
|
-
|
|
362
|
-
most
|
|
363
|
-
|
|
439
|
+
omission defaults it to `true`. If `retryAfterMs` is present it must be a finite
|
|
440
|
+
non-negative number no greater than 30 days. The code is trimmed, non-empty,
|
|
441
|
+
PostgreSQL-safe, and at most 191 characters. The message is non-empty,
|
|
442
|
+
PostgreSQL-safe, and at most 10,000 characters. Options are descriptor-captured
|
|
443
|
+
once before the Error is constructed.
|
|
364
444
|
|
|
365
445
|
Unknown errors are retryable. The default backoff is 1 minute, 5 minutes,
|
|
366
446
|
15 minutes, 1 hour, then 6 hours. Configure `retryDelayMs` and
|
|
@@ -451,6 +531,8 @@ and its `DispatchResult` reports the durable state it observed.
|
|
|
451
531
|
- No background timers or service runtime.
|
|
452
532
|
- No assumption that recipient ids are UUIDs.
|
|
453
533
|
- No assumption that every notification is in-app.
|
|
534
|
+
- Provider-agnostic channel rate/cooldown state is stored in UNC tables, not in
|
|
535
|
+
process memory.
|
|
454
536
|
- Provider callbacks are invoked only by a top-level-database core, after the
|
|
455
537
|
claim transaction commits.
|
|
456
538
|
- Preferences disabled at publication are recorded as `skipped` deliveries for
|
package/dist/core.d.ts
CHANGED
|
@@ -2,6 +2,35 @@ 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>;
|
|
5
|
+
export type DispatchUntilIdleOptions = {
|
|
6
|
+
limit?: number;
|
|
7
|
+
workerId?: string;
|
|
8
|
+
concurrency?: number;
|
|
9
|
+
maxBatches?: number;
|
|
10
|
+
deadlineMs?: number;
|
|
11
|
+
stopOnFailureRatio?: number;
|
|
12
|
+
stopOnFailureCount?: number;
|
|
13
|
+
};
|
|
14
|
+
export type DispatchUntilIdleStopReason = "idle" | "maxBatches" | "deadline" | "failure-threshold" | "rate-limited";
|
|
15
|
+
export type DispatchUntilIdleResult<TChannel extends string = string> = {
|
|
16
|
+
batches: number;
|
|
17
|
+
reason: DispatchUntilIdleStopReason;
|
|
18
|
+
dispatched: Array<DispatchResult<TChannel>>;
|
|
19
|
+
deferredUntil?: Date;
|
|
20
|
+
};
|
|
21
|
+
export type ChannelRateLimitPolicy = {
|
|
22
|
+
max: number;
|
|
23
|
+
windowMs: number;
|
|
24
|
+
};
|
|
25
|
+
export type ChannelCircuitBreakerPolicy = {
|
|
26
|
+
failureRatio?: number;
|
|
27
|
+
minAttempts?: number;
|
|
28
|
+
cooldownMs?: number;
|
|
29
|
+
};
|
|
30
|
+
export type ChannelDispatchPolicy = {
|
|
31
|
+
rateLimit?: ChannelRateLimitPolicy;
|
|
32
|
+
circuitBreaker?: ChannelCircuitBreakerPolicy;
|
|
33
|
+
};
|
|
5
34
|
export type NotificationCoreConfig<TChannel extends string, TData extends JsonObject = JsonObject> = {
|
|
6
35
|
db: AnyPgDatabase;
|
|
7
36
|
schema?: NotificationSchema;
|
|
@@ -10,6 +39,7 @@ export type NotificationCoreConfig<TChannel extends string, TData extends JsonOb
|
|
|
10
39
|
preferenceDefaults?: PreferenceDefaults<TChannel>;
|
|
11
40
|
defaultPreferenceEnabled?: boolean;
|
|
12
41
|
defaultMaxAttempts?: number;
|
|
42
|
+
channelPolicies?: Partial<Record<TChannel, ChannelDispatchPolicy>>;
|
|
13
43
|
retryDelayMs?: (attempt: number, error: unknown) => number | Promise<number>;
|
|
14
44
|
lockTimeoutMs?: number;
|
|
15
45
|
now?: () => Date;
|
|
@@ -31,6 +61,7 @@ export declare class UnifiedNotificationCore<TChannel extends string, TData exte
|
|
|
31
61
|
workerId?: string;
|
|
32
62
|
concurrency?: number;
|
|
33
63
|
}): Promise<Array<DispatchResult<TChannel>>>;
|
|
64
|
+
dispatchUntilIdle(options?: DispatchUntilIdleOptions): Promise<DispatchUntilIdleResult<TChannel>>;
|
|
34
65
|
setPreference(preference: PreferenceOverride<TChannel>): Promise<void>;
|
|
35
66
|
setPreferences(recipientId: string, preferences: ReadonlyArray<Omit<PreferenceOverride<TChannel>, "recipientId">>): Promise<void>;
|
|
36
67
|
clearPreference(input: {
|
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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"}
|