bunderstack 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +2 -1
  3. package/dist/api/catalog.d.ts +8 -0
  4. package/dist/api/catalog.d.ts.map +1 -0
  5. package/dist/api/catalog.js +56 -0
  6. package/dist/api/catalog.js.map +1 -0
  7. package/dist/api/context.d.ts +3 -0
  8. package/dist/api/context.d.ts.map +1 -1
  9. package/dist/api/context.js +2 -0
  10. package/dist/api/context.js.map +1 -1
  11. package/dist/api/registry.d.ts.map +1 -1
  12. package/dist/api/registry.js +1 -0
  13. package/dist/api/registry.js.map +1 -1
  14. package/dist/api/router.d.ts +3 -0
  15. package/dist/api/router.d.ts.map +1 -1
  16. package/dist/api/router.js +7 -2
  17. package/dist/api/router.js.map +1 -1
  18. package/dist/backend-internals.d.ts +11 -0
  19. package/dist/backend-internals.d.ts.map +1 -1
  20. package/dist/backend-internals.js.map +1 -1
  21. package/dist/backend.d.ts +2 -2
  22. package/dist/backend.d.ts.map +1 -1
  23. package/dist/backend.js +18 -5
  24. package/dist/backend.js.map +1 -1
  25. package/dist/blueprint.d.ts +21 -1
  26. package/dist/blueprint.d.ts.map +1 -1
  27. package/dist/blueprint.js +45 -17
  28. package/dist/blueprint.js.map +1 -1
  29. package/dist/env.d.ts +18 -0
  30. package/dist/env.d.ts.map +1 -1
  31. package/dist/env.js +1 -0
  32. package/dist/env.js.map +1 -1
  33. package/dist/errors.d.ts.map +1 -1
  34. package/dist/errors.js +4 -2
  35. package/dist/errors.js.map +1 -1
  36. package/dist/index.d.ts +3 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +1 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/jobs/worker.d.ts +12 -0
  41. package/dist/jobs/worker.d.ts.map +1 -1
  42. package/dist/jobs/worker.js +32 -1
  43. package/dist/jobs/worker.js.map +1 -1
  44. package/dist/logging.d.ts +8 -0
  45. package/dist/logging.d.ts.map +1 -0
  46. package/dist/logging.js +6 -0
  47. package/dist/logging.js.map +1 -0
  48. package/dist/manifest.d.ts +20 -0
  49. package/dist/manifest.d.ts.map +1 -1
  50. package/dist/manifest.js +68 -6
  51. package/dist/manifest.js.map +1 -1
  52. package/dist/readiness-probes.d.ts +4 -0
  53. package/dist/readiness-probes.d.ts.map +1 -0
  54. package/dist/readiness-probes.js +23 -0
  55. package/dist/readiness-probes.js.map +1 -0
  56. package/dist/readiness.d.ts +39 -0
  57. package/dist/readiness.d.ts.map +1 -0
  58. package/dist/readiness.js +72 -0
  59. package/dist/readiness.js.map +1 -0
  60. package/dist/runtime.d.ts +4 -0
  61. package/dist/runtime.d.ts.map +1 -1
  62. package/dist/runtime.js +32 -4
  63. package/dist/runtime.js.map +1 -1
  64. package/dist/testing/auth.d.ts +30 -2
  65. package/dist/testing/auth.d.ts.map +1 -1
  66. package/dist/testing/auth.js +103 -14
  67. package/dist/testing/auth.js.map +1 -1
  68. package/dist/testing/fixture.d.ts +18 -0
  69. package/dist/testing/fixture.d.ts.map +1 -1
  70. package/dist/testing/fixture.js +55 -2
  71. package/dist/testing/fixture.js.map +1 -1
  72. package/dist/testing/jobs.d.ts +9 -1
  73. package/dist/testing/jobs.d.ts.map +1 -1
  74. package/dist/testing/jobs.js +9 -0
  75. package/dist/testing/jobs.js.map +1 -1
  76. package/dist/testing/logs.d.ts +18 -0
  77. package/dist/testing/logs.d.ts.map +1 -0
  78. package/dist/testing/logs.js +53 -0
  79. package/dist/testing/logs.js.map +1 -0
  80. package/dist/testing.d.ts +5 -4
  81. package/dist/testing.d.ts.map +1 -1
  82. package/dist/testing.js +1 -1
  83. package/dist/testing.js.map +1 -1
  84. package/llms.txt +20 -13
  85. package/package.json +1 -1
@@ -1,6 +1,17 @@
1
1
  import type { BunderstackManifest } from './manifest.js';
2
2
  export type MigrationMode = 'migrations' | 'push';
3
3
  export type ApplicationFramework = 'tanstack-start' | 'solid' | 'bun-ssr' | 'custom';
4
+ /**
5
+ * Blueprint form of an environment key. `sensitive` and `description` are
6
+ * optional because blueprints committed before 0.23.0 do not carry them.
7
+ */
8
+ export type BlueprintEnvVar = {
9
+ key: string;
10
+ required: boolean;
11
+ scope: 'server' | 'client';
12
+ sensitive?: boolean;
13
+ description?: string;
14
+ };
4
15
  export type BunderstackBlueprint = {
5
16
  version: 1;
6
17
  generator: {
@@ -28,7 +39,11 @@ export type BunderstackBlueprint = {
28
39
  required: true;
29
40
  };
30
41
  };
31
- environment: BunderstackManifest['environment'];
42
+ environment: BlueprintEnvVar[];
43
+ /** Application-declared procedures. Absent in blueprints written before 0.23.0. */
44
+ api?: {
45
+ operations: BunderstackManifest['api']['operations'];
46
+ };
32
47
  background: BunderstackManifest['background'] & {
33
48
  worker: {
34
49
  required: boolean;
@@ -36,6 +51,11 @@ export type BunderstackBlueprint = {
36
51
  };
37
52
  };
38
53
  export declare function parseBlueprint(value: unknown): BunderstackBlueprint;
54
+ /**
55
+ * Whether a host must treat this key as a secret. A blueprint generated before
56
+ * 0.23.0 has no flag, and server keys are secret by default.
57
+ */
58
+ export declare function isSensitiveEnvVar(entry: BlueprintEnvVar): boolean;
39
59
  export declare function blueprintFromManifest(args: {
40
60
  manifest: BunderstackManifest;
41
61
  generatorVersion: string;
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAKrD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,MAAM,CAAA;AACjD,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,OAAO,GACP,SAAS,GACT,QAAQ,CAAA;AAEZ,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,CAAC,CAAA;IACV,SAAS,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,WAAW,EAAE;QACX,SAAS,EAAE,oBAAoB,CAAA;QAC/B,OAAO,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAA;KAC/D,CAAA;IACD,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,CAAC,CAAA;KAAE,CAAA;IAClD,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG;YAAE,aAAa,EAAE,aAAa,CAAA;SAAE,CAAA;QAC5E,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;QACvC,QAAQ,CAAC,EAAE;YAAE,QAAQ,EAAE,IAAI,CAAA;SAAE,CAAA;KAC9B,CAAA;IACD,WAAW,EAAE,mBAAmB,CAAC,aAAa,CAAC,CAAA;IAC/C,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,GAAG;QAC9C,MAAM,EAAE;YAAE,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;KAC9B,CAAA;CACF,CAAA;AA2GD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAuDnE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,gBAAgB,EAAE,MAAM,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,CAAC,EAAE,oBAAoB,CAAA;CACjC,GAAG,oBAAoB,CA0CvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAmBtE"}
1
+ {"version":3,"file":"blueprint.d.ts","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAKrD,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,MAAM,CAAA;AACjD,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,OAAO,GACP,SAAS,GACT,QAAQ,CAAA;AAEZ;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,CAAC,CAAA;IACV,SAAS,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,WAAW,EAAE;QACX,SAAS,EAAE,oBAAoB,CAAA;QAC/B,OAAO,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAA;KAC/D,CAAA;IACD,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,CAAC,CAAA;KAAE,CAAA;IAClD,SAAS,EAAE;QACT,QAAQ,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAAG;YAAE,aAAa,EAAE,aAAa,CAAA;SAAE,CAAA;QAC5E,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;QACvC,QAAQ,CAAC,EAAE;YAAE,QAAQ,EAAE,IAAI,CAAA;SAAE,CAAA;KAC9B,CAAA;IACD,WAAW,EAAE,eAAe,EAAE,CAAA;IAC9B,mFAAmF;IACnF,GAAG,CAAC,EAAE;QAAE,UAAU,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAA;KAAE,CAAA;IAC9D,UAAU,EAAE,mBAAmB,CAAC,YAAY,CAAC,GAAG;QAC9C,MAAM,EAAE;YAAE,QAAQ,EAAE,OAAO,CAAA;SAAE,CAAA;KAC9B,CAAA;CACF,CAAA;AAkID,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CA4DnE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAEjE;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,QAAQ,EAAE,mBAAmB,CAAA;IAC7B,gBAAgB,EAAE,MAAM,CAAA;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,aAAa,CAAA;IAC5B,SAAS,CAAC,EAAE,oBAAoB,CAAA;CACjC,GAAG,oBAAoB,CA2CvB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAmBtE"}
package/dist/blueprint.js CHANGED
@@ -15,58 +15,76 @@ const cronSchedule = v.pipe(nonEmpty, v.check((value) => {
15
15
  return false;
16
16
  }
17
17
  }, 'invalid cron schedule'));
18
- const blueprintSchema = v.strictObject({
18
+ // Hosts parse blueprints committed by applications that upgrade bunderstack on
19
+ // their own schedule, so a section written by a newer generator has to survive
20
+ // an older parser instead of failing the whole file.
21
+ function open(entries) {
22
+ return v.objectWithRest(entries, v.unknown());
23
+ }
24
+ const blueprintSchema = open({
19
25
  version: v.literal(1),
20
- generator: v.strictObject({
26
+ generator: open({
21
27
  name: v.literal('bunderstack'),
22
28
  version: nonEmpty,
23
29
  }),
24
- application: v.strictObject({
30
+ application: open({
25
31
  framework: v.picklist(['tanstack-start', 'solid', 'bun-ssr', 'custom']),
26
- scripts: v.strictObject({
32
+ scripts: open({
27
33
  build: v.literal('build'),
28
34
  start: v.literal('start'),
29
35
  worker: v.optional(v.literal('worker')),
30
36
  }),
31
37
  }),
32
- bunderstack: v.strictObject({
38
+ bunderstack: open({
33
39
  entry: relativePath,
34
40
  manifestVersion: v.literal(3),
35
41
  }),
36
- resources: v.strictObject({
37
- database: v.strictObject({
42
+ resources: open({
43
+ database: open({
38
44
  dialect: v.picklist(['sqlite', 'pg']),
39
45
  migrationsDirectory: relativePath,
40
46
  migrationMode: v.picklist(['migrations', 'push']),
41
- tables: v.array(v.strictObject({
47
+ tables: v.array(open({
42
48
  exportName: nonEmpty,
43
49
  physicalName: nonEmpty,
44
50
  system: v.boolean(),
45
51
  })),
46
52
  }),
47
- storage: v.strictObject({
53
+ storage: open({
48
54
  defaultBucket: nonEmpty,
49
- buckets: v.array(v.strictObject({
55
+ buckets: v.array(open({
50
56
  name: nonEmpty,
51
57
  visibility: v.picklist(['public', 'private']),
52
58
  })),
53
59
  }),
54
- realtime: v.optional(v.strictObject({ required: v.literal(true) })),
60
+ realtime: v.optional(open({ required: v.literal(true) })),
55
61
  }),
56
- environment: v.array(v.strictObject({
62
+ environment: v.array(open({
57
63
  key: nonEmpty,
58
64
  required: v.boolean(),
59
65
  scope: v.picklist(['server', 'client']),
66
+ sensitive: v.optional(v.boolean()),
67
+ description: v.optional(v.pipe(nonEmpty, v.maxLength(200))),
68
+ })),
69
+ api: v.optional(open({
70
+ operations: v.array(open({
71
+ handle: nonEmpty,
72
+ operationId: nonEmpty,
73
+ effect: v.picklist(['read', 'mutation', 'unknown']),
74
+ method: v.optional(nonEmpty),
75
+ path: v.optional(nonEmpty),
76
+ summary: v.optional(v.pipe(nonEmpty, v.maxLength(200))),
77
+ })),
60
78
  })),
61
- background: v.strictObject({
62
- worker: v.strictObject({ required: v.boolean() }),
63
- jobs: v.array(v.strictObject({ name: nonEmpty })),
64
- cron: v.array(v.strictObject({
79
+ background: open({
80
+ worker: open({ required: v.boolean() }),
81
+ jobs: v.array(open({ name: nonEmpty })),
82
+ cron: v.array(open({
65
83
  name: nonEmpty,
66
84
  schedule: cronSchedule,
67
85
  timezone: v.literal('UTC'),
68
86
  })),
69
- maintenance: v.array(v.strictObject({
87
+ maintenance: v.array(open({
70
88
  name: v.literal('storage-sweep'),
71
89
  schedule: cronSchedule,
72
90
  timezone: v.literal('UTC'),
@@ -90,6 +108,8 @@ export function parseBlueprint(value) {
90
108
  rejectDuplicates('database export table', blueprint.resources.database.tables.map((entry) => entry.exportName));
91
109
  rejectDuplicates('storage bucket', blueprint.resources.storage.buckets.map((entry) => entry.name));
92
110
  rejectDuplicates('environment key', blueprint.environment.map((entry) => entry.key));
111
+ if (blueprint.api)
112
+ rejectDuplicates('api operation', blueprint.api.operations.map((entry) => entry.handle));
93
113
  rejectDuplicates('background job', blueprint.background.jobs.map((entry) => entry.name));
94
114
  rejectDuplicates('background cron', blueprint.background.cron.map((entry) => entry.name));
95
115
  rejectDuplicates('background maintenance', blueprint.background.maintenance.map((entry) => entry.name));
@@ -105,6 +125,13 @@ export function parseBlueprint(value) {
105
125
  }
106
126
  return blueprint;
107
127
  }
128
+ /**
129
+ * Whether a host must treat this key as a secret. A blueprint generated before
130
+ * 0.23.0 has no flag, and server keys are secret by default.
131
+ */
132
+ export function isSensitiveEnvVar(entry) {
133
+ return entry.sensitive ?? entry.scope === 'server';
134
+ }
108
135
  export function blueprintFromManifest(args) {
109
136
  const workerRequired = args.manifest.background.jobs.length > 0;
110
137
  return parseBlueprint({
@@ -134,6 +161,7 @@ export function blueprintFromManifest(args) {
134
161
  : {}),
135
162
  },
136
163
  environment: sortBy(args.manifest.environment, (entry) => entry.key),
164
+ api: { operations: args.manifest.api.operations },
137
165
  background: {
138
166
  worker: { required: workerRequired },
139
167
  jobs: sortBy(args.manifest.background.jobs, (entry) => entry.name),
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"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","sourcesContent":["import * as v from 'valibot'\nimport { parse, stringify } from 'yaml'\n\nimport type { BunderstackManifest } from './manifest'\n\nimport { parseCron } from './jobs/cron'\nimport { validateStandardSchema } from './standard-schema'\n\nexport type MigrationMode = 'migrations' | 'push'\nexport type ApplicationFramework =\n | 'tanstack-start'\n | 'solid'\n | 'bun-ssr'\n | 'custom'\n\nexport type BunderstackBlueprint = {\n version: 1\n generator: { name: 'bunderstack'; version: string }\n application: {\n framework: ApplicationFramework\n scripts: { build: 'build'; start: 'start'; worker?: 'worker' }\n }\n bunderstack: { entry: string; manifestVersion: 3 }\n resources: {\n database: BunderstackManifest['database'] & { migrationMode: MigrationMode }\n storage: BunderstackManifest['storage']\n realtime?: { required: true }\n }\n environment: BunderstackManifest['environment']\n background: BunderstackManifest['background'] & {\n worker: { required: boolean }\n }\n}\n\nconst nonEmpty = v.pipe(v.string(), v.minLength(1))\nconst relativePath = v.pipe(\n nonEmpty,\n v.check(\n (value) =>\n !value.startsWith('/') &&\n !value.includes('\\\\') &&\n value.split('/').every((part) => part !== '' && part !== '..'),\n 'entry must be a relative path without traversal',\n ),\n)\nconst cronSchedule = v.pipe(\n nonEmpty,\n v.check((value) => {\n try {\n parseCron(value)\n return true\n } catch {\n return false\n }\n }, 'invalid cron schedule'),\n)\n\nconst blueprintSchema = v.strictObject({\n version: v.literal(1),\n generator: v.strictObject({\n name: v.literal('bunderstack'),\n version: nonEmpty,\n }),\n application: v.strictObject({\n framework: v.picklist(['tanstack-start', 'solid', 'bun-ssr', 'custom']),\n scripts: v.strictObject({\n build: v.literal('build'),\n start: v.literal('start'),\n worker: v.optional(v.literal('worker')),\n }),\n }),\n bunderstack: v.strictObject({\n entry: relativePath,\n manifestVersion: v.literal(3),\n }),\n resources: v.strictObject({\n database: v.strictObject({\n dialect: v.picklist(['sqlite', 'pg']),\n migrationsDirectory: relativePath,\n migrationMode: v.picklist(['migrations', 'push']),\n tables: v.array(\n v.strictObject({\n exportName: nonEmpty,\n physicalName: nonEmpty,\n system: v.boolean(),\n }),\n ),\n }),\n storage: v.strictObject({\n defaultBucket: nonEmpty,\n buckets: v.array(\n v.strictObject({\n name: nonEmpty,\n visibility: v.picklist(['public', 'private']),\n }),\n ),\n }),\n realtime: v.optional(v.strictObject({ required: v.literal(true) })),\n }),\n environment: v.array(\n v.strictObject({\n key: nonEmpty,\n required: v.boolean(),\n scope: v.picklist(['server', 'client']),\n }),\n ),\n background: v.strictObject({\n worker: v.strictObject({ required: v.boolean() }),\n jobs: v.array(v.strictObject({ name: nonEmpty })),\n cron: v.array(\n v.strictObject({\n name: nonEmpty,\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n maintenance: v.array(\n v.strictObject({\n name: v.literal('storage-sweep'),\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n }),\n})\n\nfunction sortBy<T>(entries: readonly T[], key: (entry: T) => string): T[] {\n return [...entries].sort((left, right) => key(left).localeCompare(key(right)))\n}\n\nfunction rejectDuplicates(collection: string, values: readonly string[]): void {\n const seen = new Set<string>()\n for (const value of values) {\n if (seen.has(value))\n throw new Error(`[bunderstack] duplicate ${collection} \"${value}\"`)\n seen.add(value)\n }\n}\n\nexport function parseBlueprint(value: unknown): BunderstackBlueprint {\n const blueprint = validateStandardSchema(\n blueprintSchema,\n value,\n 'blueprint',\n ) as BunderstackBlueprint\n rejectDuplicates(\n 'database physical table',\n blueprint.resources.database.tables.map((entry) => entry.physicalName),\n )\n rejectDuplicates(\n 'database export table',\n blueprint.resources.database.tables.map((entry) => entry.exportName),\n )\n rejectDuplicates(\n 'storage bucket',\n blueprint.resources.storage.buckets.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'environment key',\n blueprint.environment.map((entry) => entry.key),\n )\n rejectDuplicates(\n 'background job',\n blueprint.background.jobs.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background cron',\n blueprint.background.cron.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background maintenance',\n blueprint.background.maintenance.map((entry) => entry.name),\n )\n if (\n !blueprint.resources.storage.buckets.some(\n (bucket) => bucket.name === blueprint.resources.storage.defaultBucket,\n )\n ) {\n throw new Error(\n '[bunderstack] storage defaultBucket must be declared in storage buckets',\n )\n }\n const workerRequired = blueprint.background.jobs.length > 0\n if (blueprint.background.worker.required !== workerRequired) {\n throw new Error(\n '[bunderstack] background worker.required must match declared queue jobs',\n )\n }\n if (Boolean(blueprint.application.scripts.worker) !== workerRequired) {\n throw new Error(\n '[bunderstack] application worker script must match declared queue jobs',\n )\n }\n return blueprint\n}\n\nexport function blueprintFromManifest(args: {\n manifest: BunderstackManifest\n generatorVersion: string\n entry: string\n migrationMode: MigrationMode\n framework?: ApplicationFramework\n}): BunderstackBlueprint {\n const workerRequired = args.manifest.background.jobs.length > 0\n return parseBlueprint({\n version: 1,\n generator: { name: 'bunderstack', version: args.generatorVersion },\n application: {\n framework: args.framework ?? 'tanstack-start',\n scripts: {\n build: 'build',\n start: 'start',\n ...(workerRequired ? { worker: 'worker' } : {}),\n },\n },\n bunderstack: { entry: args.entry, manifestVersion: 3 },\n resources: {\n database: {\n ...args.manifest.database,\n migrationMode: args.migrationMode,\n tables: sortBy(\n args.manifest.database.tables,\n (entry) => entry.physicalName,\n ),\n },\n storage: {\n ...args.manifest.storage,\n buckets: sortBy(args.manifest.storage.buckets, (entry) => entry.name),\n },\n ...(args.manifest.realtime.required\n ? { realtime: { required: true } }\n : {}),\n },\n environment: sortBy(args.manifest.environment, (entry) => entry.key),\n background: {\n worker: { required: workerRequired },\n jobs: sortBy(args.manifest.background.jobs, (entry) => entry.name),\n cron: sortBy(args.manifest.background.cron, (entry) => entry.name),\n maintenance: sortBy(\n args.manifest.background.maintenance,\n (entry) => entry.name,\n ),\n },\n })\n}\n\nexport function parseBlueprintYaml(source: string): BunderstackBlueprint {\n return parseBlueprint(parse(source) as unknown)\n}\n\nexport function serializeBlueprint(value: BunderstackBlueprint): string {\n const blueprint = parseBlueprint(value)\n const options = {\n aliasDuplicateObjects: false,\n defaultKeyType: 'PLAIN',\n defaultStringType: 'PLAIN',\n lineWidth: 0,\n } as const\n const source = stringify(\n parseBlueprintYaml(stringify(blueprint, options)),\n options,\n )\n return source.replace(\n /^(\\s*schedule: )(.+)$/gm,\n (_match, prefix: string, value: string) => {\n const schedule = value.startsWith('\"') ? JSON.parse(value) : value\n return `${prefix}${JSON.stringify(schedule)}`\n },\n )\n}\n"]}
1
+ {"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"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","sourcesContent":["import * as v from 'valibot'\nimport { parse, stringify } from 'yaml'\n\nimport type { BunderstackManifest } from './manifest'\n\nimport { parseCron } from './jobs/cron'\nimport { validateStandardSchema } from './standard-schema'\n\nexport type MigrationMode = 'migrations' | 'push'\nexport type ApplicationFramework =\n | 'tanstack-start'\n | 'solid'\n | 'bun-ssr'\n | 'custom'\n\n/**\n * Blueprint form of an environment key. `sensitive` and `description` are\n * optional because blueprints committed before 0.23.0 do not carry them.\n */\nexport type BlueprintEnvVar = {\n key: string\n required: boolean\n scope: 'server' | 'client'\n sensitive?: boolean\n description?: string\n}\n\nexport type BunderstackBlueprint = {\n version: 1\n generator: { name: 'bunderstack'; version: string }\n application: {\n framework: ApplicationFramework\n scripts: { build: 'build'; start: 'start'; worker?: 'worker' }\n }\n bunderstack: { entry: string; manifestVersion: 3 }\n resources: {\n database: BunderstackManifest['database'] & { migrationMode: MigrationMode }\n storage: BunderstackManifest['storage']\n realtime?: { required: true }\n }\n environment: BlueprintEnvVar[]\n /** Application-declared procedures. Absent in blueprints written before 0.23.0. */\n api?: { operations: BunderstackManifest['api']['operations'] }\n background: BunderstackManifest['background'] & {\n worker: { required: boolean }\n }\n}\n\nconst nonEmpty = v.pipe(v.string(), v.minLength(1))\nconst relativePath = v.pipe(\n nonEmpty,\n v.check(\n (value) =>\n !value.startsWith('/') &&\n !value.includes('\\\\') &&\n value.split('/').every((part) => part !== '' && part !== '..'),\n 'entry must be a relative path without traversal',\n ),\n)\nconst cronSchedule = v.pipe(\n nonEmpty,\n v.check((value) => {\n try {\n parseCron(value)\n return true\n } catch {\n return false\n }\n }, 'invalid cron schedule'),\n)\n\n// Hosts parse blueprints committed by applications that upgrade bunderstack on\n// their own schedule, so a section written by a newer generator has to survive\n// an older parser instead of failing the whole file.\nfunction open<TEntries extends v.ObjectEntries>(entries: TEntries) {\n return v.objectWithRest(entries, v.unknown())\n}\n\nconst blueprintSchema = open({\n version: v.literal(1),\n generator: open({\n name: v.literal('bunderstack'),\n version: nonEmpty,\n }),\n application: open({\n framework: v.picklist(['tanstack-start', 'solid', 'bun-ssr', 'custom']),\n scripts: open({\n build: v.literal('build'),\n start: v.literal('start'),\n worker: v.optional(v.literal('worker')),\n }),\n }),\n bunderstack: open({\n entry: relativePath,\n manifestVersion: v.literal(3),\n }),\n resources: open({\n database: open({\n dialect: v.picklist(['sqlite', 'pg']),\n migrationsDirectory: relativePath,\n migrationMode: v.picklist(['migrations', 'push']),\n tables: v.array(\n open({\n exportName: nonEmpty,\n physicalName: nonEmpty,\n system: v.boolean(),\n }),\n ),\n }),\n storage: open({\n defaultBucket: nonEmpty,\n buckets: v.array(\n open({\n name: nonEmpty,\n visibility: v.picklist(['public', 'private']),\n }),\n ),\n }),\n realtime: v.optional(open({ required: v.literal(true) })),\n }),\n environment: v.array(\n open({\n key: nonEmpty,\n required: v.boolean(),\n scope: v.picklist(['server', 'client']),\n sensitive: v.optional(v.boolean()),\n description: v.optional(v.pipe(nonEmpty, v.maxLength(200))),\n }),\n ),\n api: v.optional(\n open({\n operations: v.array(\n open({\n handle: nonEmpty,\n operationId: nonEmpty,\n effect: v.picklist(['read', 'mutation', 'unknown']),\n method: v.optional(nonEmpty),\n path: v.optional(nonEmpty),\n summary: v.optional(v.pipe(nonEmpty, v.maxLength(200))),\n }),\n ),\n }),\n ),\n background: open({\n worker: open({ required: v.boolean() }),\n jobs: v.array(open({ name: nonEmpty })),\n cron: v.array(\n open({\n name: nonEmpty,\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n maintenance: v.array(\n open({\n name: v.literal('storage-sweep'),\n schedule: cronSchedule,\n timezone: v.literal('UTC'),\n }),\n ),\n }),\n})\n\nfunction sortBy<T>(entries: readonly T[], key: (entry: T) => string): T[] {\n return [...entries].sort((left, right) => key(left).localeCompare(key(right)))\n}\n\nfunction rejectDuplicates(collection: string, values: readonly string[]): void {\n const seen = new Set<string>()\n for (const value of values) {\n if (seen.has(value))\n throw new Error(`[bunderstack] duplicate ${collection} \"${value}\"`)\n seen.add(value)\n }\n}\n\nexport function parseBlueprint(value: unknown): BunderstackBlueprint {\n const blueprint = validateStandardSchema(\n blueprintSchema,\n value,\n 'blueprint',\n ) as BunderstackBlueprint\n rejectDuplicates(\n 'database physical table',\n blueprint.resources.database.tables.map((entry) => entry.physicalName),\n )\n rejectDuplicates(\n 'database export table',\n blueprint.resources.database.tables.map((entry) => entry.exportName),\n )\n rejectDuplicates(\n 'storage bucket',\n blueprint.resources.storage.buckets.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'environment key',\n blueprint.environment.map((entry) => entry.key),\n )\n if (blueprint.api)\n rejectDuplicates(\n 'api operation',\n blueprint.api.operations.map((entry) => entry.handle),\n )\n rejectDuplicates(\n 'background job',\n blueprint.background.jobs.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background cron',\n blueprint.background.cron.map((entry) => entry.name),\n )\n rejectDuplicates(\n 'background maintenance',\n blueprint.background.maintenance.map((entry) => entry.name),\n )\n if (\n !blueprint.resources.storage.buckets.some(\n (bucket) => bucket.name === blueprint.resources.storage.defaultBucket,\n )\n ) {\n throw new Error(\n '[bunderstack] storage defaultBucket must be declared in storage buckets',\n )\n }\n const workerRequired = blueprint.background.jobs.length > 0\n if (blueprint.background.worker.required !== workerRequired) {\n throw new Error(\n '[bunderstack] background worker.required must match declared queue jobs',\n )\n }\n if (Boolean(blueprint.application.scripts.worker) !== workerRequired) {\n throw new Error(\n '[bunderstack] application worker script must match declared queue jobs',\n )\n }\n return blueprint\n}\n\n/**\n * Whether a host must treat this key as a secret. A blueprint generated before\n * 0.23.0 has no flag, and server keys are secret by default.\n */\nexport function isSensitiveEnvVar(entry: BlueprintEnvVar): boolean {\n return entry.sensitive ?? entry.scope === 'server'\n}\n\nexport function blueprintFromManifest(args: {\n manifest: BunderstackManifest\n generatorVersion: string\n entry: string\n migrationMode: MigrationMode\n framework?: ApplicationFramework\n}): BunderstackBlueprint {\n const workerRequired = args.manifest.background.jobs.length > 0\n return parseBlueprint({\n version: 1,\n generator: { name: 'bunderstack', version: args.generatorVersion },\n application: {\n framework: args.framework ?? 'tanstack-start',\n scripts: {\n build: 'build',\n start: 'start',\n ...(workerRequired ? { worker: 'worker' } : {}),\n },\n },\n bunderstack: { entry: args.entry, manifestVersion: 3 },\n resources: {\n database: {\n ...args.manifest.database,\n migrationMode: args.migrationMode,\n tables: sortBy(\n args.manifest.database.tables,\n (entry) => entry.physicalName,\n ),\n },\n storage: {\n ...args.manifest.storage,\n buckets: sortBy(args.manifest.storage.buckets, (entry) => entry.name),\n },\n ...(args.manifest.realtime.required\n ? { realtime: { required: true } }\n : {}),\n },\n environment: sortBy(args.manifest.environment, (entry) => entry.key),\n api: { operations: args.manifest.api.operations },\n background: {\n worker: { required: workerRequired },\n jobs: sortBy(args.manifest.background.jobs, (entry) => entry.name),\n cron: sortBy(args.manifest.background.cron, (entry) => entry.name),\n maintenance: sortBy(\n args.manifest.background.maintenance,\n (entry) => entry.name,\n ),\n },\n })\n}\n\nexport function parseBlueprintYaml(source: string): BunderstackBlueprint {\n return parseBlueprint(parse(source) as unknown)\n}\n\nexport function serializeBlueprint(value: BunderstackBlueprint): string {\n const blueprint = parseBlueprint(value)\n const options = {\n aliasDuplicateObjects: false,\n defaultKeyType: 'PLAIN',\n defaultStringType: 'PLAIN',\n lineWidth: 0,\n } as const\n const source = stringify(\n parseBlueprintYaml(stringify(blueprint, options)),\n options,\n )\n return source.replace(\n /^(\\s*schedule: )(.+)$/gm,\n (_match, prefix: string, value: string) => {\n const schedule = value.startsWith('\"') ? JSON.parse(value) : value\n return `${prefix}${JSON.stringify(schedule)}`\n },\n )\n}\n"]}
package/dist/env.d.ts CHANGED
@@ -1,10 +1,26 @@
1
1
  import type { StandardSchemaV1 } from '@standard-schema/spec';
2
2
  export declare const CLIENT_PREFIX: "PUBLIC_";
3
+ /**
4
+ * Static, value-free metadata about an environment key. It reaches the manifest
5
+ * and the committed blueprint, so it must never contain a value or a secret.
6
+ */
7
+ export type EnvVarMeta = {
8
+ /**
9
+ * Whether the value is a secret. Defaults to `true` for server keys and
10
+ * `false` for client keys. A client key can never be sensitive: `PUBLIC_*`
11
+ * values are compiled into the browser bundle.
12
+ */
13
+ sensitive?: boolean;
14
+ /** Prose shown to whoever fills this key in. Max 200 characters. */
15
+ description?: string;
16
+ };
3
17
  export type EnvConfigInput = {
4
18
  server?: Record<string, StandardSchemaV1>;
5
19
  client?: Record<string, StandardSchemaV1>;
6
20
  /** Explicit value source for client vars (e.g. Vite's import.meta.env). */
7
21
  runtimeEnv?: Record<string, unknown>;
22
+ /** Value-free metadata per key, keyed by the key's own name. */
23
+ meta?: Record<string, EnvVarMeta>;
8
24
  };
9
25
  export type BunderstackRole = 'all' | 'web' | 'worker';
10
26
  /** Vars bunderstack itself consumes, always validated. */
@@ -19,6 +35,8 @@ export type BaseEnv = {
19
35
  BUNDERSTACK_EMAIL_PROVIDER?: string;
20
36
  BUNDERSTACK_EMAIL_FROM?: string;
21
37
  BUNDERHOST_ENVIRONMENT_ID?: string;
38
+ /** Deployed commit SHA, injected by the platform. Reported by readiness. */
39
+ BUNDERSTACK_REVISION?: string;
22
40
  BUNDERSTACK_ROLE: BunderstackRole;
23
41
  };
24
42
  type InferVars<T> = T extends Record<string, StandardSchemaV1> ? {
package/dist/env.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAO7D,eAAO,MAAM,aAAa,EAAG,SAAkB,CAAA;AAE/C,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;AAItD,0DAA0D;AAC1D,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,gBAAgB,EAAE,eAAe,CAAA;CAClC,CAAA;AAED,KAAK,SAAS,CAAC,CAAC,IACd,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACtC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtD,OAAO,CAAA;AAGb,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,IAAI;IAClE,IAAI;CACL,SAAS,CAAC,cAAc,CAAC,GACtB,OAAO,GACL,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACtC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACxC,OAAO,CAAA;AAEX,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;gBAEb,MAAM,EAAE,MAAM,EAAE;CAK7B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC3C,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAqCD,wBAAgB,WAAW,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,EACjE,SAAS,EAAE,IAAI,EACf,OAAO,GAAE,kBAAuB,GAC/B,YAAY,CAAC,IAAI,CAAC,CA4CpB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,EACzD,SAAS,EAAE,IAAI,GACd,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAsB3B"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAO7D,eAAO,MAAM,aAAa,EAAG,SAAkB,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IACzC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACpC,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAA;AAItD,0DAA0D;AAC1D,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,gBAAgB,EAAE,eAAe,CAAA;CAClC,CAAA;AAED,KAAK,SAAS,CAAC,CAAC,IACd,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACtC;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtD,OAAO,CAAA;AAGb,MAAM,MAAM,YAAY,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,IAAI;IAClE,IAAI;CACL,SAAS,CAAC,cAAc,CAAC,GACtB,OAAO,GACL,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACtC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,GACxC,OAAO,CAAA;AAEX,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;gBAEb,MAAM,EAAE,MAAM,EAAE;CAK7B;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC3C,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,CAAA;AAqCD,wBAAgB,WAAW,CAAC,IAAI,SAAS,cAAc,GAAG,SAAS,EACjE,SAAS,EAAE,IAAI,EACf,OAAO,GAAE,kBAAuB,GAC/B,YAAY,CAAC,IAAI,CAAC,CA6CpB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,EACzD,SAAS,EAAE,IAAI,GACd,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAsB3B"}
package/dist/env.js CHANGED
@@ -49,6 +49,7 @@ export function validateEnv(envConfig, options = {}) {
49
49
  BUNDERSTACK_EMAIL_PROVIDER: source.BUNDERSTACK_EMAIL_PROVIDER,
50
50
  BUNDERSTACK_EMAIL_FROM: source.BUNDERSTACK_EMAIL_FROM,
51
51
  BUNDERHOST_ENVIRONMENT_ID: source.BUNDERHOST_ENVIRONMENT_ID,
52
+ BUNDERSTACK_REVISION: source.BUNDERSTACK_REVISION,
52
53
  BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all'),
53
54
  };
54
55
  if (isProduction && !source.AUTH_SECRET) {
package/dist/env.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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","sourcesContent":["// src/env.ts — env validation. Browser-safe: type-only Standard Schema import.\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport {\n StandardSchemaValidationError,\n validateStandardSchema,\n} from './standard-schema'\n\nexport const CLIENT_PREFIX = 'PUBLIC_' as const\n\nexport type EnvConfigInput = {\n server?: Record<string, StandardSchemaV1>\n client?: Record<string, StandardSchemaV1>\n /** Explicit value source for client vars (e.g. Vite's import.meta.env). */\n runtimeEnv?: Record<string, unknown>\n}\n\nexport type BunderstackRole = 'all' | 'web' | 'worker'\n\nconst ROLES: readonly BunderstackRole[] = ['all', 'web', 'worker']\n\n/** Vars bunderstack itself consumes, always validated. */\nexport type BaseEnv = {\n NODE_ENV?: string\n DATABASE_URL: string\n DATABASE_AUTH_TOKEN?: string\n AUTH_SECRET: string\n REDIS_URL?: string\n RESEND_API_KEY?: string\n SMTP_URL?: string\n BUNDERSTACK_EMAIL_PROVIDER?: string\n BUNDERSTACK_EMAIL_FROM?: string\n BUNDERHOST_ENVIRONMENT_ID?: string\n BUNDERSTACK_ROLE: BunderstackRole\n}\n\ntype InferVars<T> =\n T extends Record<string, StandardSchemaV1>\n ? { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]> }\n : unknown\n\n// Non-distributive so `ValidatedEnv<undefined>` is BaseEnv, not `never`.\nexport type ValidatedEnv<TEnv extends EnvConfigInput | undefined> = [\n TEnv,\n] extends [EnvConfigInput]\n ? BaseEnv &\n InferVars<NonNullable<TEnv>['server']> &\n InferVars<NonNullable<TEnv>['client']>\n : BaseEnv\n\nexport class BunderstackEnvError extends Error {\n readonly issues: string[]\n\n constructor(issues: string[]) {\n super(`Invalid environment:\\n - ${issues.join('\\n - ')}`)\n this.name = 'BunderstackEnvError'\n this.issues = issues\n }\n}\n\nexport type ValidateEnvOptions = {\n /** String tag of the configured email provider ('resend' | 'smtp'), if any. */\n emailProvider?: string\n /** Value source; defaults to process.env. Tests pass this explicitly. */\n source?: Record<string, string | undefined>\n /** Dialect-aware DATABASE_URL fallback; bunderstack passes it. */\n defaultDatabaseUrl?: string\n}\n\nconst DEV_AUTH_SECRET = 'dev-secret-change-in-prod'\n\nfunction validateSection(\n section: Record<string, StandardSchemaV1> | undefined,\n kind: 'server' | 'client',\n source: Record<string, unknown>,\n issues: string[],\n out: Record<string, unknown>,\n) {\n for (const [key, schema] of Object.entries(section ?? {})) {\n const isPublic = key.startsWith(CLIENT_PREFIX)\n if (kind === 'server' && isPublic) {\n issues.push(\n `${key}: server vars must not start with ${CLIENT_PREFIX} (move it to env.client)`,\n )\n continue\n }\n if (kind === 'client' && !isPublic) {\n issues.push(\n `${key}: client vars must start with ${CLIENT_PREFIX} (rename it or move it to env.server)`,\n )\n continue\n }\n try {\n out[key] = validateStandardSchema(schema, source[key], 'env')\n } catch (error) {\n if (!(error instanceof StandardSchemaValidationError)) throw error\n for (const issue of error.issues) {\n const path = issue.path.map(String).join('.')\n issues.push(`${key}${path ? `.${path}` : ''}: ${issue.message}`)\n }\n }\n }\n}\n\nexport function validateEnv<TEnv extends EnvConfigInput | undefined>(\n envConfig: TEnv,\n options: ValidateEnvOptions = {},\n): ValidatedEnv<TEnv> {\n const source =\n options.source ?? (process.env as Record<string, string | undefined>)\n const issues: string[] = []\n const isProduction = source.NODE_ENV === 'production'\n\n const base: BaseEnv = {\n NODE_ENV: source.NODE_ENV,\n DATABASE_URL:\n source.DATABASE_URL ?? options.defaultDatabaseUrl ?? 'file:./data.db',\n DATABASE_AUTH_TOKEN: source.DATABASE_AUTH_TOKEN,\n AUTH_SECRET: source.AUTH_SECRET ?? DEV_AUTH_SECRET,\n REDIS_URL: source.REDIS_URL,\n RESEND_API_KEY: source.RESEND_API_KEY,\n SMTP_URL: source.SMTP_URL,\n BUNDERSTACK_EMAIL_PROVIDER: source.BUNDERSTACK_EMAIL_PROVIDER,\n BUNDERSTACK_EMAIL_FROM: source.BUNDERSTACK_EMAIL_FROM,\n BUNDERHOST_ENVIRONMENT_ID: source.BUNDERHOST_ENVIRONMENT_ID,\n BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all') as BunderstackRole,\n }\n if (isProduction && !source.AUTH_SECRET) {\n issues.push('AUTH_SECRET: required in production')\n }\n if (\n source.BUNDERSTACK_ROLE !== undefined &&\n !ROLES.includes(source.BUNDERSTACK_ROLE as BunderstackRole)\n ) {\n issues.push(\n `BUNDERSTACK_ROLE: must be one of ${ROLES.join(', ')} (got \"${String(source.BUNDERSTACK_ROLE)}\")`,\n )\n }\n if (options.emailProvider === 'resend' && !source.RESEND_API_KEY) {\n issues.push(\"RESEND_API_KEY: required when email provider is 'resend'\")\n }\n if (options.emailProvider === 'smtp' && !source.SMTP_URL) {\n issues.push(\"SMTP_URL: required when email provider is 'smtp'\")\n }\n\n const userVars: Record<string, unknown> = {}\n validateSection(envConfig?.server, 'server', source, issues, userVars)\n validateSection(envConfig?.client, 'client', source, issues, userVars)\n\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n return { ...base, ...userVars } as ValidatedEnv<TEnv>\n}\n\n/**\n * Browser-side companion (t3-env style): validates ONLY the client section.\n * Server keys exist on the returned object as traps that throw on access, so\n * a leaked import fails loudly instead of silently reading undefined.\n */\nexport function createClientEnv<TEnv extends EnvConfigInput>(\n envConfig: TEnv,\n): InferVars<TEnv['client']> {\n const source =\n envConfig.runtimeEnv ??\n (typeof process !== 'undefined'\n ? (process.env as Record<string, unknown>)\n : {})\n const issues: string[] = []\n const values: Record<string, unknown> = {}\n validateSection(envConfig.client, 'client', source, issues, values)\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n\n const serverKeys = new Set(Object.keys(envConfig.server ?? {}))\n return new Proxy(values, {\n get(target, prop) {\n if (typeof prop === 'string' && serverKeys.has(prop)) {\n throw new Error(\n `${prop} is server-only and not available in client env`,\n )\n }\n return Reflect.get(target, prop)\n },\n }) as InferVars<TEnv['client']>\n}\n"]}
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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","sourcesContent":["// src/env.ts — env validation. Browser-safe: type-only Standard Schema import.\nimport type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport {\n StandardSchemaValidationError,\n validateStandardSchema,\n} from './standard-schema'\n\nexport const CLIENT_PREFIX = 'PUBLIC_' as const\n\n/**\n * Static, value-free metadata about an environment key. It reaches the manifest\n * and the committed blueprint, so it must never contain a value or a secret.\n */\nexport type EnvVarMeta = {\n /**\n * Whether the value is a secret. Defaults to `true` for server keys and\n * `false` for client keys. A client key can never be sensitive: `PUBLIC_*`\n * values are compiled into the browser bundle.\n */\n sensitive?: boolean\n /** Prose shown to whoever fills this key in. Max 200 characters. */\n description?: string\n}\n\nexport type EnvConfigInput = {\n server?: Record<string, StandardSchemaV1>\n client?: Record<string, StandardSchemaV1>\n /** Explicit value source for client vars (e.g. Vite's import.meta.env). */\n runtimeEnv?: Record<string, unknown>\n /** Value-free metadata per key, keyed by the key's own name. */\n meta?: Record<string, EnvVarMeta>\n}\n\nexport type BunderstackRole = 'all' | 'web' | 'worker'\n\nconst ROLES: readonly BunderstackRole[] = ['all', 'web', 'worker']\n\n/** Vars bunderstack itself consumes, always validated. */\nexport type BaseEnv = {\n NODE_ENV?: string\n DATABASE_URL: string\n DATABASE_AUTH_TOKEN?: string\n AUTH_SECRET: string\n REDIS_URL?: string\n RESEND_API_KEY?: string\n SMTP_URL?: string\n BUNDERSTACK_EMAIL_PROVIDER?: string\n BUNDERSTACK_EMAIL_FROM?: string\n BUNDERHOST_ENVIRONMENT_ID?: string\n /** Deployed commit SHA, injected by the platform. Reported by readiness. */\n BUNDERSTACK_REVISION?: string\n BUNDERSTACK_ROLE: BunderstackRole\n}\n\ntype InferVars<T> =\n T extends Record<string, StandardSchemaV1>\n ? { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]> }\n : unknown\n\n// Non-distributive so `ValidatedEnv<undefined>` is BaseEnv, not `never`.\nexport type ValidatedEnv<TEnv extends EnvConfigInput | undefined> = [\n TEnv,\n] extends [EnvConfigInput]\n ? BaseEnv &\n InferVars<NonNullable<TEnv>['server']> &\n InferVars<NonNullable<TEnv>['client']>\n : BaseEnv\n\nexport class BunderstackEnvError extends Error {\n readonly issues: string[]\n\n constructor(issues: string[]) {\n super(`Invalid environment:\\n - ${issues.join('\\n - ')}`)\n this.name = 'BunderstackEnvError'\n this.issues = issues\n }\n}\n\nexport type ValidateEnvOptions = {\n /** String tag of the configured email provider ('resend' | 'smtp'), if any. */\n emailProvider?: string\n /** Value source; defaults to process.env. Tests pass this explicitly. */\n source?: Record<string, string | undefined>\n /** Dialect-aware DATABASE_URL fallback; bunderstack passes it. */\n defaultDatabaseUrl?: string\n}\n\nconst DEV_AUTH_SECRET = 'dev-secret-change-in-prod'\n\nfunction validateSection(\n section: Record<string, StandardSchemaV1> | undefined,\n kind: 'server' | 'client',\n source: Record<string, unknown>,\n issues: string[],\n out: Record<string, unknown>,\n) {\n for (const [key, schema] of Object.entries(section ?? {})) {\n const isPublic = key.startsWith(CLIENT_PREFIX)\n if (kind === 'server' && isPublic) {\n issues.push(\n `${key}: server vars must not start with ${CLIENT_PREFIX} (move it to env.client)`,\n )\n continue\n }\n if (kind === 'client' && !isPublic) {\n issues.push(\n `${key}: client vars must start with ${CLIENT_PREFIX} (rename it or move it to env.server)`,\n )\n continue\n }\n try {\n out[key] = validateStandardSchema(schema, source[key], 'env')\n } catch (error) {\n if (!(error instanceof StandardSchemaValidationError)) throw error\n for (const issue of error.issues) {\n const path = issue.path.map(String).join('.')\n issues.push(`${key}${path ? `.${path}` : ''}: ${issue.message}`)\n }\n }\n }\n}\n\nexport function validateEnv<TEnv extends EnvConfigInput | undefined>(\n envConfig: TEnv,\n options: ValidateEnvOptions = {},\n): ValidatedEnv<TEnv> {\n const source =\n options.source ?? (process.env as Record<string, string | undefined>)\n const issues: string[] = []\n const isProduction = source.NODE_ENV === 'production'\n\n const base: BaseEnv = {\n NODE_ENV: source.NODE_ENV,\n DATABASE_URL:\n source.DATABASE_URL ?? options.defaultDatabaseUrl ?? 'file:./data.db',\n DATABASE_AUTH_TOKEN: source.DATABASE_AUTH_TOKEN,\n AUTH_SECRET: source.AUTH_SECRET ?? DEV_AUTH_SECRET,\n REDIS_URL: source.REDIS_URL,\n RESEND_API_KEY: source.RESEND_API_KEY,\n SMTP_URL: source.SMTP_URL,\n BUNDERSTACK_EMAIL_PROVIDER: source.BUNDERSTACK_EMAIL_PROVIDER,\n BUNDERSTACK_EMAIL_FROM: source.BUNDERSTACK_EMAIL_FROM,\n BUNDERHOST_ENVIRONMENT_ID: source.BUNDERHOST_ENVIRONMENT_ID,\n BUNDERSTACK_REVISION: source.BUNDERSTACK_REVISION,\n BUNDERSTACK_ROLE: (source.BUNDERSTACK_ROLE ?? 'all') as BunderstackRole,\n }\n if (isProduction && !source.AUTH_SECRET) {\n issues.push('AUTH_SECRET: required in production')\n }\n if (\n source.BUNDERSTACK_ROLE !== undefined &&\n !ROLES.includes(source.BUNDERSTACK_ROLE as BunderstackRole)\n ) {\n issues.push(\n `BUNDERSTACK_ROLE: must be one of ${ROLES.join(', ')} (got \"${String(source.BUNDERSTACK_ROLE)}\")`,\n )\n }\n if (options.emailProvider === 'resend' && !source.RESEND_API_KEY) {\n issues.push(\"RESEND_API_KEY: required when email provider is 'resend'\")\n }\n if (options.emailProvider === 'smtp' && !source.SMTP_URL) {\n issues.push(\"SMTP_URL: required when email provider is 'smtp'\")\n }\n\n const userVars: Record<string, unknown> = {}\n validateSection(envConfig?.server, 'server', source, issues, userVars)\n validateSection(envConfig?.client, 'client', source, issues, userVars)\n\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n return { ...base, ...userVars } as ValidatedEnv<TEnv>\n}\n\n/**\n * Browser-side companion (t3-env style): validates ONLY the client section.\n * Server keys exist on the returned object as traps that throw on access, so\n * a leaked import fails loudly instead of silently reading undefined.\n */\nexport function createClientEnv<TEnv extends EnvConfigInput>(\n envConfig: TEnv,\n): InferVars<TEnv['client']> {\n const source =\n envConfig.runtimeEnv ??\n (typeof process !== 'undefined'\n ? (process.env as Record<string, unknown>)\n : {})\n const issues: string[] = []\n const values: Record<string, unknown> = {}\n validateSection(envConfig.client, 'client', source, issues, values)\n if (issues.length > 0) throw new BunderstackEnvError(issues)\n\n const serverKeys = new Set(Object.keys(envConfig.server ?? {}))\n return new Proxy(values, {\n get(target, prop) {\n if (typeof prop === 'string' && serverKeys.has(prop)) {\n throw new Error(\n `${prop} is server-only and not available in client env`,\n )\n }\n return Reflect.get(target, prop)\n },\n }) as InferVars<TEnv['client']>\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"]}
package/dist/index.d.ts CHANGED
@@ -11,7 +11,9 @@ export type { AuthConfigContext, AuthConfigFactory, AuthConfigInput, BetterAuthC
11
11
  export { validateEnv, createClientEnv, BunderstackEnvError } from './env.js';
12
12
  export type { EnvConfigInput, BaseEnv, ValidatedEnv } from './env.js';
13
13
  export { buildManifest, parseManifest } from './manifest.js';
14
- export type { BunderstackManifest, ManifestEnvVar } from './manifest.js';
14
+ export type { ApiOperation, ApiOperationEffect, BunderstackManifest, ManifestEnvVar, } from './manifest.js';
15
+ export { buildReadinessReport, readinessReportSchema } from './readiness.js';
16
+ export type { ReadinessCheck, ReadinessCheckName, ReadinessCode, ReadinessReport, ReadinessStatus, } from './readiness.js';
15
17
  export { createEmail } from './email.js';
16
18
  export type { EmailMessage, EmailAdapter, EmailConfigInput, EmailFacade, } from './email.js';
17
19
  export { createJobsBuilder } from './jobs/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,YAAY,EACV,mBAAmB,EACnB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACtD,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,MAAM,CAAA;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAC3C,YAAY,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACvE,YAAY,EACV,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AACzE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACzD,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,GACZ,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,cAAc,EACd,cAAc,EACd,cAAc,EACd,UAAU,EACV,aAAa,EACb,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,YAAY,GACb,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,aAAa,EACb,UAAU,GACX,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,MAAM,EACN,QAAQ,IAAI,cAAc,EAC1B,KAAK,IAAI,WAAW,EACpB,QAAQ,GACT,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AACtC,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,GACf,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,WAAW,GACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC3D,YAAY,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AACtE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/C,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,aAAa,CAAA;AACpB,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { BunderstackError } from './errors.js';
5
5
  export { resolveConfig, resolveAuthConfig, defineAuth } from './config.js';
6
6
  export { validateEnv, createClientEnv, BunderstackEnvError } from './env.js';
7
7
  export { buildManifest, parseManifest } from './manifest.js';
8
+ export { buildReadinessReport, readinessReportSchema } from './readiness.js';
8
9
  export { createEmail } from './email.js';
9
10
  export { createJobsBuilder } from './jobs/index.js';
10
11
  export { defineAccess, validateAndResolveAccess, checkAccess, AUTH_TABLE_NAMES, } from './access.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AASzB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AASvE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEzE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAEzD,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAOrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAoBhD,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,UAAU,CAAA;AAOjB,OAAO,EACL,MAAM,EACN,QAAQ,IAAI,cAAc,EAC1B,KAAK,IAAI,WAAW,EACpB,QAAQ,GACT,MAAM,UAAU,CAAA;AAejB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAMxD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAU3D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA","sourcesContent":["export * from './backend'\nexport type {\n AppRunWorkerOptions,\n AppStartWorkerOptions,\n AuthInstance,\n BucketNamesOf,\n BunderstackApp,\n StorageFacade,\n} from './runtime'\nexport { listSpec } from './api/list-spec'\nexport type { ListSpecOptions } from './api/list-spec'\nexport type { BunderstackDb, BunderstackTx } from './db'\nexport { MAX_LIST_LIMIT } from './list-query'\nexport { BunderstackError } from './errors'\nexport type { BunderstackErrorCode } from './errors'\nexport { resolveConfig, resolveAuthConfig, defineAuth } from './config'\nexport type {\n AuthConfigContext,\n AuthConfigFactory,\n AuthConfigInput,\n BetterAuthConfig,\n BunderstackConfig,\n ResolvedConfig,\n} from './config'\nexport { validateEnv, createClientEnv, BunderstackEnvError } from './env'\nexport type { EnvConfigInput, BaseEnv, ValidatedEnv } from './env'\nexport { buildManifest, parseManifest } from './manifest'\nexport type { BunderstackManifest, ManifestEnvVar } from './manifest'\nexport { createEmail } from './email'\nexport type {\n EmailMessage,\n EmailAdapter,\n EmailConfigInput,\n EmailFacade,\n} from './email'\nexport { createJobsBuilder } from './jobs/index'\nexport type {\n BunderstackJobContext,\n BunderstackJobsBuilder,\n BackgroundDefinition,\n BackgroundDefs,\n CronDefinition,\n CronInvocation,\n EnqueueOptions,\n JobContext,\n JobDefinition,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n QueueJobDefinition,\n QueueJobKeys,\n RunWorkerOptions,\n StartWorkerOptions,\n WorkerHandle,\n} from './jobs/index'\nexport {\n defineAccess,\n validateAndResolveAccess,\n checkAccess,\n AUTH_TABLE_NAMES,\n} from './access'\nexport type {\n TableAccessInput,\n OperationRule,\n AccessContext,\n AccessUser,\n} from './access'\nexport {\n typeid,\n generate as generateTypeId,\n parse as parseTypeId,\n asTypeId,\n} from './typeid'\nexport type { TypeId } from './typeid'\nexport type {\n DatabaseAdapter,\n DatabaseConnection,\n DatabaseConnectionResult,\n} from './database/adapter'\nexport type { StorageAdapter } from './storage/index'\nexport type {\n StorageConfigInput,\n BucketConfigInput,\n ResolvedBucket,\n} from './storage/buckets'\nexport type { TransformSpec } from './storage/thumbnails'\nexport type { RealtimeAction } from './realtime/publisher'\nexport { createRealtimeFacade } from './realtime/facade'\nexport type {\n RealtimeFacade,\n RealtimeTransport,\n SchemaTable,\n} from './realtime/facade'\nexport { createApiBuilder, defineApi } from './api/builder'\nexport type { BunderstackApiBuilder, ApiFactory } from './api/builder'\nexport type { ApiContext } from './api/context'\nexport type {\n CrudApiRouterFor,\n ExposedApiTables,\n MergeApiRouterTypes,\n UnifiedApiRouter,\n} from './api/types'\nexport type { TableCrudProcedures } from './api/crud-router'\nexport {\n buildApiRegistry,\n mergeApiRoutersStrict,\n normalizeApiPath,\n normalizeForeignOpenAPISpec,\n} from './api/registry'\nexport { mergeOpenAPISpecs } from './api/openapi'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AASzB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAG1C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AASvE,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEzE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAOzD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAQzE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAOrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAoBhD,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,WAAW,EACX,gBAAgB,GACjB,MAAM,UAAU,CAAA;AAOjB,OAAO,EACL,MAAM,EACN,QAAQ,IAAI,cAAc,EAC1B,KAAK,IAAI,WAAW,EACpB,QAAQ,GACT,MAAM,UAAU,CAAA;AAejB,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAMxD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAU3D,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA","sourcesContent":["export * from './backend'\nexport type {\n AppRunWorkerOptions,\n AppStartWorkerOptions,\n AuthInstance,\n BucketNamesOf,\n BunderstackApp,\n StorageFacade,\n} from './runtime'\nexport { listSpec } from './api/list-spec'\nexport type { ListSpecOptions } from './api/list-spec'\nexport type { BunderstackDb, BunderstackTx } from './db'\nexport { MAX_LIST_LIMIT } from './list-query'\nexport { BunderstackError } from './errors'\nexport type { BunderstackErrorCode } from './errors'\nexport { resolveConfig, resolveAuthConfig, defineAuth } from './config'\nexport type {\n AuthConfigContext,\n AuthConfigFactory,\n AuthConfigInput,\n BetterAuthConfig,\n BunderstackConfig,\n ResolvedConfig,\n} from './config'\nexport { validateEnv, createClientEnv, BunderstackEnvError } from './env'\nexport type { EnvConfigInput, BaseEnv, ValidatedEnv } from './env'\nexport { buildManifest, parseManifest } from './manifest'\nexport type {\n ApiOperation,\n ApiOperationEffect,\n BunderstackManifest,\n ManifestEnvVar,\n} from './manifest'\nexport { buildReadinessReport, readinessReportSchema } from './readiness'\nexport type {\n ReadinessCheck,\n ReadinessCheckName,\n ReadinessCode,\n ReadinessReport,\n ReadinessStatus,\n} from './readiness'\nexport { createEmail } from './email'\nexport type {\n EmailMessage,\n EmailAdapter,\n EmailConfigInput,\n EmailFacade,\n} from './email'\nexport { createJobsBuilder } from './jobs/index'\nexport type {\n BunderstackJobContext,\n BunderstackJobsBuilder,\n BackgroundDefinition,\n BackgroundDefs,\n CronDefinition,\n CronInvocation,\n EnqueueOptions,\n JobContext,\n JobDefinition,\n JobsDefs,\n JobsFacade,\n JobsRuntimeFacade,\n QueueJobDefinition,\n QueueJobKeys,\n RunWorkerOptions,\n StartWorkerOptions,\n WorkerHandle,\n} from './jobs/index'\nexport {\n defineAccess,\n validateAndResolveAccess,\n checkAccess,\n AUTH_TABLE_NAMES,\n} from './access'\nexport type {\n TableAccessInput,\n OperationRule,\n AccessContext,\n AccessUser,\n} from './access'\nexport {\n typeid,\n generate as generateTypeId,\n parse as parseTypeId,\n asTypeId,\n} from './typeid'\nexport type { TypeId } from './typeid'\nexport type {\n DatabaseAdapter,\n DatabaseConnection,\n DatabaseConnectionResult,\n} from './database/adapter'\nexport type { StorageAdapter } from './storage/index'\nexport type {\n StorageConfigInput,\n BucketConfigInput,\n ResolvedBucket,\n} from './storage/buckets'\nexport type { TransformSpec } from './storage/thumbnails'\nexport type { RealtimeAction } from './realtime/publisher'\nexport { createRealtimeFacade } from './realtime/facade'\nexport type {\n RealtimeFacade,\n RealtimeTransport,\n SchemaTable,\n} from './realtime/facade'\nexport { createApiBuilder, defineApi } from './api/builder'\nexport type { BunderstackApiBuilder, ApiFactory } from './api/builder'\nexport type { ApiContext } from './api/context'\nexport type {\n CrudApiRouterFor,\n ExposedApiTables,\n MergeApiRouterTypes,\n UnifiedApiRouter,\n} from './api/types'\nexport type { TableCrudProcedures } from './api/crud-router'\nexport {\n buildApiRegistry,\n mergeApiRoutersStrict,\n normalizeApiPath,\n normalizeForeignOpenAPISpec,\n} from './api/registry'\nexport { mergeOpenAPISpecs } from './api/openapi'\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) {