bunderstack 0.21.1 → 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 (145) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +15 -10
  3. package/dist/api/builder.d.ts +2 -2
  4. package/dist/api/builder.js +1 -1
  5. package/dist/api/builder.js.map +1 -1
  6. package/dist/api/context.d.ts +4 -1
  7. package/dist/api/context.d.ts.map +1 -1
  8. package/dist/api/context.js +2 -0
  9. package/dist/api/context.js.map +1 -1
  10. package/dist/auth.d.ts +8 -0
  11. package/dist/auth.d.ts.map +1 -1
  12. package/dist/auth.js +14 -0
  13. package/dist/auth.js.map +1 -1
  14. package/dist/backend-internals.d.ts +43 -0
  15. package/dist/backend-internals.d.ts.map +1 -0
  16. package/dist/backend-internals.js +2 -0
  17. package/dist/backend-internals.js.map +1 -0
  18. package/dist/backend.d.ts +26 -0
  19. package/dist/backend.d.ts.map +1 -0
  20. package/dist/backend.js +58 -0
  21. package/dist/backend.js.map +1 -0
  22. package/dist/blueprint-generator.d.ts.map +1 -1
  23. package/dist/blueprint-generator.js +41 -51
  24. package/dist/blueprint-generator.js.map +1 -1
  25. package/dist/config.d.ts +0 -6
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js.map +1 -1
  28. package/dist/database/adapter.d.ts +10 -3
  29. package/dist/database/adapter.d.ts.map +1 -1
  30. package/dist/database/adapter.js.map +1 -1
  31. package/dist/database/bun-sql.d.ts.map +1 -1
  32. package/dist/database/bun-sql.js +1 -3
  33. package/dist/database/bun-sql.js.map +1 -1
  34. package/dist/database/bun-sqlite.d.ts +8 -0
  35. package/dist/database/bun-sqlite.d.ts.map +1 -0
  36. package/dist/database/bun-sqlite.js +56 -0
  37. package/dist/database/bun-sqlite.js.map +1 -0
  38. package/dist/database/libsql.d.ts.map +1 -1
  39. package/dist/database/libsql.js +25 -3
  40. package/dist/database/libsql.js.map +1 -1
  41. package/dist/database/pglite.d.ts.map +1 -1
  42. package/dist/database/pglite.js +25 -4
  43. package/dist/database/pglite.js.map +1 -1
  44. package/dist/database/postgres-js.d.ts.map +1 -1
  45. package/dist/database/postgres-js.js +1 -3
  46. package/dist/database/postgres-js.js.map +1 -1
  47. package/dist/db.d.ts +1 -2
  48. package/dist/db.d.ts.map +1 -1
  49. package/dist/db.js +5 -3
  50. package/dist/db.js.map +1 -1
  51. package/dist/email.d.ts +3 -1
  52. package/dist/email.d.ts.map +1 -1
  53. package/dist/email.js +9 -2
  54. package/dist/email.js.map +1 -1
  55. package/dist/env.d.ts +1 -1
  56. package/dist/env.d.ts.map +1 -1
  57. package/dist/env.js +1 -4
  58. package/dist/env.js.map +1 -1
  59. package/dist/errors.d.ts.map +1 -1
  60. package/dist/errors.js +4 -2
  61. package/dist/errors.js.map +1 -1
  62. package/dist/index.d.ts +3 -108
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +1 -484
  65. package/dist/index.js.map +1 -1
  66. package/dist/jobs/define.d.ts +1 -1
  67. package/dist/jobs/define.d.ts.map +1 -1
  68. package/dist/jobs/define.js.map +1 -1
  69. package/dist/jobs/index.js +1 -1
  70. package/dist/jobs/index.js.map +1 -1
  71. package/dist/jobs/queue.d.ts +1 -1
  72. package/dist/jobs/queue.d.ts.map +1 -1
  73. package/dist/jobs/queue.js +1 -2
  74. package/dist/jobs/queue.js.map +1 -1
  75. package/dist/jobs/worker.d.ts +21 -0
  76. package/dist/jobs/worker.d.ts.map +1 -1
  77. package/dist/jobs/worker.js +64 -4
  78. package/dist/jobs/worker.js.map +1 -1
  79. package/dist/logging.d.ts +8 -0
  80. package/dist/logging.d.ts.map +1 -0
  81. package/dist/logging.js +6 -0
  82. package/dist/logging.js.map +1 -0
  83. package/dist/provision-internals.d.ts +1 -1
  84. package/dist/provision-internals.js +1 -1
  85. package/dist/provision-internals.js.map +1 -1
  86. package/dist/provision.d.ts +2 -0
  87. package/dist/provision.d.ts.map +1 -1
  88. package/dist/provision.js +24 -3
  89. package/dist/provision.js.map +1 -1
  90. package/dist/query/infer.d.ts +1 -1
  91. package/dist/query/infer.d.ts.map +1 -1
  92. package/dist/query/infer.js.map +1 -1
  93. package/dist/runtime.d.ts +153 -0
  94. package/dist/runtime.d.ts.map +1 -0
  95. package/dist/runtime.js +537 -0
  96. package/dist/runtime.js.map +1 -0
  97. package/dist/schema-export-pg.js +1 -1
  98. package/dist/schema-export-pg.js.map +1 -1
  99. package/dist/storage/background.d.ts +3 -0
  100. package/dist/storage/background.d.ts.map +1 -0
  101. package/dist/storage/background.js +3 -0
  102. package/dist/storage/background.js.map +1 -0
  103. package/dist/testing/auth.d.ts +85 -0
  104. package/dist/testing/auth.d.ts.map +1 -0
  105. package/dist/testing/auth.js +140 -0
  106. package/dist/testing/auth.js.map +1 -0
  107. package/dist/testing/client.d.ts +6 -0
  108. package/dist/testing/client.d.ts.map +1 -0
  109. package/dist/testing/client.js +9 -0
  110. package/dist/testing/client.js.map +1 -0
  111. package/dist/testing/database.d.ts +6 -0
  112. package/dist/testing/database.d.ts.map +1 -0
  113. package/dist/testing/database.js +8 -0
  114. package/dist/testing/database.js.map +1 -0
  115. package/dist/testing/email.d.ts +15 -0
  116. package/dist/testing/email.d.ts.map +1 -0
  117. package/dist/testing/email.js +32 -0
  118. package/dist/testing/email.js.map +1 -0
  119. package/dist/testing/fixture.d.ts +51 -0
  120. package/dist/testing/fixture.d.ts.map +1 -0
  121. package/dist/testing/fixture.js +178 -0
  122. package/dist/testing/fixture.js.map +1 -0
  123. package/dist/testing/jobs.d.ts +40 -0
  124. package/dist/testing/jobs.d.ts.map +1 -0
  125. package/dist/testing/jobs.js +76 -0
  126. package/dist/testing/jobs.js.map +1 -0
  127. package/dist/testing/logs.d.ts +18 -0
  128. package/dist/testing/logs.d.ts.map +1 -0
  129. package/dist/testing/logs.js +53 -0
  130. package/dist/testing/logs.js.map +1 -0
  131. package/dist/testing/storage.d.ts +7 -0
  132. package/dist/testing/storage.d.ts.map +1 -0
  133. package/dist/testing/storage.js +29 -0
  134. package/dist/testing/storage.js.map +1 -0
  135. package/dist/testing.d.ts +10 -26
  136. package/dist/testing.d.ts.map +1 -1
  137. package/dist/testing.js +3 -9
  138. package/dist/testing.js.map +1 -1
  139. package/llms.txt +51 -14
  140. package/package.json +23 -15
  141. package/skills/creating-bunderstack-apps/references/application-structure.md +16 -12
  142. package/skills/creating-bunderstack-apps/references/runtime-integrations.md +4 -1
  143. package/skills/migrating-to-bunderstack/SKILL.md +7 -6
  144. package/skills/migrating-to-bunderstack/references/audit-checklist.md +4 -4
  145. package/skills/migrating-to-bunderstack/references/runtime-replacements.md +55 -49
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../src/testing/fixture.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdtemp, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\n\nimport type { BunderstackBackend } from '../backend'\nimport type { AnyBunderstackApp, BunderstackClient } from '../client/rpc-client'\nimport type { TestDatabaseStrategy } from '../database/adapter'\nimport type { TestSchemaMode } from '../provision'\nimport type { TestAuth, TestIdentity } from './auth'\nimport type { TestEmail } from './email'\nimport type { TestLogs, TestLogMode } from './logs'\nimport type { TestStorage } from './storage'\n\nimport {\n BACKEND_INTERNALS,\n type RuntimeTestingHandle,\n} from '../backend-internals'\nimport { provisionForTest } from '../provision'\nimport { createTestAuth, createTestSessionRegistry } from './auth'\nimport { testClient } from './client'\nimport { createTestDatabaseTarget } from './database'\nimport { createTestEmail } from './email'\nimport { createTestJobs, type TestJobs } from './jobs'\nimport { createTestLogs } from './logs'\nimport { createTestStorage, resolveTestBuckets } from './storage'\n\nexport type TestOptions = {\n env?: Record<string, string | undefined>\n database?: {\n mode?: 'memory' | 'temporary'\n schema?: TestSchemaMode\n strategy?: TestDatabaseStrategy\n }\n logs?: TestLogMode\n}\n\nexport type TestCleanup = () => unknown | Promise<unknown>\n\nexport type TestSetup<TApp, TContext> = (\n fixture: TestFixture<TApp>,\n) => TContext | Promise<TContext>\n\nexport type TestConfigureOptions<TApp, TContext> = TestOptions & {\n setup?: TestSetup<TApp, TContext>\n}\n\nexport type TestFixture<TApp> = AsyncDisposable & {\n readonly app: TApp\n readonly auth: TestAuth\n readonly email: TestEmail\n readonly jobs: TestJobs\n readonly logs: TestLogs\n readonly storage: TestStorage\n defer(cleanup: TestCleanup): void\n client(\n identity?: TestIdentity,\n ): TApp extends AnyBunderstackApp ? BunderstackClient<TApp> : never\n close(): Promise<void>\n}\n\nexport type ConfiguredTestFixture<TApp, TContext> = TestFixture<TApp> & {\n readonly context: TContext\n}\n\nexport type TestFactory<TApp, TContext> = (\n options?: TestOptions,\n) => Promise<ConfiguredTestFixture<TApp, TContext>>\n\nexport type TestMethod<TApp> = {\n (options?: TestOptions): Promise<TestFixture<TApp>>\n configure<TContext = undefined>(\n options: TestConfigureOptions<TApp, TContext>,\n ): TestFactory<TApp, TContext>\n}\n\ntype TestableApp = {\n auth: unknown\n handler(request: Request): Promise<Response>\n close(): Promise<void>\n}\n\nconst defaultTestEnv = {\n NODE_ENV: 'test',\n AUTH_SECRET: 'bunderstack-test-secret',\n BUNDERSTACK_ROLE: 'web',\n} satisfies Record<string, string | undefined>\n\nfunction mergeTestOptions(\n defaults: TestOptions,\n overrides: TestOptions,\n): TestOptions {\n return {\n ...defaults,\n ...overrides,\n env:\n defaults.env || overrides.env\n ? { ...defaults.env, ...overrides.env }\n : undefined,\n database:\n defaults.database || overrides.database\n ? { ...defaults.database, ...overrides.database }\n : undefined,\n }\n}\n\nexport function configureTestApp<\n TApp extends TestableApp,\n TContext = undefined,\n>(\n backend: BunderstackBackend<TApp>,\n options: TestConfigureOptions<TApp, TContext>,\n): TestFactory<TApp, TContext> {\n const { setup, ...defaults } = options\n return async (overrides = {}) => {\n const fixture = await createTestApp(\n backend,\n mergeTestOptions(defaults, overrides),\n )\n try {\n const context = setup ? await setup(fixture) : (undefined as TContext)\n return Object.assign(fixture, { context })\n } catch (cause) {\n try {\n await fixture.close()\n } catch (cleanupCause) {\n throw new AggregateError(\n [cause, cleanupCause],\n '[bunderstack] test fixture setup and cleanup failed',\n )\n }\n throw cause\n }\n }\n}\n\nexport async function createTestApp<TApp extends TestableApp>(\n backend: BunderstackBackend<TApp>,\n options: TestOptions = {},\n): Promise<TestFixture<TApp>> {\n const internals = backend[BACKEND_INTERNALS]\n const storageRoot = await mkdtemp(join(tmpdir(), 'bunderstack-storage-'))\n const resolvedStorage = resolveTestBuckets(\n internals.declaration.config.storage,\n storageRoot,\n )\n const { adapter: emailAdapter, email } = createTestEmail()\n const storage = createTestStorage(resolvedStorage)\n const sessions = createTestSessionRegistry()\n const { logger, logs } = createTestLogs(options.logs)\n\n let target\n try {\n target = await createTestDatabaseTarget(\n internals.declaration.config.database.adapter,\n {\n mode: options.database?.mode ?? 'memory',\n strategy: options.database?.strategy,\n },\n )\n } catch (cause) {\n await rm(storageRoot, { recursive: true, force: true })\n throw cause\n }\n\n let app: TApp | undefined\n let testingHandle: RuntimeTestingHandle | undefined\n try {\n app = await internals.start(\n { ...defaultTestEnv, ...options.env },\n {\n database: target.connection,\n resolvedStorage,\n emailAdapter,\n authResolver: sessions.resolver,\n logger,\n forceMemoryRealtime: true,\n backgroundAutoStart: false,\n captureTestingHandle: (handle) => {\n testingHandle = handle\n },\n },\n )\n await provisionForTest(app as object, options.database?.schema ?? 'auto')\n } catch (cause) {\n const cleanupErrors: unknown[] = []\n if (app) {\n try {\n await app.close()\n } catch (error) {\n cleanupErrors.push(error)\n }\n }\n try {\n await target[Symbol.asyncDispose]()\n } catch (error) {\n cleanupErrors.push(error)\n }\n try {\n await rm(storageRoot, { recursive: true, force: true })\n } catch (error) {\n cleanupErrors.push(error)\n }\n if (cleanupErrors.length > 0) {\n throw new AggregateError(\n [cause, ...cleanupErrors],\n '[bunderstack] test fixture setup and cleanup failed',\n )\n }\n throw cause\n }\n\n if (!testingHandle) {\n await app.close()\n await target[Symbol.asyncDispose]()\n await rm(storageRoot, { recursive: true, force: true })\n throw new Error('[bunderstack] runtime did not provide test controls')\n }\n\n const auth = createTestAuth(app, email, sessions)\n const jobs = createTestJobs(testingHandle)\n const deferred: TestCleanup[] = []\n let closePromise: Promise<void> | undefined\n const defer = (cleanup: TestCleanup) => {\n if (closePromise) {\n throw new Error('[bunderstack] cannot defer cleanup after fixture close')\n }\n deferred.push(cleanup)\n }\n const close = () => {\n closePromise ??= (async () => {\n const errors: unknown[] = []\n for (const cleanup of deferred.reverse()) {\n try {\n await cleanup()\n } catch (error) {\n errors.push(error)\n }\n }\n try {\n await app.close()\n } catch (error) {\n errors.push(error)\n }\n try {\n await target[Symbol.asyncDispose]()\n } catch (error) {\n errors.push(error)\n }\n try {\n await rm(storageRoot, { recursive: true, force: true })\n } catch (error) {\n errors.push(error)\n }\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw new AggregateError(errors, '[bunderstack] fixture cleanup failed')\n }\n })()\n return closePromise\n }\n\n return {\n app,\n auth,\n email,\n jobs,\n logs,\n storage,\n defer,\n client: (identity) => testClient(app as never, identity) as never,\n close,\n [Symbol.asyncDispose]: close,\n }\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import type { RuntimeJobFailure, RuntimeTestJob, RuntimeTestingHandle } from '../backend-internals.js';
2
+ export type JobRunReport = {
3
+ ticks: number;
4
+ claimed: number;
5
+ ran: number;
6
+ failed: number;
7
+ remainingRunnable: number;
8
+ };
9
+ export type RunNextOptions = {
10
+ now?: Date | number;
11
+ };
12
+ export type RunUntilIdleOptions = RunNextOptions & {
13
+ maxTicks?: number;
14
+ failOnJobError?: boolean;
15
+ };
16
+ export type TestJob = Readonly<RuntimeTestJob>;
17
+ export type TestJobFilter = {
18
+ name?: string;
19
+ dedupeKey?: string;
20
+ };
21
+ export declare class TestJobsError extends Error {
22
+ readonly report: JobRunReport;
23
+ readonly failures: RuntimeJobFailure[];
24
+ readonly name = "TestJobsError";
25
+ constructor(report: JobRunReport, failures: RuntimeJobFailure[]);
26
+ }
27
+ export declare class TestJobsConvergenceError extends Error {
28
+ readonly report: JobRunReport;
29
+ readonly name = "TestJobsConvergenceError";
30
+ constructor(report: JobRunReport);
31
+ }
32
+ export type TestJobs = {
33
+ runNext(options?: RunNextOptions): Promise<JobRunReport>;
34
+ runUntilIdle(options?: RunUntilIdleOptions): Promise<JobRunReport>;
35
+ inspect(filter?: TestJobFilter): Promise<readonly TestJob[]>;
36
+ pending(filter?: TestJobFilter): Promise<readonly TestJob[]>;
37
+ failed(filter?: TestJobFilter): Promise<readonly TestJob[]>;
38
+ };
39
+ export declare function createTestJobs(handle: RuntimeTestingHandle): TestJobs;
40
+ //# sourceMappingURL=jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/testing/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACrB,MAAM,sBAAsB,CAAA;AAE7B,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,GAAG,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,CAAA;AAEpD,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAA;AAE9C,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,qBAAa,aAAc,SAAQ,KAAK;IAIpC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAC7B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,EAAE;IAJxC,SAAkB,IAAI,mBAAkB;gBAG7B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,iBAAiB,EAAE;CAMzC;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IAGrC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAFzC,SAAkB,IAAI,8BAA6B;gBAE9B,MAAM,EAAE,YAAY;CAK1C;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACxD,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAClE,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IAC5D,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IAC5D,MAAM,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;CAC5D,CAAA;AAMD,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,QAAQ,CAoErE"}
@@ -0,0 +1,76 @@
1
+ export class TestJobsError extends Error {
2
+ report;
3
+ failures;
4
+ name = 'TestJobsError';
5
+ constructor(report, failures) {
6
+ super(`[bunderstack] ${failures.length} background job${failures.length === 1 ? '' : 's'} failed: ${failures.map((failure) => `${failure.name}: ${failure.lastError ?? 'unknown error'}`).join('; ')}`);
7
+ this.report = report;
8
+ this.failures = failures;
9
+ }
10
+ }
11
+ export class TestJobsConvergenceError extends Error {
12
+ report;
13
+ name = 'TestJobsConvergenceError';
14
+ constructor(report) {
15
+ super(`[bunderstack] background jobs did not become idle after ${report.ticks} ticks (${report.remainingRunnable} still runnable)`);
16
+ this.report = report;
17
+ }
18
+ }
19
+ function timestamp(value) {
20
+ return value === undefined ? Date.now() : new Date(value).getTime();
21
+ }
22
+ export function createTestJobs(handle) {
23
+ const list = async (filter = {}, status) => {
24
+ const { jobs } = await handle.inspect(Date.now());
25
+ return Object.freeze(jobs.filter((job) => (!status || job.status === status) &&
26
+ (!filter.name || job.name === filter.name) &&
27
+ (!filter.dedupeKey || job.dedupeKey === filter.dedupeKey)));
28
+ };
29
+ return {
30
+ inspect: (filter) => list(filter),
31
+ pending: (filter) => list(filter, 'pending'),
32
+ failed: (filter) => list(filter, 'failed'),
33
+ async runNext(options = {}) {
34
+ const now = timestamp(options.now);
35
+ const tick = await handle.tick(now);
36
+ const inspection = await handle.inspect(now);
37
+ return {
38
+ ticks: 1,
39
+ ...tick,
40
+ remainingRunnable: inspection.runnable,
41
+ };
42
+ },
43
+ async runUntilIdle(options = {}) {
44
+ const now = timestamp(options.now);
45
+ const maxTicks = options.maxTicks ?? 100;
46
+ if (!Number.isInteger(maxTicks) || maxTicks <= 0) {
47
+ throw new TypeError('[bunderstack] maxTicks must be a positive integer');
48
+ }
49
+ const report = {
50
+ ticks: 0,
51
+ claimed: 0,
52
+ ran: 0,
53
+ failed: 0,
54
+ remainingRunnable: 0,
55
+ };
56
+ let inspection;
57
+ do {
58
+ const tick = await handle.tick(now);
59
+ report.ticks++;
60
+ report.claimed += tick.claimed;
61
+ report.ran += tick.ran;
62
+ report.failed += tick.failed;
63
+ inspection = await handle.inspect(now);
64
+ report.remainingRunnable = inspection.runnable;
65
+ if (inspection.runnable > 0 && report.ticks >= maxTicks) {
66
+ throw new TestJobsConvergenceError(report);
67
+ }
68
+ } while (inspection.runnable > 0);
69
+ if ((options.failOnJobError ?? true) && inspection.failed.length > 0) {
70
+ throw new TestJobsError(report, inspection.failed);
71
+ }
72
+ return report;
73
+ },
74
+ };
75
+ }
76
+ //# sourceMappingURL=jobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/testing/jobs.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RuntimeJobFailure,\n RuntimeTestJob,\n RuntimeTestingHandle,\n} from '../backend-internals'\n\nexport type JobRunReport = {\n ticks: number\n claimed: number\n ran: number\n failed: number\n remainingRunnable: number\n}\n\nexport type RunNextOptions = { now?: Date | number }\n\nexport type RunUntilIdleOptions = RunNextOptions & {\n maxTicks?: number\n failOnJobError?: boolean\n}\n\nexport type TestJob = Readonly<RuntimeTestJob>\n\nexport type TestJobFilter = {\n name?: string\n dedupeKey?: string\n}\n\nexport class TestJobsError extends Error {\n override readonly name = 'TestJobsError'\n\n constructor(\n readonly report: JobRunReport,\n readonly failures: RuntimeJobFailure[],\n ) {\n super(\n `[bunderstack] ${failures.length} background job${failures.length === 1 ? '' : 's'} failed: ${failures.map((failure) => `${failure.name}: ${failure.lastError ?? 'unknown error'}`).join('; ')}`,\n )\n }\n}\n\nexport class TestJobsConvergenceError extends Error {\n override readonly name = 'TestJobsConvergenceError'\n\n constructor(readonly report: JobRunReport) {\n super(\n `[bunderstack] background jobs did not become idle after ${report.ticks} ticks (${report.remainingRunnable} still runnable)`,\n )\n }\n}\n\nexport type TestJobs = {\n runNext(options?: RunNextOptions): Promise<JobRunReport>\n runUntilIdle(options?: RunUntilIdleOptions): Promise<JobRunReport>\n inspect(filter?: TestJobFilter): Promise<readonly TestJob[]>\n pending(filter?: TestJobFilter): Promise<readonly TestJob[]>\n failed(filter?: TestJobFilter): Promise<readonly TestJob[]>\n}\n\nfunction timestamp(value: Date | number | undefined): number {\n return value === undefined ? Date.now() : new Date(value).getTime()\n}\n\nexport function createTestJobs(handle: RuntimeTestingHandle): TestJobs {\n const list = async (\n filter: TestJobFilter = {},\n status?: TestJob['status'],\n ) => {\n const { jobs } = await handle.inspect(Date.now())\n return Object.freeze(\n jobs.filter(\n (job) =>\n (!status || job.status === status) &&\n (!filter.name || job.name === filter.name) &&\n (!filter.dedupeKey || job.dedupeKey === filter.dedupeKey),\n ),\n )\n }\n\n return {\n inspect: (filter) => list(filter),\n pending: (filter) => list(filter, 'pending'),\n failed: (filter) => list(filter, 'failed'),\n\n async runNext(options = {}) {\n const now = timestamp(options.now)\n const tick = await handle.tick(now)\n const inspection = await handle.inspect(now)\n return {\n ticks: 1,\n ...tick,\n remainingRunnable: inspection.runnable,\n }\n },\n\n async runUntilIdle(options = {}) {\n const now = timestamp(options.now)\n const maxTicks = options.maxTicks ?? 100\n if (!Number.isInteger(maxTicks) || maxTicks <= 0) {\n throw new TypeError('[bunderstack] maxTicks must be a positive integer')\n }\n\n const report: JobRunReport = {\n ticks: 0,\n claimed: 0,\n ran: 0,\n failed: 0,\n remainingRunnable: 0,\n }\n let inspection: Awaited<ReturnType<RuntimeTestingHandle['inspect']>>\n\n do {\n const tick = await handle.tick(now)\n report.ticks++\n report.claimed += tick.claimed\n report.ran += tick.ran\n report.failed += tick.failed\n inspection = await handle.inspect(now)\n report.remainingRunnable = inspection.runnable\n\n if (inspection.runnable > 0 && report.ticks >= maxTicks) {\n throw new TestJobsConvergenceError(report)\n }\n } while (inspection.runnable > 0)\n\n if ((options.failOnJobError ?? true) && inspection.failed.length > 0) {\n throw new TestJobsError(report, inspection.failed)\n }\n return report\n },\n }\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { BunderstackLogger, BunderstackLogLevel } from '../logging.js';
2
+ export type TestLogMode = 'capture' | 'inherit' | 'silent';
3
+ export type TestLogEntry = {
4
+ readonly level: BunderstackLogLevel;
5
+ readonly message: string;
6
+ readonly args: readonly unknown[];
7
+ };
8
+ export type TestLogs = {
9
+ readonly entries: readonly Readonly<TestLogEntry>[];
10
+ readonly errors: readonly Readonly<TestLogEntry>[];
11
+ readonly warnings: readonly Readonly<TestLogEntry>[];
12
+ clear(): void;
13
+ };
14
+ export declare function createTestLogs(mode?: TestLogMode): {
15
+ logger: BunderstackLogger;
16
+ logs: TestLogs;
17
+ };
18
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/testing/logs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAExE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE1D,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAA;IACnD,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAA;IAClD,QAAQ,CAAC,QAAQ,EAAE,SAAS,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAA;IACpD,KAAK,IAAI,IAAI,CAAA;CACd,CAAA;AAcD,wBAAgB,cAAc,CAAC,IAAI,GAAE,WAAuB,GAAG;IAC7D,MAAM,EAAE,iBAAiB,CAAA;IACzB,IAAI,EAAE,QAAQ,CAAA;CACf,CAuCA"}
@@ -0,0 +1,53 @@
1
+ function render(value) {
2
+ if (value instanceof Error)
3
+ return `${value.name}: ${value.message}`;
4
+ if (typeof value === 'string')
5
+ return value;
6
+ if (value === null)
7
+ return 'null';
8
+ if (value === undefined)
9
+ return 'undefined';
10
+ try {
11
+ return JSON.stringify(value);
12
+ }
13
+ catch {
14
+ return String(value);
15
+ }
16
+ }
17
+ export function createTestLogs(mode = 'capture') {
18
+ const entries = [];
19
+ const write = (level, args) => {
20
+ if (mode !== 'silent') {
21
+ entries.push(Object.freeze({
22
+ level,
23
+ message: args.map(render).join(' '),
24
+ args: Object.freeze([...args]),
25
+ }));
26
+ }
27
+ if (mode === 'inherit')
28
+ console[level](...args);
29
+ };
30
+ const snapshot = (level) => Object.freeze(entries.filter((entry) => !level || entry.level === level).slice());
31
+ return {
32
+ logger: {
33
+ info: (...args) => write('info', args),
34
+ warn: (...args) => write('warn', args),
35
+ error: (...args) => write('error', args),
36
+ },
37
+ logs: {
38
+ get entries() {
39
+ return snapshot();
40
+ },
41
+ get errors() {
42
+ return snapshot('error');
43
+ },
44
+ get warnings() {
45
+ return snapshot('warn');
46
+ },
47
+ clear() {
48
+ entries.length = 0;
49
+ },
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/testing/logs.ts"],"names":[],"mappings":"AAiBA,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAA;IACpE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IACjC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,WAAW,CAAA;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAoB,SAAS;IAI1D,MAAM,OAAO,GAAmB,EAAE,CAAA;IAClC,MAAM,KAAK,GAAG,CAAC,KAA0B,EAAE,IAAe,EAAE,EAAE;QAC5D,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CACV,MAAM,CAAC,MAAM,CAAC;gBACZ,KAAK;gBACL,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACnC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;aAC/B,CAAC,CACH,CAAA;QACH,CAAC;QACD,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACjD,CAAC,CAAA;IACD,MAAM,QAAQ,GAAG,CAAC,KAA2B,EAAE,EAAE,CAC/C,MAAM,CAAC,MAAM,CACX,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,KAAK,EAAE,CACnE,CAAA;IACH,OAAO;QACL,MAAM,EAAE;YACN,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;YACtC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;YACtC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC;SACzC;QACD,IAAI,EAAE;YACJ,IAAI,OAAO;gBACT,OAAO,QAAQ,EAAE,CAAA;YACnB,CAAC;YACD,IAAI,MAAM;gBACR,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAA;YAC1B,CAAC;YACD,IAAI,QAAQ;gBACV,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAA;YACzB,CAAC;YACD,KAAK;gBACH,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;YACpB,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["import type { BunderstackLogger, BunderstackLogLevel } from '../logging'\n\nexport type TestLogMode = 'capture' | 'inherit' | 'silent'\n\nexport type TestLogEntry = {\n readonly level: BunderstackLogLevel\n readonly message: string\n readonly args: readonly unknown[]\n}\n\nexport type TestLogs = {\n readonly entries: readonly Readonly<TestLogEntry>[]\n readonly errors: readonly Readonly<TestLogEntry>[]\n readonly warnings: readonly Readonly<TestLogEntry>[]\n clear(): void\n}\n\nfunction render(value: unknown): string {\n if (value instanceof Error) return `${value.name}: ${value.message}`\n if (typeof value === 'string') return value\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nexport function createTestLogs(mode: TestLogMode = 'capture'): {\n logger: BunderstackLogger\n logs: TestLogs\n} {\n const entries: TestLogEntry[] = []\n const write = (level: BunderstackLogLevel, args: unknown[]) => {\n if (mode !== 'silent') {\n entries.push(\n Object.freeze({\n level,\n message: args.map(render).join(' '),\n args: Object.freeze([...args]),\n }),\n )\n }\n if (mode === 'inherit') console[level](...args)\n }\n const snapshot = (level?: BunderstackLogLevel) =>\n Object.freeze(\n entries.filter((entry) => !level || entry.level === level).slice(),\n )\n return {\n logger: {\n info: (...args) => write('info', args),\n warn: (...args) => write('warn', args),\n error: (...args) => write('error', args),\n },\n logs: {\n get entries() {\n return snapshot()\n },\n get errors() {\n return snapshot('error')\n },\n get warnings() {\n return snapshot('warn')\n },\n clear() {\n entries.length = 0\n },\n },\n }\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import type { ResolvedStorageBuckets, StorageConfigInput } from '../storage/buckets.js';
2
+ export type TestStorage = {
3
+ read(key: string): Promise<Uint8Array>;
4
+ };
5
+ export declare function resolveTestBuckets(input: StorageConfigInput | undefined, root: string): ResolvedStorageBuckets;
6
+ export declare function createTestStorage(resolved: ResolvedStorageBuckets): TestStorage;
7
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/testing/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,kBAAkB,EACnB,MAAM,oBAAoB,CAAA;AAK3B,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CACvC,CAAA;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,kBAAkB,GAAG,SAAS,EACrC,IAAI,EAAE,MAAM,GACX,sBAAsB,CAWxB;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,sBAAsB,GAC/B,WAAW,CAcb"}
@@ -0,0 +1,29 @@
1
+ import { resolveBuckets } from '../storage/buckets.js';
2
+ import { createBucketStorages } from '../storage/registry.js';
3
+ export function resolveTestBuckets(input, root) {
4
+ const resolved = resolveBuckets(input, {});
5
+ return {
6
+ defaultBucket: resolved.defaultBucket,
7
+ buckets: new Map([...resolved.buckets].map(([name, bucket]) => [
8
+ name,
9
+ { ...bucket, backend: { type: 'local', path: root } },
10
+ ])),
11
+ };
12
+ }
13
+ export function createTestStorage(resolved) {
14
+ const registry = createBucketStorages(resolved);
15
+ return {
16
+ async read(key) {
17
+ const bucketName = key.split('/')[0] || resolved.defaultBucket;
18
+ const adapter = registry.get(bucketName)?.adapter;
19
+ if (!adapter)
20
+ throw new Error(`Unknown bucket: ${bucketName}`);
21
+ const response = await adapter.get(key);
22
+ if (!response.ok) {
23
+ throw new Error(`Storage object not found: ${key}`);
24
+ }
25
+ return new Uint8Array(await response.arrayBuffer());
26
+ },
27
+ };
28
+ }
29
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/testing/storage.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAM1D,MAAM,UAAU,kBAAkB,CAChC,KAAqC,EACrC,IAAY;IAEZ,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC1C,OAAO;QACL,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,OAAO,EAAE,IAAI,GAAG,CACd,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;YAC5C,IAAI;YACJ,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;SAC/D,CAAC,CACH;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,QAAgC;IAEhC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAA;IAC/C,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,GAAG;YACZ,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,aAAa,CAAA;YAC9D,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;YACjD,IAAI,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAA;YAC9D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,EAAE,CAAC,CAAA;YACrD,CAAC;YACD,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAA;QACrD,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import type {\n ResolvedStorageBuckets,\n StorageConfigInput,\n} from '../storage/buckets'\n\nimport { resolveBuckets } from '../storage/buckets'\nimport { createBucketStorages } from '../storage/registry'\n\nexport type TestStorage = {\n read(key: string): Promise<Uint8Array>\n}\n\nexport function resolveTestBuckets(\n input: StorageConfigInput | undefined,\n root: string,\n): ResolvedStorageBuckets {\n const resolved = resolveBuckets(input, {})\n return {\n defaultBucket: resolved.defaultBucket,\n buckets: new Map(\n [...resolved.buckets].map(([name, bucket]) => [\n name,\n { ...bucket, backend: { type: 'local' as const, path: root } },\n ]),\n ),\n }\n}\n\nexport function createTestStorage(\n resolved: ResolvedStorageBuckets,\n): TestStorage {\n const registry = createBucketStorages(resolved)\n return {\n async read(key) {\n const bucketName = key.split('/')[0] || resolved.defaultBucket\n const adapter = registry.get(bucketName)?.adapter\n if (!adapter) throw new Error(`Unknown bucket: ${bucketName}`)\n const response = await adapter.get(key)\n if (!response.ok) {\n throw new Error(`Storage object not found: ${key}`)\n }\n return new Uint8Array(await response.arrayBuffer())\n },\n }\n}\n"]}
package/dist/testing.d.ts CHANGED
@@ -1,27 +1,11 @@
1
- export type AuthSessionResolverLike = {
2
- api: {
3
- getSession: (opts: {
4
- headers: Headers;
5
- }) => Promise<unknown>;
6
- };
7
- };
8
- export type BunderstackAppLike = {
9
- auth: unknown;
10
- };
11
- /**
12
- * Mock the auth session resolver on a Bunderstack app instance for unit testing.
13
- */
14
- export declare function mockAuthSession<TUser extends {
15
- id: string;
16
- email: string;
17
- name?: string;
18
- role?: string;
19
- }>(app: BunderstackAppLike, resolver: (opts: {
20
- headers: Headers;
21
- }) => Promise<{
22
- user: TUser;
23
- session?: {
24
- activeOrganizationId?: string | null;
25
- } | null;
26
- } | null>): void;
1
+ export { configureTestApp, createTestApp } from './testing/fixture.js';
2
+ export type { ConfiguredTestFixture, TestCleanup, TestConfigureOptions, TestFactory, TestFixture, TestMethod, TestOptions, TestSetup, } from './testing/fixture.js';
3
+ export { mockAuthSession, TestAuthError } from './testing/auth.js';
4
+ export type { SignInEmailInput, SignUpEmailInput, TestAuth, TestIdentity, TestSession, TestUser, } from './testing/auth.js';
5
+ export type { CapturedEmail, TestEmail } from './testing/email.js';
6
+ export type { TestStorage } from './testing/storage.js';
7
+ export type { TestLogEntry, TestLogMode, TestLogs } from './testing/logs.js';
8
+ export { TestJobsConvergenceError, TestJobsError } from './testing/jobs.js';
9
+ export type { JobRunReport, RunNextOptions, RunUntilIdleOptions, TestJob, TestJobFilter, TestJobs, } from './testing/jobs.js';
10
+ export type { TestDatabaseStrategy, TestDatabaseTarget, TestDatabaseTargetOptions, } from './database/adapter.js';
27
11
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE;QACH,UAAU,EAAE,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,OAAO,CAAA;SAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAC7D,CAAA;CACF,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,EAEzE,GAAG,EAAE,kBAAkB,EACvB,QAAQ,EAAE,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC;IAChD,IAAI,EAAE,KAAK,CAAA;IACX,OAAO,CAAC,EAAE;QAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;CAC1D,GAAG,IAAI,CAAC,GACR,IAAI,CAON"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACnE,YAAY,EACV,qBAAqB,EACrB,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,UAAU,EACV,WAAW,EACX,SAAS,GACV,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACxE,YAAY,EACV,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,OAAO,EACP,aAAa,EACb,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA"}
package/dist/testing.js CHANGED
@@ -1,11 +1,5 @@
1
1
  // src/testing.ts — test utilities for Bunderstack applications.
2
- /**
3
- * Mock the auth session resolver on a Bunderstack app instance for unit testing.
4
- */
5
- export function mockAuthSession(app, resolver) {
6
- const auth = app.auth;
7
- if (auth?.api) {
8
- auth.api.getSession = resolver;
9
- }
10
- }
2
+ export { configureTestApp, createTestApp } from './testing/fixture.js';
3
+ export { mockAuthSession, TestAuthError } from './testing/auth.js';
4
+ export { TestJobsConvergenceError, TestJobsError } from './testing/jobs.js';
11
5
  //# sourceMappingURL=testing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAYhE;;GAEG;AACH,MAAM,UAAU,eAAe,CAG7B,GAAuB,EACvB,QAGS;IAET,MAAM,IAAI,GAAG,GAAG,CAAC,IAA0C,CAAA;IAC3D,IAAI,IAAI,EAAE,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,QAEA,CAAA;IACxB,CAAC;AACH,CAAC","sourcesContent":["// src/testing.ts — test utilities for Bunderstack applications.\n\nexport type AuthSessionResolverLike = {\n api: {\n getSession: (opts: { headers: Headers }) => Promise<unknown>\n }\n}\n\nexport type BunderstackAppLike = {\n auth: unknown\n}\n\n/**\n * Mock the auth session resolver on a Bunderstack app instance for unit testing.\n */\nexport function mockAuthSession<\n TUser extends { id: string; email: string; name?: string; role?: string },\n>(\n app: BunderstackAppLike,\n resolver: (opts: { headers: Headers }) => Promise<{\n user: TUser\n session?: { activeOrganizationId?: string | null } | null\n } | null>,\n): void {\n const auth = app.auth as unknown as AuthSessionResolverLike\n if (auth?.api) {\n auth.api.getSession = resolver as unknown as (opts: {\n headers: Headers\n }) => Promise<unknown>\n }\n}\n"]}
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAWnE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAY/D,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA","sourcesContent":["// src/testing.ts — test utilities for Bunderstack applications.\n\nexport { configureTestApp, createTestApp } from './testing/fixture'\nexport type {\n ConfiguredTestFixture,\n TestCleanup,\n TestConfigureOptions,\n TestFactory,\n TestFixture,\n TestMethod,\n TestOptions,\n TestSetup,\n} from './testing/fixture'\nexport { mockAuthSession, TestAuthError } from './testing/auth'\nexport type {\n SignInEmailInput,\n SignUpEmailInput,\n TestAuth,\n TestIdentity,\n TestSession,\n TestUser,\n} from './testing/auth'\nexport type { CapturedEmail, TestEmail } from './testing/email'\nexport type { TestStorage } from './testing/storage'\nexport type { TestLogEntry, TestLogMode, TestLogs } from './testing/logs'\nexport { TestJobsConvergenceError, TestJobsError } from './testing/jobs'\nexport type {\n JobRunReport,\n RunNextOptions,\n RunUntilIdleOptions,\n TestJob,\n TestJobFilter,\n TestJobs,\n} from './testing/jobs'\nexport type {\n TestDatabaseStrategy,\n TestDatabaseTarget,\n TestDatabaseTargetOptions,\n} from './database/adapter'\n"]}
package/llms.txt CHANGED
@@ -1,16 +1,18 @@
1
1
  BUNDERSTACK
2
2
 
3
- A batteries-included backend framework for Bun. Version 0.17 (beta).
3
+ A batteries-included backend framework for Bun. Version 0.22.1.
4
4
  Docs: https://bunderstack.kcrz.dev/docs
5
5
  This file is written for coding agents. It is dense on purpose.
6
6
 
7
7
  WHAT IT IS
8
8
 
9
- createBunderstack() takes a Drizzle schema and returns an app whose handler is
10
- a single Web-Standard Request -> Response function. From the schema it
11
- generates secured CRUD procedures. Your own procedures, file storage, realtime
12
- subscriptions, and a health check live in the same oRPC graph, reachable both
13
- as typed RPC and as ordinary HTTP.
9
+ bunderstack() takes a Drizzle schema and returns a synchronous, side-effect-free
10
+ backend declaration. backend.start() materializes the production runtime whose
11
+ handler is a single Web-Standard Request -> Response function. backend.test()
12
+ materializes an isolated, lexically owned test fixture. From the schema the
13
+ runtime generates secured CRUD procedures. Your own procedures, file storage,
14
+ realtime subscriptions, and a health check live in the same oRPC graph,
15
+ reachable both as typed RPC and as ordinary HTTP.
14
16
 
15
17
  Stack: Bun, Drizzle (+ drizzle-kit), Better Auth, oRPC v2, libSQL or Postgres,
16
18
  Bun.Image. Validation accepts any Standard Schema library; generated schemas use
@@ -22,19 +24,22 @@ integration).
22
24
 
23
25
  MINIMAL APP
24
26
 
25
- import { createBunderstack } from 'bunderstack'
26
- import { libsql } from 'bunderstack/database/libsql'
27
+ import { bunderstack } from 'bunderstack'
28
+ import { libsql } from 'bunderstack/libsql'
27
29
  import * as schema from './schema'
28
30
 
29
- export const app = await createBunderstack({
31
+ export const backend = bunderstack({
30
32
  schema,
31
33
  database: { adapter: libsql(), url: 'file:./data.db' },
32
34
  access: { posts: { list: 'public', get: 'public' } },
33
35
  })
34
36
 
35
- export type App = typeof app
37
+ export type App = Awaited<ReturnType<typeof backend.start>>
38
+ export const app = await backend.start()
39
+
36
40
  Bun.serve({ fetch: app.handler })
37
41
 
42
+ The blueprint imports the backend declaration and never starts the runtime.
38
43
  Database adapters are imported from their own entry points: libsql(),
39
44
  pglite(), bunSql(), postgresJs(). Provisioning: `await provision(app)` pushes
40
45
  the schema in development and applies committed migrations once a migrations/
@@ -71,13 +76,45 @@ Router modules are plain objects that import the base they need:
71
76
  export const api = { boards: boardsRouter }
72
77
 
73
78
  // config
74
- createBunderstack({ schema, database, api })
79
+ bunderstack({ schema, database, api })
75
80
 
76
81
  Do NOT write a factory that receives a bag of procedures. That pattern exists
77
82
  only because the api option used to be a callback. The callback form still
78
83
  works — api: (o) => ({ ... }) — for a router that must be built from the
79
84
  framework builder at configuration time, but the object form is the default.
80
85
 
86
+ TESTING
87
+
88
+ Use lexical fixture ownership; do not use a process-wide app registry or a
89
+ global afterEach hook:
90
+
91
+ export const createFixture = backend.test.configure({
92
+ database: { schema: 'push' },
93
+ setup: async (t) => {
94
+ const identity = t.auth.mockSession({
95
+ id: 'user-1', email: 'dev@example.com', name: 'Developer',
96
+ })
97
+ return { identity, client: t.client(identity) }
98
+ },
99
+ })
100
+
101
+ import { backend } from './backend'
102
+
103
+ test('creates a post', async () => {
104
+ await using t = await createFixture()
105
+ const { client, identity } = t.context
106
+ await client.posts.create({ title: 'Hello', userId: identity.user.id })
107
+ })
108
+
109
+ The fixture owns isolated database, email, storage, realtime, and job state.
110
+ fixture.defer() registers LIFO async cleanup. fixture.logs captures internal
111
+ runtime logs by default. Auth supports sign-up, sign-in, session lookup, sign-out,
112
+ email verification, and multiple header-scoped mock identities. t.jobs.runNext()
113
+ claims one snapshot of runnable jobs. t.jobs.runUntilIdle() repeats snapshots
114
+ until the queue converges, a terminal job fails, or its limits are reached; it
115
+ does not advance the clock to delayed jobs automatically. inspect(), pending(),
116
+ and failed() expose queue rows for assertions.
117
+
81
118
  BASES
82
119
 
83
120
  o.public session resolved only if the handler calls context.getSession()
@@ -118,7 +155,7 @@ Two placements, and the difference is the thing people get wrong.
118
155
  .use() on a base reaches only procedures built from that base. Use it for
119
156
  rules about a group of procedures: role checks, organization scope, quotas.
120
157
 
121
- middleware: [...] in createBunderstack reaches EVERY procedure, including the
158
+ middleware: [...] in bunderstack reaches EVERY procedure, including the
122
159
  generated CRUD, storage, realtime, and health. Use it for observability. A
123
160
  tracing middleware attached to a base leaves generated CRUD unmeasured,
124
161
  because the framework builds those procedures itself and they never pass
@@ -137,7 +174,7 @@ through an application base.
137
174
  }
138
175
  })
139
176
 
140
- createBunderstack({ schema, database, middleware: [instrumentation], api })
177
+ bunderstack({ schema, database, middleware: [instrumentation], api })
141
178
 
142
179
  Rules for graph-wide middleware:
143
180
  - It runs before authentication. context.user does not exist there.
@@ -262,7 +299,7 @@ ENV
262
299
 
263
300
  Server variables must not start with PUBLIC_; client variables must. Validated
264
301
  at boot; app.env and context.env are typed from the schema. Declare the schema
265
- in its own module so both createBunderstack and defineApi can use it.
302
+ in its own module so both bunderstack and defineApi can use it.
266
303
 
267
304
  AUTH
268
305
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunderstack",
3
- "version": "0.21.1",
3
+ "version": "0.22.1",
4
4
  "description": "Batteries-included backend framework for Bun: type-safe oRPC APIs, auth, storage, realtime, jobs, email, and validated env from one config.",
5
5
  "keywords": [
6
6
  "backend",
@@ -48,19 +48,23 @@
48
48
  "types": "./dist/codegen.d.ts",
49
49
  "default": "./dist/codegen.js"
50
50
  },
51
- "./database/bun-sql": {
51
+ "./bun-sql": {
52
52
  "types": "./dist/database/bun-sql.d.ts",
53
53
  "default": "./dist/database/bun-sql.js"
54
54
  },
55
- "./database/libsql": {
55
+ "./bun-sqlite": {
56
+ "types": "./dist/database/bun-sqlite.d.ts",
57
+ "default": "./dist/database/bun-sqlite.js"
58
+ },
59
+ "./libsql": {
56
60
  "types": "./dist/database/libsql.d.ts",
57
61
  "default": "./dist/database/libsql.js"
58
62
  },
59
- "./database/pglite": {
63
+ "./pglite": {
60
64
  "types": "./dist/database/pglite.d.ts",
61
65
  "default": "./dist/database/pglite.js"
62
66
  },
63
- "./database/postgres-js": {
67
+ "./postgres-js": {
64
68
  "types": "./dist/database/postgres-js.d.ts",
65
69
  "default": "./dist/database/postgres-js.js"
66
70
  },
@@ -72,11 +76,15 @@
72
76
  "types": "./dist/provision.d.ts",
73
77
  "default": "./dist/provision.js"
74
78
  },
79
+ "./testing": {
80
+ "types": "./dist/testing.d.ts",
81
+ "default": "./dist/testing.js"
82
+ },
75
83
  "./schema": {
76
84
  "types": "./dist/schema-export.d.ts",
77
85
  "default": "./dist/schema-export.js"
78
86
  },
79
- "./schema/pg": {
87
+ "./schema-pg": {
80
88
  "types": "./dist/schema-export-pg.d.ts",
81
89
  "default": "./dist/schema-export-pg.js"
82
90
  },
@@ -84,7 +92,7 @@
84
92
  "types": "./dist/typeid.d.ts",
85
93
  "default": "./dist/typeid.js"
86
94
  },
87
- "./typeid/pg": {
95
+ "./typeid-pg": {
88
96
  "types": "./dist/typeid-pg.d.ts",
89
97
  "default": "./dist/typeid-pg.js"
90
98
  },
@@ -100,7 +108,7 @@
100
108
  "types": "./dist/cron.d.ts",
101
109
  "default": "./dist/cron.js"
102
110
  },
103
- "./email/smtp": {
111
+ "./email-smtp": {
104
112
  "types": "./dist/email/smtp.d.ts",
105
113
  "default": "./dist/email/smtp.js"
106
114
  },
@@ -112,23 +120,23 @@
112
120
  "types": "./dist/client/index.d.ts",
113
121
  "default": "./dist/client/index.js"
114
122
  },
115
- "./client/rest": {
123
+ "./client-rest": {
116
124
  "types": "./dist/client/rest.d.ts",
117
125
  "default": "./dist/client/rest.js"
118
126
  },
119
- "./client/react": {
127
+ "./client-react": {
120
128
  "types": "./dist/client/react.d.ts",
121
129
  "default": "./dist/client/react.js"
122
130
  },
123
- "./client/solid": {
131
+ "./client-solid": {
124
132
  "types": "./dist/client/solid.d.ts",
125
133
  "default": "./dist/client/solid.js"
126
134
  },
127
- "./client/svelte": {
135
+ "./client-svelte": {
128
136
  "types": "./dist/client/svelte.d.ts",
129
137
  "default": "./dist/client/svelte.js"
130
138
  },
131
- "./client/vue": {
139
+ "./client-vue": {
132
140
  "types": "./dist/client/vue.d.ts",
133
141
  "default": "./dist/client/vue.js"
134
142
  },
@@ -136,7 +144,7 @@
136
144
  "types": "./dist/query/index.d.ts",
137
145
  "default": "./dist/query/index.js"
138
146
  },
139
- "./query/react": {
147
+ "./query-react": {
140
148
  "types": "./dist/query/react.d.ts",
141
149
  "default": "./dist/query/react.js"
142
150
  },
@@ -148,7 +156,7 @@
148
156
  "types": "./dist/start/index.d.ts",
149
157
  "default": "./dist/start/index.js"
150
158
  },
151
- "./start/auth": {
159
+ "./start-auth": {
152
160
  "types": "./dist/start/auth-client.d.ts",
153
161
  "default": "./dist/start/auth-client.js"
154
162
  }