@rebasepro/server 0.22.0 → 0.24.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 (211) hide show
  1. package/README.md +1 -1
  2. package/bin/rebase-server.js +4 -2
  3. package/dist/{GCSStorageController-CLIJXwGS.js → GCSStorageController-BSiP1c-f.js} +57 -29
  4. package/dist/GCSStorageController-BSiP1c-f.js.map +1 -0
  5. package/dist/{S3StorageController-Dcuf8lMA.js → S3StorageController-CAwFRgjV.js} +19 -7
  6. package/dist/S3StorageController-CAwFRgjV.js.map +1 -0
  7. package/dist/api/ast-schema-editor.d.ts +127 -1
  8. package/dist/api/errors.d.ts +9 -0
  9. package/dist/api/live-schema-routes.d.ts +52 -8
  10. package/dist/api/logs-routes.d.ts +39 -1
  11. package/dist/api/openapi-generator.d.ts +17 -0
  12. package/dist/api/rest/api-generator.d.ts +158 -42
  13. package/dist/api/rest/auth-collection-writes.d.ts +85 -0
  14. package/dist/api/rest/field-access-query.d.ts +6 -2
  15. package/dist/api/rest/idempotency.d.ts +7 -1
  16. package/dist/api/rest/nested-write-access.d.ts +46 -0
  17. package/dist/api/rest/write-validation.d.ts +34 -2
  18. package/dist/api/types.d.ts +17 -1
  19. package/dist/{ast-schema-editor-CslO8Oje.js → ast-schema-editor-CWqS_sLJ.js} +411 -13
  20. package/dist/ast-schema-editor-CWqS_sLJ.js.map +1 -0
  21. package/dist/auth/access.d.ts +105 -0
  22. package/dist/auth/adapter-middleware.d.ts +2 -1
  23. package/dist/auth/address-ownership.d.ts +68 -0
  24. package/dist/auth/admin-roles-route.d.ts +4 -2
  25. package/dist/auth/admin-roles.d.ts +17 -20
  26. package/dist/auth/admin-user-ops.d.ts +35 -2
  27. package/dist/auth/admin-users-route.d.ts +1 -0
  28. package/dist/auth/api-keys/api-key-middleware.d.ts +56 -55
  29. package/dist/auth/api-keys/api-key-routes.d.ts +41 -11
  30. package/dist/auth/api-keys/api-key-store.d.ts +31 -8
  31. package/dist/auth/api-keys/api-key-types.d.ts +14 -16
  32. package/dist/auth/api-keys/http-operation.d.ts +19 -0
  33. package/dist/auth/api-keys/index.d.ts +11 -11
  34. package/dist/auth/api-keys/key-grant.d.ts +41 -0
  35. package/dist/auth/api-keys/legacy-permissions.d.ts +33 -0
  36. package/dist/auth/auth-hooks.d.ts +50 -7
  37. package/dist/auth/builtin-auth-adapter.d.ts +8 -0
  38. package/dist/auth/captcha.d.ts +5 -0
  39. package/dist/auth/cookie-utils.d.ts +7 -0
  40. package/dist/auth/deliverable-address.d.ts +6 -0
  41. package/dist/auth/email-change-routes.d.ts +41 -0
  42. package/dist/auth/expired-token-sweep.d.ts +67 -0
  43. package/dist/auth/impersonation.d.ts +110 -0
  44. package/dist/auth/index.d.ts +4 -2
  45. package/dist/auth/interfaces.d.ts +146 -65
  46. package/dist/auth/jwt.d.ts +66 -3
  47. package/dist/auth/magic-link-routes.d.ts +2 -6
  48. package/dist/auth/mfa-routes.d.ts +2 -9
  49. package/dist/auth/middleware.d.ts +17 -5
  50. package/dist/auth/oauth-signin-policy.d.ts +25 -8
  51. package/dist/auth/otp-routes.d.ts +2 -6
  52. package/dist/auth/passwordless-signup.d.ts +27 -0
  53. package/dist/auth/platform-token.d.ts +122 -0
  54. package/dist/auth/rate-limiter.d.ts +72 -1
  55. package/dist/auth/routes.d.ts +45 -0
  56. package/dist/auth/scope-routes.d.ts +22 -0
  57. package/dist/auth/session-routes.d.ts +18 -6
  58. package/dist/auth/token-revocation.d.ts +53 -1
  59. package/dist/auth/verify-credential.d.ts +28 -0
  60. package/dist/{auth-CCDpk2rn.js → auth-DMLngxn_.js} +2712 -711
  61. package/dist/auth-DMLngxn_.js.map +1 -0
  62. package/dist/backend-DTAOsLQc.js +30 -0
  63. package/dist/backend-DTAOsLQc.js.map +1 -0
  64. package/dist/backup/backup-common.d.ts +29 -0
  65. package/dist/backup/backup-routes.d.ts +24 -4
  66. package/dist/backup/backup-schedule.d.ts +33 -0
  67. package/dist/backup/backup-storage.d.ts +14 -0
  68. package/dist/backup/index.d.ts +2 -0
  69. package/dist/backup-CN0s50D2.js +444 -0
  70. package/dist/backup-CN0s50D2.js.map +1 -0
  71. package/dist/boot/bundle.d.ts +19 -0
  72. package/dist/boot/driver.d.ts +10 -0
  73. package/dist/boot/env.d.ts +51 -6
  74. package/dist/boot/fetch-bundle.d.ts +18 -1
  75. package/dist/boot/rls-audit-option.d.ts +26 -0
  76. package/dist/boot/security-headers.d.ts +26 -0
  77. package/dist/boot/sources.d.ts +1 -0
  78. package/dist/boot/static-routing.d.ts +56 -0
  79. package/dist/collection_patch-BRu-BvDv.js +472 -0
  80. package/dist/collection_patch-BRu-BvDv.js.map +1 -0
  81. package/dist/{contract-routes-eLxV0le1.js → contract-routes-fz8i4pxs.js} +17 -4
  82. package/dist/contract-routes-fz8i4pxs.js.map +1 -0
  83. package/dist/cron/cron-routes.d.ts +7 -2
  84. package/dist/cron/cron-scheduler.d.ts +146 -21
  85. package/dist/cron/cron-store.d.ts +76 -8
  86. package/dist/cron/index.d.ts +1 -1
  87. package/dist/{cron-loader-CQjvjpEw.js → cron-loader-CwaANlOG.js} +4 -4
  88. package/dist/cron-loader-CwaANlOG.js.map +1 -0
  89. package/dist/cron-routes-Bc-SB0Se.js +96 -0
  90. package/dist/cron-routes-Bc-SB0Se.js.map +1 -0
  91. package/dist/{cron-scheduler-COPQxlEq.js → cron-scheduler-CYQgco86.js} +427 -83
  92. package/dist/cron-scheduler-CYQgco86.js.map +1 -0
  93. package/dist/{cron-store-BYGZFNWk.js → cron-store-D2Q9-Aco.js} +139 -23
  94. package/dist/cron-store-D2Q9-Aco.js.map +1 -0
  95. package/dist/{ddl-bootstrap-CfNvxMuK.js → ddl-bootstrap-BaqMSa4Y.js} +3 -26
  96. package/dist/ddl-bootstrap-BaqMSa4Y.js.map +1 -0
  97. package/dist/email/index.d.ts +2 -2
  98. package/dist/email/link-base.d.ts +5 -4
  99. package/dist/email/smtp-email-service.d.ts +13 -1
  100. package/dist/email/templates.d.ts +31 -0
  101. package/dist/email/types.d.ts +29 -2
  102. package/dist/env.d.ts +25 -7
  103. package/dist/{errors-DWsX4yTd.js → errors-D6_y86c5.js} +102 -8
  104. package/dist/errors-D6_y86c5.js.map +1 -0
  105. package/dist/{function-loader-xnbDAPfa.js → function-loader-D7o5Epjj.js} +2 -2
  106. package/dist/{function-loader-xnbDAPfa.js.map → function-loader-D7o5Epjj.js.map} +1 -1
  107. package/dist/{function-routes-Chet4-lB.js → function-routes-CaNG4waN.js} +24 -12
  108. package/dist/function-routes-CaNG4waN.js.map +1 -0
  109. package/dist/functions/context.d.ts +17 -6
  110. package/dist/functions/guards.d.ts +22 -5
  111. package/dist/functions/index.d.ts +2 -2
  112. package/dist/functions/index.js +90 -36
  113. package/dist/functions/index.js.map +1 -1
  114. package/dist/{history-recorder-BQmB0P_j.js → history-recorder-Nr8zLvoU.js} +9 -7
  115. package/dist/history-recorder-Nr8zLvoU.js.map +1 -0
  116. package/dist/{history-store-CetkrBBD.js → history-store-rcAm_xFR.js} +2 -2
  117. package/dist/{history-store-CetkrBBD.js.map → history-store-rcAm_xFR.js.map} +1 -1
  118. package/dist/index.d.ts +14 -4
  119. package/dist/index.es.js +5733 -1551
  120. package/dist/index.es.js.map +1 -1
  121. package/dist/init/docs.d.ts +5 -2
  122. package/dist/init/health.d.ts +17 -2
  123. package/dist/init/shutdown.d.ts +18 -3
  124. package/dist/init.d.ts +54 -0
  125. package/dist/jobs/index.d.ts +2 -2
  126. package/dist/jobs/job-queue.d.ts +23 -2
  127. package/dist/jobs/job-store.d.ts +37 -5
  128. package/dist/jobs/types.d.ts +8 -6
  129. package/dist/{jobs-Bjr8DZAi.js → jobs-DqYNfquG.js} +306 -167
  130. package/dist/jobs-DqYNfquG.js.map +1 -0
  131. package/dist/{jwt-C4OW-DNq.js → jwt-R6bSPMjk.js} +114 -38
  132. package/dist/{jwt-C4OW-DNq.js.map → jwt-R6bSPMjk.js.map} +1 -1
  133. package/dist/{keys-Qfc4XieN.js → keys-GAVZqbqx.js} +18 -17
  134. package/dist/{keys-Qfc4XieN.js.map → keys-GAVZqbqx.js.map} +1 -1
  135. package/dist/{logger-DO2PZc4i.js → logger-D-S-hO5e.js} +26 -3
  136. package/dist/logger-D-S-hO5e.js.map +1 -0
  137. package/dist/{logs-routes-3EEzPjhl.js → logs-routes-DAdv37GI.js} +54 -11
  138. package/dist/logs-routes-DAdv37GI.js.map +1 -0
  139. package/dist/mcp/consent-page.d.ts +1 -1
  140. package/dist/mcp/mcp-routes.d.ts +45 -2
  141. package/dist/mcp/mcp-tools.d.ts +22 -10
  142. package/dist/mcp/oauth-metadata.d.ts +21 -16
  143. package/dist/mcp/oauth-routes.d.ts +34 -1
  144. package/dist/mcp/oauth-store.d.ts +29 -13
  145. package/dist/metrics/history-recorder.d.ts +1 -1
  146. package/dist/{openapi-generator-D8uFz-LW.js → openapi-generator-DAq_XVDu.js} +135 -22
  147. package/dist/openapi-generator-DAq_XVDu.js.map +1 -0
  148. package/dist/{proxy-Czngl3p9.js → proxy-qRlqeUmO.js} +2 -2
  149. package/dist/{proxy-Czngl3p9.js.map → proxy-qRlqeUmO.js.map} +1 -1
  150. package/dist/{query-parser-BleZmY18.js → query-parser-BgiKJKvc.js} +41 -82
  151. package/dist/query-parser-BgiKJKvc.js.map +1 -0
  152. package/dist/{request-timeout-C_4C2BeR.js → request-timeout-DgH7j8qO.js} +3 -3
  153. package/dist/{request-timeout-C_4C2BeR.js.map → request-timeout-DgH7j8qO.js.map} +1 -1
  154. package/dist/rls-audit/index.d.ts +4 -0
  155. package/dist/schema-edit/apply-schema-change.d.ts +63 -3
  156. package/dist/schema-edit/project-root.d.ts +3 -2
  157. package/dist/schema-edit/remote-source.d.ts +9 -4
  158. package/dist/{schema-editor-routes-DdLihzp0.js → schema-editor-routes-oIyuWl3L.js} +12 -7
  159. package/dist/schema-editor-routes-oIyuWl3L.js.map +1 -0
  160. package/dist/serve-spa.d.ts +58 -0
  161. package/dist/services/routed-realtime-service.d.ts +11 -0
  162. package/dist/soft-delete-params-BWPilMPF.js +59 -0
  163. package/dist/soft-delete-params-BWPilMPF.js.map +1 -0
  164. package/dist/{src-Br6ARbs6.js → src-CatHFUym.js} +439 -45
  165. package/dist/src-CatHFUym.js.map +1 -0
  166. package/dist/{src-1vL-I1Po.js → src-I3aG1PcY.js} +371 -81
  167. package/dist/src-I3aG1PcY.js.map +1 -0
  168. package/dist/storage/GCSStorageController.d.ts +13 -1
  169. package/dist/storage/LocalStorageController.d.ts +2 -0
  170. package/dist/storage/S3StorageController.d.ts +2 -0
  171. package/dist/storage/index.d.ts +2 -2
  172. package/dist/storage/keys.d.ts +12 -0
  173. package/dist/storage/property-limits.d.ts +41 -6
  174. package/dist/storage/rendition-cache.d.ts +11 -1
  175. package/dist/storage/request-keys.d.ts +82 -0
  176. package/dist/storage/requested-object.d.ts +74 -0
  177. package/dist/storage/routes.d.ts +36 -18
  178. package/dist/storage/tus-handler.d.ts +30 -5
  179. package/dist/storage/types.d.ts +36 -1
  180. package/dist/types-BfKcm9do.js.map +1 -1
  181. package/dist/utils/logger.d.ts +12 -0
  182. package/package.json +9 -9
  183. package/dist/GCSStorageController-CLIJXwGS.js.map +0 -1
  184. package/dist/S3StorageController-Dcuf8lMA.js.map +0 -1
  185. package/dist/admin-roles-vYdp_Pil.js +0 -36
  186. package/dist/admin-roles-vYdp_Pil.js.map +0 -1
  187. package/dist/admin_block-DxKLmdiv.js +0 -206
  188. package/dist/admin_block-DxKLmdiv.js.map +0 -1
  189. package/dist/ast-schema-editor-CslO8Oje.js.map +0 -1
  190. package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
  191. package/dist/auth-CCDpk2rn.js.map +0 -1
  192. package/dist/backup-DzI9jLwc.js +0 -192
  193. package/dist/backup-DzI9jLwc.js.map +0 -1
  194. package/dist/contract-routes-eLxV0le1.js.map +0 -1
  195. package/dist/cron-loader-CQjvjpEw.js.map +0 -1
  196. package/dist/cron-routes-B7CRGfiq.js +0 -72
  197. package/dist/cron-routes-B7CRGfiq.js.map +0 -1
  198. package/dist/cron-scheduler-COPQxlEq.js.map +0 -1
  199. package/dist/cron-store-BYGZFNWk.js.map +0 -1
  200. package/dist/ddl-bootstrap-CfNvxMuK.js.map +0 -1
  201. package/dist/errors-DWsX4yTd.js.map +0 -1
  202. package/dist/function-routes-Chet4-lB.js.map +0 -1
  203. package/dist/history-recorder-BQmB0P_j.js.map +0 -1
  204. package/dist/jobs-Bjr8DZAi.js.map +0 -1
  205. package/dist/logger-DO2PZc4i.js.map +0 -1
  206. package/dist/logs-routes-3EEzPjhl.js.map +0 -1
  207. package/dist/openapi-generator-D8uFz-LW.js.map +0 -1
  208. package/dist/query-parser-BleZmY18.js.map +0 -1
  209. package/dist/schema-editor-routes-DdLihzp0.js.map +0 -1
  210. package/dist/src-1vL-I1Po.js.map +0 -1
  211. package/dist/src-Br6ARbs6.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { Hono } from "hono";
1
+ import { Hono, type MiddlewareHandler } from "hono";
2
2
  import { CollectionConfig } from "@rebasepro/types";
3
3
  import { HonoEnv } from "../api/types.js";
4
4
  /**
@@ -9,5 +9,8 @@ import { HonoEnv } from "../api/types.js";
9
9
  * active collection. A direct-transport collection is served by the client
10
10
  * against its own backend; documenting it here publishes a full set of CRUD
11
11
  * paths that 404, with a Try-It button next to each.
12
+ *
13
+ * `adminGate` is the runtime's admin gate — the middlewares every admin surface
14
+ * sits behind — for the spec when it is not public.
12
15
  */
13
- export declare function mountOpenApiDocs(app: Hono<HonoEnv>, basePath: string, enableSwagger: boolean | undefined, serverCollections: CollectionConfig[], requireAuth: boolean): Promise<void>;
16
+ export declare function mountOpenApiDocs(app: Hono<HonoEnv>, basePath: string, enableSwagger: boolean | undefined, serverCollections: CollectionConfig[], requireAuth: boolean, adminGate: MiddlewareHandler<HonoEnv>[]): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { AuthSchemaHealth, DataDriver, HealthCheckResult } from "@rebasepro/types";
1
+ import { AuthSchemaHealth, DataDriver, HealthCheckResult, RealtimeProvider } from "@rebasepro/types";
2
2
  /**
3
3
  * @param defaultDriver — probed for basic database reachability.
4
4
  * @param authSchemaCheck — optional; asserts the auth schema is one this
@@ -8,4 +8,19 @@ import { AuthSchemaHealth, DataDriver, HealthCheckResult } from "@rebasepro/type
8
8
  * check. Reporting that as healthy is what lets an orchestrator keep routing
9
9
  * traffic to a server that cannot authenticate anyone.
10
10
  */
11
- export declare function createHealthCheck(defaultDriver: DataDriver, authSchemaCheck?: () => Promise<AuthSchemaHealth>): () => Promise<HealthCheckResult>;
11
+ /**
12
+ * How long a realtime LISTEN connection may be down before `/health` says so.
13
+ *
14
+ * A dropped connection is replaced within seconds, and a pod that reports
15
+ * itself degraded over that would shed traffic for nothing. One still down
16
+ * after this is not reconnecting: every external and cross-instance change is
17
+ * being lost while writes through this pod still look live, which is the
18
+ * failure an orchestrator should route around.
19
+ */
20
+ export declare const REALTIME_LISTENER_GRACE_MS = 60000;
21
+ /**
22
+ * @param realtimeProviders — optional; each reports the LISTEN connections it
23
+ * depends on. One down past {@link REALTIME_LISTENER_GRACE_MS} makes the
24
+ * check unhealthy; one down for less is reported but not failed on.
25
+ */
26
+ export declare function createHealthCheck(defaultDriver: DataDriver, authSchemaCheck?: () => Promise<AuthSchemaHealth>, realtimeProviders?: RealtimeProvider[]): () => Promise<HealthCheckResult>;
@@ -2,18 +2,31 @@ import { Server } from "http";
2
2
  import { RealtimeProvider } from "@rebasepro/types";
3
3
  interface ShutdownConfig {
4
4
  server: Server;
5
+ /** Structural, for the same no-circular-imports reason as the backend below. */
5
6
  cronScheduler?: {
6
- stop(): void;
7
+ stop(timeoutMs?: number): Promise<void> | void;
7
8
  };
8
9
  /** Structural, for the same no-circular-imports reason as the backend below. */
9
10
  jobQueue?: {
10
- stop(): Promise<void>;
11
+ stop(timeoutMs?: number): Promise<void>;
11
12
  };
12
13
  /** Structural, same reason. */
13
14
  rlsAudit?: {
14
15
  stop(): void;
15
16
  };
17
+ /** The expired-token sweep's timer. */
18
+ authTokenSweep?: {
19
+ stop(): void;
20
+ };
21
+ /** The stop `MetricsHistory.start()` returned: its interval writes to the pool. */
22
+ stopMetricsSampler?: () => void;
16
23
  realtimeServices: Record<string, RealtimeProvider>;
24
+ /**
25
+ * End the responses that stay open until their client leaves — the Logs
26
+ * Explorer's SSE tail. `server.close()` waits for every open connection, so
27
+ * one of these left open held the shutdown to its force timeout.
28
+ */
29
+ closeLongLivedResponses?: () => void;
17
30
  }
18
31
  /**
19
32
  * Minimal structural view of the backend instance needed by
@@ -32,7 +45,9 @@ export interface ShutdownHandlerOptions {
32
45
  /**
33
46
  * Hard force-exit timeout in milliseconds. If the shutdown sequence
34
47
  * (drain + cleanup) has not completed by then, the process exits with
35
- * code 1. Also passed to `backend.shutdown()` as its drain timeout.
48
+ * code 1. `backend.shutdown()` is given this budget less a reserve for
49
+ * `onCleanup` (the smaller of 2s and a fifth of it), so a drain the
50
+ * backend has to force still ends in the pool close.
36
51
  *
37
52
  * @default 15000
38
53
  */
package/dist/init.d.ts CHANGED
@@ -285,6 +285,49 @@ export interface RebaseAuthConfig {
285
285
  * auth endpoints, and CORS must allow credentials (no `origin: "*"`).
286
286
  */
287
287
  cookieAuth?: import("./auth/index.js").CookieAuthConfig;
288
+ /**
289
+ * Refuse password sign-in until the account's email address is verified,
290
+ * and make registration confirm-first. Off by default.
291
+ *
292
+ * Off, `POST /auth/register` signs the new account in and mails it a
293
+ * verification link; an address that already has an account is answered
294
+ * `409 EMAIL_EXISTS`. On, register signs nobody in and answers the same
295
+ * "check your inbox" whether or not the address has an account (an
296
+ * unconfirmed one is mailed its link again), and `POST /auth/login`
297
+ * answers `403 EMAIL_NOT_CONFIRMED` for an unverified account —
298
+ * only once the password is right. Following the link with the password completes
299
+ * the sign-up (`POST /auth/verify-email`).
300
+ *
301
+ * Needs email: the boot refuses it without, since nobody could ever
302
+ * confirm. Set by `AUTH_REQUIRE_EMAIL_VERIFICATION=true`.
303
+ */
304
+ requireEmailVerification?: boolean;
305
+ /**
306
+ * How long a refresh token that was rotated away still mints a sibling of
307
+ * the same session, in seconds. Default 10 (GoTrue's
308
+ * `refresh_token_reuse_interval`): a client that lost a refresh answer —
309
+ * a deploy, a suspended laptop, two tabs at once — is not signed out.
310
+ */
311
+ refreshTokenReuseIntervalSeconds?: number;
312
+ /**
313
+ * What a refresh token presented *after* that window does. Default
314
+ * `"reject"`: the request is refused and logged, and the session stands,
315
+ * because its live token is still good — which also means a thief who
316
+ * refreshed first keeps it. `"revoke-session"` ends the whole sign-in on
317
+ * such a replay, thief and owner alike, as GoTrue does: the owner signs
318
+ * in again. Set by `AUTH_REFRESH_TOKEN_REUSE`; anything else fails the boot.
319
+ */
320
+ refreshTokenReuse?: "reject" | "revoke-session";
321
+ /**
322
+ * Let a magic-link or email-code request for an address with no account
323
+ * create one — passwordless sign-up — while registration is open
324
+ * (`allowRegistration`, not `disableSelfRegistration`). The account has no
325
+ * password and is unverified until the link or code is used, which proves
326
+ * the address and signs it in. Off by default, so those requests answer an
327
+ * unknown address the same as a known one and create nothing. Set by
328
+ * `AUTH_MAGIC_LINK_CREATES_USERS`.
329
+ */
330
+ magicLinkCreatesUsers?: boolean;
288
331
  }
289
332
  /** @see RebaseBackendConfig.baas */
290
333
  export interface BaasOptions {
@@ -502,6 +545,17 @@ export interface RebaseBackendConfig {
502
545
  * behind. Set a lifecycle rule on `_rebase/renditions/` to collect them.
503
546
  */
504
547
  storageRenditionCache?: import("./storage/rendition-cache.js").RenditionCacheConfig;
548
+ /**
549
+ * How long a private file's download URL works, in seconds — the lifetime
550
+ * of the token `GET /storage/metadata/*` mints. Default 300; at most a week.
551
+ * `STORAGE_DOWNLOAD_TOKEN_TTL` on a bundle deployment.
552
+ *
553
+ * Five minutes renders a page; a private `<video>` that plays longer asks
554
+ * for its next range with an expired token. The token travels in the URL,
555
+ * which is the reason for the ceiling: an object that must be linked
556
+ * indefinitely belongs under the public prefix instead.
557
+ */
558
+ storageDownloadTokenTtl?: number;
505
559
  /**
506
560
  * Run something when an object lands, or when one goes.
507
561
  *
@@ -1,5 +1,5 @@
1
1
  export { createJobStore } from "./job-store.js";
2
- export type { JobStore } from "./job-store.js";
3
- export { createJobQueue, defaultBackoff } from "./job-queue.js";
2
+ export type { JobClaim, JobStore } from "./job-store.js";
3
+ export { createJobQueue, defaultBackoff, PermanentJobError } from "./job-queue.js";
4
4
  export type { JobQueue } from "./job-queue.js";
5
5
  export type { EnqueueOptions, JobContext, JobHandler, JobQueueClient, JobQueueOptions, JobRecord, JobStatus } from "./types.js";
@@ -2,10 +2,31 @@ import type { JobStore } from "./job-store.js";
2
2
  import type { JobHandler, JobQueueClient, JobQueueOptions } from "./types.js";
3
3
  /** 1s, 5s, 25s, 125s … capped at an hour. */
4
4
  export declare function defaultBackoff(attempt: number): number;
5
+ /**
6
+ * Thrown by a handler for a failure no retry can change — a destination that
7
+ * is refused, a receiver that redirects. The job is dead-lettered on this
8
+ * attempt, with the message as its `last_error`, instead of spending its
9
+ * remaining attempts proving the same thing. Returning normally is not the
10
+ * alternative: that records the job as succeeded.
11
+ */
12
+ export declare class PermanentJobError extends Error {
13
+ constructor(message: string, options?: {
14
+ cause?: unknown;
15
+ });
16
+ }
5
17
  export interface JobQueue extends JobQueueClient {
6
18
  start(): void;
7
- stop(): Promise<void>;
8
- /** Run one poll's worth of work and return how many jobs ran. For tests and for `/jobs/drain`. */
19
+ /**
20
+ * Stop claiming, and wait for the jobs in flight — for at most `timeoutMs`
21
+ * when given. A job still running when the budget runs out keeps its claim
22
+ * and is recovered by the visibility timeout; waiting on it without a bound
23
+ * would let one handler that never settles hold the whole shutdown.
24
+ */
25
+ stop(timeoutMs?: number): Promise<void>;
26
+ /**
27
+ * Claim what fits in the free slots, run it, and resolve with how many jobs
28
+ * ran. For tests and for `/jobs/drain`; not meant to run beside `start()`.
29
+ */
9
30
  runOnce(): Promise<number>;
10
31
  /** Registered after construction — how `tasks` from config and internal producers meet. */
11
32
  register<P = unknown>(task: string, handler: JobHandler<P>): void;
@@ -1,5 +1,21 @@
1
1
  import type { DataDriver } from "@rebasepro/types";
2
2
  import type { JobRecord } from "./types.js";
3
+ /**
4
+ * Which claim an outcome belongs to: the worker holding the job, and the
5
+ * attempt that claim spent.
6
+ *
7
+ * The visibility timeout hands a job to a second worker when the first stops
8
+ * renewing its claim, and the first may still be running — slow, not dead. Its
9
+ * outcome is then about an attempt the table has moved past, and writing it
10
+ * would mark a running job `succeeded`, or send a finished one back to
11
+ * `pending` for another run. Both writes therefore apply only while the claim
12
+ * they name still holds the row. The attempt is part of the identity because a
13
+ * worker's own reaper can hand a job back to the same worker in another slot.
14
+ */
15
+ export interface JobClaim {
16
+ workerId: string;
17
+ attempt: number;
18
+ }
3
19
  export interface JobStore {
4
20
  ensureTable(): Promise<void>;
5
21
  /** Returns the new job's id, or `null` if an idempotency key matched unfinished work. */
@@ -10,11 +26,27 @@ export interface JobStore {
10
26
  maxAttempts: number;
11
27
  idempotencyKey?: string;
12
28
  }): Promise<string | null>;
13
- /** Atomically take up to `limit` runnable jobs for this worker. */
14
- claim(limit: number, workerId: string): Promise<JobRecord[]>;
15
- complete(id: string): Promise<void>;
16
- /** Back to `pending` with a later `runAt`, or `failed` when out of attempts. */
17
- fail(id: string, error: string, retryAt: Date | null): Promise<void>;
29
+ /**
30
+ * Atomically take up to `limit` runnable jobs for this worker — only jobs
31
+ * whose task is in `tasks`, when it is given. A worker never claims work it
32
+ * cannot run: a claim spends an attempt, so an instance running older code
33
+ * during a rollout would otherwise dead-letter the newer code's jobs.
34
+ */
35
+ claim(limit: number, workerId: string, tasks?: readonly string[]): Promise<JobRecord[]>;
36
+ /** Mark the job `succeeded` — a no-op once `claim` no longer holds it. */
37
+ complete(id: string, claim: JobClaim): Promise<void>;
38
+ /**
39
+ * Back to `pending` with a later `runAt`, or `failed` when out of attempts —
40
+ * a no-op once `claim` no longer holds it.
41
+ */
42
+ fail(id: string, error: string, retryAt: Date | null, claim: JobClaim): Promise<void>;
43
+ /**
44
+ * Renew `claim` on a job whose handler is still running, so the visibility
45
+ * timeout only ever reclaims a job from a worker that stopped renewing.
46
+ * Resolves with whether the claim still held. A store without it leaves a
47
+ * handler that outlives the timeout to be run a second time beside itself.
48
+ */
49
+ heartbeat?(id: string, claim: JobClaim): Promise<boolean>;
18
50
  /** Return jobs stranded by a worker that died holding them. Resolves with how many. */
19
51
  reapExpired(visibilityTimeoutMs: number): Promise<number>;
20
52
  fetch(id: string): Promise<JobRecord | null>;
@@ -101,14 +101,16 @@ export interface JobQueueOptions {
101
101
  /** How often to look for work when the last look found none. Default 2000ms. */
102
102
  pollIntervalMs?: number;
103
103
  /**
104
- * How long a claimed job may stay claimed before another worker may take
105
- * it. Default 5 minutes.
104
+ * How long a claim may go unrenewed before another worker may take the job.
105
+ * Default 5 minutes.
106
106
  *
107
107
  * This is the only thing that recovers work from a worker that died holding
108
- * it — a `SIGKILL`ed pod cannot release its own claim. It is therefore also
109
- * the interval after which a job that legitimately runs longer than this
110
- * gets a *second* worker running it concurrently, so it must exceed the
111
- * slowest handler.
108
+ * it — a `SIGKILL`ed pod cannot release its own claim. A live worker renews
109
+ * the claim every third of this while the handler runs, so a handler may
110
+ * run longer than it; what it bounds is how long a dead worker's job waits.
111
+ * A worker whose event loop is blocked for longer than this cannot renew,
112
+ * and its job is run again beside it — though only the claim that holds
113
+ * the job when a run finishes gets to record its outcome.
112
114
  */
113
115
  visibilityTimeoutMs?: number;
114
116
  /** Attempts before a job is left `failed`. Default 3. */