@shipfox/api-auth 11.0.0 → 12.2.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 (46) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +19 -19
  4. package/dist/core/errors.d.ts.map +1 -1
  5. package/dist/core/job-lease-token.d.ts +1 -1
  6. package/dist/core/job-lease-token.d.ts.map +1 -1
  7. package/dist/core/job-lease-token.js +1 -1
  8. package/dist/core/job-lease-token.js.map +1 -1
  9. package/dist/core/jwt.d.ts +2 -2
  10. package/dist/core/rate-limit.d.ts.map +1 -1
  11. package/dist/core/runner-session-token.d.ts +1 -1
  12. package/dist/core/runner-session-token.d.ts.map +1 -1
  13. package/dist/core/runner-session-token.js +1 -1
  14. package/dist/core/runner-session-token.js.map +1 -1
  15. package/dist/db/admin-user-moderation.d.ts.map +1 -1
  16. package/dist/db/db.d.ts +16 -16
  17. package/dist/db/rate-limits.d.ts +6 -15
  18. package/dist/db/rate-limits.d.ts.map +1 -1
  19. package/dist/db/rate-limits.js +17 -18
  20. package/dist/db/rate-limits.js.map +1 -1
  21. package/dist/db/refresh-tokens.d.ts +3 -3
  22. package/dist/db/refresh-tokens.js +3 -3
  23. package/dist/db/refresh-tokens.js.map +1 -1
  24. package/dist/db/schema/admin-command-results.d.ts +1 -1
  25. package/dist/db/schema/admin-grants.d.ts +1 -1
  26. package/dist/db/schema/outbox.d.ts +1 -1
  27. package/dist/db/schema/password-resets.d.ts +1 -1
  28. package/dist/db/schema/rate-limits.d.ts +1 -1
  29. package/dist/db/schema/refresh-tokens.d.ts +1 -1
  30. package/dist/db/schema/users.d.ts +2 -2
  31. package/dist/presentation/auth/lease-token-auth.d.ts +1 -1
  32. package/dist/presentation/auth/lease-token-auth.js +1 -1
  33. package/dist/presentation/auth/lease-token-auth.js.map +1 -1
  34. package/dist/presentation/routes/rate-limit.d.ts.map +1 -1
  35. package/dist/presentation/routes/rate-limit.js +32 -53
  36. package/dist/presentation/routes/rate-limit.js.map +1 -1
  37. package/dist/tsconfig.test.tsbuildinfo +1 -1
  38. package/package.json +15 -15
  39. package/src/core/job-lease-token.ts +1 -1
  40. package/src/core/runner-session-token.ts +1 -1
  41. package/src/db/rate-limits.ts +28 -42
  42. package/src/db/refresh-tokens.ts +3 -3
  43. package/src/presentation/auth/lease-token-auth.ts +1 -1
  44. package/src/presentation/routes/rate-limit.ts +31 -64
  45. package/test/routes.ts +6 -2
  46. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 67 files with swc (446.61ms)
2
+ Successfully compiled: 67 files with swc (574.33ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @shipfox/api-auth
2
2
 
3
+ ## 12.2.0
4
+
5
+ ### Patch Changes
6
+
7
+ - @shipfox/node-opentelemetry@0.6.4
8
+ - @shipfox/api-email-challenges@1.1.8
9
+ - @shipfox/node-fastify@0.4.2
10
+ - @shipfox/node-mailer@0.2.5
11
+ - @shipfox/node-module@1.0.6
12
+ - @shipfox/api-auth-context@12.2.0
13
+
14
+ ## 12.0.0
15
+
16
+ ### Patch Changes
17
+
18
+ - f78740d: Remove Unicode dash punctuation from package prose and source comments.
19
+ - 9ebc5b4: Add an authenticated, rate-limited workspace slug availability endpoint.
20
+ - Updated dependencies [f78740d]
21
+ - Updated dependencies [94aba88]
22
+ - Updated dependencies [f13e8bb]
23
+ - Updated dependencies [9ebc5b4]
24
+ - Updated dependencies [34a5639]
25
+ - Updated dependencies [e1efaee]
26
+ - @shipfox/api-auth-dto@12.0.0
27
+ - @shipfox/api-common-dto@12.0.0
28
+ - @shipfox/inter-module@0.2.3
29
+ - @shipfox/node-fastify@0.4.1
30
+ - @shipfox/node-module@1.0.5
31
+ - @shipfox/api-workspaces-dto@12.0.0
32
+ - @shipfox/node-jwt@0.4.0
33
+ - @shipfox/node-postgres@0.5.0
34
+ - @shipfox/node-rate-limit@0.4.0
35
+ - @shipfox/api-auth-context@12.0.0
36
+ - @shipfox/api-email-challenges@1.1.7
37
+ - @shipfox/node-drizzle@0.3.5
38
+ - @shipfox/node-outbox@0.2.6
39
+
3
40
  ## 11.0.0
4
41
 
5
42
  ### Major Changes
package/README.md CHANGED
@@ -60,7 +60,7 @@ Required environment:
60
60
  | `AUTH_JOB_LEASE_TOKEN_EXPIRES_IN` | `90m` | Job lease token lifetime. |
61
61
  | `AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN` | `1h` | Runner session token lifetime. |
62
62
  | `AUTH_REFRESH_TOKEN_EXPIRES_IN_DAYS` | `14` | Refresh token and cookie lifetime. |
63
- | `AUTH_REFRESH_ROTATION_GRACE_SECONDS` | `30` | Grace window for accepting a just-rotated refresh token during concurrent refreshes. |
63
+ | `AUTH_REFRESH_ROTATION_GRACE_SECONDS` | `30` | Grace window for accepting a recently rotated refresh token during concurrent refreshes. |
64
64
  | `AUTH_REFRESH_COOKIE_NAME` | `shipfox_refresh_token` | HTTP cookie name for refresh sessions. |
65
65
  | `AUTH_PASSWORD_ENABLED` | `true` | Enables password and email-verification routes. Set to `false` when another module provides login. Server construction fails if no login method is available. |
66
66
  | `AUTH_SIGNUP_GATE_ENABLED` | `false` | Restricts new account creation to the configured signup email allowlist. Requires `AUTH_SIGNUP_ALLOWED_EMAIL_DOMAINS` or `AUTH_SIGNUP_ALLOWED_EMAILS` to be set. |
@@ -116,16 +116,16 @@ itself.
116
116
  - **Audience:** signed-in users on first-party clients (web app, CLI).
117
117
  - **Grants:** the user's own account routes and, downstream, any route that
118
118
  authorizes against the membership claims. Its scope is "this user, with these
119
- memberships" never a single resource.
119
+ memberships", never a single resource.
120
120
  - **Lifecycle:**
121
- - *Emit* issued on login, signup verification, and password reset.
122
- - *Exchange* sent on each request; a longer-lived refresh session mints a
121
+ - *Emit*: issued on login, signup verification, and password reset.
122
+ - *Exchange*: sent on each request; a longer-lived refresh session mints a
123
123
  fresh token when it expires.
124
- - *Store* held in client memory only, never persisted to disk or local
124
+ - *Store*: held in client memory only, never persisted to disk or local
125
125
  storage. Refresh sessions are stored server-side as hashes, never in raw form.
126
- - *Discard* expires at the configured `exp` time. Revoking the refresh
126
+ - *Discard*: expires at the configured `exp` time. Revoking the refresh
127
127
  session blocks renewal but does not invalidate the issued access token.
128
- - **Tradeoff stale memberships:** because memberships ride in the claims, a
128
+ - **Tradeoff: stale memberships:** because memberships ride in the claims, a
129
129
  membership or workspace-status change only takes effect on the next token
130
130
  issuance or refresh. Accepted because the recommended lifetime is 15 minutes,
131
131
  so the staleness window is bounded and a per-request membership read is avoided.
@@ -137,7 +137,7 @@ The runner exchanges its long-lived registration token at startup, then uses the
137
137
  short-lived session token to claim jobs. Heartbeat and step/report/log operations
138
138
  use the per-job lease token instead.
139
139
 
140
- - **Scope one workspace's runner data plane.** The token names one runner
140
+ - **Scope: one workspace's runner data plane.** The token names one runner
141
141
  session, one workspace, the fixed `workspace` scope, and the session's immutable
142
142
  label set. It can claim jobs for that workspace. It is not a user identity and
143
143
  does not authorize dashboard management routes.
@@ -148,7 +148,7 @@ use the per-job lease token instead.
148
148
  - **Mechanics.** Signed with HMAC-SHA256 using the derived runner-session key
149
149
  and the `runner-session` audience. The default lifetime is `1h`, short enough
150
150
  to bound the residual claim window when a registration token is revoked.
151
- - **Tradeoff no per-session revocation in v1.** Claim stays stateless and does
151
+ - **Tradeoff: no per-session revocation in v1.** Claim stays stateless and does
152
152
  not check the runner session row on each poll. Revoking the registration token
153
153
  blocks new sessions, but an existing session can keep claiming until
154
154
  `AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN` elapses. A job execution claimed before
@@ -163,7 +163,7 @@ A single-job **capability** token, not a session. It is the means by which a
163
163
  runner proves it is the legitimate holder of one specific job while it reports
164
164
  progress and drives that job to completion.
165
165
 
166
- - **Scope exactly one job execution.** The token authorizes action on the
166
+ - **Scope: exactly one job execution.** The token authorizes action on the
167
167
  single job execution it names, for the runner holding it. It is **not** a runner
168
168
  identity, it is **not** workspace-wide, and it is **not** a substitute for the
169
169
  long-lived runner credential used to claim work in the first place. The
@@ -175,9 +175,9 @@ progress and drives that job to completion.
175
175
  - **Trust boundary.** There is exactly one issuer: the scheduling side mints a
176
176
  lease when a runner claims a job execution, next-step re-scopes it to the
177
177
  dispatched step attempt, and heartbeat re-mints the same narrow capability
178
- after server state accepts the heartbeat. Everything
179
- downstream only *verifies* an in-process signature check, with no callback to
180
- the issuer. The runner, and the untrusted agent workload it hosts, never mints
178
+ after server state accepts the heartbeat. Everything downstream only *verifies*
179
+ the token with an in-process signature check. It makes no callback to the
180
+ issuer. The runner, and the untrusted agent workload it hosts, never mints
181
181
  or modifies a token; it only presents the one it was handed.
182
182
  - **Mechanics.** Signed with HMAC-SHA256 using the job-lease key derived from
183
183
  `AUTH_ROOT_KEY`. Its claims name the job, job execution, and surrounding
@@ -187,13 +187,13 @@ progress and drives that job to completion.
187
187
  execution. The root key is supplied through configuration, never embedded in
188
188
  code or committed. The raw token must **never** be written to logs, traces, or
189
189
  error payloads. There is no automatic redaction to fall back on.
190
- - **Defense in depth server state is the final authority.** A valid token is
190
+ - **Defense in depth: server state is the final authority.** A valid token is
191
191
  never sufficient on its own to advance work. On the lease's own request path the
192
192
  gate is server-side step and progression state: a report against a step that has
193
193
  already reached a terminal state (finished, failed, or cancelled) is ignored, so
194
194
  a still-valid token cannot re-drive work that is already done. Job-level
195
195
  finalization is enforced outside the lease path. Cancellation flows the other way
196
- as well the server can ask the runner to stop at any point, and that request
196
+ as well: the server can ask the runner to stop at any point, and that request
197
197
  rides on the response to each heartbeat rather than depending on the token.
198
198
  - **Log append scope.** A step-scoped lease is a signed membership and attempt
199
199
  check for append-log authorization. It is not an active-step proof: until the
@@ -207,7 +207,7 @@ progress and drives that job to completion.
207
207
  other workspaces. If `AUTH_ROOT_KEY` leaks, rotate it. This invalidates every
208
208
  live token and email challenge derived from the old root, including every live
209
209
  lease, and forces fresh claims.
210
- - **Tradeoff no per-token revocation.** A single outstanding lease cannot be
210
+ - **Tradeoff: no per-token revocation.** A single outstanding lease cannot be
211
211
  revoked on its own by token ID. The claiming runner's credential is not
212
212
  re-checked on each request, so a lease for already-claimed work can continue to
213
213
  be renewed by heartbeat until server state cancels, completes, times out, or
@@ -217,7 +217,7 @@ progress and drives that job to completion.
217
217
  - **Guidelines for future changes.** Keep the authority narrow: do not add claims
218
218
  that grant access beyond the single job, keep the lifetime bounded, keep a single
219
219
  issuer, and keep every other side verify-only. If the no-revocation window ever
220
- proves too wide, the right fix is to bind the lease to live runner or job state
220
+ proves too wide, the right fix is to bind the lease to live runner or job state,
221
221
  not to broaden what the token itself can authorize.
222
222
 
223
223
  ## Routes
@@ -433,7 +433,7 @@ unauthorized` error when the verified request context is missing, inconsistent,
433
433
  or has no refresh-session ID. It exposes neither refresh-token material nor
434
434
  storage details.
435
435
 
436
- ## Data Model
436
+ ## Data model
437
437
 
438
438
  The module creates tables with the `auth_` prefix:
439
439
 
@@ -446,7 +446,7 @@ The module creates tables with the `auth_` prefix:
446
446
 
447
447
  Passwords use Argon2id. Password reset tokens and refresh tokens are opaque tokens stored as hashes. Email verification uses the shared email-challenges module.
448
448
 
449
- ## Behavior Notes
449
+ ## Behavior notes
450
450
 
451
451
  - Signup sends an eight-digit verification code and returns the new user with its challenge ID.
452
452
  - Signup gating blocks account creation only. Existing users keep signing in, even when their address is not on the current allowlist.
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,WAAW,EAAE,MAAM;CAIhC;AAED,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,EAAE,EAAE,MAAM;CAIvB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,EAAE,EAAE,MAAM;CAIvB;AAED,qBAAa,gCAAiC,SAAQ,KAAK;;CAK1D;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM;CAIhD;AAED,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,SAAS,EAAE,MAAM;CAI9B;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,KAAK,EAAE,MAAM;CAI1B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;;CAKjD;AAED,qBAAa,qBAAsB,SAAQ,KAAK;;CAK/C;AAED,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,SAAkB,KAAK,EAAE,OAAO,CAAC;gBAErB,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;CAM/C;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC;gBAEpD,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS;CAKnE;AAED,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED,qBAAa,uBAAwB,SAAQ,KAAK;;CAKjD;AAED,qBAAa,4BAA6B,SAAQ,KAAK;;CAKtD;AAED,qBAAa,yBAA0B,SAAQ,KAAK;;CAKnD;AAED,qBAAa,+BAAgC,SAAQ,KAAK;;CAKzD;AAED,qBAAa,6BAA8B,SAAQ,KAAK;;CAKvD;AAED,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,MAAM,CAAC,EAAE,MAAM;CAI5B;AAED,qBAAa,iBAAkB,SAAQ,KAAK;;CAK3C;AAED,qBAAa,qBAAsB,SAAQ,KAAK;;CAK/C;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,WAAW,EAAE,MAAM;CAIhC;AAED,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,WAAW,EAAE,MAAM;CAIhC;AAED,qBAAa,4BAA6B,SAAQ,KAAK;;CAKtD;AAED,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,KAAK,EAAE,MAAM;CAI1B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,EAAE,EAAE,MAAM,EAGrB;CACF;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAG9C;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,SAAS,EAAE,MAAM,EAG5B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,SAAkB,KAAK,EAAE,OAAO,CAAC;IAEjC,YAAY,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAK7C;CACF;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,CAAC;IAEhE,YAAY,WAAW,EAAE,OAAO,uBAAuB,EAAE,SAAS,EAIjE;CACF;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,cAGC;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,cAGC;CACF;AAED,qBAAa,+BAAgC,SAAQ,KAAK;IACxD,cAGC;CACF;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,cAGC;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,MAAM,CAAC,EAAE,MAAM,EAG1B;CACF;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,cAGC;CACF;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,cAGC;CACF;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,YAAY,WAAW,EAAE,MAAM,EAG9B;CACF;AAED,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,cAGC;CACF;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAY,KAAK,EAAE,MAAM,EAGxB;CACF"}
@@ -6,7 +6,7 @@ export declare function jobLeaseParamsFrom(source: JobLeaseParamSource, stepScop
6
6
  currentStepAttempt: number;
7
7
  }): IssueJobLeaseTokenParams;
8
8
  export declare function issueJobLeaseToken(claims: IssueJobLeaseTokenParams): Promise<string>;
9
- /** Returns the claims on success, or `null` for any invalid input never throws. */
9
+ /** Returns the claims on success, or `null` for any invalid input. Never throws. */
10
10
  export declare function verifyJobLeaseToken(token: string): Promise<JobLeaseTokenClaims | null>;
11
11
  export {};
12
12
  //# sourceMappingURL=job-lease-token.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"job-lease-token.d.ts","sourceRoot":"","sources":["../../src/core/job-lease-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAExF,KAAK,mBAAmB,GAAG,IAAI,CAC7B,mBAAmB,EACjB,eAAe,GACf,sBAAsB,GACtB,OAAO,GACP,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,iBAAiB,CACpB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,SAAS,CAAC,EAAE;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAC,GAC9D,wBAAwB,CAW1B;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B1F;AAED,qFAAqF;AACrF,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAc5F"}
1
+ {"version":3,"file":"job-lease-token.d.ts","sourceRoot":"","sources":["../../src/core/job-lease-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAExF,KAAK,mBAAmB,GAAG,IAAI,CAC7B,mBAAmB,EACjB,eAAe,GACf,sBAAsB,GACtB,OAAO,GACP,gBAAgB,GAChB,WAAW,GACX,aAAa,GACb,iBAAiB,CACpB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,mBAAmB,EAC3B,SAAS,CAAC,EAAE;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAA;CAAC,GAC9D,wBAAwB,CAW1B;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0B1F;AAED,oFAAoF;AACpF,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAc5F"}
@@ -40,7 +40,7 @@ export async function issueJobLeaseToken(claims) {
40
40
  recordTokenIssued('job_lease');
41
41
  return token;
42
42
  }
43
- /** Returns the claims on success, or `null` for any invalid input never throws. */ export async function verifyJobLeaseToken(token) {
43
+ /** Returns the claims on success, or `null` for any invalid input. Never throws. */ export async function verifyJobLeaseToken(token) {
44
44
  try {
45
45
  const claims = await verifyHs256({
46
46
  token,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/job-lease-token.ts"],"sourcesContent":["import {\n JOB_LEASE_TOKEN_AUDIENCE,\n type JobLeaseTokenClaims,\n jobLeaseTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {jobLeaseTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueJobLeaseTokenParams = Omit<JobLeaseTokenClaims, 'aud' | 'iat' | 'exp'>;\n\ntype JobLeaseParamSource = Pick<\n JobLeaseTokenClaims,\n | 'workflowRunId'\n | 'workflowRunAttemptId'\n | 'jobId'\n | 'jobExecutionId'\n | 'projectId'\n | 'workspaceId'\n | 'runnerSessionId'\n>;\n\nexport function jobLeaseParamsFrom(\n source: JobLeaseParamSource,\n stepScope?: {currentStepId: string; currentStepAttempt: number},\n): IssueJobLeaseTokenParams {\n return {\n workflowRunId: source.workflowRunId,\n workflowRunAttemptId: source.workflowRunAttemptId,\n jobId: source.jobId,\n jobExecutionId: source.jobExecutionId,\n projectId: source.projectId,\n workspaceId: source.workspaceId,\n runnerSessionId: source.runnerSessionId,\n ...(stepScope ? stepScope : {}),\n };\n}\n\nexport async function issueJobLeaseToken(claims: IssueJobLeaseTokenParams): Promise<string> {\n const token = await signHs256({\n payload: {\n workflowRunId: claims.workflowRunId,\n ...(claims.workflowRunAttempt === undefined\n ? {}\n : {workflowRunAttempt: claims.workflowRunAttempt}),\n workflowRunAttemptId: claims.workflowRunAttemptId,\n jobId: claims.jobId,\n jobExecutionId: claims.jobExecutionId,\n projectId: claims.projectId,\n workspaceId: claims.workspaceId,\n runnerSessionId: claims.runnerSessionId,\n ...(claims.currentStepId && claims.currentStepAttempt !== undefined\n ? {\n currentStepId: claims.currentStepId,\n currentStepAttempt: claims.currentStepAttempt,\n }\n : {}),\n },\n secret: jobLeaseTokenKey(),\n expiresIn: config.AUTH_JOB_LEASE_TOKEN_EXPIRES_IN,\n audience: JOB_LEASE_TOKEN_AUDIENCE,\n });\n recordTokenIssued('job_lease');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input never throws. */\nexport async function verifyJobLeaseToken(token: string): Promise<JobLeaseTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: jobLeaseTokenKey(),\n schema: jobLeaseTokenClaimsSchema,\n audience: JOB_LEASE_TOKEN_AUDIENCE,\n });\n recordTokenVerified('job_lease', 'ok');\n return claims;\n } catch {\n recordTokenVerified('job_lease', 'rejected');\n return null;\n }\n}\n"],"names":["JOB_LEASE_TOKEN_AUDIENCE","jobLeaseTokenClaimsSchema","jobLeaseTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","jobLeaseParamsFrom","source","stepScope","workflowRunId","workflowRunAttemptId","jobId","jobExecutionId","projectId","workspaceId","runnerSessionId","issueJobLeaseToken","claims","token","payload","workflowRunAttempt","undefined","currentStepId","currentStepAttempt","secret","expiresIn","AUTH_JOB_LEASE_TOKEN_EXPIRES_IN","audience","verifyJobLeaseToken","schema"],"mappings":"AAAA,SACEA,wBAAwB,EAExBC,yBAAyB,QACpB,wBAAwB;AAC/B,SAAQC,gBAAgB,QAAO,8BAA8B;AAC7D,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAgBzE,OAAO,SAASC,mBACdC,MAA2B,EAC3BC,SAA+D;IAE/D,OAAO;QACLC,eAAeF,OAAOE,aAAa;QACnCC,sBAAsBH,OAAOG,oBAAoB;QACjDC,OAAOJ,OAAOI,KAAK;QACnBC,gBAAgBL,OAAOK,cAAc;QACrCC,WAAWN,OAAOM,SAAS;QAC3BC,aAAaP,OAAOO,WAAW;QAC/BC,iBAAiBR,OAAOQ,eAAe;QACvC,GAAIP,YAAYA,YAAY,CAAC,CAAC;IAChC;AACF;AAEA,OAAO,eAAeQ,mBAAmBC,MAAgC;IACvE,MAAMC,QAAQ,MAAMjB,UAAU;QAC5BkB,SAAS;YACPV,eAAeQ,OAAOR,aAAa;YACnC,GAAIQ,OAAOG,kBAAkB,KAAKC,YAC9B,CAAC,IACD;gBAACD,oBAAoBH,OAAOG,kBAAkB;YAAA,CAAC;YACnDV,sBAAsBO,OAAOP,oBAAoB;YACjDC,OAAOM,OAAON,KAAK;YACnBC,gBAAgBK,OAAOL,cAAc;YACrCC,WAAWI,OAAOJ,SAAS;YAC3BC,aAAaG,OAAOH,WAAW;YAC/BC,iBAAiBE,OAAOF,eAAe;YACvC,GAAIE,OAAOK,aAAa,IAAIL,OAAOM,kBAAkB,KAAKF,YACtD;gBACEC,eAAeL,OAAOK,aAAa;gBACnCC,oBAAoBN,OAAOM,kBAAkB;YAC/C,IACA,CAAC,CAAC;QACR;QACAC,QAAQxB;QACRyB,WAAWtB,OAAOuB,+BAA+B;QACjDC,UAAU7B;IACZ;IACAM,kBAAkB;IAClB,OAAOc;AACT;AAEA,mFAAmF,GACnF,OAAO,eAAeU,oBAAoBV,KAAa;IACrD,IAAI;QACF,MAAMD,SAAS,MAAMf,YAAY;YAC/BgB;YACAM,QAAQxB;YACR6B,QAAQ9B;YACR4B,UAAU7B;QACZ;QACAO,oBAAoB,aAAa;QACjC,OAAOY;IACT,EAAE,OAAM;QACNZ,oBAAoB,aAAa;QACjC,OAAO;IACT;AACF"}
1
+ {"version":3,"sources":["../../src/core/job-lease-token.ts"],"sourcesContent":["import {\n JOB_LEASE_TOKEN_AUDIENCE,\n type JobLeaseTokenClaims,\n jobLeaseTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {jobLeaseTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueJobLeaseTokenParams = Omit<JobLeaseTokenClaims, 'aud' | 'iat' | 'exp'>;\n\ntype JobLeaseParamSource = Pick<\n JobLeaseTokenClaims,\n | 'workflowRunId'\n | 'workflowRunAttemptId'\n | 'jobId'\n | 'jobExecutionId'\n | 'projectId'\n | 'workspaceId'\n | 'runnerSessionId'\n>;\n\nexport function jobLeaseParamsFrom(\n source: JobLeaseParamSource,\n stepScope?: {currentStepId: string; currentStepAttempt: number},\n): IssueJobLeaseTokenParams {\n return {\n workflowRunId: source.workflowRunId,\n workflowRunAttemptId: source.workflowRunAttemptId,\n jobId: source.jobId,\n jobExecutionId: source.jobExecutionId,\n projectId: source.projectId,\n workspaceId: source.workspaceId,\n runnerSessionId: source.runnerSessionId,\n ...(stepScope ? stepScope : {}),\n };\n}\n\nexport async function issueJobLeaseToken(claims: IssueJobLeaseTokenParams): Promise<string> {\n const token = await signHs256({\n payload: {\n workflowRunId: claims.workflowRunId,\n ...(claims.workflowRunAttempt === undefined\n ? {}\n : {workflowRunAttempt: claims.workflowRunAttempt}),\n workflowRunAttemptId: claims.workflowRunAttemptId,\n jobId: claims.jobId,\n jobExecutionId: claims.jobExecutionId,\n projectId: claims.projectId,\n workspaceId: claims.workspaceId,\n runnerSessionId: claims.runnerSessionId,\n ...(claims.currentStepId && claims.currentStepAttempt !== undefined\n ? {\n currentStepId: claims.currentStepId,\n currentStepAttempt: claims.currentStepAttempt,\n }\n : {}),\n },\n secret: jobLeaseTokenKey(),\n expiresIn: config.AUTH_JOB_LEASE_TOKEN_EXPIRES_IN,\n audience: JOB_LEASE_TOKEN_AUDIENCE,\n });\n recordTokenIssued('job_lease');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input. Never throws. */\nexport async function verifyJobLeaseToken(token: string): Promise<JobLeaseTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: jobLeaseTokenKey(),\n schema: jobLeaseTokenClaimsSchema,\n audience: JOB_LEASE_TOKEN_AUDIENCE,\n });\n recordTokenVerified('job_lease', 'ok');\n return claims;\n } catch {\n recordTokenVerified('job_lease', 'rejected');\n return null;\n }\n}\n"],"names":["JOB_LEASE_TOKEN_AUDIENCE","jobLeaseTokenClaimsSchema","jobLeaseTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","jobLeaseParamsFrom","source","stepScope","workflowRunId","workflowRunAttemptId","jobId","jobExecutionId","projectId","workspaceId","runnerSessionId","issueJobLeaseToken","claims","token","payload","workflowRunAttempt","undefined","currentStepId","currentStepAttempt","secret","expiresIn","AUTH_JOB_LEASE_TOKEN_EXPIRES_IN","audience","verifyJobLeaseToken","schema"],"mappings":"AAAA,SACEA,wBAAwB,EAExBC,yBAAyB,QACpB,wBAAwB;AAC/B,SAAQC,gBAAgB,QAAO,8BAA8B;AAC7D,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAgBzE,OAAO,SAASC,mBACdC,MAA2B,EAC3BC,SAA+D;IAE/D,OAAO;QACLC,eAAeF,OAAOE,aAAa;QACnCC,sBAAsBH,OAAOG,oBAAoB;QACjDC,OAAOJ,OAAOI,KAAK;QACnBC,gBAAgBL,OAAOK,cAAc;QACrCC,WAAWN,OAAOM,SAAS;QAC3BC,aAAaP,OAAOO,WAAW;QAC/BC,iBAAiBR,OAAOQ,eAAe;QACvC,GAAIP,YAAYA,YAAY,CAAC,CAAC;IAChC;AACF;AAEA,OAAO,eAAeQ,mBAAmBC,MAAgC;IACvE,MAAMC,QAAQ,MAAMjB,UAAU;QAC5BkB,SAAS;YACPV,eAAeQ,OAAOR,aAAa;YACnC,GAAIQ,OAAOG,kBAAkB,KAAKC,YAC9B,CAAC,IACD;gBAACD,oBAAoBH,OAAOG,kBAAkB;YAAA,CAAC;YACnDV,sBAAsBO,OAAOP,oBAAoB;YACjDC,OAAOM,OAAON,KAAK;YACnBC,gBAAgBK,OAAOL,cAAc;YACrCC,WAAWI,OAAOJ,SAAS;YAC3BC,aAAaG,OAAOH,WAAW;YAC/BC,iBAAiBE,OAAOF,eAAe;YACvC,GAAIE,OAAOK,aAAa,IAAIL,OAAOM,kBAAkB,KAAKF,YACtD;gBACEC,eAAeL,OAAOK,aAAa;gBACnCC,oBAAoBN,OAAOM,kBAAkB;YAC/C,IACA,CAAC,CAAC;QACR;QACAC,QAAQxB;QACRyB,WAAWtB,OAAOuB,+BAA+B;QACjDC,UAAU7B;IACZ;IACAM,kBAAkB;IAClB,OAAOc;AACT;AAEA,kFAAkF,GAClF,OAAO,eAAeU,oBAAoBV,KAAa;IACrD,IAAI;QACF,MAAMD,SAAS,MAAMf,YAAY;YAC/BgB;YACAM,QAAQxB;YACR6B,QAAQ9B;YACR4B,UAAU7B;QACZ;QACAO,oBAAoB,aAAa;QACjC,OAAOY;IACT,EAAE,OAAM;QACNZ,oBAAoB,aAAa;QACjC,OAAO;IACT;AACF"}
@@ -6,8 +6,8 @@ export declare const tokenMembershipSchema: z.ZodObject<{
6
6
  }>;
7
7
  workspaceStatus: z.ZodDefault<z.ZodEnum<{
8
8
  active: "active";
9
- suspended: "suspended";
10
9
  deleted: "deleted";
10
+ suspended: "suspended";
11
11
  }>>;
12
12
  }, z.core.$strip>;
13
13
  export type TokenMembership = z.infer<typeof tokenMembershipSchema>;
@@ -23,8 +23,8 @@ export declare const userTokenClaimsSchema: z.ZodObject<{
23
23
  }>;
24
24
  workspaceStatus: z.ZodDefault<z.ZodEnum<{
25
25
  active: "active";
26
- suspended: "suspended";
27
26
  deleted: "deleted";
27
+ suspended: "suspended";
28
28
  }>>;
29
29
  }, z.core.$strip>>;
30
30
  iat: z.ZodNumber;
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/core/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,sBAAsB,EACtB,KAAK,eAAe,EACpB,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGxB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAElD,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACnE,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,qBAAa,0BAA2B,SAAQ,sBAAsB,CACpE,mBAAmB,EACnB,kBAAkB,CACnB;gBACa,MAAM,EAAE;QAClB,MAAM,EAAE,mBAAmB,CAAC;QAC5B,KAAK,EAAE,kBAAkB,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;KAC9B;CAKF;AAED,qBAAa,6BAA8B,SAAQ,yBAAyB,CAC1E,mBAAmB,EACnB,kBAAkB,CACnB;gBACa,MAAM,EAAE;QAClB,MAAM,EAAE,mBAAmB,CAAC;QAC5B,KAAK,EAAE,kBAAkB,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,KAAK,EAAE,OAAO,CAAC;KAChB;CAKF;AASD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAQT;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCxF"}
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/core/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,sBAAsB,EACtB,KAAK,eAAe,EACpB,yBAAyB,EAC1B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAGxB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAElD,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACnE,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,qBAAa,0BAA2B,SAAQ,sBAAsB,CACpE,mBAAmB,EACnB,kBAAkB,CACnB;IACC,YAAY,MAAM,EAAE;QAClB,MAAM,EAAE,mBAAmB,CAAC;QAC5B,KAAK,EAAE,kBAAkB,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;KAC9B,EAIA;CACF;AAED,qBAAa,6BAA8B,SAAQ,yBAAyB,CAC1E,mBAAmB,EACnB,kBAAkB,CACnB;IACC,YAAY,MAAM,EAAE;QAClB,MAAM,EAAE,mBAAmB,CAAC;QAC5B,KAAK,EAAE,kBAAkB,CAAC;QAC1B,oBAAoB,EAAE,MAAM,CAAC;QAC7B,KAAK,EAAE,OAAO,CAAC;KAChB,EAIA;CACF;AASD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,MAAM,CAQT;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoCxF"}
@@ -1,6 +1,6 @@
1
1
  import { type RunnerSessionTokenClaims } from '@shipfox/api-auth-dto';
2
2
  export type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;
3
3
  export declare function issueRunnerSessionToken(claims: IssueRunnerSessionTokenParams): Promise<string>;
4
- /** Returns the claims on success, or `null` for any invalid input never throws. */
4
+ /** Returns the claims on success, or `null` for any invalid input. Never throws. */
5
5
  export declare function verifyRunnerSessionToken(token: string): Promise<RunnerSessionTokenClaims | null>;
6
6
  //# sourceMappingURL=runner-session-token.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner-session-token.d.ts","sourceRoot":"","sources":["../../src/core/runner-session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAElG,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,qFAAqF;AACrF,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAc1C"}
1
+ {"version":3,"file":"runner-session-token.d.ts","sourceRoot":"","sources":["../../src/core/runner-session-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,wBAAwB,EAE9B,MAAM,uBAAuB,CAAC;AAO/B,MAAM,MAAM,6BAA6B,GAAG,IAAI,CAAC,wBAAwB,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,CAAC;AAElG,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,oFAAoF;AACpF,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAc1C"}
@@ -19,7 +19,7 @@ export async function issueRunnerSessionToken(claims) {
19
19
  recordTokenIssued('runner_session');
20
20
  return token;
21
21
  }
22
- /** Returns the claims on success, or `null` for any invalid input never throws. */ export async function verifyRunnerSessionToken(token) {
22
+ /** Returns the claims on success, or `null` for any invalid input. Never throws. */ export async function verifyRunnerSessionToken(token) {
23
23
  try {
24
24
  const claims = await verifyHs256({
25
25
  token,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/runner-session-token.ts"],"sourcesContent":["import {\n RUNNER_SESSION_TOKEN_AUDIENCE,\n type RunnerSessionTokenClaims,\n runnerSessionTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {runnerSessionTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;\n\nexport async function issueRunnerSessionToken(\n claims: IssueRunnerSessionTokenParams,\n): Promise<string> {\n const token = await signHs256({\n payload: {\n runnerSessionId: claims.runnerSessionId,\n workspaceId: claims.workspaceId,\n scope: claims.scope,\n labels: claims.labels,\n maxClaims: claims.maxClaims,\n },\n secret: runnerSessionTokenKey(),\n expiresIn: config.AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenIssued('runner_session');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input never throws. */\nexport async function verifyRunnerSessionToken(\n token: string,\n): Promise<RunnerSessionTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: runnerSessionTokenKey(),\n schema: runnerSessionTokenClaimsSchema,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenVerified('runner_session', 'ok');\n return claims;\n } catch {\n recordTokenVerified('runner_session', 'rejected');\n return null;\n }\n}\n"],"names":["RUNNER_SESSION_TOKEN_AUDIENCE","runnerSessionTokenClaimsSchema","runnerSessionTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","issueRunnerSessionToken","claims","token","payload","runnerSessionId","workspaceId","scope","labels","maxClaims","secret","expiresIn","AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN","audience","verifyRunnerSessionToken","schema"],"mappings":"AAAA,SACEA,6BAA6B,EAE7BC,8BAA8B,QACzB,wBAAwB;AAC/B,SAAQC,qBAAqB,QAAO,8BAA8B;AAClE,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAKzE,OAAO,eAAeC,wBACpBC,MAAqC;IAErC,MAAMC,QAAQ,MAAMP,UAAU;QAC5BQ,SAAS;YACPC,iBAAiBH,OAAOG,eAAe;YACvCC,aAAaJ,OAAOI,WAAW;YAC/BC,OAAOL,OAAOK,KAAK;YACnBC,QAAQN,OAAOM,MAAM;YACrBC,WAAWP,OAAOO,SAAS;QAC7B;QACAC,QAAQf;QACRgB,WAAWb,OAAOc,oCAAoC;QACtDC,UAAUpB;IACZ;IACAM,kBAAkB;IAClB,OAAOI;AACT;AAEA,mFAAmF,GACnF,OAAO,eAAeW,yBACpBX,KAAa;IAEb,IAAI;QACF,MAAMD,SAAS,MAAML,YAAY;YAC/BM;YACAO,QAAQf;YACRoB,QAAQrB;YACRmB,UAAUpB;QACZ;QACAO,oBAAoB,kBAAkB;QACtC,OAAOE;IACT,EAAE,OAAM;QACNF,oBAAoB,kBAAkB;QACtC,OAAO;IACT;AACF"}
1
+ {"version":3,"sources":["../../src/core/runner-session-token.ts"],"sourcesContent":["import {\n RUNNER_SESSION_TOKEN_AUDIENCE,\n type RunnerSessionTokenClaims,\n runnerSessionTokenClaimsSchema,\n} from '@shipfox/api-auth-dto';\nimport {runnerSessionTokenKey} from '@shipfox/node-auth-root-key';\nimport {signHs256, verifyHs256} from '@shipfox/node-jwt';\nimport {config} from '#config.js';\nimport {recordTokenIssued, recordTokenVerified} from '#metrics/index.js';\n\n// `aud`, `iat` and `exp` are set by the codec (jose); callers supply the business ids only.\nexport type IssueRunnerSessionTokenParams = Omit<RunnerSessionTokenClaims, 'aud' | 'iat' | 'exp'>;\n\nexport async function issueRunnerSessionToken(\n claims: IssueRunnerSessionTokenParams,\n): Promise<string> {\n const token = await signHs256({\n payload: {\n runnerSessionId: claims.runnerSessionId,\n workspaceId: claims.workspaceId,\n scope: claims.scope,\n labels: claims.labels,\n maxClaims: claims.maxClaims,\n },\n secret: runnerSessionTokenKey(),\n expiresIn: config.AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenIssued('runner_session');\n return token;\n}\n\n/** Returns the claims on success, or `null` for any invalid input. Never throws. */\nexport async function verifyRunnerSessionToken(\n token: string,\n): Promise<RunnerSessionTokenClaims | null> {\n try {\n const claims = await verifyHs256({\n token,\n secret: runnerSessionTokenKey(),\n schema: runnerSessionTokenClaimsSchema,\n audience: RUNNER_SESSION_TOKEN_AUDIENCE,\n });\n recordTokenVerified('runner_session', 'ok');\n return claims;\n } catch {\n recordTokenVerified('runner_session', 'rejected');\n return null;\n }\n}\n"],"names":["RUNNER_SESSION_TOKEN_AUDIENCE","runnerSessionTokenClaimsSchema","runnerSessionTokenKey","signHs256","verifyHs256","config","recordTokenIssued","recordTokenVerified","issueRunnerSessionToken","claims","token","payload","runnerSessionId","workspaceId","scope","labels","maxClaims","secret","expiresIn","AUTH_RUNNER_SESSION_TOKEN_EXPIRES_IN","audience","verifyRunnerSessionToken","schema"],"mappings":"AAAA,SACEA,6BAA6B,EAE7BC,8BAA8B,QACzB,wBAAwB;AAC/B,SAAQC,qBAAqB,QAAO,8BAA8B;AAClE,SAAQC,SAAS,EAAEC,WAAW,QAAO,oBAAoB;AACzD,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,iBAAiB,EAAEC,mBAAmB,QAAO,oBAAoB;AAKzE,OAAO,eAAeC,wBACpBC,MAAqC;IAErC,MAAMC,QAAQ,MAAMP,UAAU;QAC5BQ,SAAS;YACPC,iBAAiBH,OAAOG,eAAe;YACvCC,aAAaJ,OAAOI,WAAW;YAC/BC,OAAOL,OAAOK,KAAK;YACnBC,QAAQN,OAAOM,MAAM;YACrBC,WAAWP,OAAOO,SAAS;QAC7B;QACAC,QAAQf;QACRgB,WAAWb,OAAOc,oCAAoC;QACtDC,UAAUpB;IACZ;IACAM,kBAAkB;IAClB,OAAOI;AACT;AAEA,kFAAkF,GAClF,OAAO,eAAeW,yBACpBX,KAAa;IAEb,IAAI;QACF,MAAMD,SAAS,MAAML,YAAY;YAC/BM;YACAO,QAAQf;YACRoB,QAAQrB;YACRmB,UAAUpB;QACZ;QACAO,oBAAoB,kBAAkB;QACtC,OAAOE;IACT,EAAE,OAAM;QACNF,oBAAoB,kBAAkB;QACtC,OAAO;IACT;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"admin-user-moderation.d.ts","sourceRoot":"","sources":["../../src/db/admin-user-moderation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,yBAAyB,CAAC;AAwBvE,UAAU,2BAA2B;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,EAAE,MAAM,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,yBAAyB,CAAC;CAClC;AAyGD,iBAAe,4BAA4B,CACzC,MAAM,EAAE,2BAA2B,GAAG;IACpC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,iBAAiB,CAAC;CACzD;;;;;;;;;;;;GAuEF;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC;AAE5F,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B"}
1
+ {"version":3,"file":"admin-user-moderation.d.ts","sourceRoot":"","sources":["../../src/db/admin-user-moderation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,yBAAyB,CAAC;AAwBvE,UAAU,2BAA2B;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,EAAE,MAAM,CAAC;IAClC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,yBAAyB,CAAC;CAClC;AAyGD,iBAAe,4BAA4B,CACzC,MAAM,EAAE,2BAA2B,GAAG;IACpC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,iBAAiB,CAAC;CACzD;;QArFG,EAAE;QACF,KAAK;QACL,IAAI;QACJ,eAAe;QACf,MAAM;QACN,SAAS;QACT,SAAS;;;;GAsJd;AAED,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC,CAAC;AAE5F,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B;AAED,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,oBAAoB,CAAC,CAE/B"}
package/dist/db/db.d.ts CHANGED
@@ -107,7 +107,7 @@ export declare const schema: {
107
107
  generated: undefined;
108
108
  }, {}, {}>;
109
109
  };
110
- dialect: "pg";
110
+ dialect: 'pg';
111
111
  }>;
112
112
  adminCommandResults: import("drizzle-orm/pg-core").PgTableWithColumns<{
113
113
  name: "admin_command_results";
@@ -235,7 +235,7 @@ export declare const schema: {
235
235
  generated: undefined;
236
236
  }, {}, {}>;
237
237
  };
238
- dialect: "pg";
238
+ dialect: 'pg';
239
239
  }>;
240
240
  users: import("drizzle-orm/pg-core").PgTableWithColumns<{
241
241
  name: "users";
@@ -331,7 +331,7 @@ export declare const schema: {
331
331
  tableName: "users";
332
332
  dataType: "string";
333
333
  columnType: "PgEnumColumn";
334
- data: "active" | "suspended" | "deleted";
334
+ data: "active" | "deleted" | "suspended";
335
335
  driverParam: string;
336
336
  notNull: true;
337
337
  hasDefault: true;
@@ -378,7 +378,7 @@ export declare const schema: {
378
378
  generated: undefined;
379
379
  }, {}, {}>;
380
380
  };
381
- dialect: "pg";
381
+ dialect: 'pg';
382
382
  }>;
383
383
  passwordResets: import("drizzle-orm/pg-core").PgTableWithColumns<{
384
384
  name: "password_resets";
@@ -487,7 +487,7 @@ export declare const schema: {
487
487
  generated: undefined;
488
488
  }, {}, {}>;
489
489
  };
490
- dialect: "pg";
490
+ dialect: 'pg';
491
491
  }>;
492
492
  refreshTokens: import("drizzle-orm/pg-core").PgTableWithColumns<{
493
493
  name: "refresh_tokens";
@@ -664,7 +664,7 @@ export declare const schema: {
664
664
  generated: undefined;
665
665
  }, {}, {}>;
666
666
  };
667
- dialect: "pg";
667
+ dialect: 'pg';
668
668
  }>;
669
669
  authOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
670
670
  name: "outbox";
@@ -858,7 +858,7 @@ export declare const schema: {
858
858
  generated: undefined;
859
859
  }, {}, {}>;
860
860
  };
861
- dialect: "pg";
861
+ dialect: 'pg';
862
862
  }>;
863
863
  authRateLimits: import("drizzle-orm/pg-core").PgTableWithColumns<{
864
864
  name: "rate_limits";
@@ -1001,7 +1001,7 @@ export declare const schema: {
1001
1001
  generated: undefined;
1002
1002
  }, {}, {}>;
1003
1003
  };
1004
- dialect: "pg";
1004
+ dialect: 'pg';
1005
1005
  }>;
1006
1006
  };
1007
1007
  export declare function db(): NodePgDatabase<{
@@ -1112,7 +1112,7 @@ export declare function db(): NodePgDatabase<{
1112
1112
  generated: undefined;
1113
1113
  }, {}, {}>;
1114
1114
  };
1115
- dialect: "pg";
1115
+ dialect: 'pg';
1116
1116
  }>;
1117
1117
  adminCommandResults: import("drizzle-orm/pg-core").PgTableWithColumns<{
1118
1118
  name: "admin_command_results";
@@ -1240,7 +1240,7 @@ export declare function db(): NodePgDatabase<{
1240
1240
  generated: undefined;
1241
1241
  }, {}, {}>;
1242
1242
  };
1243
- dialect: "pg";
1243
+ dialect: 'pg';
1244
1244
  }>;
1245
1245
  users: import("drizzle-orm/pg-core").PgTableWithColumns<{
1246
1246
  name: "users";
@@ -1336,7 +1336,7 @@ export declare function db(): NodePgDatabase<{
1336
1336
  tableName: "users";
1337
1337
  dataType: "string";
1338
1338
  columnType: "PgEnumColumn";
1339
- data: "active" | "suspended" | "deleted";
1339
+ data: "active" | "deleted" | "suspended";
1340
1340
  driverParam: string;
1341
1341
  notNull: true;
1342
1342
  hasDefault: true;
@@ -1383,7 +1383,7 @@ export declare function db(): NodePgDatabase<{
1383
1383
  generated: undefined;
1384
1384
  }, {}, {}>;
1385
1385
  };
1386
- dialect: "pg";
1386
+ dialect: 'pg';
1387
1387
  }>;
1388
1388
  passwordResets: import("drizzle-orm/pg-core").PgTableWithColumns<{
1389
1389
  name: "password_resets";
@@ -1492,7 +1492,7 @@ export declare function db(): NodePgDatabase<{
1492
1492
  generated: undefined;
1493
1493
  }, {}, {}>;
1494
1494
  };
1495
- dialect: "pg";
1495
+ dialect: 'pg';
1496
1496
  }>;
1497
1497
  refreshTokens: import("drizzle-orm/pg-core").PgTableWithColumns<{
1498
1498
  name: "refresh_tokens";
@@ -1669,7 +1669,7 @@ export declare function db(): NodePgDatabase<{
1669
1669
  generated: undefined;
1670
1670
  }, {}, {}>;
1671
1671
  };
1672
- dialect: "pg";
1672
+ dialect: 'pg';
1673
1673
  }>;
1674
1674
  authOutbox: import("drizzle-orm/pg-core").PgTableWithColumns<{
1675
1675
  name: "outbox";
@@ -1863,7 +1863,7 @@ export declare function db(): NodePgDatabase<{
1863
1863
  generated: undefined;
1864
1864
  }, {}, {}>;
1865
1865
  };
1866
- dialect: "pg";
1866
+ dialect: 'pg';
1867
1867
  }>;
1868
1868
  authRateLimits: import("drizzle-orm/pg-core").PgTableWithColumns<{
1869
1869
  name: "rate_limits";
@@ -2006,7 +2006,7 @@ export declare function db(): NodePgDatabase<{
2006
2006
  generated: undefined;
2007
2007
  }, {}, {}>;
2008
2008
  };
2009
- dialect: "pg";
2009
+ dialect: 'pg';
2010
2010
  }>;
2011
2011
  }>;
2012
2012
  export declare function closeDb(): void;
@@ -1,18 +1,9 @@
1
- export interface ConsumeAuthRateLimitParams {
2
- action: string;
3
- scope: string;
4
- identifierHmac: string;
5
- windowStart: Date;
6
- expiresAt: Date;
7
- timeoutMs: number;
8
- }
9
- export interface ConsumeAuthRateLimitResult {
10
- count: number;
11
- expiresAt: Date;
12
- }
13
- export declare function consumeAuthRateLimit(params: ConsumeAuthRateLimitParams): Promise<ConsumeAuthRateLimitResult>;
14
- export declare function pruneExpiredAuthRateLimits(params?: {
1
+ import { type ConsumeRateLimitParams, type ConsumeRateLimitResult } from '@shipfox/node-rate-limit';
2
+ export type ConsumeAuthRateLimitParams = ConsumeRateLimitParams<string, string>;
3
+ export type ConsumeAuthRateLimitResult = ConsumeRateLimitResult;
4
+ export declare const consumeAuthRateLimit: (params: ConsumeRateLimitParams<string, string>) => Promise<ConsumeRateLimitResult>;
5
+ export declare const pruneExpiredAuthRateLimits: (params?: {
15
6
  now?: Date | undefined;
16
7
  minIntervalMs?: number | undefined;
17
- }): Promise<number | undefined>;
8
+ }) => Promise<number | undefined>;
18
9
  //# sourceMappingURL=rate-limits.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../../src/db/rate-limits.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CAgCrC;AAID,wBAAsB,0BAA0B,CAC9C,MAAM,GAAE;IAAC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAM,GACxE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAU7B"}
1
+ {"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../../src/db/rate-limits.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAE5B,MAAM,0BAA0B,CAAC;AA+ClC,MAAM,MAAM,0BAA0B,GAAG,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAChF,MAAM,MAAM,0BAA0B,GAAG,sBAAsB,CAAC;AAChE,eAAO,MAAM,oBAAoB,qFAAsB,CAAC;AACxD,eAAO,MAAM,0BAA0B;;;iCAAoB,CAAC"}
@@ -1,10 +1,14 @@
1
+ import { createRateLimitPersistence } from '@shipfox/node-rate-limit';
1
2
  import { lt, sql } from 'drizzle-orm';
2
3
  import { db } from './db.js';
3
4
  import { authRateLimits } from './schema/rate-limits.js';
4
- export async function consumeAuthRateLimit(params) {
5
- return await db().transaction(async (tx)=>{
6
- await tx.execute(sql`select set_config('statement_timeout', ${`${params.timeoutMs}ms`}, true)`);
7
- const rows = await tx.insert(authRateLimits).values({
5
+ const persistence = createRateLimitPersistence({
6
+ transaction: (callback)=>db().transaction(callback),
7
+ setStatementTimeout: async (transaction, timeoutMs)=>{
8
+ await transaction.execute(sql`select set_config('statement_timeout', ${`${timeoutMs}ms`}, true)`);
9
+ },
10
+ consume: async (transaction, params)=>{
11
+ const rows = await transaction.insert(authRateLimits).values({
8
12
  action: params.action,
9
13
  scope: params.scope,
10
14
  identifierHmac: params.identifierHmac,
@@ -26,19 +30,14 @@ export async function consumeAuthRateLimit(params) {
26
30
  count: authRateLimits.count,
27
31
  expiresAt: authRateLimits.expiresAt
28
32
  });
29
- const row = rows[0];
30
- if (!row) throw new Error('Rate limit upsert returned no rows');
31
- return row;
32
- });
33
- }
34
- let nextPruneAt = 0;
35
- export async function pruneExpiredAuthRateLimits(params = {}) {
36
- const now = params.now ?? new Date();
37
- const minIntervalMs = params.minIntervalMs ?? 60_000;
38
- if (minIntervalMs > 0 && now.getTime() < nextPruneAt) return undefined;
39
- nextPruneAt = now.getTime() + minIntervalMs;
40
- const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));
41
- return result.rowCount ?? 0;
42
- }
33
+ return rows[0];
34
+ },
35
+ prune: async (now)=>{
36
+ const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));
37
+ return result.rowCount ?? 0;
38
+ }
39
+ });
40
+ export const consumeAuthRateLimit = persistence.consume;
41
+ export const pruneExpiredAuthRateLimits = persistence.prune;
43
42
 
44
43
  //# sourceMappingURL=rate-limits.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/db/rate-limits.ts"],"sourcesContent":["import {lt, sql} from 'drizzle-orm';\nimport {db} from './db.js';\nimport {authRateLimits} from './schema/rate-limits.js';\n\nexport interface ConsumeAuthRateLimitParams {\n action: string;\n scope: string;\n identifierHmac: string;\n windowStart: Date;\n expiresAt: Date;\n timeoutMs: number;\n}\n\nexport interface ConsumeAuthRateLimitResult {\n count: number;\n expiresAt: Date;\n}\n\nexport async function consumeAuthRateLimit(\n params: ConsumeAuthRateLimitParams,\n): Promise<ConsumeAuthRateLimitResult> {\n return await db().transaction(async (tx) => {\n await tx.execute(sql`select set_config('statement_timeout', ${`${params.timeoutMs}ms`}, true)`);\n\n const rows = await tx\n .insert(authRateLimits)\n .values({\n action: params.action,\n scope: params.scope,\n identifierHmac: params.identifierHmac,\n windowStart: params.windowStart,\n count: 1,\n expiresAt: params.expiresAt,\n })\n .onConflictDoUpdate({\n target: [\n authRateLimits.action,\n authRateLimits.scope,\n authRateLimits.identifierHmac,\n authRateLimits.windowStart,\n ],\n set: {\n count: sql`${authRateLimits.count} + 1`,\n updatedAt: sql`now()`,\n },\n })\n .returning({count: authRateLimits.count, expiresAt: authRateLimits.expiresAt});\n\n const row = rows[0];\n if (!row) throw new Error('Rate limit upsert returned no rows');\n return row;\n });\n}\n\nlet nextPruneAt = 0;\n\nexport async function pruneExpiredAuthRateLimits(\n params: {now?: Date | undefined; minIntervalMs?: number | undefined} = {},\n): Promise<number | undefined> {\n const now = params.now ?? new Date();\n const minIntervalMs = params.minIntervalMs ?? 60_000;\n if (minIntervalMs > 0 && now.getTime() < nextPruneAt) return undefined;\n\n nextPruneAt = now.getTime() + minIntervalMs;\n\n const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));\n\n return result.rowCount ?? 0;\n}\n"],"names":["lt","sql","db","authRateLimits","consumeAuthRateLimit","params","transaction","tx","execute","timeoutMs","rows","insert","values","action","scope","identifierHmac","windowStart","count","expiresAt","onConflictDoUpdate","target","set","updatedAt","returning","row","Error","nextPruneAt","pruneExpiredAuthRateLimits","now","Date","minIntervalMs","getTime","undefined","result","delete","where","rowCount"],"mappings":"AAAA,SAAQA,EAAE,EAAEC,GAAG,QAAO,cAAc;AACpC,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,cAAc,QAAO,0BAA0B;AAgBvD,OAAO,eAAeC,qBACpBC,MAAkC;IAElC,OAAO,MAAMH,KAAKI,WAAW,CAAC,OAAOC;QACnC,MAAMA,GAAGC,OAAO,CAACP,GAAG,CAAC,uCAAuC,EAAE,GAAGI,OAAOI,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;QAE9F,MAAMC,OAAO,MAAMH,GAChBI,MAAM,CAACR,gBACPS,MAAM,CAAC;YACNC,QAAQR,OAAOQ,MAAM;YACrBC,OAAOT,OAAOS,KAAK;YACnBC,gBAAgBV,OAAOU,cAAc;YACrCC,aAAaX,OAAOW,WAAW;YAC/BC,OAAO;YACPC,WAAWb,OAAOa,SAAS;QAC7B,GACCC,kBAAkB,CAAC;YAClBC,QAAQ;gBACNjB,eAAeU,MAAM;gBACrBV,eAAeW,KAAK;gBACpBX,eAAeY,cAAc;gBAC7BZ,eAAea,WAAW;aAC3B;YACDK,KAAK;gBACHJ,OAAOhB,GAAG,CAAC,EAAEE,eAAec,KAAK,CAAC,IAAI,CAAC;gBACvCK,WAAWrB,GAAG,CAAC,KAAK,CAAC;YACvB;QACF,GACCsB,SAAS,CAAC;YAACN,OAAOd,eAAec,KAAK;YAAEC,WAAWf,eAAee,SAAS;QAAA;QAE9E,MAAMM,MAAMd,IAAI,CAAC,EAAE;QACnB,IAAI,CAACc,KAAK,MAAM,IAAIC,MAAM;QAC1B,OAAOD;IACT;AACF;AAEA,IAAIE,cAAc;AAElB,OAAO,eAAeC,2BACpBtB,SAAuE,CAAC,CAAC;IAEzE,MAAMuB,MAAMvB,OAAOuB,GAAG,IAAI,IAAIC;IAC9B,MAAMC,gBAAgBzB,OAAOyB,aAAa,IAAI;IAC9C,IAAIA,gBAAgB,KAAKF,IAAIG,OAAO,KAAKL,aAAa,OAAOM;IAE7DN,cAAcE,IAAIG,OAAO,KAAKD;IAE9B,MAAMG,SAAS,MAAM/B,KAAKgC,MAAM,CAAC/B,gBAAgBgC,KAAK,CAACnC,GAAGG,eAAee,SAAS,EAAEU;IAEpF,OAAOK,OAAOG,QAAQ,IAAI;AAC5B"}
1
+ {"version":3,"sources":["../../src/db/rate-limits.ts"],"sourcesContent":["import {\n type ConsumeRateLimitParams,\n type ConsumeRateLimitResult,\n createRateLimitPersistence,\n} from '@shipfox/node-rate-limit';\nimport {lt, sql} from 'drizzle-orm';\nimport {db} from './db.js';\nimport {authRateLimits} from './schema/rate-limits.js';\n\ntype AuthRateLimitTransaction = Parameters<Parameters<ReturnType<typeof db>['transaction']>[0]>[0];\n\nconst persistence = createRateLimitPersistence<AuthRateLimitTransaction>({\n transaction: (callback) => db().transaction(callback),\n setStatementTimeout: async (transaction, timeoutMs) => {\n await transaction.execute(\n sql`select set_config('statement_timeout', ${`${timeoutMs}ms`}, true)`,\n );\n },\n consume: async (transaction, params) => {\n const rows = await transaction\n .insert(authRateLimits)\n .values({\n action: params.action,\n scope: params.scope,\n identifierHmac: params.identifierHmac,\n windowStart: params.windowStart,\n count: 1,\n expiresAt: params.expiresAt,\n })\n .onConflictDoUpdate({\n target: [\n authRateLimits.action,\n authRateLimits.scope,\n authRateLimits.identifierHmac,\n authRateLimits.windowStart,\n ],\n set: {\n count: sql`${authRateLimits.count} + 1`,\n updatedAt: sql`now()`,\n },\n })\n .returning({count: authRateLimits.count, expiresAt: authRateLimits.expiresAt});\n\n return rows[0];\n },\n prune: async (now) => {\n const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));\n return result.rowCount ?? 0;\n },\n});\n\nexport type ConsumeAuthRateLimitParams = ConsumeRateLimitParams<string, string>;\nexport type ConsumeAuthRateLimitResult = ConsumeRateLimitResult;\nexport const consumeAuthRateLimit = persistence.consume;\nexport const pruneExpiredAuthRateLimits = persistence.prune;\n"],"names":["createRateLimitPersistence","lt","sql","db","authRateLimits","persistence","transaction","callback","setStatementTimeout","timeoutMs","execute","consume","params","rows","insert","values","action","scope","identifierHmac","windowStart","count","expiresAt","onConflictDoUpdate","target","set","updatedAt","returning","prune","now","result","delete","where","rowCount","consumeAuthRateLimit","pruneExpiredAuthRateLimits"],"mappings":"AAAA,SAGEA,0BAA0B,QACrB,2BAA2B;AAClC,SAAQC,EAAE,EAAEC,GAAG,QAAO,cAAc;AACpC,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,cAAc,QAAO,0BAA0B;AAIvD,MAAMC,cAAcL,2BAAqD;IACvEM,aAAa,CAACC,WAAaJ,KAAKG,WAAW,CAACC;IAC5CC,qBAAqB,OAAOF,aAAaG;QACvC,MAAMH,YAAYI,OAAO,CACvBR,GAAG,CAAC,uCAAuC,EAAE,GAAGO,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC;IAE1E;IACAE,SAAS,OAAOL,aAAaM;QAC3B,MAAMC,OAAO,MAAMP,YAChBQ,MAAM,CAACV,gBACPW,MAAM,CAAC;YACNC,QAAQJ,OAAOI,MAAM;YACrBC,OAAOL,OAAOK,KAAK;YACnBC,gBAAgBN,OAAOM,cAAc;YACrCC,aAAaP,OAAOO,WAAW;YAC/BC,OAAO;YACPC,WAAWT,OAAOS,SAAS;QAC7B,GACCC,kBAAkB,CAAC;YAClBC,QAAQ;gBACNnB,eAAeY,MAAM;gBACrBZ,eAAea,KAAK;gBACpBb,eAAec,cAAc;gBAC7Bd,eAAee,WAAW;aAC3B;YACDK,KAAK;gBACHJ,OAAOlB,GAAG,CAAC,EAAEE,eAAegB,KAAK,CAAC,IAAI,CAAC;gBACvCK,WAAWvB,GAAG,CAAC,KAAK,CAAC;YACvB;QACF,GACCwB,SAAS,CAAC;YAACN,OAAOhB,eAAegB,KAAK;YAAEC,WAAWjB,eAAeiB,SAAS;QAAA;QAE9E,OAAOR,IAAI,CAAC,EAAE;IAChB;IACAc,OAAO,OAAOC;QACZ,MAAMC,SAAS,MAAM1B,KAAK2B,MAAM,CAAC1B,gBAAgB2B,KAAK,CAAC9B,GAAGG,eAAeiB,SAAS,EAAEO;QACpF,OAAOC,OAAOG,QAAQ,IAAI;IAC5B;AACF;AAIA,OAAO,MAAMC,uBAAuB5B,YAAYM,OAAO,CAAC;AACxD,OAAO,MAAMuB,6BAA6B7B,YAAYsB,KAAK,CAAC"}
@@ -11,7 +11,7 @@ export interface CreateRefreshTokenParams {
11
11
  export declare function createRefreshToken(params: CreateRefreshTokenParams): Promise<RefreshToken>;
12
12
  export declare function createRefreshTokenForActiveUser(params: CreateRefreshTokenParams): Promise<RefreshToken | undefined>;
13
13
  /**
14
- * Looks up the live session token by hash one that can still authenticate as
14
+ * Looks up the live session token by hash. It can still authenticate as
15
15
  * the current session. Returns `undefined` for revoked, expired, or already
16
16
  * rotated tokens; rotated rows survive in the table only for the grace window
17
17
  * and are not "active".
@@ -22,8 +22,8 @@ export declare function findActiveRefreshTokenByHash(params: {
22
22
  /**
23
23
  * Looks up a non-revoked, non-expired token by hash, including ones already
24
24
  * rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
25
- * rotate, honour the grace window, or reject reuse, so unlike
26
- * {@link findActiveRefreshTokenByHash} it must still see rotated rows.
25
+ * rotate, honour the grace window, or reject reuse, so, unlike
26
+ * {@link findActiveRefreshTokenByHash}, it must still see rotated rows.
27
27
  */
28
28
  export declare function findRefreshTokenByHash(params: {
29
29
  hashedToken: string;
@@ -35,7 +35,7 @@ export async function createRefreshTokenForActiveUser(params) {
35
35
  });
36
36
  }
37
37
  /**
38
- * Looks up the live session token by hash one that can still authenticate as
38
+ * Looks up the live session token by hash. It can still authenticate as
39
39
  * the current session. Returns `undefined` for revoked, expired, or already
40
40
  * rotated tokens; rotated rows survive in the table only for the grace window
41
41
  * and are not "active".
@@ -48,8 +48,8 @@ export async function createRefreshTokenForActiveUser(params) {
48
48
  /**
49
49
  * Looks up a non-revoked, non-expired token by hash, including ones already
50
50
  * rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
51
- * rotate, honour the grace window, or reject reuse, so unlike
52
- * {@link findActiveRefreshTokenByHash} it must still see rotated rows.
51
+ * rotate, honour the grace window, or reject reuse, so, unlike
52
+ * {@link findActiveRefreshTokenByHash}, it must still see rotated rows.
53
53
  */ export async function findRefreshTokenByHash(params) {
54
54
  const rows = await db().select().from(refreshTokens).where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt), gt(refreshTokens.expiresAt, sql`now()`))).limit(1);
55
55
  const row = rows[0];