@daloyjs/core 0.36.0 → 0.37.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.
Files changed (77) hide show
  1. package/README.md +21 -2
  2. package/bin/daloy.mjs +2 -0
  3. package/dist/adapters/bun.js +16 -9
  4. package/dist/adapters/deno.js +7 -1
  5. package/dist/adapters/node.d.ts +11 -0
  6. package/dist/adapters/node.js +24 -0
  7. package/dist/app.d.ts +144 -1
  8. package/dist/app.js +208 -1
  9. package/dist/asyncapi.d.ts +98 -0
  10. package/dist/asyncapi.js +212 -0
  11. package/dist/auto-ban.d.ts +205 -0
  12. package/dist/auto-ban.js +222 -0
  13. package/dist/bot-guard.d.ts +209 -0
  14. package/dist/bot-guard.js +291 -0
  15. package/dist/cli.d.ts +8 -0
  16. package/dist/cli.js +88 -4
  17. package/dist/concurrency-limit.d.ts +135 -0
  18. package/dist/concurrency-limit.js +254 -0
  19. package/dist/docs.d.ts +57 -6
  20. package/dist/docs.js +34 -3
  21. package/dist/errors.d.ts +20 -0
  22. package/dist/errors.js +27 -0
  23. package/dist/fetch-guard.js +4 -0
  24. package/dist/fetch-resilience.d.ts +295 -0
  25. package/dist/fetch-resilience.js +485 -0
  26. package/dist/geo-block.d.ts +184 -0
  27. package/dist/geo-block.js +153 -0
  28. package/dist/hashing.d.ts +2 -1
  29. package/dist/hashing.js +12 -1
  30. package/dist/http-signatures.d.ts +303 -0
  31. package/dist/http-signatures.js +782 -0
  32. package/dist/idempotency.d.ts +204 -0
  33. package/dist/idempotency.js +341 -0
  34. package/dist/index.d.ts +38 -4
  35. package/dist/index.js +18 -1
  36. package/dist/ip-reputation.d.ts +198 -0
  37. package/dist/ip-reputation.js +253 -0
  38. package/dist/jwk.d.ts +15 -0
  39. package/dist/jwk.js +24 -2
  40. package/dist/load-shedding.d.ts +5 -0
  41. package/dist/logger.js +6 -2
  42. package/dist/metrics.d.ts +208 -0
  43. package/dist/metrics.js +452 -0
  44. package/dist/middleware.js +0 -10
  45. package/dist/mtls.d.ts +266 -0
  46. package/dist/mtls.js +488 -0
  47. package/dist/multipart.js +1 -1
  48. package/dist/openapi-diff.d.ts +79 -0
  49. package/dist/openapi-diff.js +246 -0
  50. package/dist/openapi.js +4 -1
  51. package/dist/pagination.d.ts +210 -0
  52. package/dist/pagination.js +353 -0
  53. package/dist/rate-limit-redis.d.ts +8 -0
  54. package/dist/rate-limit-redis.js +8 -0
  55. package/dist/request-decompression.d.ts +200 -0
  56. package/dist/request-decompression.js +363 -0
  57. package/dist/response-cache.d.ts +205 -0
  58. package/dist/response-cache.js +374 -0
  59. package/dist/router.d.ts +22 -0
  60. package/dist/router.js +64 -7
  61. package/dist/safe-redirect.d.ts +2 -2
  62. package/dist/safe-redirect.js +3 -8
  63. package/dist/sbom.cdx.json +9 -9
  64. package/dist/sbom.spdx.json +5 -5
  65. package/dist/scheduler.d.ts +315 -0
  66. package/dist/scheduler.js +546 -0
  67. package/dist/security.d.ts +27 -7
  68. package/dist/security.js +27 -7
  69. package/dist/session.js +3 -3
  70. package/dist/types.d.ts +33 -0
  71. package/dist/waf.d.ts +213 -0
  72. package/dist/waf.js +334 -0
  73. package/dist/webhook-delivery.d.ts +263 -0
  74. package/dist/webhook-delivery.js +311 -0
  75. package/dist/websocket.d.ts +52 -0
  76. package/dist/websocket.js +13 -0
  77. package/package.json +76 -2
@@ -0,0 +1,315 @@
1
+ /**
2
+ * In-process scheduled tasks (cron) for DaloyJS.
3
+ *
4
+ * A queue-agnostic schedule primitive — the in-process counterpart to an
5
+ * external job queue. Where a queue answers *“run this work somewhere,
6
+ * eventually”*, the {@link Scheduler} answers *“run this work in
7
+ * this process, on this clock”* — the three things a production
8
+ * in-process scheduler needs:
9
+ *
10
+ * - **Flexible schedules.** Fixed intervals (`intervalMs`) or 5-field cron
11
+ * expressions (`cron`, with `@hourly`/`@daily`/… aliases and an optional
12
+ * IANA `timeZone`), parsed once into a fast matcher with no backtracking
13
+ * regex.
14
+ * - **Single-flight guarantees.** A task never overlaps itself: if a tick
15
+ * fires while the previous run is still in progress, the tick is *skipped*
16
+ * (and counted), so a slow task can never pile up unbounded concurrent runs.
17
+ * - **Graceful-shutdown integration.** {@link Scheduler.stop} clears every
18
+ * timer and waits for in-flight runs to settle (up to a deadline, then
19
+ * aborts their {@link AbortSignal}), so it slots cleanly into the app's
20
+ * `onClose` drain. Timers are `unref`'d, so a scheduler never keeps an
21
+ * otherwise-idle process alive on its own.
22
+ *
23
+ * Everything is built on Web-standard primitives (`AbortController`,
24
+ * `Intl.DateTimeFormat` for timezone wall-clock math, `setTimeout`), so it
25
+ * runs unchanged on Node, Bun, Deno, Cloudflare Workers, and Vercel Edge, with
26
+ * zero runtime dependencies. Pair it with {@link App.cron} for an app-managed
27
+ * scheduler whose lifecycle is tied to graceful shutdown, or drive a
28
+ * {@link Scheduler} directly.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const scheduler = new Scheduler();
33
+ * scheduler.define({ name: "cleanup", cron: "0 * * * *" }, async ({ signal }) => {
34
+ * await purgeExpiredSessions({ signal });
35
+ * });
36
+ * scheduler.start();
37
+ * // ... later, during shutdown:
38
+ * await scheduler.stop(5_000);
39
+ * ```
40
+ *
41
+ * @module
42
+ * @since 0.37.0
43
+ */
44
+ /**
45
+ * Thrown when a cron expression cannot be parsed, or describes a time that can
46
+ * never occur (for example `0 0 30 2 *` — the 30th of February).
47
+ *
48
+ * @since 0.37.0
49
+ */
50
+ export declare class CronParseError extends Error {
51
+ constructor(message: string);
52
+ }
53
+ /**
54
+ * A minimal structured logger, structurally compatible with the DaloyJS
55
+ * application logger. Only the levels the scheduler emits are required.
56
+ *
57
+ * @since 0.37.0
58
+ */
59
+ export interface SchedulerLogger {
60
+ debug(obj: object | string, msg?: string): void;
61
+ info(obj: object | string, msg?: string): void;
62
+ warn(obj: object | string, msg?: string): void;
63
+ error(obj: object | string, msg?: string): void;
64
+ }
65
+ /**
66
+ * Pluggable timer primitives, injectable for deterministic testing. The
67
+ * default uses `setTimeout`/`clearTimeout` and `unref`s the handle so a
68
+ * pending tick never keeps an idle process alive.
69
+ *
70
+ * @since 0.37.0
71
+ */
72
+ export interface TimerFns {
73
+ /** Schedule `callback` to run after `delayMs`, returning an opaque handle. */
74
+ set(callback: () => void, delayMs: number): unknown;
75
+ /** Cancel a previously scheduled timer by its handle. */
76
+ clear(handle: unknown): void;
77
+ }
78
+ /**
79
+ * Context handed to a task handler on each run.
80
+ *
81
+ * @since 0.37.0
82
+ */
83
+ export interface TaskRunContext {
84
+ /** The task's unique name. */
85
+ readonly name: string;
86
+ /** The wall-clock time the run was scheduled for. */
87
+ readonly scheduledFor: Date;
88
+ /** Monotonically increasing run number for this task (1-based). */
89
+ readonly runCount: number;
90
+ /**
91
+ * Aborted when the per-run `timeoutMs` elapses, or when {@link Scheduler.stop}
92
+ * runs out of grace time. Well-behaved handlers should forward it to any
93
+ * I/O they perform so shutdown stays prompt.
94
+ */
95
+ readonly signal: AbortSignal;
96
+ }
97
+ /**
98
+ * A handler invoked on each scheduled run.
99
+ *
100
+ * @since 0.37.0
101
+ */
102
+ export type TaskHandler = (ctx: TaskRunContext) => void | Promise<void>;
103
+ /**
104
+ * Information passed to a task's `onError` callback.
105
+ *
106
+ * @since 0.37.0
107
+ */
108
+ export interface TaskErrorInfo {
109
+ /** The task's unique name. */
110
+ readonly name: string;
111
+ /** The run number that failed (1-based). */
112
+ readonly runCount: number;
113
+ /** `true` when the failure was the per-run timeout aborting the handler. */
114
+ readonly timedOut: boolean;
115
+ }
116
+ /**
117
+ * Definition of a single scheduled task. Exactly one of {@link intervalMs} or
118
+ * {@link cron} must be provided.
119
+ *
120
+ * @since 0.37.0
121
+ */
122
+ export interface TaskDefinition {
123
+ /** Unique, non-empty task name. Used in logs and {@link Scheduler.getState}. */
124
+ name: string;
125
+ /**
126
+ * Fixed delay, in milliseconds, between the **start** of consecutive runs
127
+ * (fixed-rate cadence). Must be a positive integer. If a run outlasts the
128
+ * interval, the next tick is skipped (single-flight) rather than overlapping.
129
+ * Mutually exclusive with {@link cron}.
130
+ */
131
+ intervalMs?: number;
132
+ /**
133
+ * A 5-field cron expression (`minute hour day-of-month month day-of-week`)
134
+ * or a named alias (`@yearly`/`@annually`, `@monthly`, `@weekly`, `@daily`/
135
+ * `@midnight`, `@hourly`). Mutually exclusive with {@link intervalMs}.
136
+ */
137
+ cron?: string;
138
+ /**
139
+ * IANA timezone (e.g. `"America/New_York"`) the cron expression is evaluated
140
+ * in. Defaults to UTC. Ignored for interval schedules.
141
+ */
142
+ timeZone?: string;
143
+ /**
144
+ * Run the task once immediately when the scheduler starts, in addition to
145
+ * its normal schedule. Defaults to `false`.
146
+ */
147
+ runOnStart?: boolean;
148
+ /**
149
+ * Abort the run's {@link TaskRunContext.signal} after this many milliseconds.
150
+ * `0` (the default) disables the per-run timeout.
151
+ */
152
+ timeoutMs?: number;
153
+ /**
154
+ * Invoked when a run throws or times out. Errors are always logged; this
155
+ * hook is for custom handling (alerting, metrics). Exceptions thrown here
156
+ * are swallowed.
157
+ */
158
+ onError?: (error: unknown, info: TaskErrorInfo) => void;
159
+ }
160
+ /**
161
+ * Options for constructing a {@link Scheduler}.
162
+ *
163
+ * @since 0.37.0
164
+ */
165
+ export interface SchedulerOptions {
166
+ /** Structured logger for scheduler lifecycle and task events. */
167
+ logger?: SchedulerLogger;
168
+ /** Injectable clock (milliseconds since epoch). Defaults to `Date.now`. */
169
+ now?: () => number;
170
+ /** Injectable timer primitives. Defaults to `unref`'d `setTimeout`. */
171
+ timers?: TimerFns;
172
+ }
173
+ /**
174
+ * A point-in-time snapshot of a task's execution statistics.
175
+ *
176
+ * @since 0.37.0
177
+ */
178
+ export interface TaskState {
179
+ /** The task's unique name. */
180
+ readonly name: string;
181
+ /** Whether a run is currently in progress. */
182
+ readonly running: boolean;
183
+ /** Total completed runs (successful or failed). */
184
+ readonly runs: number;
185
+ /** Total failed runs (threw or timed out). */
186
+ readonly failures: number;
187
+ /** Ticks skipped because the previous run was still in progress. */
188
+ readonly skipped: number;
189
+ /** Epoch ms the most recent run started, or `undefined` if never run. */
190
+ readonly lastRunAt: number | undefined;
191
+ /** Wall-clock duration of the most recent completed run, in ms. */
192
+ readonly lastDurationMs: number | undefined;
193
+ /** The most recent run error, or `undefined`. */
194
+ readonly lastError: unknown;
195
+ /** Epoch ms the next run is scheduled for, or `undefined` if stopped. */
196
+ readonly nextRunAt: number | undefined;
197
+ }
198
+ /**
199
+ * A cron expression compiled into per-field membership sets. Each set lists
200
+ * the allowed numeric values for that field.
201
+ *
202
+ * @since 0.37.0
203
+ */
204
+ export interface CronFields {
205
+ /** Allowed minutes (0–59). */
206
+ readonly minute: ReadonlySet<number>;
207
+ /** Allowed hours (0–23). */
208
+ readonly hour: ReadonlySet<number>;
209
+ /** Allowed days of month (1–31). */
210
+ readonly dayOfMonth: ReadonlySet<number>;
211
+ /** Allowed months (1–12). */
212
+ readonly month: ReadonlySet<number>;
213
+ /** Allowed days of week (0–6, Sunday = 0). */
214
+ readonly dayOfWeek: ReadonlySet<number>;
215
+ /** `true` when day-of-month was restricted (not `*`). */
216
+ readonly domRestricted: boolean;
217
+ /** `true` when day-of-week was restricted (not `*`). */
218
+ readonly dowRestricted: boolean;
219
+ }
220
+ /**
221
+ * Parse a 5-field cron expression (or a named alias) into a {@link CronFields}
222
+ * matcher.
223
+ *
224
+ * Supported syntax per field: `*`, lists (`1,2,3`), ranges (`1-5`), steps
225
+ * (`*\/5`, `1-10/2`), and case-insensitive month (`JAN`–`DEC`) / day
226
+ * (`SUN`–`SAT`) names. Day-of-week accepts both `0` and `7` for Sunday.
227
+ *
228
+ * @param expression - A cron expression or alias.
229
+ * @returns The compiled field sets.
230
+ * @throws {@link CronParseError} if the expression is malformed.
231
+ * @since 0.37.0
232
+ */
233
+ export declare function parseCron(expression: string): CronFields;
234
+ /**
235
+ * Compute the next instant (strictly after `after`) that a cron expression
236
+ * matches, evaluated in `timeZone` (UTC by default).
237
+ *
238
+ * @param expression - A compiled {@link CronFields} or a raw cron expression.
239
+ * @param after - The instant to search after. Defaults to now.
240
+ * @param timeZone - IANA timezone the expression is evaluated in.
241
+ * @returns The next matching `Date`.
242
+ * @throws {@link CronParseError} if no match occurs within five years
243
+ * (an unsatisfiable expression).
244
+ * @since 0.37.0
245
+ */
246
+ export declare function nextCronRun(expression: string | CronFields, after?: Date, timeZone?: string): Date;
247
+ /**
248
+ * An in-process task scheduler with cron / interval schedules, single-flight
249
+ * overlap protection, and graceful shutdown. See the module overview for the
250
+ * design rationale.
251
+ *
252
+ * @since 0.37.0
253
+ */
254
+ export declare class Scheduler {
255
+ #private;
256
+ constructor(options?: SchedulerOptions);
257
+ /** `true` once {@link start} has been called and {@link stop} has not. */
258
+ get running(): boolean;
259
+ /** The number of registered tasks. */
260
+ get size(): number;
261
+ /**
262
+ * Register a task. May be called before or after {@link start}; tasks added
263
+ * after start are scheduled immediately.
264
+ *
265
+ * @param def - The task definition. Exactly one of `intervalMs` / `cron`.
266
+ * @param handler - The function to run on each tick.
267
+ * @returns This scheduler, for chaining.
268
+ * @throws {RangeError} on invalid options (bad name, both/neither schedule,
269
+ * non-positive interval, negative timeout, duplicate name).
270
+ * @throws {@link CronParseError} if a `cron` expression is malformed.
271
+ */
272
+ define(def: TaskDefinition, handler: TaskHandler): this;
273
+ /**
274
+ * Start the scheduler. Idempotent: a second call is a no-op. Each task is
275
+ * armed for its next run (or run immediately when `runOnStart` is set).
276
+ *
277
+ * @returns This scheduler, for chaining.
278
+ */
279
+ start(): this;
280
+ /**
281
+ * Stop the scheduler gracefully. Clears every pending timer so no new runs
282
+ * start, then waits up to `graceMs` for in-flight runs to finish; any run
283
+ * still going when the grace period elapses has its {@link AbortSignal}
284
+ * aborted. Idempotent.
285
+ *
286
+ * @param graceMs - Milliseconds to wait for in-flight runs. Defaults to 5000.
287
+ * @returns A promise that resolves once all runs have settled (or been
288
+ * aborted and settled).
289
+ */
290
+ stop(graceMs?: number): Promise<void>;
291
+ /**
292
+ * Trigger a task immediately, out of band, respecting the single-flight
293
+ * guarantee (a manual run is skipped if the task is already running). The
294
+ * task's normal schedule is unaffected.
295
+ *
296
+ * @param name - The task name.
297
+ * @returns A promise that resolves when the manual run settles, with `true`
298
+ * if it ran or `false` if it was skipped because a run was in progress.
299
+ * @throws {RangeError} if no task with that name exists.
300
+ */
301
+ runNow(name: string): Promise<boolean>;
302
+ /**
303
+ * Read a snapshot of a task's execution statistics.
304
+ *
305
+ * @param name - The task name.
306
+ * @returns The {@link TaskState}, or `undefined` if no such task exists.
307
+ */
308
+ getState(name: string): TaskState | undefined;
309
+ /**
310
+ * List execution-statistics snapshots for every registered task.
311
+ *
312
+ * @returns A snapshot array in definition order.
313
+ */
314
+ list(): readonly TaskState[];
315
+ }