easy-ping 0.4.0 → 0.6.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/README.md +548 -427
- package/dist/adapter-B7R4rXtJ.d.cts +17 -0
- package/dist/adapter-B7R4rXtJ.d.ts +17 -0
- package/dist/{adapter-BfLe2J0t.d.cts → adapter-D3lTy-4E.d.cts} +6 -32
- package/dist/{adapter-BfLe2J0t.d.ts → adapter-D3lTy-4E.d.ts} +6 -32
- package/dist/adapters/drizzle.cjs +1 -2
- package/dist/adapters/drizzle.cjs.map +1 -1
- package/dist/adapters/drizzle.d.cts +1 -1
- package/dist/adapters/drizzle.d.ts +1 -1
- package/dist/adapters/drizzle.js +3 -4
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/adapters/mongodb.cjs +144 -5
- package/dist/adapters/mongodb.cjs.map +1 -1
- package/dist/adapters/mongodb.d.cts +43 -18
- package/dist/adapters/mongodb.d.ts +43 -18
- package/dist/adapters/mongodb.js +77 -8
- package/dist/adapters/mongodb.js.map +1 -1
- package/dist/adapters/mysql.cjs +605 -0
- package/dist/adapters/mysql.cjs.map +1 -0
- package/dist/adapters/mysql.d.cts +38 -0
- package/dist/adapters/mysql.d.ts +38 -0
- package/dist/adapters/mysql.js +51 -0
- package/dist/adapters/mysql.js.map +1 -0
- package/dist/adapters/postgres.cjs +162 -9
- package/dist/adapters/postgres.cjs.map +1 -1
- package/dist/adapters/postgres.d.cts +44 -54
- package/dist/adapters/postgres.d.ts +44 -54
- package/dist/adapters/postgres.js +82 -42
- package/dist/adapters/postgres.js.map +1 -1
- package/dist/adapters/sqlite.cjs +593 -0
- package/dist/adapters/sqlite.cjs.map +1 -0
- package/dist/adapters/sqlite.d.cts +37 -0
- package/dist/adapters/sqlite.d.ts +37 -0
- package/dist/adapters/sqlite.js +53 -0
- package/dist/adapters/sqlite.js.map +1 -0
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +2 -14
- package/dist/browser.d.ts +2 -14
- package/dist/browser.js.map +1 -1
- package/dist/chunk-2EYKPF5A.js +393 -0
- package/dist/chunk-2EYKPF5A.js.map +1 -0
- package/dist/{chunk-3Y2MBUAW.js → chunk-3VOQOGDR.js} +4 -3
- package/dist/chunk-3VOQOGDR.js.map +1 -0
- package/dist/{chunk-PSJHXZPR.js → chunk-AEYYTE5I.js} +2 -2
- package/dist/chunk-AEYYTE5I.js.map +1 -0
- package/dist/chunk-CK5UPVYP.js +48 -0
- package/dist/chunk-CK5UPVYP.js.map +1 -0
- package/dist/{chunk-W6I3B2CI.js → chunk-E4IZ2CQC.js} +2 -2
- package/dist/{chunk-W6I3B2CI.js.map → chunk-E4IZ2CQC.js.map} +1 -1
- package/dist/chunk-HY47TQMY.js +52 -0
- package/dist/chunk-HY47TQMY.js.map +1 -0
- package/dist/{chunk-CYKAM7LE.js → chunk-JKTMUMQK.js} +31 -6
- package/dist/chunk-JKTMUMQK.js.map +1 -0
- package/dist/chunk-M4R77BJD.js +64 -0
- package/dist/chunk-M4R77BJD.js.map +1 -0
- package/dist/chunk-ORZKH224.js +75 -0
- package/dist/chunk-ORZKH224.js.map +1 -0
- package/dist/{chunk-DG3ZL5NP.js → chunk-USQ6CQTJ.js} +88 -3
- package/dist/chunk-USQ6CQTJ.js.map +1 -0
- package/dist/{chunk-YEMRRMJQ.js → chunk-XIDVLYNQ.js} +14 -4
- package/dist/chunk-XIDVLYNQ.js.map +1 -0
- package/dist/client.cjs +288 -17
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +53 -1
- package/dist/client.d.ts +53 -1
- package/dist/client.js +288 -17
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +239 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -57
- package/dist/index.d.ts +53 -57
- package/dist/index.js +60 -13
- package/dist/index.js.map +1 -1
- package/dist/node.cjs +26 -5
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +28 -6
- package/dist/node.js.map +1 -1
- package/dist/{plugin-C9Hoxus2.d.ts → plugin-CHd2ZiGC.d.cts} +5 -21
- package/dist/{plugin-CiMsGlQe.d.cts → plugin-Cg9usuUi.d.ts} +5 -21
- package/dist/plugins/digests.cjs +1 -4
- package/dist/plugins/digests.cjs.map +1 -1
- package/dist/plugins/digests.d.cts +6 -31
- package/dist/plugins/digests.d.ts +6 -31
- package/dist/plugins/digests.js +1 -4
- package/dist/plugins/digests.js.map +1 -1
- package/dist/plugins/preferences-client.cjs.map +1 -1
- package/dist/plugins/preferences-client.d.cts +2 -2
- package/dist/plugins/preferences-client.d.ts +2 -2
- package/dist/plugins/preferences-client.js +1 -1
- package/dist/plugins/preferences-react.cjs.map +1 -1
- package/dist/plugins/preferences-react.js.map +1 -1
- package/dist/plugins/preferences.cjs +1 -2
- package/dist/plugins/preferences.cjs.map +1 -1
- package/dist/plugins/preferences.d.cts +2 -2
- package/dist/plugins/preferences.d.ts +2 -2
- package/dist/plugins/preferences.js +3 -3
- package/dist/plugins/preferences.js.map +1 -1
- package/dist/plugins/push.cjs +1 -2
- package/dist/plugins/push.cjs.map +1 -1
- package/dist/plugins/push.d.cts +6 -26
- package/dist/plugins/push.d.ts +6 -26
- package/dist/plugins/push.js +4 -4
- package/dist/plugins/push.js.map +1 -1
- package/dist/providers/web-push.cjs.map +1 -1
- package/dist/providers/web-push.d.cts +3 -10
- package/dist/providers/web-push.d.ts +3 -10
- package/dist/providers/web-push.js.map +1 -1
- package/dist/react.cjs +311 -19
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +53 -4
- package/dist/react.d.ts +53 -4
- package/dist/react.js +311 -19
- package/dist/react.js.map +1 -1
- package/dist/schema.cjs +119 -1
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +20 -14
- package/dist/schema.d.ts +20 -14
- package/dist/schema.js +4 -2
- package/dist/signals-kPmOlKDX.d.cts +27 -0
- package/dist/signals-kPmOlKDX.d.ts +27 -0
- package/dist/sw.cjs +39 -0
- package/dist/sw.cjs.map +1 -0
- package/dist/sw.d.cts +52 -0
- package/dist/sw.d.ts +52 -0
- package/dist/sw.js +36 -0
- package/dist/sw.js.map +1 -0
- package/dist/testing.cjs +3 -19
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +4 -22
- package/dist/testing.d.ts +4 -22
- package/dist/testing.js +3 -19
- package/dist/testing.js.map +1 -1
- package/package.json +17 -1
- package/dist/chunk-3Y2MBUAW.js.map +0 -1
- package/dist/chunk-CYKAM7LE.js.map +0 -1
- package/dist/chunk-DG3ZL5NP.js.map +0 -1
- package/dist/chunk-PSJHXZPR.js.map +0 -1
- package/dist/chunk-YEMRRMJQ.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { C as Channel, b as DeliveryMode, S as SkipReason, c as DeliveryRecord, D as DatabaseAdapter, R as Recipient } from './adapter-
|
|
2
|
-
export { d as ClaimArgs, e as ClaimedDelivery, f as DeliveryOutcome, g as DeliveryRelease, a as DeliveryStatus, h as FeedPage, i as FeedQuery, F as Frequency, I as InsertDelivery, j as InsertNotification, N as NotificationRecord, P as PreferenceRecord } from './adapter-
|
|
1
|
+
import { C as Channel, b as DeliveryMode, S as SkipReason, c as DeliveryRecord, D as DatabaseAdapter, R as Recipient } from './adapter-D3lTy-4E.cjs';
|
|
2
|
+
export { d as ClaimArgs, e as ClaimedDelivery, f as DeliveryOutcome, g as DeliveryRelease, a as DeliveryStatus, h as FeedPage, i as FeedQuery, F as Frequency, I as InsertDelivery, j as InsertNotification, N as NotificationRecord, P as PreferenceRecord } from './adapter-D3lTy-4E.cjs';
|
|
3
3
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
4
|
-
import { R as RouteScope, A as AnyPlugin, P as Promisable, L as Logger } from './plugin-
|
|
5
|
-
export { a as ActionsOf, b as AfterDeliverContext, c as AnyClientPlugin, B as BeforeSendContext, d as BeforeSendResult, C as ChannelDecision, e as ClientFetch, f as ClientPlugin, g as ConfigError, D as DEFAULT_TOKEN_TTL_SECONDS, E as EasyPingError, h as EasyPingPlugin, F as FieldDeclaration, i as FieldType, M as MAX_TOKEN_TTL_SECONDS, j as PluginHooks, k as PrepareContext, l as ResolveChannelsContext, m as RouteContext, n as RouteDefinition, S as SchemaDeclaration, o as SignRequest, T as TableDeclaration, p as TokenClaims, U as UnionToIntersection, V as ValidationError, q as consoleLogger, r as defineClientPlugin, s as definePlugin, t as expiresIn, u as signToken, v as verifyToken } from './plugin-
|
|
4
|
+
import { R as RouteScope, A as AnyPlugin, P as Promisable, L as Logger } from './plugin-CHd2ZiGC.cjs';
|
|
5
|
+
export { a as ActionsOf, b as AfterDeliverContext, c as AnyClientPlugin, B as BeforeSendContext, d as BeforeSendResult, C as ChannelDecision, e as ClientFetch, f as ClientPlugin, g as ConfigError, D as DEFAULT_TOKEN_TTL_SECONDS, E as EasyPingError, h as EasyPingPlugin, F as FieldDeclaration, i as FieldType, M as MAX_TOKEN_TTL_SECONDS, j as PluginHooks, k as PrepareContext, l as ResolveChannelsContext, m as RouteContext, n as RouteDefinition, S as SchemaDeclaration, o as SignRequest, T as TableDeclaration, p as TokenClaims, U as UnionToIntersection, V as ValidationError, q as consoleLogger, r as defineClientPlugin, s as definePlugin, t as expiresIn, u as signToken, v as verifyToken } from './plugin-CHd2ZiGC.cjs';
|
|
6
6
|
import { E as EmailProvider } from './provider-CA45WVqt.cjs';
|
|
7
7
|
export { a as EmailMessage, P as ProviderSendResult } from './provider-CA45WVqt.cjs';
|
|
8
|
+
import { S as Signals } from './signals-kPmOlKDX.cjs';
|
|
9
|
+
export { D as DELIVERIES_CHANNEL, c as createBridgedSignals, a as createMemorySignals, i as inboxChannel } from './signals-kPmOlKDX.cjs';
|
|
8
10
|
|
|
9
11
|
declare function encodeBase64Url(input: string | Uint8Array): string;
|
|
10
12
|
declare function decodeBase64Url(value: string): string | null;
|
|
@@ -23,10 +25,7 @@ type NotificationDefinition<TSchema extends StandardSchemaV1 | undefined = Stand
|
|
|
23
25
|
};
|
|
24
26
|
/** Binds TSchema so email callbacks infer the payload; TS cannot infer from a sibling property. */
|
|
25
27
|
declare function defineNotification<TSchema extends StandardSchemaV1 | undefined = undefined>(definition: NotificationDefinition<TSchema>): NotificationDefinition<TSchema>;
|
|
26
|
-
/**
|
|
27
|
-
* Erased form. `EmailTemplate<never>` because callbacks are contravariant —
|
|
28
|
-
* anything else makes every schema-typed definition fail to assign.
|
|
29
|
-
*/
|
|
28
|
+
/** Erased form. `EmailTemplate<never>` because callbacks are contravariant. */
|
|
30
29
|
type AnyNotificationDefinition = {
|
|
31
30
|
schema?: StandardSchemaV1 | undefined;
|
|
32
31
|
channels: readonly Channel[];
|
|
@@ -42,19 +41,10 @@ type RateLimitConfig = {
|
|
|
42
41
|
/** Requests allowed per key per window. */
|
|
43
42
|
max: number;
|
|
44
43
|
windowMs: number;
|
|
45
|
-
/**
|
|
46
|
-
* Which bucket a request lands in. Defaults to the client address as the
|
|
47
|
-
* usual proxy headers report it; returning null exempts the request. Behind
|
|
48
|
-
* no proxy those headers are absent, so supply your own key there.
|
|
49
|
-
*/
|
|
44
|
+
/** Bucket key. Defaults to the client address from proxy headers; null exempts the request. */
|
|
50
45
|
key?: (request: Request) => string | null;
|
|
51
46
|
};
|
|
52
|
-
/**
|
|
53
|
-
* Fixed-window counter held in process memory. Per isolate on edge runtimes
|
|
54
|
-
* and per replica anywhere else, so it bounds one client's cost to one
|
|
55
|
-
* process rather than enforcing a global quota. That is enough to blunt
|
|
56
|
-
* secret guessing and poll storms, which is what it is for.
|
|
57
|
-
*/
|
|
47
|
+
/** Fixed window in process memory: per isolate or replica, so a bound rather than a quota. */
|
|
58
48
|
declare function createRateLimiter(config: RateLimitConfig): (request: Request) => Response | null;
|
|
59
49
|
|
|
60
50
|
type SessionConfig = {
|
|
@@ -70,11 +60,28 @@ type DeliveryConfig = {
|
|
|
70
60
|
/** inline mode only. */
|
|
71
61
|
throwOnError?: boolean;
|
|
72
62
|
backoff?: "exponential" | ((attempt: number) => number);
|
|
63
|
+
/** Next.js `after` or Cloudflare `ctx.waitUntil`. Injected, not detected. */
|
|
64
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
73
65
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
66
|
+
* Run one small claim-and-deliver pass after any request the handler serves,
|
|
67
|
+
* at most once per `everyMs` per process. Delivery then rides on your own
|
|
68
|
+
* traffic and the cron becomes a backstop. Ignored in `inline` mode. RFC 0006 idea 5.
|
|
76
69
|
*/
|
|
77
|
-
|
|
70
|
+
sweepOnRequest?: true | {
|
|
71
|
+
everyMs?: number;
|
|
72
|
+
limit?: number;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
type EventsConfig = {
|
|
76
|
+
/** Comment line keeping proxies from closing an idle stream. Default 25 s. */
|
|
77
|
+
heartbeatMs?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Without a cross-process signal source, each stream checks the database
|
|
80
|
+
* for changes this often on its subscriber's behalf. Default 30 s; 0 disables.
|
|
81
|
+
*/
|
|
82
|
+
probeIntervalMs?: number;
|
|
83
|
+
/** Close the stream after this long; the client reconnects. 0 (default) leaves it open. */
|
|
84
|
+
maxDurationMs?: number;
|
|
78
85
|
};
|
|
79
86
|
type ChannelsConfig = {
|
|
80
87
|
inApp?: {
|
|
@@ -99,25 +106,22 @@ type EasyPingConfig<TDefs extends NotificationDefinitions> = {
|
|
|
99
106
|
/** Sweeps one POST /cron may run before returning. Defaults to 50. */
|
|
100
107
|
maxSweeps?: number;
|
|
101
108
|
};
|
|
102
|
-
/**
|
|
103
|
-
* Bearer secret for plugins' machine routes (`/push/prune`, `/digests/cron`).
|
|
104
|
-
* Falls back to `cron.secret`; set it when the scheduler that hits /cron
|
|
105
|
-
* should not also be able to prune devices or fire digests.
|
|
106
|
-
*/
|
|
109
|
+
/** Bearer secret for plugin machine routes. Falls back to `cron.secret`. */
|
|
107
110
|
machineSecret?: string;
|
|
111
|
+
/** In-process fixed-window limiter, applied before routing. */
|
|
112
|
+
rateLimit?: RateLimitConfig;
|
|
108
113
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
114
|
+
* How "something changed" travels between the sender, the worker and open
|
|
115
|
+
* event streams. Defaults to in-process. Supply a LISTEN/NOTIFY or change
|
|
116
|
+
* stream implementation to cross replicas. RFC 0006 §4C.
|
|
111
117
|
*/
|
|
112
|
-
|
|
118
|
+
signals?: Signals;
|
|
119
|
+
/** `GET /events`, a Server-Sent Events stream per user. `false` unmounts it. */
|
|
120
|
+
events?: EventsConfig | false;
|
|
113
121
|
plugins?: readonly AnyPlugin[];
|
|
114
122
|
/** Where the handler is mounted, used to strip the prefix off incoming URLs. */
|
|
115
123
|
basePath?: string;
|
|
116
|
-
/**
|
|
117
|
-
* Origins allowed to POST besides the request's own host. Cross-origin
|
|
118
|
-
* POSTs are otherwise refused with 403, and every POST must be
|
|
119
|
-
* `application/json`. `"*.example.com"` matches subdomains.
|
|
120
|
-
*/
|
|
124
|
+
/** Origins other than the request host that may POST. `"*.example.com"` matches subdomains. */
|
|
121
125
|
trustedOrigins?: readonly string[];
|
|
122
126
|
/** Runs before routing. Return a Response to short-circuit — a rate limiter's 429, say. */
|
|
123
127
|
onRequest?: (request: Request) => Promisable<Response | undefined | void>;
|
|
@@ -167,6 +171,8 @@ type HealthReport = {
|
|
|
167
171
|
cronRequiredButMissing: boolean;
|
|
168
172
|
warnings: readonly string[];
|
|
169
173
|
};
|
|
174
|
+
/** Response header carrying a per-user inbox version, so app traffic can replace polling. */
|
|
175
|
+
declare const INBOX_VERSION_HEADER = "x-easy-ping-inbox";
|
|
170
176
|
type EasyPing<TDefs extends NotificationDefinitions> = {
|
|
171
177
|
send<TKey extends keyof TDefs & string>(type: TKey, args: SendArgs<TDefs, TKey>): Promise<SendResult>;
|
|
172
178
|
handler: {
|
|
@@ -175,6 +181,7 @@ type EasyPing<TDefs extends NotificationDefinitions> = {
|
|
|
175
181
|
GET: (request: Request) => Promise<Response>;
|
|
176
182
|
POST: (request: Request) => Promise<Response>;
|
|
177
183
|
};
|
|
184
|
+
/** Idle interval defaults to 10 s; a send in this process wakes the loop at once. */
|
|
178
185
|
startWorker(options?: {
|
|
179
186
|
intervalMs?: number;
|
|
180
187
|
batchSize?: number;
|
|
@@ -183,12 +190,12 @@ type EasyPing<TDefs extends NotificationDefinitions> = {
|
|
|
183
190
|
/** Every mounted route with its auth scope, so `custom`-scoped ones stay visible. RFC 0002 §3. */
|
|
184
191
|
listRoutes(): readonly MountedRoute[];
|
|
185
192
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* way to ask what happened is SQL against tables the library owns. Wire this
|
|
190
|
-
* to an admin page or an alert.
|
|
193
|
+
* Headers to attach to any response your app sends this user. The client's
|
|
194
|
+
* `instrument(fetch)` reads them and refreshes only when the version moved,
|
|
195
|
+
* so an active user never needs a dedicated poll. RFC 0006 idea 1.
|
|
191
196
|
*/
|
|
197
|
+
inboxHeaders(userId: string): Record<string, string>;
|
|
198
|
+
/** Deliveries that exhausted their attempts, newest first. Wire it to an admin page or an alert. */
|
|
192
199
|
getFailedDeliveries(options?: {
|
|
193
200
|
/** Defaults to the last 24 hours. */
|
|
194
201
|
since?: Date;
|
|
@@ -209,6 +216,8 @@ type RunnerDeps = {
|
|
|
209
216
|
leaseMs: number;
|
|
210
217
|
batchSize: number;
|
|
211
218
|
backoff: Backoff;
|
|
219
|
+
/** Wakes an idle worker the moment a send commits, so the idle interval can be long. */
|
|
220
|
+
signals?: Signals | undefined;
|
|
212
221
|
};
|
|
213
222
|
type SweepResult = {
|
|
214
223
|
claimed: number;
|
|
@@ -217,11 +226,7 @@ type SweepResult = {
|
|
|
217
226
|
/** Deliveries that had nothing to do. Not errors; see DeliveryOutcome. */
|
|
218
227
|
skipped: number;
|
|
219
228
|
};
|
|
220
|
-
/**
|
|
221
|
-
* What gets persisted to `last_error`. Provider responses quote the offending
|
|
222
|
-
* field, which for a bad recipient is the address itself; that column outlives
|
|
223
|
-
* the notification and is read by admin tooling, so addresses do not go in.
|
|
224
|
-
*/
|
|
229
|
+
/** Persisted to `last_error`, which admin tooling reads: addresses do not go in. */
|
|
225
230
|
declare const redactErrorMessage: (text: string) => string;
|
|
226
231
|
declare function createRunner(deps: RunnerDeps): {
|
|
227
232
|
runOnce: (options?: {
|
|
@@ -248,22 +253,13 @@ type JsonBody = {
|
|
|
248
253
|
} | {
|
|
249
254
|
error: Response;
|
|
250
255
|
};
|
|
251
|
-
/**
|
|
252
|
-
* Reads a JSON object body with a byte cap enforced while streaming, so an
|
|
253
|
-
* oversized body is refused before it is buffered. Malformed JSON is 400; an
|
|
254
|
-
* empty body is `{}`. Plugin routes should use this rather than
|
|
255
|
-
* `request.json()`, which has no limit.
|
|
256
|
-
*/
|
|
256
|
+
/** Reads a JSON object body with a streaming byte cap. Malformed JSON is 400; an empty body is `{}`. */
|
|
257
257
|
declare function readJsonBody(request: Request, maxBytes?: number): Promise<JsonBody>;
|
|
258
258
|
|
|
259
|
-
/**
|
|
260
|
-
* For email templates built with template literals. Anything in a payload
|
|
261
|
-
* that a user typed — a display name, a comment excerpt — goes through this
|
|
262
|
-
* or the recipient gets that user's HTML delivered from your sending domain.
|
|
263
|
-
*/
|
|
259
|
+
/** For template literals: anything a user typed goes through this, or their HTML ships from your domain. */
|
|
264
260
|
declare const escapeHtml: (value: unknown) => string;
|
|
265
261
|
|
|
266
262
|
declare const MIN_SECRET_LENGTH = 16;
|
|
267
263
|
declare function easyPing<TDefs extends NotificationDefinitions>(config: EasyPingConfig<TDefs>): EasyPing<TDefs>;
|
|
268
264
|
|
|
269
|
-
export { type AnyNotificationDefinition, AnyPlugin, Channel, type ChannelsConfig, DatabaseAdapter, type DeliveryConfig, DeliveryMode, DeliveryRecord, type EasyPing, type EasyPingConfig, EmailProvider, type EmailTemplate, type HealthReport, type JsonBody, Logger, MAX_BODY_BYTES, MIN_SECRET_LENGTH, type MountedRoute, type NotificationDefinition, type NotificationDefinitions, type PayloadFromSchema, type PayloadOf, Promisable, type RateLimitConfig, Recipient, RouteScope, type Runner, type RunnerDeps, type SendArgs, type SendResult, type SessionConfig, SkipReason, type SweepResult, type Worker, createRateLimiter, createRunner, decodeBase64Url, defineNotification, easyPing, encodeBase64Url, escapeHtml, readJsonBody, redactErrorMessage };
|
|
265
|
+
export { type AnyNotificationDefinition, AnyPlugin, Channel, type ChannelsConfig, DatabaseAdapter, type DeliveryConfig, DeliveryMode, DeliveryRecord, type EasyPing, type EasyPingConfig, EmailProvider, type EmailTemplate, type EventsConfig, type HealthReport, INBOX_VERSION_HEADER, type JsonBody, Logger, MAX_BODY_BYTES, MIN_SECRET_LENGTH, type MountedRoute, type NotificationDefinition, type NotificationDefinitions, type PayloadFromSchema, type PayloadOf, Promisable, type RateLimitConfig, Recipient, RouteScope, type Runner, type RunnerDeps, type SendArgs, type SendResult, type SessionConfig, Signals, SkipReason, type SweepResult, type Worker, createRateLimiter, createRunner, decodeBase64Url, defineNotification, easyPing, encodeBase64Url, escapeHtml, readJsonBody, redactErrorMessage };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { C as Channel, b as DeliveryMode, S as SkipReason, c as DeliveryRecord, D as DatabaseAdapter, R as Recipient } from './adapter-
|
|
2
|
-
export { d as ClaimArgs, e as ClaimedDelivery, f as DeliveryOutcome, g as DeliveryRelease, a as DeliveryStatus, h as FeedPage, i as FeedQuery, F as Frequency, I as InsertDelivery, j as InsertNotification, N as NotificationRecord, P as PreferenceRecord } from './adapter-
|
|
1
|
+
import { C as Channel, b as DeliveryMode, S as SkipReason, c as DeliveryRecord, D as DatabaseAdapter, R as Recipient } from './adapter-D3lTy-4E.js';
|
|
2
|
+
export { d as ClaimArgs, e as ClaimedDelivery, f as DeliveryOutcome, g as DeliveryRelease, a as DeliveryStatus, h as FeedPage, i as FeedQuery, F as Frequency, I as InsertDelivery, j as InsertNotification, N as NotificationRecord, P as PreferenceRecord } from './adapter-D3lTy-4E.js';
|
|
3
3
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
4
|
-
import { R as RouteScope, A as AnyPlugin, P as Promisable, L as Logger } from './plugin-
|
|
5
|
-
export { a as ActionsOf, b as AfterDeliverContext, c as AnyClientPlugin, B as BeforeSendContext, d as BeforeSendResult, C as ChannelDecision, e as ClientFetch, f as ClientPlugin, g as ConfigError, D as DEFAULT_TOKEN_TTL_SECONDS, E as EasyPingError, h as EasyPingPlugin, F as FieldDeclaration, i as FieldType, M as MAX_TOKEN_TTL_SECONDS, j as PluginHooks, k as PrepareContext, l as ResolveChannelsContext, m as RouteContext, n as RouteDefinition, S as SchemaDeclaration, o as SignRequest, T as TableDeclaration, p as TokenClaims, U as UnionToIntersection, V as ValidationError, q as consoleLogger, r as defineClientPlugin, s as definePlugin, t as expiresIn, u as signToken, v as verifyToken } from './plugin-
|
|
4
|
+
import { R as RouteScope, A as AnyPlugin, P as Promisable, L as Logger } from './plugin-Cg9usuUi.js';
|
|
5
|
+
export { a as ActionsOf, b as AfterDeliverContext, c as AnyClientPlugin, B as BeforeSendContext, d as BeforeSendResult, C as ChannelDecision, e as ClientFetch, f as ClientPlugin, g as ConfigError, D as DEFAULT_TOKEN_TTL_SECONDS, E as EasyPingError, h as EasyPingPlugin, F as FieldDeclaration, i as FieldType, M as MAX_TOKEN_TTL_SECONDS, j as PluginHooks, k as PrepareContext, l as ResolveChannelsContext, m as RouteContext, n as RouteDefinition, S as SchemaDeclaration, o as SignRequest, T as TableDeclaration, p as TokenClaims, U as UnionToIntersection, V as ValidationError, q as consoleLogger, r as defineClientPlugin, s as definePlugin, t as expiresIn, u as signToken, v as verifyToken } from './plugin-Cg9usuUi.js';
|
|
6
6
|
import { E as EmailProvider } from './provider-CA45WVqt.js';
|
|
7
7
|
export { a as EmailMessage, P as ProviderSendResult } from './provider-CA45WVqt.js';
|
|
8
|
+
import { S as Signals } from './signals-kPmOlKDX.js';
|
|
9
|
+
export { D as DELIVERIES_CHANNEL, c as createBridgedSignals, a as createMemorySignals, i as inboxChannel } from './signals-kPmOlKDX.js';
|
|
8
10
|
|
|
9
11
|
declare function encodeBase64Url(input: string | Uint8Array): string;
|
|
10
12
|
declare function decodeBase64Url(value: string): string | null;
|
|
@@ -23,10 +25,7 @@ type NotificationDefinition<TSchema extends StandardSchemaV1 | undefined = Stand
|
|
|
23
25
|
};
|
|
24
26
|
/** Binds TSchema so email callbacks infer the payload; TS cannot infer from a sibling property. */
|
|
25
27
|
declare function defineNotification<TSchema extends StandardSchemaV1 | undefined = undefined>(definition: NotificationDefinition<TSchema>): NotificationDefinition<TSchema>;
|
|
26
|
-
/**
|
|
27
|
-
* Erased form. `EmailTemplate<never>` because callbacks are contravariant —
|
|
28
|
-
* anything else makes every schema-typed definition fail to assign.
|
|
29
|
-
*/
|
|
28
|
+
/** Erased form. `EmailTemplate<never>` because callbacks are contravariant. */
|
|
30
29
|
type AnyNotificationDefinition = {
|
|
31
30
|
schema?: StandardSchemaV1 | undefined;
|
|
32
31
|
channels: readonly Channel[];
|
|
@@ -42,19 +41,10 @@ type RateLimitConfig = {
|
|
|
42
41
|
/** Requests allowed per key per window. */
|
|
43
42
|
max: number;
|
|
44
43
|
windowMs: number;
|
|
45
|
-
/**
|
|
46
|
-
* Which bucket a request lands in. Defaults to the client address as the
|
|
47
|
-
* usual proxy headers report it; returning null exempts the request. Behind
|
|
48
|
-
* no proxy those headers are absent, so supply your own key there.
|
|
49
|
-
*/
|
|
44
|
+
/** Bucket key. Defaults to the client address from proxy headers; null exempts the request. */
|
|
50
45
|
key?: (request: Request) => string | null;
|
|
51
46
|
};
|
|
52
|
-
/**
|
|
53
|
-
* Fixed-window counter held in process memory. Per isolate on edge runtimes
|
|
54
|
-
* and per replica anywhere else, so it bounds one client's cost to one
|
|
55
|
-
* process rather than enforcing a global quota. That is enough to blunt
|
|
56
|
-
* secret guessing and poll storms, which is what it is for.
|
|
57
|
-
*/
|
|
47
|
+
/** Fixed window in process memory: per isolate or replica, so a bound rather than a quota. */
|
|
58
48
|
declare function createRateLimiter(config: RateLimitConfig): (request: Request) => Response | null;
|
|
59
49
|
|
|
60
50
|
type SessionConfig = {
|
|
@@ -70,11 +60,28 @@ type DeliveryConfig = {
|
|
|
70
60
|
/** inline mode only. */
|
|
71
61
|
throwOnError?: boolean;
|
|
72
62
|
backoff?: "exponential" | ((attempt: number) => number);
|
|
63
|
+
/** Next.js `after` or Cloudflare `ctx.waitUntil`. Injected, not detected. */
|
|
64
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
73
65
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
66
|
+
* Run one small claim-and-deliver pass after any request the handler serves,
|
|
67
|
+
* at most once per `everyMs` per process. Delivery then rides on your own
|
|
68
|
+
* traffic and the cron becomes a backstop. Ignored in `inline` mode. RFC 0006 idea 5.
|
|
76
69
|
*/
|
|
77
|
-
|
|
70
|
+
sweepOnRequest?: true | {
|
|
71
|
+
everyMs?: number;
|
|
72
|
+
limit?: number;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
type EventsConfig = {
|
|
76
|
+
/** Comment line keeping proxies from closing an idle stream. Default 25 s. */
|
|
77
|
+
heartbeatMs?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Without a cross-process signal source, each stream checks the database
|
|
80
|
+
* for changes this often on its subscriber's behalf. Default 30 s; 0 disables.
|
|
81
|
+
*/
|
|
82
|
+
probeIntervalMs?: number;
|
|
83
|
+
/** Close the stream after this long; the client reconnects. 0 (default) leaves it open. */
|
|
84
|
+
maxDurationMs?: number;
|
|
78
85
|
};
|
|
79
86
|
type ChannelsConfig = {
|
|
80
87
|
inApp?: {
|
|
@@ -99,25 +106,22 @@ type EasyPingConfig<TDefs extends NotificationDefinitions> = {
|
|
|
99
106
|
/** Sweeps one POST /cron may run before returning. Defaults to 50. */
|
|
100
107
|
maxSweeps?: number;
|
|
101
108
|
};
|
|
102
|
-
/**
|
|
103
|
-
* Bearer secret for plugins' machine routes (`/push/prune`, `/digests/cron`).
|
|
104
|
-
* Falls back to `cron.secret`; set it when the scheduler that hits /cron
|
|
105
|
-
* should not also be able to prune devices or fire digests.
|
|
106
|
-
*/
|
|
109
|
+
/** Bearer secret for plugin machine routes. Falls back to `cron.secret`. */
|
|
107
110
|
machineSecret?: string;
|
|
111
|
+
/** In-process fixed-window limiter, applied before routing. */
|
|
112
|
+
rateLimit?: RateLimitConfig;
|
|
108
113
|
/**
|
|
109
|
-
*
|
|
110
|
-
*
|
|
114
|
+
* How "something changed" travels between the sender, the worker and open
|
|
115
|
+
* event streams. Defaults to in-process. Supply a LISTEN/NOTIFY or change
|
|
116
|
+
* stream implementation to cross replicas. RFC 0006 §4C.
|
|
111
117
|
*/
|
|
112
|
-
|
|
118
|
+
signals?: Signals;
|
|
119
|
+
/** `GET /events`, a Server-Sent Events stream per user. `false` unmounts it. */
|
|
120
|
+
events?: EventsConfig | false;
|
|
113
121
|
plugins?: readonly AnyPlugin[];
|
|
114
122
|
/** Where the handler is mounted, used to strip the prefix off incoming URLs. */
|
|
115
123
|
basePath?: string;
|
|
116
|
-
/**
|
|
117
|
-
* Origins allowed to POST besides the request's own host. Cross-origin
|
|
118
|
-
* POSTs are otherwise refused with 403, and every POST must be
|
|
119
|
-
* `application/json`. `"*.example.com"` matches subdomains.
|
|
120
|
-
*/
|
|
124
|
+
/** Origins other than the request host that may POST. `"*.example.com"` matches subdomains. */
|
|
121
125
|
trustedOrigins?: readonly string[];
|
|
122
126
|
/** Runs before routing. Return a Response to short-circuit — a rate limiter's 429, say. */
|
|
123
127
|
onRequest?: (request: Request) => Promisable<Response | undefined | void>;
|
|
@@ -167,6 +171,8 @@ type HealthReport = {
|
|
|
167
171
|
cronRequiredButMissing: boolean;
|
|
168
172
|
warnings: readonly string[];
|
|
169
173
|
};
|
|
174
|
+
/** Response header carrying a per-user inbox version, so app traffic can replace polling. */
|
|
175
|
+
declare const INBOX_VERSION_HEADER = "x-easy-ping-inbox";
|
|
170
176
|
type EasyPing<TDefs extends NotificationDefinitions> = {
|
|
171
177
|
send<TKey extends keyof TDefs & string>(type: TKey, args: SendArgs<TDefs, TKey>): Promise<SendResult>;
|
|
172
178
|
handler: {
|
|
@@ -175,6 +181,7 @@ type EasyPing<TDefs extends NotificationDefinitions> = {
|
|
|
175
181
|
GET: (request: Request) => Promise<Response>;
|
|
176
182
|
POST: (request: Request) => Promise<Response>;
|
|
177
183
|
};
|
|
184
|
+
/** Idle interval defaults to 10 s; a send in this process wakes the loop at once. */
|
|
178
185
|
startWorker(options?: {
|
|
179
186
|
intervalMs?: number;
|
|
180
187
|
batchSize?: number;
|
|
@@ -183,12 +190,12 @@ type EasyPing<TDefs extends NotificationDefinitions> = {
|
|
|
183
190
|
/** Every mounted route with its auth scope, so `custom`-scoped ones stay visible. RFC 0002 §3. */
|
|
184
191
|
listRoutes(): readonly MountedRoute[];
|
|
185
192
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* way to ask what happened is SQL against tables the library owns. Wire this
|
|
190
|
-
* to an admin page or an alert.
|
|
193
|
+
* Headers to attach to any response your app sends this user. The client's
|
|
194
|
+
* `instrument(fetch)` reads them and refreshes only when the version moved,
|
|
195
|
+
* so an active user never needs a dedicated poll. RFC 0006 idea 1.
|
|
191
196
|
*/
|
|
197
|
+
inboxHeaders(userId: string): Record<string, string>;
|
|
198
|
+
/** Deliveries that exhausted their attempts, newest first. Wire it to an admin page or an alert. */
|
|
192
199
|
getFailedDeliveries(options?: {
|
|
193
200
|
/** Defaults to the last 24 hours. */
|
|
194
201
|
since?: Date;
|
|
@@ -209,6 +216,8 @@ type RunnerDeps = {
|
|
|
209
216
|
leaseMs: number;
|
|
210
217
|
batchSize: number;
|
|
211
218
|
backoff: Backoff;
|
|
219
|
+
/** Wakes an idle worker the moment a send commits, so the idle interval can be long. */
|
|
220
|
+
signals?: Signals | undefined;
|
|
212
221
|
};
|
|
213
222
|
type SweepResult = {
|
|
214
223
|
claimed: number;
|
|
@@ -217,11 +226,7 @@ type SweepResult = {
|
|
|
217
226
|
/** Deliveries that had nothing to do. Not errors; see DeliveryOutcome. */
|
|
218
227
|
skipped: number;
|
|
219
228
|
};
|
|
220
|
-
/**
|
|
221
|
-
* What gets persisted to `last_error`. Provider responses quote the offending
|
|
222
|
-
* field, which for a bad recipient is the address itself; that column outlives
|
|
223
|
-
* the notification and is read by admin tooling, so addresses do not go in.
|
|
224
|
-
*/
|
|
229
|
+
/** Persisted to `last_error`, which admin tooling reads: addresses do not go in. */
|
|
225
230
|
declare const redactErrorMessage: (text: string) => string;
|
|
226
231
|
declare function createRunner(deps: RunnerDeps): {
|
|
227
232
|
runOnce: (options?: {
|
|
@@ -248,22 +253,13 @@ type JsonBody = {
|
|
|
248
253
|
} | {
|
|
249
254
|
error: Response;
|
|
250
255
|
};
|
|
251
|
-
/**
|
|
252
|
-
* Reads a JSON object body with a byte cap enforced while streaming, so an
|
|
253
|
-
* oversized body is refused before it is buffered. Malformed JSON is 400; an
|
|
254
|
-
* empty body is `{}`. Plugin routes should use this rather than
|
|
255
|
-
* `request.json()`, which has no limit.
|
|
256
|
-
*/
|
|
256
|
+
/** Reads a JSON object body with a streaming byte cap. Malformed JSON is 400; an empty body is `{}`. */
|
|
257
257
|
declare function readJsonBody(request: Request, maxBytes?: number): Promise<JsonBody>;
|
|
258
258
|
|
|
259
|
-
/**
|
|
260
|
-
* For email templates built with template literals. Anything in a payload
|
|
261
|
-
* that a user typed — a display name, a comment excerpt — goes through this
|
|
262
|
-
* or the recipient gets that user's HTML delivered from your sending domain.
|
|
263
|
-
*/
|
|
259
|
+
/** For template literals: anything a user typed goes through this, or their HTML ships from your domain. */
|
|
264
260
|
declare const escapeHtml: (value: unknown) => string;
|
|
265
261
|
|
|
266
262
|
declare const MIN_SECRET_LENGTH = 16;
|
|
267
263
|
declare function easyPing<TDefs extends NotificationDefinitions>(config: EasyPingConfig<TDefs>): EasyPing<TDefs>;
|
|
268
264
|
|
|
269
|
-
export { type AnyNotificationDefinition, AnyPlugin, Channel, type ChannelsConfig, DatabaseAdapter, type DeliveryConfig, DeliveryMode, DeliveryRecord, type EasyPing, type EasyPingConfig, EmailProvider, type EmailTemplate, type HealthReport, type JsonBody, Logger, MAX_BODY_BYTES, MIN_SECRET_LENGTH, type MountedRoute, type NotificationDefinition, type NotificationDefinitions, type PayloadFromSchema, type PayloadOf, Promisable, type RateLimitConfig, Recipient, RouteScope, type Runner, type RunnerDeps, type SendArgs, type SendResult, type SessionConfig, SkipReason, type SweepResult, type Worker, createRateLimiter, createRunner, decodeBase64Url, defineNotification, easyPing, encodeBase64Url, escapeHtml, readJsonBody, redactErrorMessage };
|
|
265
|
+
export { type AnyNotificationDefinition, AnyPlugin, Channel, type ChannelsConfig, DatabaseAdapter, type DeliveryConfig, DeliveryMode, DeliveryRecord, type EasyPing, type EasyPingConfig, EmailProvider, type EmailTemplate, type EventsConfig, type HealthReport, INBOX_VERSION_HEADER, type JsonBody, Logger, MAX_BODY_BYTES, MIN_SECRET_LENGTH, type MountedRoute, type NotificationDefinition, type NotificationDefinitions, type PayloadFromSchema, type PayloadOf, Promisable, type RateLimitConfig, Recipient, RouteScope, type Runner, type RunnerDeps, type SendArgs, type SendResult, type SessionConfig, Signals, SkipReason, type SweepResult, type Worker, createRateLimiter, createRunner, decodeBase64Url, defineNotification, easyPing, encodeBase64Url, escapeHtml, readJsonBody, redactErrorMessage };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { createRunner } from './chunk-
|
|
2
|
-
export { createRunner, redactErrorMessage } from './chunk-
|
|
3
|
-
export { defineClientPlugin, definePlugin } from './chunk-
|
|
4
|
-
import { createHandler, createScopedSigner } from './chunk-
|
|
5
|
-
export { DEFAULT_TOKEN_TTL_SECONDS, MAX_BODY_BYTES, MAX_TOKEN_TTL_SECONDS, expiresIn, readJsonBody, signToken, verifyToken } from './chunk-
|
|
6
|
-
import {
|
|
7
|
-
export {
|
|
1
|
+
import { createRunner } from './chunk-JKTMUMQK.js';
|
|
2
|
+
export { createRunner, redactErrorMessage } from './chunk-JKTMUMQK.js';
|
|
3
|
+
export { defineClientPlugin, definePlugin } from './chunk-E4IZ2CQC.js';
|
|
4
|
+
import { createHandler, createScopedSigner } from './chunk-USQ6CQTJ.js';
|
|
5
|
+
export { DEFAULT_TOKEN_TTL_SECONDS, MAX_BODY_BYTES, MAX_TOKEN_TTL_SECONDS, expiresIn, readJsonBody, signToken, verifyToken } from './chunk-USQ6CQTJ.js';
|
|
6
|
+
import { createMemorySignals, inboxChannel, DELIVERIES_CHANNEL } from './chunk-ORZKH224.js';
|
|
7
|
+
export { DELIVERIES_CHANNEL, createBridgedSignals, createMemorySignals, inboxChannel } from './chunk-ORZKH224.js';
|
|
8
|
+
import { consoleLogger, ConfigError, createPluginStore, ValidationError } from './chunk-3VOQOGDR.js';
|
|
9
|
+
export { ConfigError, EasyPingError, ValidationError, consoleLogger } from './chunk-3VOQOGDR.js';
|
|
10
|
+
import './chunk-AEYYTE5I.js';
|
|
8
11
|
export { decodeBase64Url, encodeBase64Url } from './chunk-PTGHMCCG.js';
|
|
9
|
-
|
|
12
|
+
|
|
13
|
+
// src/core/config.ts
|
|
14
|
+
var INBOX_VERSION_HEADER = "x-easy-ping-inbox";
|
|
10
15
|
|
|
11
16
|
// src/core/definition.ts
|
|
12
17
|
function defineNotification(definition) {
|
|
@@ -197,8 +202,7 @@ async function runSend(type, input, deps) {
|
|
|
197
202
|
id: newId(),
|
|
198
203
|
channel: decision.channel,
|
|
199
204
|
maxAttempts: definition.maxAttempts ?? deps.maxAttempts,
|
|
200
|
-
//
|
|
201
|
-
// already refuses rows whose time has not come. RFC 0004 §4.2.
|
|
205
|
+
// defer is just not_before; the claim query refuses rows whose time has not come. RFC 0004 §4.2.
|
|
202
206
|
notBefore: decision.action === "defer" && decision.notBefore ? decision.notBefore : now
|
|
203
207
|
}));
|
|
204
208
|
if (deliveries.length === 0) {
|
|
@@ -254,6 +258,7 @@ var CORE_ROUTES = [
|
|
|
254
258
|
{ method: "POST", path: "/seen", scope: { type: "user" }, owner: "core" },
|
|
255
259
|
{ method: "POST", path: "/read", scope: { type: "user" }, owner: "core" },
|
|
256
260
|
{ method: "POST", path: "/read-all", scope: { type: "user" }, owner: "core" },
|
|
261
|
+
{ method: "GET", path: "/events", scope: { type: "user" }, owner: "core" },
|
|
257
262
|
{ method: "POST", path: "/cron", scope: { type: "machine" }, owner: "core" }
|
|
258
263
|
];
|
|
259
264
|
var DEFAULTS = {
|
|
@@ -378,6 +383,20 @@ function easyPing(config) {
|
|
|
378
383
|
}
|
|
379
384
|
}
|
|
380
385
|
for (const warning of warnings) logger.warn(warning);
|
|
386
|
+
const baseSignals = config.signals ?? createMemorySignals();
|
|
387
|
+
const inboxVersions = /* @__PURE__ */ new Map();
|
|
388
|
+
const signals = {
|
|
389
|
+
crossProcess: baseSignals.crossProcess,
|
|
390
|
+
publish(channel) {
|
|
391
|
+
if (channel.startsWith("inbox:")) {
|
|
392
|
+
if (inboxVersions.size > 5e4) inboxVersions.clear();
|
|
393
|
+
const userId = channel.slice("inbox:".length);
|
|
394
|
+
inboxVersions.set(userId, (inboxVersions.get(userId) ?? 0) + 1);
|
|
395
|
+
}
|
|
396
|
+
baseSignals.publish(channel);
|
|
397
|
+
},
|
|
398
|
+
subscribe: (channel, handler2) => baseSignals.subscribe(channel, handler2)
|
|
399
|
+
};
|
|
381
400
|
const runner = createRunner({
|
|
382
401
|
adapter: config.database,
|
|
383
402
|
definitions: config.notifications,
|
|
@@ -387,15 +406,34 @@ function easyPing(config) {
|
|
|
387
406
|
logger,
|
|
388
407
|
leaseMs,
|
|
389
408
|
batchSize: config.delivery?.batchSize ?? DEFAULTS.batchSize,
|
|
390
|
-
backoff: config.delivery?.backoff ?? "exponential"
|
|
409
|
+
backoff: config.delivery?.backoff ?? "exponential",
|
|
410
|
+
signals
|
|
391
411
|
});
|
|
392
412
|
const pluginRoutes = plugins.flatMap((plugin) => plugin.routes ?? []);
|
|
413
|
+
const events = config.events === false ? false : {
|
|
414
|
+
heartbeatMs: config.events?.heartbeatMs ?? 25e3,
|
|
415
|
+
probeIntervalMs: config.events?.probeIntervalMs ?? 3e4,
|
|
416
|
+
maxDurationMs: config.events?.maxDurationMs ?? 0
|
|
417
|
+
};
|
|
418
|
+
const sweepConfig = config.delivery?.sweepOnRequest;
|
|
419
|
+
let lastRequestSweep = 0;
|
|
420
|
+
const sweepOnRequest = sweepConfig && mode !== "inline" ? () => {
|
|
421
|
+
const settings = sweepConfig === true ? {} : sweepConfig;
|
|
422
|
+
const now = Date.now();
|
|
423
|
+
if (now - lastRequestSweep < (settings.everyMs ?? 5e3)) return;
|
|
424
|
+
lastRequestSweep = now;
|
|
425
|
+
const pass = runner.runOnce({ limit: settings.limit ?? 5 }).catch((error) => logger.error("request-driven sweep failed", { error }));
|
|
426
|
+
config.delivery?.waitUntil?.(pass);
|
|
427
|
+
} : void 0;
|
|
393
428
|
const handler = createHandler({
|
|
394
429
|
adapter: config.database,
|
|
395
430
|
session: config.session,
|
|
396
431
|
runner,
|
|
397
432
|
logger,
|
|
398
433
|
secret: config.secret,
|
|
434
|
+
signals,
|
|
435
|
+
events,
|
|
436
|
+
sweepOnRequest,
|
|
399
437
|
basePath: config.basePath ?? DEFAULTS.basePath,
|
|
400
438
|
pluginRoutes,
|
|
401
439
|
trustedOrigins: config.trustedOrigins,
|
|
@@ -427,6 +465,10 @@ function easyPing(config) {
|
|
|
427
465
|
}
|
|
428
466
|
);
|
|
429
467
|
if (result.notifications.length > 0) {
|
|
468
|
+
for (const notification of result.notifications) {
|
|
469
|
+
signals.publish(inboxChannel(notification.userId));
|
|
470
|
+
}
|
|
471
|
+
signals.publish(DELIVERIES_CHANNEL);
|
|
430
472
|
const ids = result.notifications.flatMap(
|
|
431
473
|
(notification) => notification.deliveries.map((delivery) => delivery.id)
|
|
432
474
|
);
|
|
@@ -459,8 +501,13 @@ function easyPing(config) {
|
|
|
459
501
|
send,
|
|
460
502
|
handler,
|
|
461
503
|
startWorker: (options) => runner.startWorker(options ?? {}),
|
|
504
|
+
inboxHeaders: (userId) => ({
|
|
505
|
+
[INBOX_VERSION_HEADER]: String(inboxVersions.get(userId) ?? 0)
|
|
506
|
+
}),
|
|
462
507
|
listRoutes: () => [
|
|
463
|
-
...CORE_ROUTES.filter(
|
|
508
|
+
...CORE_ROUTES.filter(
|
|
509
|
+
(route) => (route.path !== "/cron" || Boolean(config.cron?.secret)) && (route.path !== "/events" || events !== false)
|
|
510
|
+
),
|
|
464
511
|
...plugins.flatMap(
|
|
465
512
|
(plugin) => (plugin.routes ?? []).map((route) => ({
|
|
466
513
|
method: route.method,
|
|
@@ -486,6 +533,6 @@ function easyPing(config) {
|
|
|
486
533
|
};
|
|
487
534
|
}
|
|
488
535
|
|
|
489
|
-
export { MIN_SECRET_LENGTH, createRateLimiter, defineNotification, easyPing, escapeHtml };
|
|
536
|
+
export { INBOX_VERSION_HEADER, MIN_SECRET_LENGTH, createRateLimiter, defineNotification, easyPing, escapeHtml };
|
|
490
537
|
//# sourceMappingURL=index.js.map
|
|
491
538
|
//# sourceMappingURL=index.js.map
|