bunderstack 0.22.0 → 0.22.1

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 (50) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -1
  3. package/dist/api/context.d.ts +3 -0
  4. package/dist/api/context.d.ts.map +1 -1
  5. package/dist/api/context.js +2 -0
  6. package/dist/api/context.js.map +1 -1
  7. package/dist/backend-internals.d.ts +11 -0
  8. package/dist/backend-internals.d.ts.map +1 -1
  9. package/dist/backend-internals.js.map +1 -1
  10. package/dist/backend.d.ts +2 -2
  11. package/dist/backend.d.ts.map +1 -1
  12. package/dist/backend.js +9 -4
  13. package/dist/backend.js.map +1 -1
  14. package/dist/errors.d.ts.map +1 -1
  15. package/dist/errors.js +4 -2
  16. package/dist/errors.js.map +1 -1
  17. package/dist/jobs/worker.d.ts +12 -0
  18. package/dist/jobs/worker.d.ts.map +1 -1
  19. package/dist/jobs/worker.js +32 -1
  20. package/dist/jobs/worker.js.map +1 -1
  21. package/dist/logging.d.ts +8 -0
  22. package/dist/logging.d.ts.map +1 -0
  23. package/dist/logging.js +6 -0
  24. package/dist/logging.js.map +1 -0
  25. package/dist/runtime.d.ts +4 -0
  26. package/dist/runtime.d.ts.map +1 -1
  27. package/dist/runtime.js +19 -4
  28. package/dist/runtime.js.map +1 -1
  29. package/dist/testing/auth.d.ts +30 -2
  30. package/dist/testing/auth.d.ts.map +1 -1
  31. package/dist/testing/auth.js +103 -14
  32. package/dist/testing/auth.js.map +1 -1
  33. package/dist/testing/fixture.d.ts +18 -0
  34. package/dist/testing/fixture.d.ts.map +1 -1
  35. package/dist/testing/fixture.js +55 -2
  36. package/dist/testing/fixture.js.map +1 -1
  37. package/dist/testing/jobs.d.ts +9 -1
  38. package/dist/testing/jobs.d.ts.map +1 -1
  39. package/dist/testing/jobs.js +9 -0
  40. package/dist/testing/jobs.js.map +1 -1
  41. package/dist/testing/logs.d.ts +18 -0
  42. package/dist/testing/logs.d.ts.map +1 -0
  43. package/dist/testing/logs.js +53 -0
  44. package/dist/testing/logs.js.map +1 -0
  45. package/dist/testing.d.ts +5 -4
  46. package/dist/testing.d.ts.map +1 -1
  47. package/dist/testing.js +1 -1
  48. package/dist/testing.js.map +1 -1
  49. package/llms.txt +20 -13
  50. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to `bunderstack` will be documented in this file.
4
4
 
5
+ ## [0.22.1] - 2026-08-28
6
+
7
+ ### Added
8
+
9
+ - **Configured test factories.** `backend.test.configure({ env, database, logs, setup })`
10
+ creates a reusable fixture factory. Per-test options deep-merge `env` and
11
+ `database`, and the setup result is available as `fixture.context`.
12
+ - **Fixture-owned cleanup and logs.** `fixture.defer()` runs async cleanup in LIFO
13
+ order before runtime shutdown. Internal runtime logs are captured by default in
14
+ `fixture.logs`; use `logs: 'inherit'` to forward them or `logs: 'silent'` to discard
15
+ them.
16
+ - **Complete auth test lifecycle.** Added `signInEmail()`, `getSession()`, `signOut()`,
17
+ and `verifyEmail()`. Mock identities are header-scoped, so multiple users can safely
18
+ coexist in one fixture.
19
+ - **Queue inspection.** `fixture.jobs.inspect()`, `.pending()`, and `.failed()` expose
20
+ normalized queue rows and accept `name` and `dedupeKey` filters.
21
+
5
22
  ## [0.22.0] - 2026-08-28
6
23
 
7
24
  ### Breaking Changes
package/README.md CHANGED
@@ -48,7 +48,8 @@ replay. Deployment metadata is generated with `bunx bunderstack blueprint`.
48
48
  ## Package Subpaths (0.21+)
49
49
 
50
50
  - `bunderstack` — Declarative backend and runtime (`bunderstack`, `defineApi`, `buildApiRegistry`)
51
- - `bunderstack/testing` — Isolated lexical test fixtures (`backend.test()`)
51
+ - `bunderstack/testing` — Isolated lexical fixtures and reusable test factories
52
+ (`backend.test()` / `backend.test.configure()`)
52
53
  - `bunderstack/provision` — Explicit production schema provisioning
53
54
  - `bunderstack/client` — Framework-neutral RPC & LiveView client (`createClient`, `createLiveView`)
54
55
  - `bunderstack/client-rest` — Type-safe REST client
@@ -2,6 +2,7 @@ import type { AccessUser, AuthSessionResolver } from '../access.js';
2
2
  import type { DbFor } from '../db.js';
3
3
  import type { EmailFacade } from '../email.js';
4
4
  import type { JobsRuntimeFacade } from '../jobs/define.js';
5
+ import type { BunderstackLogger } from '../logging.js';
5
6
  import type { RealtimeFacade } from '../realtime/facade.js';
6
7
  import type { AuthInstance, StorageFacade } from '../runtime.js';
7
8
  export interface ApiContextDeps<TSchema extends Record<string, unknown> = Record<string, unknown>, TEnv = Record<string, unknown>> {
@@ -13,6 +14,7 @@ export interface ApiContextDeps<TSchema extends Record<string, unknown> = Record
13
14
  realtime: RealtimeFacade<TSchema>;
14
15
  auth: AuthInstance;
15
16
  authResolver?: AuthSessionResolver;
17
+ logger?: BunderstackLogger;
16
18
  }
17
19
  export interface ApiContext<TSchema extends Record<string, unknown> = Record<string, unknown>, TEnv = Record<string, unknown>> {
18
20
  db: DbFor<TSchema>;
@@ -22,6 +24,7 @@ export interface ApiContext<TSchema extends Record<string, unknown> = Record<str
22
24
  jobs: JobsRuntimeFacade;
23
25
  realtime: RealtimeFacade<TSchema>;
24
26
  auth: AuthInstance;
27
+ logger: BunderstackLogger;
25
28
  request: Request;
26
29
  resHeaders: Headers;
27
30
  getRawBody: () => Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/api/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAI7D,MAAM,WAAW,cAAc,CAC7B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE9B,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAClB,GAAG,EAAE,IAAI,CAAA;IACT,OAAO,EAAE,aAAa,CAAA;IACtB,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,iBAAiB,CAAA;IACvB,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IACjC,IAAI,EAAE,YAAY,CAAA;IAClB,YAAY,CAAC,EAAE,mBAAmB,CAAA;CACnC;AAED,MAAM,WAAW,UAAU,CACzB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE9B,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAClB,GAAG,EAAE,IAAI,CAAA;IACT,OAAO,EAAE,aAAa,CAAA;IACtB,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,iBAAiB,CAAA;IACvB,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IACjC,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,OAAO,CAAC;QACxB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;QACvB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;KACpC,CAAC,CAAA;IACF;;;;;OAKG;IACH,WAAW,EAAE,MACT;QAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;QAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAChE,SAAS,CAAA;CACd;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAE9B,IAAI,EAAE,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,EACnC,OAAO,EAAE,OAAO,GACf,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CA6C3B"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/api/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAK7D,MAAM,WAAW,cAAc,CAC7B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE9B,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAClB,GAAG,EAAE,IAAI,CAAA;IACT,OAAO,EAAE,aAAa,CAAA;IACtB,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,iBAAiB,CAAA;IACvB,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IACjC,IAAI,EAAE,YAAY,CAAA;IAClB,YAAY,CAAC,EAAE,mBAAmB,CAAA;IAClC,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAC3B;AAED,MAAM,WAAW,UAAU,CACzB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE9B,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IAClB,GAAG,EAAE,IAAI,CAAA;IACT,OAAO,EAAE,aAAa,CAAA;IACtB,KAAK,EAAE,WAAW,CAAA;IAClB,IAAI,EAAE,iBAAiB,CAAA;IACvB,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;IACjC,IAAI,EAAE,YAAY,CAAA;IAClB,MAAM,EAAE,iBAAiB,CAAA;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACjC,UAAU,EAAE,MAAM,OAAO,CAAC;QACxB,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;QACvB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;KACpC,CAAC,CAAA;IACF;;;;;OAKG;IACH,WAAW,EAAE,MACT;QAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;QAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAChE,SAAS,CAAA;CACd;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAE9B,IAAI,EAAE,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,EACnC,OAAO,EAAE,OAAO,GACf,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CA8C3B"}
@@ -1,4 +1,5 @@
1
1
  import { resolveSession } from '../access.js';
2
+ import { consoleLogger } from '../logging.js';
2
3
  export function createApiContext(deps, request) {
3
4
  // Reserve the body stream before a transport codec consumes `request`.
4
5
  const rawBodyRequest = request.clone();
@@ -28,6 +29,7 @@ export function createApiContext(deps, request) {
28
29
  jobs: deps.jobs,
29
30
  realtime: deps.realtime,
30
31
  auth: deps.auth,
32
+ logger: deps.logger ?? consoleLogger,
31
33
  request,
32
34
  resHeaders: new Headers(),
33
35
  getRawBody,
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/api/context.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AA6C1C,MAAM,UAAU,gBAAgB,CAI9B,IAAmC,EACnC,OAAgB;IAEhB,uEAAuE;IACvE,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,EAAE,CAAA;IACtC,IAAI,cAA2C,CAAA;IAC/C,IAAI,cAES,CAAA;IAEb,IAAI,cAES,CAAA;IAEb,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CACtE,CAAC,OAAO,EAAE,EAAE;gBACV,cAAc,GAAG,OAAO,CAAA;gBACxB,OAAO,OAAO,CAAA;YAChB,CAAC,CACF,CAAA;QACH,CAAC;QACD,OAAO,cAAc,CAAA;IACvB,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,cAAc,CAAA;IAExC,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,CAAC,cAAc;YAAE,cAAc,GAAG,cAAc,CAAC,IAAI,EAAE,CAAA;QAC3D,OAAO,cAAc,CAAA;IACvB,CAAC,CAAA;IAED,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO;QACP,UAAU,EAAE,IAAI,OAAO,EAAE;QACzB,UAAU;QACV,UAAU;QACV,WAAW;KACZ,CAAA;AACH,CAAC","sourcesContent":["import type { AccessUser, AuthSessionResolver } from '../access'\nimport type { DbFor } from '../db'\nimport type { EmailFacade } from '../email'\nimport type { JobsRuntimeFacade } from '../jobs/define'\nimport type { RealtimeFacade } from '../realtime/facade'\nimport type { AuthInstance, StorageFacade } from '../runtime'\n\nimport { resolveSession } from '../access'\n\nexport interface ApiContextDeps<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnv = Record<string, unknown>,\n> {\n db: DbFor<TSchema>\n env: TEnv\n storage: StorageFacade\n email: EmailFacade\n jobs: JobsRuntimeFacade\n realtime: RealtimeFacade<TSchema>\n auth: AuthInstance\n authResolver?: AuthSessionResolver\n}\n\nexport interface ApiContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnv = Record<string, unknown>,\n> {\n db: DbFor<TSchema>\n env: TEnv\n storage: StorageFacade\n email: EmailFacade\n jobs: JobsRuntimeFacade\n realtime: RealtimeFacade<TSchema>\n auth: AuthInstance\n request: Request\n resHeaders: Headers\n getRawBody: () => Promise<string>\n getSession: () => Promise<{\n user: AccessUser | null\n activeOrganizationId: string | null\n }>\n /**\n * The session that some earlier code already resolved, or `undefined`.\n * Never starts a resolution, so a global middleware can log the caller\n * without removing the lazy session behavior that signed webhooks rely on.\n * Use it for observability only. Never use it for authorization.\n */\n peekSession: () =>\n | { user: AccessUser | null; activeOrganizationId: string | null }\n | undefined\n}\n\nexport function createApiContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnv = Record<string, unknown>,\n>(\n deps: ApiContextDeps<TSchema, TEnv>,\n request: Request,\n): ApiContext<TSchema, TEnv> {\n // Reserve the body stream before a transport codec consumes `request`.\n const rawBodyRequest = request.clone()\n let rawBodyPromise: Promise<string> | undefined\n let sessionPromise:\n | Promise<{ user: AccessUser | null; activeOrganizationId: string | null }>\n | undefined\n\n let settledSession:\n | { user: AccessUser | null; activeOrganizationId: string | null }\n | undefined\n\n const getSession = () => {\n if (!sessionPromise) {\n sessionPromise = resolveSession(deps.authResolver, request.headers).then(\n (session) => {\n settledSession = session\n return session\n },\n )\n }\n return sessionPromise\n }\n\n const peekSession = () => settledSession\n\n const getRawBody = () => {\n if (!rawBodyPromise) rawBodyPromise = rawBodyRequest.text()\n return rawBodyPromise\n }\n\n return {\n db: deps.db,\n env: deps.env,\n storage: deps.storage,\n email: deps.email,\n jobs: deps.jobs,\n realtime: deps.realtime,\n auth: deps.auth,\n request,\n resHeaders: new Headers(),\n getRawBody,\n getSession,\n peekSession,\n }\n}\n"]}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/api/context.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AA+C1C,MAAM,UAAU,gBAAgB,CAI9B,IAAmC,EACnC,OAAgB;IAEhB,uEAAuE;IACvE,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,EAAE,CAAA;IACtC,IAAI,cAA2C,CAAA;IAC/C,IAAI,cAES,CAAA;IAEb,IAAI,cAES,CAAA;IAEb,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,cAAc,GAAG,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CACtE,CAAC,OAAO,EAAE,EAAE;gBACV,cAAc,GAAG,OAAO,CAAA;gBACxB,OAAO,OAAO,CAAA;YAChB,CAAC,CACF,CAAA;QACH,CAAC;QACD,OAAO,cAAc,CAAA;IACvB,CAAC,CAAA;IAED,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,cAAc,CAAA;IAExC,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,CAAC,cAAc;YAAE,cAAc,GAAG,cAAc,CAAC,IAAI,EAAE,CAAA;QAC3D,OAAO,cAAc,CAAA;IACvB,CAAC,CAAA;IAED,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,aAAa;QACpC,OAAO;QACP,UAAU,EAAE,IAAI,OAAO,EAAE;QACzB,UAAU;QACV,UAAU;QACV,WAAW;KACZ,CAAA;AACH,CAAC","sourcesContent":["import type { AccessUser, AuthSessionResolver } from '../access'\nimport type { DbFor } from '../db'\nimport type { EmailFacade } from '../email'\nimport type { JobsRuntimeFacade } from '../jobs/define'\nimport type { BunderstackLogger } from '../logging'\nimport type { RealtimeFacade } from '../realtime/facade'\nimport type { AuthInstance, StorageFacade } from '../runtime'\n\nimport { resolveSession } from '../access'\nimport { consoleLogger } from '../logging'\n\nexport interface ApiContextDeps<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnv = Record<string, unknown>,\n> {\n db: DbFor<TSchema>\n env: TEnv\n storage: StorageFacade\n email: EmailFacade\n jobs: JobsRuntimeFacade\n realtime: RealtimeFacade<TSchema>\n auth: AuthInstance\n authResolver?: AuthSessionResolver\n logger?: BunderstackLogger\n}\n\nexport interface ApiContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnv = Record<string, unknown>,\n> {\n db: DbFor<TSchema>\n env: TEnv\n storage: StorageFacade\n email: EmailFacade\n jobs: JobsRuntimeFacade\n realtime: RealtimeFacade<TSchema>\n auth: AuthInstance\n logger: BunderstackLogger\n request: Request\n resHeaders: Headers\n getRawBody: () => Promise<string>\n getSession: () => Promise<{\n user: AccessUser | null\n activeOrganizationId: string | null\n }>\n /**\n * The session that some earlier code already resolved, or `undefined`.\n * Never starts a resolution, so a global middleware can log the caller\n * without removing the lazy session behavior that signed webhooks rely on.\n * Use it for observability only. Never use it for authorization.\n */\n peekSession: () =>\n | { user: AccessUser | null; activeOrganizationId: string | null }\n | undefined\n}\n\nexport function createApiContext<\n TSchema extends Record<string, unknown> = Record<string, unknown>,\n TEnv = Record<string, unknown>,\n>(\n deps: ApiContextDeps<TSchema, TEnv>,\n request: Request,\n): ApiContext<TSchema, TEnv> {\n // Reserve the body stream before a transport codec consumes `request`.\n const rawBodyRequest = request.clone()\n let rawBodyPromise: Promise<string> | undefined\n let sessionPromise:\n | Promise<{ user: AccessUser | null; activeOrganizationId: string | null }>\n | undefined\n\n let settledSession:\n | { user: AccessUser | null; activeOrganizationId: string | null }\n | undefined\n\n const getSession = () => {\n if (!sessionPromise) {\n sessionPromise = resolveSession(deps.authResolver, request.headers).then(\n (session) => {\n settledSession = session\n return session\n },\n )\n }\n return sessionPromise\n }\n\n const peekSession = () => settledSession\n\n const getRawBody = () => {\n if (!rawBodyPromise) rawBodyPromise = rawBodyRequest.text()\n return rawBodyPromise\n }\n\n return {\n db: deps.db,\n env: deps.env,\n storage: deps.storage,\n email: deps.email,\n jobs: deps.jobs,\n realtime: deps.realtime,\n auth: deps.auth,\n logger: deps.logger ?? consoleLogger,\n request,\n resHeaders: new Headers(),\n getRawBody,\n getSession,\n peekSession,\n }\n}\n"]}
@@ -18,11 +18,22 @@ export type RuntimeJobFailure = {
18
18
  attempts: number;
19
19
  lastError: string | null;
20
20
  };
21
+ export type RuntimeTestJob = {
22
+ id: string;
23
+ name: string;
24
+ kind: 'job' | 'cron';
25
+ status: 'pending' | 'running' | 'succeeded' | 'failed';
26
+ attempts: number;
27
+ runAt: number;
28
+ dedupeKey: string | null;
29
+ lastError: string | null;
30
+ };
21
31
  export type RuntimeTestingHandle = {
22
32
  tick(now: number): Promise<TickResult>;
23
33
  inspect(now: number): Promise<{
24
34
  runnable: number;
25
35
  failed: RuntimeJobFailure[];
36
+ jobs: RuntimeTestJob[];
26
37
  }>;
27
38
  };
28
39
  export type BackendInternals<TApp> = {
@@ -1 +1 @@
1
- {"version":3,"file":"backend-internals.d.ts","sourceRoot":"","sources":["../src/backend-internals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAEtC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,QAAQ,EAAE;YAAE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;SAAE,CAAA;QACxD,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;KACtC,CAAA;IACD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACtC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAC5B,QAAQ,EAAE,MAAM,CAAA;QAChB,MAAM,EAAE,iBAAiB,EAAE,CAAA;KAC5B,CAAC,CAAA;CACH,CAAA;AAED,MAAM,MAAM,gBAAgB,CAAC,IAAI,IAAI;IACnC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;IACzC,KAAK,CACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC1C,SAAS,CAAC,EAAE,gBAAgB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAA;CACjB,CAAA"}
1
+ {"version":3,"file":"backend-internals.d.ts","sourceRoot":"","sources":["../src/backend-internals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AACjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAE3D,eAAO,MAAM,iBAAiB,EAAE,OAAO,MAEtC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,QAAQ,EAAE;YAAE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;SAAE,CAAA;QACxD,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAA;KACtC,CAAA;IACD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,KAAK,GAAG,MAAM,CAAA;IACpB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAA;IACtD,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IACtC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAC5B,QAAQ,EAAE,MAAM,CAAA;QAChB,MAAM,EAAE,iBAAiB,EAAE,CAAA;QAC3B,IAAI,EAAE,cAAc,EAAE,CAAA;KACvB,CAAC,CAAA;CACH,CAAA;AAED,MAAM,MAAM,gBAAgB,CAAC,IAAI,IAAI;IACnC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAA;IACzC,KAAK,CACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC1C,SAAS,CAAC,EAAE,gBAAgB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAA;CACjB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"backend-internals.js","sourceRoot":"","sources":["../src/backend-internals.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,iBAAiB,GAAkB,MAAM,CAAC,GAAG,CACxD,+BAA+B,CAChC,CAAA","sourcesContent":["import type { DatabaseAdapter } from './database/adapter'\nimport type { TickResult } from './jobs'\nimport type { RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\n\nexport const BACKEND_INTERNALS: unique symbol = Symbol.for(\n 'bunderstack.backend-internals',\n)\n\nexport type ResolvedDeclaration = {\n readonly config: {\n readonly database: { readonly adapter: DatabaseAdapter }\n readonly storage?: StorageConfigInput\n }\n readonly jobsDefs: unknown\n}\n\nexport type RuntimeJobFailure = {\n id: string\n name: string\n attempts: number\n lastError: string | null\n}\n\nexport type RuntimeTestingHandle = {\n tick(now: number): Promise<TickResult>\n inspect(now: number): Promise<{\n runnable: number\n failed: RuntimeJobFailure[]\n }>\n}\n\nexport type BackendInternals<TApp> = {\n readonly declaration: ResolvedDeclaration\n start(\n source: Record<string, string | undefined>,\n overrides?: RuntimeOverrides,\n ): Promise<TApp>\n}\n"]}
1
+ {"version":3,"file":"backend-internals.js","sourceRoot":"","sources":["../src/backend-internals.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,iBAAiB,GAAkB,MAAM,CAAC,GAAG,CACxD,+BAA+B,CAChC,CAAA","sourcesContent":["import type { DatabaseAdapter } from './database/adapter'\nimport type { TickResult } from './jobs'\nimport type { RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\n\nexport const BACKEND_INTERNALS: unique symbol = Symbol.for(\n 'bunderstack.backend-internals',\n)\n\nexport type ResolvedDeclaration = {\n readonly config: {\n readonly database: { readonly adapter: DatabaseAdapter }\n readonly storage?: StorageConfigInput\n }\n readonly jobsDefs: unknown\n}\n\nexport type RuntimeJobFailure = {\n id: string\n name: string\n attempts: number\n lastError: string | null\n}\n\nexport type RuntimeTestJob = {\n id: string\n name: string\n kind: 'job' | 'cron'\n status: 'pending' | 'running' | 'succeeded' | 'failed'\n attempts: number\n runAt: number\n dedupeKey: string | null\n lastError: string | null\n}\n\nexport type RuntimeTestingHandle = {\n tick(now: number): Promise<TickResult>\n inspect(now: number): Promise<{\n runnable: number\n failed: RuntimeJobFailure[]\n jobs: RuntimeTestJob[]\n }>\n}\n\nexport type BackendInternals<TApp> = {\n readonly declaration: ResolvedDeclaration\n start(\n source: Record<string, string | undefined>,\n overrides?: RuntimeOverrides,\n ): Promise<TApp>\n}\n"]}
package/dist/backend.d.ts CHANGED
@@ -6,7 +6,7 @@ import type { BunderstackJobsBuilder, JobsDefs } from './jobs/index.js';
6
6
  import type { BunderstackManifest } from './manifest.js';
7
7
  import type { BunderstackApp, BucketNamesOf } from './runtime.js';
8
8
  import type { StorageConfigInput } from './storage/buckets.js';
9
- import type { TestFixture, TestOptions } from './testing/fixture.js';
9
+ import type { TestMethod } from './testing/fixture.js';
10
10
  import { BACKEND_INTERNALS, type BackendInternals } from './backend-internals.js';
11
11
  export type StartOptions = {
12
12
  env?: Record<string, string | undefined>;
@@ -14,7 +14,7 @@ export type StartOptions = {
14
14
  export type BunderstackBackend<TApp> = {
15
15
  readonly manifest: BunderstackManifest;
16
16
  start(options?: StartOptions): Promise<TApp>;
17
- test(options?: TestOptions): Promise<TestFixture<TApp>>;
17
+ test: TestMethod<TApp>;
18
18
  readonly [BACKEND_INTERNALS]: BackendInternals<TApp>;
19
19
  };
20
20
  export type BunderstackDefinitionConfig<TSchema extends Record<string, unknown>, TAccess extends Record<string, TableAccessInput> | undefined = undefined, TStorage extends StorageConfigInput | undefined = undefined, TEnv extends EnvConfigInput | undefined = undefined, TJobsDefs extends JobsDefs | undefined = undefined, TCustomApiRouter extends AnyORPCRouter | undefined = undefined, TRealtime = undefined> = Omit<BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>, 'realtime'> & {
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,cAAc,CAAA;AAE9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,KAAK,EAAE,sBAAsB,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAoB,MAAM,WAAW,CAAA;AAChF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEjE,OAAO,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAY9E,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,IAAI,IAAI;IACrC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAA;IACtC,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5C,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;IACvD,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAA;CACrD,CAAA;AAED,MAAM,MAAM,2BAA2B,CACrC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAAG,SAAS,EACxE,QAAQ,SAAS,kBAAkB,GAAG,SAAS,GAAG,SAAS,EAC3D,IAAI,SAAS,cAAc,GAAG,SAAS,GAAG,SAAS,EACnD,SAAS,SAAS,QAAQ,GAAG,SAAS,GAAG,SAAS,EAClD,gBAAgB,SAAS,aAAa,GAAG,SAAS,GAAG,SAAS,EAC9D,SAAS,GAAG,SAAS,IACnB,IAAI,CACN,iBAAiB,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,CAAC,EACrE,UAAU,CACX,GAAG;IACF,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,IAAI,CAAC,EACD,SAAS,GACT,CAAC,CACC,OAAO,EAAE,sBAAsB,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,KACzD,SAAS,CAAC,CAAA;CACpB,CAAA;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAItC;AAED,wBAAgB,WAAW,CACzB,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,KAAK,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,SAAS,GAChE,SAAS,EACX,KAAK,CAAC,QAAQ,SAAS,kBAAkB,GAAG,SAAS,GAAG,SAAS,EACjE,KAAK,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,GAAG,SAAS,EACzD,KAAK,CAAC,SAAS,SAAS,QAAQ,GAAG,SAAS,GAAG,SAAS,EACxD,gBAAgB,SAAS,aAAa,GAAG,SAAS,GAAG,SAAS,EAC9D,KAAK,CAAC,SAAS,SAAS,iBAAiB,CACvC,OAAO,EACP,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,gBAAgB,CACjB,CAAC,UAAU,CAAC,GAAG,SAAS,EAEzB,MAAM,EAAE,2BAA2B,CACjC,OAAO,EACP,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,gBAAgB,EAChB,SAAS,CACV,GACA,kBAAkB,CACnB,cAAc,CACZ,OAAO,EACP,OAAO,EACP,aAAa,CAAC,QAAQ,CAAC,EACvB,IAAI,EACJ,SAAS,EACT,gBAAgB,EAChB,SAAS,CACV,CACF,CA6DA"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
package/dist/backend.js CHANGED
@@ -39,13 +39,18 @@ export function bunderstack(config) {
39
39
  });
40
40
  const start = (source, overrides = {}) => materializeBunderstack({ ...config, jobs: jobsDefs }, source, overrides);
41
41
  let backend;
42
+ const test = (async (options) => {
43
+ const testing = await import('./testing.js');
44
+ return testing.createTestApp(backend, options);
45
+ });
46
+ test.configure = (options) => (async (overrides = {}) => {
47
+ const testing = await import('./testing.js');
48
+ return testing.configureTestApp(backend, options)(overrides);
49
+ });
42
50
  backend = {
43
51
  manifest,
44
52
  start: ({ env } = {}) => start(env ?? process.env),
45
- async test(options) {
46
- const testing = await import('./testing.js');
47
- return testing.createTestApp(backend, options);
48
- },
53
+ test,
49
54
  [BACKEND_INTERNALS]: { declaration: { config, jobsDefs }, start },
50
55
  };
51
56
  return backend;
@@ -1 +1 @@
1
- {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,iBAAiB,EAAyB,MAAM,qBAAqB,CAAA;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAiClD,MAAM,UAAU,oBAAoB,CAClC,KAAc;IAEd,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,iBAAiB,IAAI,KAAK,CAC1E,CAAA;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAgBzB,MAQC;IAYD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI;QAC1B,CAAC,CAAC,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAA+B,CAAC;YAC/D,CAAC,CAAC,MAAM,CAAC,IAAI;QACf,CAAC,CAAC,SAAS,CAAA;IACb,IAAI,QAAQ;QAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAExC,MAAM,QAAQ,GAAG,aAAa,CAAC;QAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO;QACP,mBAAmB,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU,IAAI,cAAc;QACjE,OAAO,EAAE,cAAc,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3C,SAAS,EAAE,MAAM,CAAC,GAAG;QACrB,aAAa,EAAE,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC;QAC7C,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC;QAClC,IAAI,EAAE,MAAM,CAAC,OAAO;YAClB,CAAC,CAAC;gBACE,GAAG,QAAQ;gBACX,CAAC,sBAAsB,CAAC,EAAE;oBACxB,IAAI,EAAE,MAAM;oBACZ,QAAQ,EAAE,sBAAsB;oBAChC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC;iBAClB;aACF;YACH,CAAC,CAAC,QAAQ;KACb,CAAC,CAAA;IAYF,MAAM,KAAK,GAAG,CACZ,MAA0C,EAC1C,YAA8B,EAAE,EAClB,EAAE,CAChB,sBAAsB,CACpB,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAW,EACtC,MAAM,EACN,SAAS,CACM,CAAA;IAEnB,IAAI,OAAgC,CAAA;IACpC,OAAO,GAAG;QACR,QAAQ;QACR,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,CACtB,KAAK,CAAC,GAAG,IAAK,OAAO,CAAC,GAA0C,CAAC;QACnE,KAAK,CAAC,IAAI,CAAC,OAAO;YAChB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAA;YACzC,OAAO,OAAO,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAChD,CAAC;QACD,CAAC,iBAAiB,CAAC,EAAE,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE;KAClE,CAAA;IACD,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["import type { AnyRouter as AnyORPCRouter } from '@orpc/server'\n\nimport type { TableAccessInput } from './access'\nimport type { BunderstackConfig } from './config'\nimport type { EnvConfigInput, ValidatedEnv } from './env'\nimport type { BunderstackJobsBuilder, JobsDefs } from './jobs'\nimport type { BunderstackManifest } from './manifest'\nimport type { BunderstackApp, BucketNamesOf, RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\nimport type { TestFixture, TestOptions } from './testing/fixture'\n\nimport { BACKEND_INTERNALS, type BackendInternals } from './backend-internals'\nimport { detectDialect } from './dialect'\nimport { emailProviderTag } from './email'\nimport { createJobsBuilder, validateJobsDefs } from './jobs'\nimport { buildManifest } from './manifest'\nimport { materializeBunderstack } from './runtime'\nimport {\n STORAGE_SWEEP_JOB_NAME,\n STORAGE_SWEEP_SCHEDULE,\n} from './storage/background'\nimport { resolveBuckets } from './storage/buckets'\n\nexport type StartOptions = {\n env?: Record<string, string | undefined>\n}\n\nexport type BunderstackBackend<TApp> = {\n readonly manifest: BunderstackManifest\n start(options?: StartOptions): Promise<TApp>\n test(options?: TestOptions): Promise<TestFixture<TApp>>\n readonly [BACKEND_INTERNALS]: BackendInternals<TApp>\n}\n\nexport type BunderstackDefinitionConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n> = Omit<\n BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>,\n 'realtime'\n> & {\n realtime?: TRealtime\n jobs?:\n | TJobsDefs\n | ((\n builder: BunderstackJobsBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TJobsDefs)\n}\n\nexport function isBunderstackBackend(\n value: unknown,\n): value is BunderstackBackend<unknown> {\n return (\n typeof value === 'object' && value !== null && BACKEND_INTERNALS in value\n )\n}\n\nexport function bunderstack<\n TSchema extends Record<string, unknown>,\n const TAccess extends Record<string, TableAccessInput> | undefined =\n undefined,\n const TStorage extends StorageConfigInput | undefined = undefined,\n const TEnv extends EnvConfigInput | undefined = undefined,\n const TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n const TRealtime extends BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter\n >['realtime'] = undefined,\n>(\n config: BunderstackDefinitionConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >,\n): BunderstackBackend<\n BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n> {\n const dialect = detectDialect(config.schema)\n const jobsDefs = config.jobs\n ? typeof config.jobs === 'function'\n ? config.jobs(createJobsBuilder<TSchema, ValidatedEnv<TEnv>>())\n : config.jobs\n : undefined\n if (jobsDefs) validateJobsDefs(jobsDefs)\n\n const manifest = buildManifest({\n schema: config.schema,\n dialect,\n migrationsDirectory: config.database.migrations ?? './migrations',\n storage: resolveBuckets(config.storage, {}),\n envConfig: config.env,\n emailProvider: emailProviderTag(config.email),\n realtime: Boolean(config.realtime),\n jobs: config.storage\n ? {\n ...jobsDefs,\n [STORAGE_SWEEP_JOB_NAME]: {\n kind: 'cron',\n schedule: STORAGE_SWEEP_SCHEDULE,\n handler: () => {},\n },\n }\n : jobsDefs,\n })\n\n type App = BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n\n const start = (\n source: Record<string, string | undefined>,\n overrides: RuntimeOverrides = {},\n ): Promise<App> =>\n materializeBunderstack(\n { ...config, jobs: jobsDefs } as never,\n source,\n overrides,\n ) as Promise<App>\n\n let backend: BunderstackBackend<App>\n backend = {\n manifest,\n start: ({ env } = {}) =>\n start(env ?? (process.env as Record<string, string | undefined>)),\n async test(options) {\n const testing = await import('./testing')\n return testing.createTestApp(backend, options)\n },\n [BACKEND_INTERNALS]: { declaration: { config, jobsDefs }, start },\n }\n return backend\n}\n"]}
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyRouter as AnyORPCRouter } from '@orpc/server'\n\nimport type { TableAccessInput } from './access'\nimport type { BunderstackConfig } from './config'\nimport type { EnvConfigInput, ValidatedEnv } from './env'\nimport type { BunderstackJobsBuilder, JobsDefs } from './jobs'\nimport type { BunderstackManifest } from './manifest'\nimport type { BunderstackApp, BucketNamesOf, RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\nimport type { TestMethod, TestOptions } from './testing/fixture'\n\nimport { BACKEND_INTERNALS, type BackendInternals } from './backend-internals'\nimport { detectDialect } from './dialect'\nimport { emailProviderTag } from './email'\nimport { createJobsBuilder, validateJobsDefs } from './jobs'\nimport { buildManifest } from './manifest'\nimport { materializeBunderstack } from './runtime'\nimport {\n STORAGE_SWEEP_JOB_NAME,\n STORAGE_SWEEP_SCHEDULE,\n} from './storage/background'\nimport { resolveBuckets } from './storage/buckets'\n\nexport type StartOptions = {\n env?: Record<string, string | undefined>\n}\n\nexport type BunderstackBackend<TApp> = {\n readonly manifest: BunderstackManifest\n start(options?: StartOptions): Promise<TApp>\n test: TestMethod<TApp>\n readonly [BACKEND_INTERNALS]: BackendInternals<TApp>\n}\n\nexport type BunderstackDefinitionConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n> = Omit<\n BunderstackConfig<TSchema, TAccess, TStorage, TEnv, TCustomApiRouter>,\n 'realtime'\n> & {\n realtime?: TRealtime\n jobs?:\n | TJobsDefs\n | ((\n builder: BunderstackJobsBuilder<TSchema, ValidatedEnv<TEnv>>,\n ) => TJobsDefs)\n}\n\nexport function isBunderstackBackend(\n value: unknown,\n): value is BunderstackBackend<unknown> {\n return (\n typeof value === 'object' && value !== null && BACKEND_INTERNALS in value\n )\n}\n\nexport function bunderstack<\n TSchema extends Record<string, unknown>,\n const TAccess extends Record<string, TableAccessInput> | undefined =\n undefined,\n const TStorage extends StorageConfigInput | undefined = undefined,\n const TEnv extends EnvConfigInput | undefined = undefined,\n const TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n const TRealtime extends BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter\n >['realtime'] = undefined,\n>(\n config: BunderstackDefinitionConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >,\n): BunderstackBackend<\n BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n> {\n const dialect = detectDialect(config.schema)\n const jobsDefs = config.jobs\n ? typeof config.jobs === 'function'\n ? config.jobs(createJobsBuilder<TSchema, ValidatedEnv<TEnv>>())\n : config.jobs\n : undefined\n if (jobsDefs) validateJobsDefs(jobsDefs)\n\n const manifest = buildManifest({\n schema: config.schema,\n dialect,\n migrationsDirectory: config.database.migrations ?? './migrations',\n storage: resolveBuckets(config.storage, {}),\n envConfig: config.env,\n emailProvider: emailProviderTag(config.email),\n realtime: Boolean(config.realtime),\n jobs: config.storage\n ? {\n ...jobsDefs,\n [STORAGE_SWEEP_JOB_NAME]: {\n kind: 'cron',\n schedule: STORAGE_SWEEP_SCHEDULE,\n handler: () => {},\n },\n }\n : jobsDefs,\n })\n\n type App = BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime\n >\n\n const start = (\n source: Record<string, string | undefined>,\n overrides: RuntimeOverrides = {},\n ): Promise<App> =>\n materializeBunderstack(\n { ...config, jobs: jobsDefs } as never,\n source,\n overrides,\n ) as Promise<App>\n\n let backend: BunderstackBackend<App>\n const test = (async (options) => {\n const testing = await import('./testing')\n return testing.createTestApp(backend, options)\n }) as TestMethod<App>\n test.configure = (options) =>\n (async (overrides: TestOptions = {}) => {\n const testing = await import('./testing')\n return testing.configureTestApp(backend, options)(overrides)\n }) as never\n backend = {\n manifest,\n start: ({ env } = {}) =>\n start(env ?? (process.env as Record<string, string | undefined>)),\n test,\n [BACKEND_INTERNALS]: { declaration: { config, jobsDefs }, start },\n }\n return backend\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAI5B;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,0HAQkC,CAAA;AAEtE,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA;AAqB3E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQF,CAAA;AAE7B,qBAAa,gBAAiB,SAAQ,KAAK;IAIvC,QAAQ,CAAC,IAAI,EAAE,oBAAoB;IAEnC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;IAL5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAGZ,IAAI,EAAE,oBAAoB,EACnC,OAAO,EAAE,MAAM,EACN,OAAO,CAAC,EAAE,OAAO,YAAA,EAC1B,OAAO,CAAC,EAAE,YAAY;CAMzB;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8B7B,CAAA;AAEJ;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;;;;;;CASZ,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAA;AAEvE,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;gBAGxB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,cAAsC,EAC5C,OAAO,CAAC,EAAE,OAAO;CAOpB"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAO5B;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,0HAQkC,CAAA;AAEtE,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAA;AAqB3E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQF,CAAA;AAE7B,qBAAa,gBAAiB,SAAQ,KAAK;IAIvC,QAAQ,CAAC,IAAI,EAAE,oBAAoB;IAEnC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO;IAL5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBAGZ,IAAI,EAAE,oBAAoB,EACnC,OAAO,EAAE,MAAM,EACN,OAAO,CAAC,EAAE,OAAO,YAAA,EAC1B,OAAO,CAAC,EAAE,YAAY;CAMzB;AAED,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgC7B,CAAA;AAEJ;;;;GAIG;AACH,eAAO,MAAM,SAAS;;;;;;;;;CASZ,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAA;AAEvE,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;gBAGxB,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,cAAsC,EAC5C,OAAO,CAAC,EAAE,OAAO;CAOpB"}
package/dist/errors.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { COMMON_ERROR_STATUS_MAP, ORPCError, os, } from '@orpc/server';
2
2
  import * as v from 'valibot';
3
+ import { consoleLogger } from './logging.js';
3
4
  import { StandardSchemaValidationError } from './standard-schema.js';
4
5
  /**
5
6
  * Every code we raise is one oRPC already knows, so handlers derive the HTTP
@@ -52,7 +53,7 @@ export class BunderstackError extends Error {
52
53
  }
53
54
  export const mapBunderstackErrors = os
54
55
  .errors(BUNDERSTACK_ERRORS)
55
- .middleware(async ({ next, path }) => {
56
+ .middleware(async ({ next, path, context }) => {
56
57
  try {
57
58
  return await next();
58
59
  }
@@ -64,7 +65,8 @@ export const mapBunderstackErrors = os
64
65
  : error;
65
66
  if (!(mapped instanceof BunderstackError)) {
66
67
  const procName = path?.length ? path.join('.') : 'unknown';
67
- console.error(`[bunderstack-api] Unhandled error in procedure "${procName}":`, error);
68
+ const logger = context.logger ?? consoleLogger;
69
+ logger.error(`[bunderstack-api] Unhandled error in procedure "${procName}":`, error);
68
70
  throw error;
69
71
  }
70
72
  throw new ORPCError(mapped.code, {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n COMMON_ERROR_STATUS_MAP,\n ORPCError,\n os,\n type ErrorMap,\n} from '@orpc/server'\nimport * as v from 'valibot'\n\nimport { StandardSchemaValidationError } from './standard-schema'\n\n/**\n * Every code we raise is one oRPC already knows, so handlers derive the HTTP\n * status from `COMMON_ERROR_STATUS_MAP` on their own and clients can use the\n * standard `isDefinedError` helpers. Do not add a code oRPC has no status for —\n * it would silently answer 500.\n */\nexport const BUNDERSTACK_ERROR_CODES = [\n 'BAD_REQUEST',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'NOT_FOUND',\n 'CONFLICT',\n 'PAYLOAD_TOO_LARGE',\n 'TOO_MANY_REQUESTS',\n] as const satisfies readonly (keyof typeof COMMON_ERROR_STATUS_MAP)[]\n\nexport type BunderstackErrorCode = (typeof BUNDERSTACK_ERROR_CODES)[number]\n\n/**\n * `data` and its `code` both default, so an application raises a declared\n * error with a message alone: `errors.NOT_FOUND({ message })`. The code is\n * already the name of the constructor, and repeating it at every call site is\n * the reason applications fall back to a raw `ORPCError`. Clients still read a\n * populated `data.code`, because the default fills it.\n */\nfunction errorDataSchema<const TCode extends BunderstackErrorCode>(\n code: TCode,\n) {\n return v.optional(\n v.strictObject({\n code: v.optional(v.literal(code), code),\n details: v.optional(v.unknown()),\n }),\n { code },\n )\n}\n\nexport const BUNDERSTACK_ERRORS = {\n BAD_REQUEST: { data: errorDataSchema('BAD_REQUEST') },\n UNAUTHORIZED: { data: errorDataSchema('UNAUTHORIZED') },\n FORBIDDEN: { data: errorDataSchema('FORBIDDEN') },\n NOT_FOUND: { data: errorDataSchema('NOT_FOUND') },\n CONFLICT: { data: errorDataSchema('CONFLICT') },\n PAYLOAD_TOO_LARGE: { data: errorDataSchema('PAYLOAD_TOO_LARGE') },\n TOO_MANY_REQUESTS: { data: errorDataSchema('TOO_MANY_REQUESTS') },\n} as const satisfies ErrorMap\n\nexport class BunderstackError extends Error {\n readonly status: number\n\n constructor(\n readonly code: BunderstackErrorCode,\n message: string,\n readonly details?: unknown,\n options?: ErrorOptions,\n ) {\n super(message, options)\n this.name = 'BunderstackError'\n this.status = COMMON_ERROR_STATUS_MAP[code]\n }\n}\n\nexport const mapBunderstackErrors = os\n .errors(BUNDERSTACK_ERRORS)\n .middleware(async ({ next, path }) => {\n try {\n return await next()\n } catch (error) {\n const mapped =\n error instanceof StandardSchemaValidationError\n ? new BunderstackError('BAD_REQUEST', error.message, error.issues, {\n cause: error,\n })\n : error\n if (!(mapped instanceof BunderstackError)) {\n const procName = path?.length ? path.join('.') : 'unknown'\n console.error(\n `[bunderstack-api] Unhandled error in procedure \"${procName}\":`,\n error,\n )\n throw error\n }\n\n throw new ORPCError(mapped.code, {\n message: mapped.message,\n data:\n mapped.details === undefined\n ? { code: mapped.code }\n : { code: mapped.code, details: mapped.details },\n cause: mapped,\n })\n }\n })\n\n/**\n * Sub-codes carried in `data.details.code` when the oRPC code alone loses\n * information the client may want to branch on. Codes that duplicate an oRPC\n * code are suppressed as redundant, see {@link CrudOperationError}.\n */\nexport const ErrorCode = {\n BAD_REQUEST: 'BAD_REQUEST',\n FORBIDDEN: 'FORBIDDEN',\n NOT_FOUND: 'NOT_FOUND',\n CONFLICT: 'CONFLICT',\n INVALID_CURSOR: 'INVALID_CURSOR',\n TOO_MANY_REQUESTS: 'TOO_MANY_REQUESTS',\n IDEMPOTENCY_REPLAY: 'IDEMPOTENCY_REPLAY',\n IDEMPOTENCY_CONFLICT: 'IDEMPOTENCY_CONFLICT',\n} as const\n\nexport type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode]\n\nexport class ListQueryError extends Error {\n readonly code: ErrorCodeValue\n readonly details?: unknown\n\n constructor(\n message: string,\n code: ErrorCodeValue = ErrorCode.BAD_REQUEST,\n details?: unknown,\n ) {\n super(message)\n this.name = 'ListQueryError'\n this.code = code\n this.details = details\n }\n}\n"]}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n COMMON_ERROR_STATUS_MAP,\n ORPCError,\n os,\n type ErrorMap,\n} from '@orpc/server'\nimport * as v from 'valibot'\n\nimport type { BunderstackLogger } from './logging'\n\nimport { consoleLogger } from './logging'\nimport { StandardSchemaValidationError } from './standard-schema'\n\n/**\n * Every code we raise is one oRPC already knows, so handlers derive the HTTP\n * status from `COMMON_ERROR_STATUS_MAP` on their own and clients can use the\n * standard `isDefinedError` helpers. Do not add a code oRPC has no status for —\n * it would silently answer 500.\n */\nexport const BUNDERSTACK_ERROR_CODES = [\n 'BAD_REQUEST',\n 'UNAUTHORIZED',\n 'FORBIDDEN',\n 'NOT_FOUND',\n 'CONFLICT',\n 'PAYLOAD_TOO_LARGE',\n 'TOO_MANY_REQUESTS',\n] as const satisfies readonly (keyof typeof COMMON_ERROR_STATUS_MAP)[]\n\nexport type BunderstackErrorCode = (typeof BUNDERSTACK_ERROR_CODES)[number]\n\n/**\n * `data` and its `code` both default, so an application raises a declared\n * error with a message alone: `errors.NOT_FOUND({ message })`. The code is\n * already the name of the constructor, and repeating it at every call site is\n * the reason applications fall back to a raw `ORPCError`. Clients still read a\n * populated `data.code`, because the default fills it.\n */\nfunction errorDataSchema<const TCode extends BunderstackErrorCode>(\n code: TCode,\n) {\n return v.optional(\n v.strictObject({\n code: v.optional(v.literal(code), code),\n details: v.optional(v.unknown()),\n }),\n { code },\n )\n}\n\nexport const BUNDERSTACK_ERRORS = {\n BAD_REQUEST: { data: errorDataSchema('BAD_REQUEST') },\n UNAUTHORIZED: { data: errorDataSchema('UNAUTHORIZED') },\n FORBIDDEN: { data: errorDataSchema('FORBIDDEN') },\n NOT_FOUND: { data: errorDataSchema('NOT_FOUND') },\n CONFLICT: { data: errorDataSchema('CONFLICT') },\n PAYLOAD_TOO_LARGE: { data: errorDataSchema('PAYLOAD_TOO_LARGE') },\n TOO_MANY_REQUESTS: { data: errorDataSchema('TOO_MANY_REQUESTS') },\n} as const satisfies ErrorMap\n\nexport class BunderstackError extends Error {\n readonly status: number\n\n constructor(\n readonly code: BunderstackErrorCode,\n message: string,\n readonly details?: unknown,\n options?: ErrorOptions,\n ) {\n super(message, options)\n this.name = 'BunderstackError'\n this.status = COMMON_ERROR_STATUS_MAP[code]\n }\n}\n\nexport const mapBunderstackErrors = os\n .errors(BUNDERSTACK_ERRORS)\n .middleware(async ({ next, path, context }) => {\n try {\n return await next()\n } catch (error) {\n const mapped =\n error instanceof StandardSchemaValidationError\n ? new BunderstackError('BAD_REQUEST', error.message, error.issues, {\n cause: error,\n })\n : error\n if (!(mapped instanceof BunderstackError)) {\n const procName = path?.length ? path.join('.') : 'unknown'\n const logger =\n (context as { logger?: BunderstackLogger }).logger ?? consoleLogger\n logger.error(\n `[bunderstack-api] Unhandled error in procedure \"${procName}\":`,\n error,\n )\n throw error\n }\n\n throw new ORPCError(mapped.code, {\n message: mapped.message,\n data:\n mapped.details === undefined\n ? { code: mapped.code }\n : { code: mapped.code, details: mapped.details },\n cause: mapped,\n })\n }\n })\n\n/**\n * Sub-codes carried in `data.details.code` when the oRPC code alone loses\n * information the client may want to branch on. Codes that duplicate an oRPC\n * code are suppressed as redundant, see {@link CrudOperationError}.\n */\nexport const ErrorCode = {\n BAD_REQUEST: 'BAD_REQUEST',\n FORBIDDEN: 'FORBIDDEN',\n NOT_FOUND: 'NOT_FOUND',\n CONFLICT: 'CONFLICT',\n INVALID_CURSOR: 'INVALID_CURSOR',\n TOO_MANY_REQUESTS: 'TOO_MANY_REQUESTS',\n IDEMPOTENCY_REPLAY: 'IDEMPOTENCY_REPLAY',\n IDEMPOTENCY_CONFLICT: 'IDEMPOTENCY_CONFLICT',\n} as const\n\nexport type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode]\n\nexport class ListQueryError extends Error {\n readonly code: ErrorCodeValue\n readonly details?: unknown\n\n constructor(\n message: string,\n code: ErrorCodeValue = ErrorCode.BAD_REQUEST,\n details?: unknown,\n ) {\n super(message)\n this.name = 'ListQueryError'\n this.code = code\n this.details = details\n }\n}\n"]}
@@ -1,10 +1,12 @@
1
1
  import type { AnyDb } from '../dialect.js';
2
+ import type { BunderstackLogger } from '../logging.js';
2
3
  import type { JobsDefs, JobsRuntimeFacade, TickResult } from './define.js';
3
4
  export declare function createJobRunner(deps: {
4
5
  db: AnyDb;
5
6
  defs: JobsDefs;
6
7
  /** Handler ctx WITHOUT `jobs`; the facade is injected via setJobsFacade. */
7
8
  ctx: Record<string, unknown>;
9
+ logger?: BunderstackLogger;
8
10
  }): {
9
11
  tick(now?: number): Promise<TickResult>;
10
12
  inspect(now: number): Promise<{
@@ -15,6 +17,16 @@ export declare function createJobRunner(deps: {
15
17
  attempts: number;
16
18
  lastError: string | null;
17
19
  }[];
20
+ jobs: {
21
+ id: string;
22
+ name: string;
23
+ kind: "job" | "cron";
24
+ status: "pending" | "running" | "succeeded" | "failed";
25
+ attempts: number;
26
+ runAt: number;
27
+ dedupeKey: string | null;
28
+ lastError: string | null;
29
+ }[];
18
30
  }>;
19
31
  setJobsFacade(f: JobsRuntimeFacade): void;
20
32
  };
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAEV,QAAQ,EACR,iBAAiB,EACjB,UAAU,EACX,MAAM,UAAU,CAAA;AAgDjB,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,EAAE,EAAE,KAAK,CAAA;IACT,IAAI,EAAE,QAAQ,CAAA;IACd,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC7B;eA8SmB,MAAM,GAAgB,OAAO,CAAC,UAAU,CAAC;iBAStC,MAAM;;;;;;;;;qBA+BR,iBAAiB;EAIrC"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAEV,QAAQ,EACR,iBAAiB,EACjB,UAAU,EACX,MAAM,UAAU,CAAA;AAiDjB,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,EAAE,EAAE,KAAK,CAAA;IACT,IAAI,EAAE,QAAQ,CAAA;IACd,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAC3B;eA+SmB,MAAM,GAAgB,OAAO,CAAC,UAAU,CAAC;iBAStC,MAAM;;;;;;;;;;;;oBAiEb,SAAS,GACT,SAAS,GACT,WAAW,GACX,QAAQ;;;;;;;qBAQH,iBAAiB;EAIrC"}
@@ -3,6 +3,7 @@
3
3
  import { and, eq, inArray, is, isNotNull, lt, lte, max, sql } from 'drizzle-orm';
4
4
  import { PgDatabase } from 'drizzle-orm/pg-core';
5
5
  import { jobsTableFor } from '../internal-tables.js';
6
+ import { consoleLogger } from '../logging.js';
6
7
  import { validateStandardSchema } from '../standard-schema.js';
7
8
  import { parseCron } from './cron.js';
8
9
  import { backoffMs, DEFAULT_RETRIES, DEFAULT_TIMEOUT_MS } from './define.js';
@@ -33,6 +34,7 @@ function terminalPatch() {
33
34
  }
34
35
  export function createJobRunner(deps) {
35
36
  const { db, defs } = deps;
37
+ const logger = deps.logger ?? consoleLogger;
36
38
  const t = jobsTableFor(db);
37
39
  const ctx = { ...deps.ctx };
38
40
  let lastReapAt = 0;
@@ -89,7 +91,7 @@ export function createJobRunner(deps) {
89
91
  await def.onFailed(input, error, ctx);
90
92
  }
91
93
  catch (hookErr) {
92
- console.error('[bunderstack] onFailed hook threw:', hookErr);
94
+ logger.error('[bunderstack] onFailed hook threw:', hookErr);
93
95
  }
94
96
  }
95
97
  /** running rows whose lease expired → pending (or failed when exhausted). */
@@ -320,6 +322,18 @@ export function createJobRunner(deps) {
320
322
  })
321
323
  .from(t)
322
324
  .where(eq(t.status, 'failed'));
325
+ const rows = await db
326
+ .select({
327
+ id: t.id,
328
+ type: t.type,
329
+ status: t.status,
330
+ attempts: t.attempts,
331
+ runAt: t.runAt,
332
+ dedupeKey: t.dedupeKey,
333
+ lastError: t.lastError,
334
+ createdAt: t.createdAt,
335
+ })
336
+ .from(t);
323
337
  return {
324
338
  runnable: runnableRows.length,
325
339
  failed: failed.map((row) => ({
@@ -330,6 +344,23 @@ export function createJobRunner(deps) {
330
344
  attempts: Number(row.attempts),
331
345
  lastError: row.lastError,
332
346
  })),
347
+ jobs: rows
348
+ .sort((left, right) => Number(left.createdAt) - Number(right.createdAt) ||
349
+ String(left.id).localeCompare(String(right.id)))
350
+ .map((row) => ({
351
+ id: String(row.id),
352
+ name: row.type.startsWith(CRON_PREFIX)
353
+ ? row.type.slice(CRON_PREFIX.length)
354
+ : row.type,
355
+ kind: row.type.startsWith(CRON_PREFIX)
356
+ ? 'cron'
357
+ : 'job',
358
+ status: row.status,
359
+ attempts: Number(row.attempts),
360
+ runAt: Number(row.runAt),
361
+ dedupeKey: row.dedupeKey,
362
+ lastError: row.lastError,
363
+ })),
333
364
  };
334
365
  },
335
366
  setJobsFacade(f) {
@@ -1 +1 @@
1
- {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/worker.ts — the queue worker. One `tick()` is a full cycle:\n// recover expired leases → reap old succeeded rows → claim and run queue jobs.\nimport { and, eq, inArray, is, isNotNull, lt, lte, max, sql } from 'drizzle-orm'\nimport { PgDatabase } from 'drizzle-orm/pg-core'\n\nimport type { AnyDb } from '../dialect'\nimport type {\n AnyBackgroundDefinition,\n JobsDefs,\n JobsRuntimeFacade,\n TickResult,\n} from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { validateStandardSchema } from '../standard-schema'\nimport { parseCron } from './cron'\nimport { backoffMs, DEFAULT_RETRIES, DEFAULT_TIMEOUT_MS } from './define'\nimport { enqueueJob } from './queue'\nimport { CRON_PREFIX, floorSlot, slotsDue, SLOT_MS } from './slots'\n\nconst CLAIM_BATCH = 10\nconst SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000\nconst REAP_INTERVAL_MS = 60 * 60_000\n\ntype JobRow = {\n id: string\n type: string\n payloadJson: string\n attempts: number\n runAt: number\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err))\n}\n\nfunction maxAttempts(def: AnyBackgroundDefinition): number {\n return 1 + (def.retries ?? DEFAULT_RETRIES)\n}\n\n/** Resolve a stored row type back to its definition. Cron rows carry the\n * reserved prefix; queue rows use the definition key verbatim. */\nfunction definitionFor(\n defs: JobsDefs,\n type: string,\n): AnyBackgroundDefinition | undefined {\n if (type.startsWith(CRON_PREFIX)) {\n const def = defs[type.slice(CRON_PREFIX.length)]\n return def?.kind === 'cron' ? def : undefined\n }\n const def = defs[type]\n return def?.kind === 'job' ? def : undefined\n}\n\n/** Terminal queue rows release their dedupe key. */\nfunction terminalPatch() {\n return { dedupeKey: null }\n}\n\nexport function createJobRunner(deps: {\n db: AnyDb\n defs: JobsDefs\n /** Handler ctx WITHOUT `jobs`; the facade is injected via setJobsFacade. */\n ctx: Record<string, unknown>\n}) {\n const { db, defs } = deps\n const t = jobsTableFor(db)\n const ctx = { ...deps.ctx } as Record<string, unknown>\n let lastReapAt = 0\n\n /** Cron rows carry no payload — their handler input is the slot itself. */\n function resolveInput(def: AnyBackgroundDefinition, row: JobRow): unknown {\n if (def.kind === 'cron') {\n return { scheduledFor: new Date(Number(row.runAt)) }\n }\n const raw = JSON.parse(row.payloadJson)\n return def.input\n ? validateStandardSchema(def.input, raw, 'job payload')\n : undefined\n }\n\n /**\n * The watermark is the newest slot we already stored for this cron. When no\n * rows exist — a newly declared cron, or one whose rows were reaped — anchor\n * one slot before now so the current minute is eligible and nothing older is.\n */\n async function cronWatermark(type: string, now: number): Promise<number> {\n const rows = await db\n .select({ latest: max(t.runAt) })\n .from(t)\n .where(eq(t.type, type))\n const latest = rows[0]?.latest\n return latest == null ? floorSlot(now) - SLOT_MS : Number(latest)\n }\n\n /** Enqueue a row per due slot. The unique(type, dedupeKey) constraint makes\n * this safe to run concurrently in any number of processes. */\n async function materializeCronSlots(now: number) {\n for (const [name, def] of Object.entries(defs)) {\n if (def.kind !== 'cron') continue\n const type = `${CRON_PREFIX}${name}`\n const from = await cronWatermark(type, now)\n const slots = slotsDue({\n cron: parseCron(def.schedule),\n from,\n to: now,\n catchUp: def.catchUp,\n catchUpWindowMs: def.catchUpWindow,\n })\n for (const slot of slots) {\n await enqueueJob(db, defs, name, null, {\n runAt: slot,\n dedupeKey: String(slot),\n })\n }\n }\n }\n\n async function fireOnFailed(\n def: AnyBackgroundDefinition,\n input: unknown,\n error: Error,\n ) {\n if (!def.onFailed) return\n try {\n await (def.onFailed as (i: unknown, e: Error, c: unknown) => unknown)(\n input,\n error,\n ctx,\n )\n } catch (hookErr) {\n console.error('[bunderstack] onFailed hook threw:', hookErr)\n }\n }\n\n /** running rows whose lease expired → pending (or failed when exhausted). */\n async function recoverExpiredLeases(now: number) {\n const expired: (JobRow & { lastError: string | null })[] = await db\n .select({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n lastError: t.lastError,\n runAt: t.runAt,\n })\n .from(t)\n .where(\n and(\n eq(t.status, 'running'),\n isNotNull(t.lockedUntil),\n lt(t.lockedUntil, now),\n ),\n )\n for (const row of expired) {\n const def = definitionFor(defs, row.type)\n const error = new Error('lease expired (worker crashed or timed out)')\n if (!def) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: `unknown job type \"${row.type}\"`,\n dedupeKey: null,\n })\n .where(eq(t.id, row.id))\n continue\n }\n if (Number(row.attempts) >= maxAttempts(def)) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: error.message,\n ...terminalPatch(),\n })\n .where(eq(t.id, row.id))\n await fireOnFailed(def, resolveInput(def, row), error)\n } else {\n await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: error.message,\n })\n .where(eq(t.id, row.id))\n }\n }\n }\n\n async function reapSucceeded(now: number) {\n await db\n .delete(t)\n .where(\n and(\n eq(t.status, 'succeeded'),\n lt(t.finishedAt, now - SUCCEEDED_RETENTION_MS),\n ),\n )\n }\n\n /** Atomically claim up to `limit` runnable jobs of one type. */\n async function claim(\n type: string,\n limit: number,\n now: number,\n leaseUntil: number,\n ): Promise<JobRow[]> {\n const pendingIds = db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'pending'), lte(t.runAt, now)))\n .orderBy(t.runAt)\n .limit(limit)\n // PG: lock the selected rows so concurrent replicas skip them. SQLite's\n // single-writer model makes the one-statement UPDATE atomic on its own.\n const sub = is(db, PgDatabase)\n ? (\n pendingIds as unknown as {\n for: (m: string, o: object) => typeof pendingIds\n }\n ).for('update', { skipLocked: true })\n : pendingIds\n const rows: JobRow[] = await db\n .update(t)\n .set({\n status: 'running',\n lockedUntil: leaseUntil,\n attempts: sql`${t.attempts} + 1`,\n })\n .where(and(inArray(t.id, sub), eq(t.status, 'pending')))\n .returning({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n runAt: t.runAt,\n })\n return rows\n }\n\n // `now` is the tick's injected clock: retry runAt math uses it so tests can\n // drive backoff deterministically. finishedAt uses the real clock (a handler\n // may run long past the tick's start).\n async function runJob(\n row: JobRow,\n def: AnyBackgroundDefinition,\n now: number,\n leaseUntil: number,\n ): Promise<'ran' | 'failed' | 'lost'> {\n let input: unknown\n try {\n input = resolveInput(def, row)\n } catch (err) {\n // Stored payload no longer parses (schema drift): retrying can't help.\n const e = toError(err)\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, undefined, e)\n return 'failed'\n }\n try {\n await (def.handler as (i: unknown, c: unknown) => unknown)(input, ctx)\n const updated = await db\n .update(t)\n .set({\n status: 'succeeded',\n finishedAt: Date.now(),\n lockedUntil: null,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n return 'ran'\n } catch (err) {\n const e = toError(err)\n if (Number(row.attempts) < maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: e.message,\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n } else {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, input, e)\n }\n return 'failed'\n }\n }\n\n async function runClaimable(now: number): Promise<TickResult> {\n const claimedWork: Array<{\n row: JobRow\n def: AnyBackgroundDefinition\n leaseUntil: number\n }> = []\n let totalClaimed = 0\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n let limit = CLAIM_BATCH\n if (def.kind === 'job' && def.concurrency !== undefined) {\n const runningRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'running')))\n const capacity = def.concurrency - runningRows.length\n if (capacity <= 0) continue\n limit = Math.min(limit, capacity)\n }\n const leaseUntil = now + (def.timeout ?? DEFAULT_TIMEOUT_MS)\n const claimed = await claim(type, limit, now, leaseUntil)\n totalClaimed += claimed.length\n for (const row of claimed) claimedWork.push({ row, def, leaseUntil })\n }\n // Claim the whole tick snapshot before starting any handler. Work enqueued\n // by a handler therefore belongs to the next tick, regardless of the\n // declaration order of its target job type.\n const outcomes = await Promise.all(\n claimedWork.map(({ row, def, leaseUntil }) =>\n runJob(row, def, now, leaseUntil),\n ),\n )\n let ran = 0\n let failed = 0\n for (const outcome of outcomes) {\n if (outcome === 'ran') ran++\n else if (outcome === 'failed') failed++\n }\n return { claimed: totalClaimed, ran, failed }\n }\n\n return {\n async tick(now: number = Date.now()): Promise<TickResult> {\n await materializeCronSlots(now)\n await recoverExpiredLeases(now)\n if (now - lastReapAt >= REAP_INTERVAL_MS) {\n lastReapAt = now\n await reapSucceeded(now)\n }\n return runClaimable(now)\n },\n async inspect(now: number) {\n const runnableRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.status, 'pending'), lte(t.runAt, now)))\n const failed: Array<{\n id: string\n type: string\n attempts: number\n lastError: string | null\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n attempts: t.attempts,\n lastError: t.lastError,\n })\n .from(t)\n .where(eq(t.status, 'failed'))\n return {\n runnable: runnableRows.length,\n failed: failed.map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n attempts: Number(row.attempts),\n lastError: row.lastError,\n })),\n }\n },\n setJobsFacade(f: JobsRuntimeFacade) {\n ctx.jobs = f\n },\n }\n}\n"]}
1
+ {"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/jobs/worker.ts"],"names":[],"mappings":"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","sourcesContent":["// src/jobs/worker.ts — the queue worker. One `tick()` is a full cycle:\n// recover expired leases → reap old succeeded rows → claim and run queue jobs.\nimport { and, eq, inArray, is, isNotNull, lt, lte, max, sql } from 'drizzle-orm'\nimport { PgDatabase } from 'drizzle-orm/pg-core'\n\nimport type { AnyDb } from '../dialect'\nimport type { BunderstackLogger } from '../logging'\nimport type {\n AnyBackgroundDefinition,\n JobsDefs,\n JobsRuntimeFacade,\n TickResult,\n} from './define'\n\nimport { jobsTableFor } from '../internal-tables'\nimport { consoleLogger } from '../logging'\nimport { validateStandardSchema } from '../standard-schema'\nimport { parseCron } from './cron'\nimport { backoffMs, DEFAULT_RETRIES, DEFAULT_TIMEOUT_MS } from './define'\nimport { enqueueJob } from './queue'\nimport { CRON_PREFIX, floorSlot, slotsDue, SLOT_MS } from './slots'\n\nconst CLAIM_BATCH = 10\nconst SUCCEEDED_RETENTION_MS = 24 * 60 * 60 * 1000\nconst REAP_INTERVAL_MS = 60 * 60_000\n\ntype JobRow = {\n id: string\n type: string\n payloadJson: string\n attempts: number\n runAt: number\n}\n\nfunction toError(err: unknown): Error {\n return err instanceof Error ? err : new Error(String(err))\n}\n\nfunction maxAttempts(def: AnyBackgroundDefinition): number {\n return 1 + (def.retries ?? DEFAULT_RETRIES)\n}\n\n/** Resolve a stored row type back to its definition. Cron rows carry the\n * reserved prefix; queue rows use the definition key verbatim. */\nfunction definitionFor(\n defs: JobsDefs,\n type: string,\n): AnyBackgroundDefinition | undefined {\n if (type.startsWith(CRON_PREFIX)) {\n const def = defs[type.slice(CRON_PREFIX.length)]\n return def?.kind === 'cron' ? def : undefined\n }\n const def = defs[type]\n return def?.kind === 'job' ? def : undefined\n}\n\n/** Terminal queue rows release their dedupe key. */\nfunction terminalPatch() {\n return { dedupeKey: null }\n}\n\nexport function createJobRunner(deps: {\n db: AnyDb\n defs: JobsDefs\n /** Handler ctx WITHOUT `jobs`; the facade is injected via setJobsFacade. */\n ctx: Record<string, unknown>\n logger?: BunderstackLogger\n}) {\n const { db, defs } = deps\n const logger = deps.logger ?? consoleLogger\n const t = jobsTableFor(db)\n const ctx = { ...deps.ctx } as Record<string, unknown>\n let lastReapAt = 0\n\n /** Cron rows carry no payload — their handler input is the slot itself. */\n function resolveInput(def: AnyBackgroundDefinition, row: JobRow): unknown {\n if (def.kind === 'cron') {\n return { scheduledFor: new Date(Number(row.runAt)) }\n }\n const raw = JSON.parse(row.payloadJson)\n return def.input\n ? validateStandardSchema(def.input, raw, 'job payload')\n : undefined\n }\n\n /**\n * The watermark is the newest slot we already stored for this cron. When no\n * rows exist — a newly declared cron, or one whose rows were reaped — anchor\n * one slot before now so the current minute is eligible and nothing older is.\n */\n async function cronWatermark(type: string, now: number): Promise<number> {\n const rows = await db\n .select({ latest: max(t.runAt) })\n .from(t)\n .where(eq(t.type, type))\n const latest = rows[0]?.latest\n return latest == null ? floorSlot(now) - SLOT_MS : Number(latest)\n }\n\n /** Enqueue a row per due slot. The unique(type, dedupeKey) constraint makes\n * this safe to run concurrently in any number of processes. */\n async function materializeCronSlots(now: number) {\n for (const [name, def] of Object.entries(defs)) {\n if (def.kind !== 'cron') continue\n const type = `${CRON_PREFIX}${name}`\n const from = await cronWatermark(type, now)\n const slots = slotsDue({\n cron: parseCron(def.schedule),\n from,\n to: now,\n catchUp: def.catchUp,\n catchUpWindowMs: def.catchUpWindow,\n })\n for (const slot of slots) {\n await enqueueJob(db, defs, name, null, {\n runAt: slot,\n dedupeKey: String(slot),\n })\n }\n }\n }\n\n async function fireOnFailed(\n def: AnyBackgroundDefinition,\n input: unknown,\n error: Error,\n ) {\n if (!def.onFailed) return\n try {\n await (def.onFailed as (i: unknown, e: Error, c: unknown) => unknown)(\n input,\n error,\n ctx,\n )\n } catch (hookErr) {\n logger.error('[bunderstack] onFailed hook threw:', hookErr)\n }\n }\n\n /** running rows whose lease expired → pending (or failed when exhausted). */\n async function recoverExpiredLeases(now: number) {\n const expired: (JobRow & { lastError: string | null })[] = await db\n .select({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n lastError: t.lastError,\n runAt: t.runAt,\n })\n .from(t)\n .where(\n and(\n eq(t.status, 'running'),\n isNotNull(t.lockedUntil),\n lt(t.lockedUntil, now),\n ),\n )\n for (const row of expired) {\n const def = definitionFor(defs, row.type)\n const error = new Error('lease expired (worker crashed or timed out)')\n if (!def) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: `unknown job type \"${row.type}\"`,\n dedupeKey: null,\n })\n .where(eq(t.id, row.id))\n continue\n }\n if (Number(row.attempts) >= maxAttempts(def)) {\n await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: now,\n lockedUntil: null,\n lastError: error.message,\n ...terminalPatch(),\n })\n .where(eq(t.id, row.id))\n await fireOnFailed(def, resolveInput(def, row), error)\n } else {\n await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: error.message,\n })\n .where(eq(t.id, row.id))\n }\n }\n }\n\n async function reapSucceeded(now: number) {\n await db\n .delete(t)\n .where(\n and(\n eq(t.status, 'succeeded'),\n lt(t.finishedAt, now - SUCCEEDED_RETENTION_MS),\n ),\n )\n }\n\n /** Atomically claim up to `limit` runnable jobs of one type. */\n async function claim(\n type: string,\n limit: number,\n now: number,\n leaseUntil: number,\n ): Promise<JobRow[]> {\n const pendingIds = db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'pending'), lte(t.runAt, now)))\n .orderBy(t.runAt)\n .limit(limit)\n // PG: lock the selected rows so concurrent replicas skip them. SQLite's\n // single-writer model makes the one-statement UPDATE atomic on its own.\n const sub = is(db, PgDatabase)\n ? (\n pendingIds as unknown as {\n for: (m: string, o: object) => typeof pendingIds\n }\n ).for('update', { skipLocked: true })\n : pendingIds\n const rows: JobRow[] = await db\n .update(t)\n .set({\n status: 'running',\n lockedUntil: leaseUntil,\n attempts: sql`${t.attempts} + 1`,\n })\n .where(and(inArray(t.id, sub), eq(t.status, 'pending')))\n .returning({\n id: t.id,\n type: t.type,\n payloadJson: t.payloadJson,\n attempts: t.attempts,\n runAt: t.runAt,\n })\n return rows\n }\n\n // `now` is the tick's injected clock: retry runAt math uses it so tests can\n // drive backoff deterministically. finishedAt uses the real clock (a handler\n // may run long past the tick's start).\n async function runJob(\n row: JobRow,\n def: AnyBackgroundDefinition,\n now: number,\n leaseUntil: number,\n ): Promise<'ran' | 'failed' | 'lost'> {\n let input: unknown\n try {\n input = resolveInput(def, row)\n } catch (err) {\n // Stored payload no longer parses (schema drift): retrying can't help.\n const e = toError(err)\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, undefined, e)\n return 'failed'\n }\n try {\n await (def.handler as (i: unknown, c: unknown) => unknown)(input, ctx)\n const updated = await db\n .update(t)\n .set({\n status: 'succeeded',\n finishedAt: Date.now(),\n lockedUntil: null,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n return 'ran'\n } catch (err) {\n const e = toError(err)\n if (Number(row.attempts) < maxAttempts(def)) {\n const updated = await db\n .update(t)\n .set({\n status: 'pending',\n lockedUntil: null,\n runAt: now + backoffMs(def, Number(row.attempts)),\n lastError: e.message,\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n } else {\n const updated = await db\n .update(t)\n .set({\n status: 'failed',\n finishedAt: Date.now(),\n lockedUntil: null,\n lastError: e.message,\n ...terminalPatch(),\n })\n .where(and(eq(t.id, row.id), eq(t.lockedUntil, leaseUntil)))\n .returning({ id: t.id })\n if (!updated[0]) return 'lost'\n await fireOnFailed(def, input, e)\n }\n return 'failed'\n }\n }\n\n async function runClaimable(now: number): Promise<TickResult> {\n const claimedWork: Array<{\n row: JobRow\n def: AnyBackgroundDefinition\n leaseUntil: number\n }> = []\n let totalClaimed = 0\n for (const [name, def] of Object.entries(defs)) {\n const type = def.kind === 'cron' ? `${CRON_PREFIX}${name}` : name\n let limit = CLAIM_BATCH\n if (def.kind === 'job' && def.concurrency !== undefined) {\n const runningRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.type, type), eq(t.status, 'running')))\n const capacity = def.concurrency - runningRows.length\n if (capacity <= 0) continue\n limit = Math.min(limit, capacity)\n }\n const leaseUntil = now + (def.timeout ?? DEFAULT_TIMEOUT_MS)\n const claimed = await claim(type, limit, now, leaseUntil)\n totalClaimed += claimed.length\n for (const row of claimed) claimedWork.push({ row, def, leaseUntil })\n }\n // Claim the whole tick snapshot before starting any handler. Work enqueued\n // by a handler therefore belongs to the next tick, regardless of the\n // declaration order of its target job type.\n const outcomes = await Promise.all(\n claimedWork.map(({ row, def, leaseUntil }) =>\n runJob(row, def, now, leaseUntil),\n ),\n )\n let ran = 0\n let failed = 0\n for (const outcome of outcomes) {\n if (outcome === 'ran') ran++\n else if (outcome === 'failed') failed++\n }\n return { claimed: totalClaimed, ran, failed }\n }\n\n return {\n async tick(now: number = Date.now()): Promise<TickResult> {\n await materializeCronSlots(now)\n await recoverExpiredLeases(now)\n if (now - lastReapAt >= REAP_INTERVAL_MS) {\n lastReapAt = now\n await reapSucceeded(now)\n }\n return runClaimable(now)\n },\n async inspect(now: number) {\n const runnableRows = await db\n .select({ id: t.id })\n .from(t)\n .where(and(eq(t.status, 'pending'), lte(t.runAt, now)))\n const failed: Array<{\n id: string\n type: string\n attempts: number\n lastError: string | null\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n attempts: t.attempts,\n lastError: t.lastError,\n })\n .from(t)\n .where(eq(t.status, 'failed'))\n const rows: Array<{\n id: string\n type: string\n status: string\n attempts: number\n runAt: number\n dedupeKey: string | null\n lastError: string | null\n createdAt: number\n }> = await db\n .select({\n id: t.id,\n type: t.type,\n status: t.status,\n attempts: t.attempts,\n runAt: t.runAt,\n dedupeKey: t.dedupeKey,\n lastError: t.lastError,\n createdAt: t.createdAt,\n })\n .from(t)\n return {\n runnable: runnableRows.length,\n failed: failed.map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n attempts: Number(row.attempts),\n lastError: row.lastError,\n })),\n jobs: rows\n .sort(\n (left, right) =>\n Number(left.createdAt) - Number(right.createdAt) ||\n String(left.id).localeCompare(String(right.id)),\n )\n .map((row) => ({\n id: String(row.id),\n name: row.type.startsWith(CRON_PREFIX)\n ? row.type.slice(CRON_PREFIX.length)\n : row.type,\n kind: row.type.startsWith(CRON_PREFIX)\n ? ('cron' as const)\n : ('job' as const),\n status: row.status as\n | 'pending'\n | 'running'\n | 'succeeded'\n | 'failed',\n attempts: Number(row.attempts),\n runAt: Number(row.runAt),\n dedupeKey: row.dedupeKey,\n lastError: row.lastError,\n })),\n }\n },\n setJobsFacade(f: JobsRuntimeFacade) {\n ctx.jobs = f\n },\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export type BunderstackLogLevel = 'info' | 'warn' | 'error';
2
+ export type BunderstackLogger = {
3
+ info(...args: unknown[]): void;
4
+ warn(...args: unknown[]): void;
5
+ error(...args: unknown[]): void;
6
+ };
7
+ export declare const consoleLogger: BunderstackLogger;
8
+ //# sourceMappingURL=logging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC9B,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC9B,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,iBAI3B,CAAA"}
@@ -0,0 +1,6 @@
1
+ export const consoleLogger = {
2
+ info: (...args) => console.info(...args),
3
+ warn: (...args) => console.warn(...args),
4
+ error: (...args) => console.error(...args),
5
+ };
6
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.js","sourceRoot":"","sources":["../src/logging.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,aAAa,GAAsB;IAC9C,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;CAC3C,CAAA","sourcesContent":["export type BunderstackLogLevel = 'info' | 'warn' | 'error'\n\nexport type BunderstackLogger = {\n info(...args: unknown[]): void\n warn(...args: unknown[]): void\n error(...args: unknown[]): void\n}\n\nexport const consoleLogger: BunderstackLogger = {\n info: (...args) => console.info(...args),\n warn: (...args) => console.warn(...args),\n error: (...args) => console.error(...args),\n}\n"]}
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { AnyRouter as AnyORPCRouter } from '@orpc/server';
2
2
  import type { TableAccessInput } from './access.js';
3
+ import type { AuthSessionResolver } from './access.js';
3
4
  import type { RealtimeApiRouter } from './api/realtime-router.js';
4
5
  import type { CrudApiRouterFor, MergeApiRouterTypes, UnifiedApiRouter } from './api/types.js';
5
6
  import type { RuntimeTestingHandle } from './backend-internals.js';
@@ -13,6 +14,7 @@ import { type BunderstackConfig } from './config.js';
13
14
  import { type EmailAdapter, type EmailFacade } from './email.js';
14
15
  import { type EnvConfigInput, type ValidatedEnv } from './env.js';
15
16
  import { type LifecycleStatus } from './lifecycle.js';
17
+ import { type BunderstackLogger } from './logging.js';
16
18
  import { type RealtimeFacade } from './realtime/facade.js';
17
19
  export type AuthInstance = ReturnType<typeof createAuth>;
18
20
  export type RuntimeOverrides = {
@@ -21,6 +23,8 @@ export type RuntimeOverrides = {
21
23
  emailAdapter?: EmailAdapter;
22
24
  forceMemoryRealtime?: boolean;
23
25
  backgroundAutoStart?: false;
26
+ authResolver?: AuthSessionResolver;
27
+ logger?: BunderstackLogger;
24
28
  /** Private callback used by backend.test(); never exposed on the app. */
25
29
  captureTestingHandle?: (handle: RuntimeTestingHandle) => void;
26
30
  };