bunderstack 0.25.2 → 1.0.0-beta.2

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 (172) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/auth-password.d.ts +27 -0
  3. package/dist/auth-password.d.ts.map +1 -0
  4. package/dist/auth-password.js +60 -0
  5. package/dist/auth-password.js.map +1 -0
  6. package/dist/auth.d.ts +1 -1
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/auth.js +1 -0
  9. package/dist/auth.js.map +1 -1
  10. package/dist/backend.d.ts +3 -0
  11. package/dist/backend.d.ts.map +1 -1
  12. package/dist/backend.js +3 -1
  13. package/dist/backend.js.map +1 -1
  14. package/dist/blueprint-generator.d.ts.map +1 -1
  15. package/dist/blueprint-generator.js +5 -2
  16. package/dist/blueprint-generator.js.map +1 -1
  17. package/dist/blueprint.d.ts +4 -1
  18. package/dist/blueprint.d.ts.map +1 -1
  19. package/dist/blueprint.js +25 -8
  20. package/dist/blueprint.js.map +1 -1
  21. package/dist/cli.d.ts +11 -1
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +99 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/config.d.ts +0 -8
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +0 -18
  28. package/dist/config.js.map +1 -1
  29. package/dist/crud-operations.d.ts.map +1 -1
  30. package/dist/crud-operations.js +19 -3
  31. package/dist/crud-operations.js.map +1 -1
  32. package/dist/dev/binaries.d.ts +22 -0
  33. package/dist/dev/binaries.d.ts.map +1 -0
  34. package/dist/dev/binaries.js +119 -0
  35. package/dist/dev/binaries.js.map +1 -0
  36. package/dist/dev/env.d.ts +11 -0
  37. package/dist/dev/env.d.ts.map +1 -0
  38. package/dist/dev/env.js +71 -0
  39. package/dist/dev/env.js.map +1 -0
  40. package/dist/dev/index.d.ts +45 -0
  41. package/dist/dev/index.d.ts.map +1 -0
  42. package/dist/dev/index.js +290 -0
  43. package/dist/dev/index.js.map +1 -0
  44. package/dist/dev/processes.d.ts +37 -0
  45. package/dist/dev/processes.d.ts.map +1 -0
  46. package/dist/dev/processes.js +104 -0
  47. package/dist/dev/processes.js.map +1 -0
  48. package/dist/dev/push.d.ts +2 -0
  49. package/dist/dev/push.d.ts.map +1 -0
  50. package/dist/dev/push.js +30 -0
  51. package/dist/dev/push.js.map +1 -0
  52. package/dist/env-probe.d.ts.map +1 -1
  53. package/dist/env-probe.js +0 -1
  54. package/dist/env-probe.js.map +1 -1
  55. package/dist/env.d.ts +0 -3
  56. package/dist/env.d.ts.map +1 -1
  57. package/dist/env.js +0 -7
  58. package/dist/env.js.map +1 -1
  59. package/dist/handler.d.ts +2 -0
  60. package/dist/handler.d.ts.map +1 -1
  61. package/dist/handler.js +1 -2
  62. package/dist/handler.js.map +1 -1
  63. package/dist/hosted-contract.d.ts.map +1 -1
  64. package/dist/hosted-contract.js +7 -3
  65. package/dist/hosted-contract.js.map +1 -1
  66. package/dist/index.d.ts +4 -2
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +1 -0
  69. package/dist/index.js.map +1 -1
  70. package/dist/jobs/define.d.ts +5 -0
  71. package/dist/jobs/define.d.ts.map +1 -1
  72. package/dist/jobs/define.js.map +1 -1
  73. package/dist/jobs/index.d.ts +2 -4
  74. package/dist/jobs/index.d.ts.map +1 -1
  75. package/dist/jobs/index.js +2 -3
  76. package/dist/jobs/index.js.map +1 -1
  77. package/dist/jobs/queue.d.ts +1 -0
  78. package/dist/jobs/queue.d.ts.map +1 -1
  79. package/dist/jobs/queue.js +6 -3
  80. package/dist/jobs/queue.js.map +1 -1
  81. package/dist/jobs/slots.d.ts +5 -0
  82. package/dist/jobs/slots.d.ts.map +1 -1
  83. package/dist/jobs/slots.js +12 -0
  84. package/dist/jobs/slots.js.map +1 -1
  85. package/dist/jobs/worker.d.ts +6 -0
  86. package/dist/jobs/worker.d.ts.map +1 -1
  87. package/dist/jobs/worker.js +32 -1
  88. package/dist/jobs/worker.js.map +1 -1
  89. package/dist/platform.d.ts +25 -0
  90. package/dist/platform.d.ts.map +1 -0
  91. package/dist/platform.js +24 -0
  92. package/dist/platform.js.map +1 -0
  93. package/dist/rate-limit.d.ts +2 -1
  94. package/dist/rate-limit.d.ts.map +1 -1
  95. package/dist/rate-limit.js +19 -22
  96. package/dist/rate-limit.js.map +1 -1
  97. package/dist/realtime/facade.d.ts +1 -1
  98. package/dist/realtime/facade.d.ts.map +1 -1
  99. package/dist/realtime/facade.js.map +1 -1
  100. package/dist/realtime/publisher.d.ts +0 -4
  101. package/dist/realtime/publisher.d.ts.map +1 -1
  102. package/dist/realtime/publisher.js +0 -9
  103. package/dist/realtime/publisher.js.map +1 -1
  104. package/dist/runtime.d.ts +5 -19
  105. package/dist/runtime.d.ts.map +1 -1
  106. package/dist/runtime.js +28 -102
  107. package/dist/runtime.js.map +1 -1
  108. package/dist/storage/registry.d.ts +2 -1
  109. package/dist/storage/registry.d.ts.map +1 -1
  110. package/dist/storage/registry.js +2 -2
  111. package/dist/storage/registry.js.map +1 -1
  112. package/dist/storage/s3.d.ts +11 -4
  113. package/dist/storage/s3.d.ts.map +1 -1
  114. package/dist/storage/s3.js +88 -34
  115. package/dist/storage/s3.js.map +1 -1
  116. package/dist/storage/sigv4.d.ts +24 -0
  117. package/dist/storage/sigv4.d.ts.map +1 -0
  118. package/dist/storage/sigv4.js +107 -0
  119. package/dist/storage/sigv4.js.map +1 -0
  120. package/dist/testing/fixture.d.ts.map +1 -1
  121. package/dist/testing/fixture.js +0 -3
  122. package/dist/testing/fixture.js.map +1 -1
  123. package/dist/testing/workers-fakes.d.ts +17 -0
  124. package/dist/testing/workers-fakes.d.ts.map +1 -0
  125. package/dist/testing/workers-fakes.js +98 -0
  126. package/dist/testing/workers-fakes.js.map +1 -0
  127. package/dist/typeid.d.ts +1 -0
  128. package/dist/typeid.d.ts.map +1 -1
  129. package/dist/typeid.js +34 -4
  130. package/dist/typeid.js.map +1 -1
  131. package/dist/vite.d.ts +30 -0
  132. package/dist/vite.d.ts.map +1 -0
  133. package/dist/vite.js +37 -0
  134. package/dist/vite.js.map +1 -0
  135. package/dist/workers/app.d.ts +19 -0
  136. package/dist/workers/app.d.ts.map +1 -0
  137. package/dist/workers/app.js +56 -0
  138. package/dist/workers/app.js.map +1 -0
  139. package/dist/workers/index.d.ts +21 -0
  140. package/dist/workers/index.d.ts.map +1 -0
  141. package/dist/workers/index.js +33 -0
  142. package/dist/workers/index.js.map +1 -0
  143. package/dist/workers/r2.d.ts +23 -0
  144. package/dist/workers/r2.d.ts.map +1 -0
  145. package/dist/workers/r2.js +77 -0
  146. package/dist/workers/r2.js.map +1 -0
  147. package/dist/workers/rate-limiter.d.ts +9 -0
  148. package/dist/workers/rate-limiter.d.ts.map +1 -0
  149. package/dist/workers/rate-limiter.js +22 -0
  150. package/dist/workers/rate-limiter.js.map +1 -0
  151. package/dist/workers/realtime-hub.d.ts +20 -0
  152. package/dist/workers/realtime-hub.d.ts.map +1 -0
  153. package/dist/workers/realtime-hub.js +117 -0
  154. package/dist/workers/realtime-hub.js.map +1 -0
  155. package/dist/workers/scheduler.d.ts +13 -0
  156. package/dist/workers/scheduler.d.ts.map +1 -0
  157. package/dist/workers/scheduler.js +71 -0
  158. package/dist/workers/scheduler.js.map +1 -0
  159. package/dist/workers/types.d.ts +55 -0
  160. package/dist/workers/types.d.ts.map +1 -0
  161. package/dist/workers/types.js +4 -0
  162. package/dist/workers/types.js.map +1 -0
  163. package/dist/workers/wrangler.d.ts +64 -0
  164. package/dist/workers/wrangler.d.ts.map +1 -0
  165. package/dist/workers/wrangler.js +118 -0
  166. package/dist/workers/wrangler.js.map +1 -0
  167. package/package.json +11 -3
  168. package/skills/creating-bunderstack-apps/references/verification.md +4 -1
  169. package/dist/jobs/runtime.d.ts +0 -17
  170. package/dist/jobs/runtime.d.ts.map +0 -1
  171. package/dist/jobs/runtime.js +0 -56
  172. package/dist/jobs/runtime.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,46 @@
2
2
 
3
3
  All notable changes to `bunderstack` will be documented in this file.
4
4
 
5
+ ## [1.0.0-beta.2] — 2026-09-29
6
+
7
+ ### Added
8
+
9
+ - `bunderstack blueprint` supports Worker applications (`application.runtime: "worker"`), requiring only the `build` script in `package.json`.
10
+ - `bunderstack dev` updates both `wrangler.json` and `bunderstack.blueprint.yaml`, and `bunderstack build` checks both committed files for drift.
11
+
12
+ ## [1.0.0-beta.1] — 2026-09-28
13
+
14
+ ### Added
15
+
16
+ - `bunderstack dev` starts sqld, celld, and Vite for SPA development;
17
+ `bunderstack build` creates `dist/client`; `bunderstack/vite` proxies API
18
+ requests during development.
19
+ - `bunderstack/workers`: `createWorker(backend)` runs an app as a Worker on
20
+ Cloudflare or celld, with the `Scheduler`, `RealtimeHub`, and `RateLimiter`
21
+ Durable Objects and an R2 storage adapter.
22
+ - `bunderstack wrangler` generates `wrangler.json` from the backend, with
23
+ Cron Triggers for the declared cron schedules.
24
+
25
+ ### Changed
26
+
27
+ - CRUD writes wait for their realtime publish before they respond. A failed
28
+ publish only logs; the write still succeeds.
29
+ - `backend.start({ env, platform })` takes host services: `jobs.notify`,
30
+ `realtime`, `rateLimit`, and `storage`. Missing ones use in-memory defaults.
31
+ - `app.jobs.nextDueAt(now?, until?)` tells a host scheduler when the next
32
+ background work is due. Each enqueue calls `platform.jobs.notify(runAt)`.
33
+ - TypeIDs, S3 storage, password hashing, and the hosted blueprint check no
34
+ longer use Bun APIs, so the core runs in workerd and celld. The password hash
35
+ format does not change.
36
+
37
+ ### Removed
38
+
39
+ - The Redis realtime transport, `REDIS_URL`, and `realtime.redis`. A host
40
+ passes an oRPC `Publisher` as `platform.realtime`.
41
+ - The in-process worker: `app.startWorker()`, `app.runWorker()`,
42
+ `app.backgroundRunning`, the `background` config key, and
43
+ `BUNDERSTACK_ROLE`. A host calls `app.jobs.tick()`.
44
+
5
45
  ## [0.25.2] — 2026-09-27
6
46
 
7
47
  ### Fixed
@@ -0,0 +1,27 @@
1
+ import type { BetterAuthConfig } from './config.js';
2
+ type ScryptFn = (password: string, salt: string, keylen: number, options: {
3
+ N: number;
4
+ r: number;
5
+ p: number;
6
+ maxmem: number;
7
+ }) => Promise<Uint8Array>;
8
+ export declare function createPasswordHasher(options?: {
9
+ nativeScrypt?: ScryptFn | null;
10
+ }): {
11
+ hash(password: string): Promise<string>;
12
+ verify({ hash, password, }: {
13
+ hash: string;
14
+ password: string;
15
+ }): Promise<boolean>;
16
+ };
17
+ export declare const passwordHasher: {
18
+ hash(password: string): Promise<string>;
19
+ verify({ hash, password, }: {
20
+ hash: string;
21
+ password: string;
22
+ }): Promise<boolean>;
23
+ };
24
+ /** Only fills a gap: a user-supplied password hasher always wins. */
25
+ export declare function withPasswordDefaults<T extends Pick<BetterAuthConfig, 'emailAndPassword'>>(cfg: T): T;
26
+ export {};
27
+ //# sourceMappingURL=auth-password.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-password.d.ts","sourceRoot":"","sources":["../src/auth-password.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAEhD,KAAK,QAAQ,GAAG,CACd,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KACzD,OAAO,CAAC,UAAU,CAAC,CAAA;AAwBxB,wBAAgB,oBAAoB,CAClC,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAO;mBAoBzB,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;gCAO1C;QACD,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,MAAM,CAAA;KACjB,GAAG,OAAO,CAAC,OAAO,CAAC;EAMvB;AAED,eAAO,MAAM,cAAc;mBAlBF,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;gCAO1C;QACD,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,MAAM,CAAA;KACjB,GAAG,OAAO,CAAC,OAAO,CAAC;CAQ4B,CAAA;AAEpD,qEAAqE;AACrE,wBAAgB,oBAAoB,CAClC,CAAC,SAAS,IAAI,CAAC,gBAAgB,EAAE,kBAAkB,CAAC,EACpD,GAAG,EAAE,CAAC,GAAG,CAAC,CAWX"}
@@ -0,0 +1,60 @@
1
+ // src/auth-password.ts — BetterAuth's scrypt password format, with a plain-JS
2
+ // fallback. BetterAuth picks node:crypto scrypt through its `workerd` and
3
+ // `node` export conditions; celld implements neither, so we choose at runtime.
4
+ import { scryptAsync } from '@noble/hashes/scrypt.js';
5
+ import * as nodeCrypto from 'node:crypto';
6
+ // Must match @better-auth/utils/password: existing hashes stay valid.
7
+ const PARAMS = { N: 16384, r: 16, p: 1 };
8
+ const KEY_LENGTH = 64;
9
+ const MAXMEM = 128 * PARAMS.N * PARAMS.r * 2;
10
+ function toHex(bytes) {
11
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
12
+ }
13
+ const nodeScrypt = typeof nodeCrypto.scrypt === 'function'
14
+ ? (password, salt, keylen, options) => new Promise((resolve, reject) => nodeCrypto.scrypt(password, salt, keylen, options, (error, key) => error ? reject(error) : resolve(new Uint8Array(key))))
15
+ : null;
16
+ const pureScrypt = (password, salt, keylen, options) => scryptAsync(password, salt, { ...options, dkLen: keylen });
17
+ export function createPasswordHasher(options = {}) {
18
+ let native = options.nativeScrypt === undefined ? nodeScrypt : options.nativeScrypt;
19
+ async function derive(password, salt) {
20
+ const input = password.normalize('NFKC');
21
+ const opts = { ...PARAMS, maxmem: MAXMEM };
22
+ if (native) {
23
+ try {
24
+ return await native(input, salt, KEY_LENGTH, opts);
25
+ }
26
+ catch {
27
+ // celld: "The scrypt method is not implemented". Stop trying.
28
+ native = null;
29
+ }
30
+ }
31
+ return pureScrypt(input, salt, KEY_LENGTH, opts);
32
+ }
33
+ return {
34
+ async hash(password) {
35
+ const salt = toHex(crypto.getRandomValues(new Uint8Array(16)));
36
+ return `${salt}:${toHex(await derive(password, salt))}`;
37
+ },
38
+ async verify({ hash, password, }) {
39
+ const [salt, key] = hash.split(':');
40
+ if (!salt || !key)
41
+ throw new Error('Invalid password hash');
42
+ return toHex(await derive(password, salt)) === key;
43
+ },
44
+ };
45
+ }
46
+ export const passwordHasher = createPasswordHasher();
47
+ /** Only fills a gap: a user-supplied password hasher always wins. */
48
+ export function withPasswordDefaults(cfg) {
49
+ if (!cfg.emailAndPassword?.enabled || cfg.emailAndPassword.password) {
50
+ return cfg;
51
+ }
52
+ return {
53
+ ...cfg,
54
+ emailAndPassword: {
55
+ ...cfg.emailAndPassword,
56
+ password: { hash: passwordHasher.hash, verify: passwordHasher.verify },
57
+ },
58
+ };
59
+ }
60
+ //# sourceMappingURL=auth-password.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-password.js","sourceRoot":"","sources":["../src/auth-password.ts"],"names":[],"mappings":"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","sourcesContent":["// src/auth-password.ts — BetterAuth's scrypt password format, with a plain-JS\n// fallback. BetterAuth picks node:crypto scrypt through its `workerd` and\n// `node` export conditions; celld implements neither, so we choose at runtime.\nimport { scryptAsync } from '@noble/hashes/scrypt.js'\nimport * as nodeCrypto from 'node:crypto'\n\nimport type { BetterAuthConfig } from './config'\n\ntype ScryptFn = (\n password: string,\n salt: string,\n keylen: number,\n options: { N: number; r: number; p: number; maxmem: number },\n) => Promise<Uint8Array>\n\n// Must match @better-auth/utils/password: existing hashes stay valid.\nconst PARAMS = { N: 16384, r: 16, p: 1 } as const\nconst KEY_LENGTH = 64\nconst MAXMEM = 128 * PARAMS.N * PARAMS.r * 2\n\nfunction toHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\nconst nodeScrypt: ScryptFn | null =\n typeof nodeCrypto.scrypt === 'function'\n ? (password, salt, keylen, options) =>\n new Promise((resolve, reject) =>\n nodeCrypto.scrypt(password, salt, keylen, options, (error, key) =>\n error ? reject(error) : resolve(new Uint8Array(key)),\n ),\n )\n : null\n\nconst pureScrypt: ScryptFn = (password, salt, keylen, options) =>\n scryptAsync(password, salt, { ...options, dkLen: keylen })\n\nexport function createPasswordHasher(\n options: { nativeScrypt?: ScryptFn | null } = {},\n) {\n let native =\n options.nativeScrypt === undefined ? nodeScrypt : options.nativeScrypt\n\n async function derive(password: string, salt: string): Promise<Uint8Array> {\n const input = password.normalize('NFKC')\n const opts = { ...PARAMS, maxmem: MAXMEM }\n if (native) {\n try {\n return await native(input, salt, KEY_LENGTH, opts)\n } catch {\n // celld: \"The scrypt method is not implemented\". Stop trying.\n native = null\n }\n }\n return pureScrypt(input, salt, KEY_LENGTH, opts)\n }\n\n return {\n async hash(password: string): Promise<string> {\n const salt = toHex(crypto.getRandomValues(new Uint8Array(16)))\n return `${salt}:${toHex(await derive(password, salt))}`\n },\n async verify({\n hash,\n password,\n }: {\n hash: string\n password: string\n }): Promise<boolean> {\n const [salt, key] = hash.split(':')\n if (!salt || !key) throw new Error('Invalid password hash')\n return toHex(await derive(password, salt)) === key\n },\n }\n}\n\nexport const passwordHasher = createPasswordHasher()\n\n/** Only fills a gap: a user-supplied password hasher always wins. */\nexport function withPasswordDefaults<\n T extends Pick<BetterAuthConfig, 'emailAndPassword'>,\n>(cfg: T): T {\n if (!cfg.emailAndPassword?.enabled || cfg.emailAndPassword.password) {\n return cfg\n }\n return {\n ...cfg,\n emailAndPassword: {\n ...cfg.emailAndPassword,\n password: { hash: passwordHasher.hash, verify: passwordHasher.verify },\n },\n }\n}\n"]}
package/dist/auth.d.ts CHANGED
@@ -55,5 +55,5 @@ export declare function toAuthSessionResolver<TExtra extends Record<string, unkn
55
55
  * (injecting it unasked would enable the feature).
56
56
  */
57
57
  export declare function withEmailAuthDefaults(cfg: BetterAuthConfig, email: EmailFacade, emailConfigured: boolean): BetterAuthConfig;
58
- export {};
58
+ export { withPasswordDefaults } from './auth-password.js';
59
59
  //# sourceMappingURL=auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,KAAK,qBAAqB,CAAC,OAAO,SAAS,gBAAgB,IAAI,OAAO,SAAS;IAC7E,OAAO,EAAE,MAAM,QAAQ,SAAS,SAAS,gBAAgB,EAAE,CAAA;CAC5D,GACG,QAAQ,GACR,EAAE,CAAA;AAEN,KAAK,2BAA2B,CAAC,OAAO,SAAS,gBAAgB,IAAI,IAAI,CACvE,gBAAgB,EAChB,SAAS,CACV,GACC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG;IACzB,OAAO,EAAE,CAAC,GAAG,qBAAqB,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAA;CACzE,CAAA;AAEH;;;;GAIG;AACH,MAAM,MAAM,eAAe,CACzB,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,IACjD,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAA;AAE9C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EAC5E,EAAE,EAAE,KAAK,EACT,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,OAAO,EAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,eAAe,CAAC,OAAO,CAAC,CAY1B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,EAAE,gBAAgB,GACpB,MAAM,EAAE,CAMV;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,GAAG,KAAK,CAezE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE7D,IAAI,EAAE,IAAI,EACV,WAAW,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,GACtC,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,cAAc,CAAC,CAAC,CA4CzD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,OAAO,GACvB,gBAAgB,CAkClB"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,IAAI,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE1E,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,KAAK,EACV,cAAc,EACd,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,UAAU,CAAA;AACjB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE1C,KAAK,qBAAqB,CAAC,OAAO,SAAS,gBAAgB,IAAI,OAAO,SAAS;IAC7E,OAAO,EAAE,MAAM,QAAQ,SAAS,SAAS,gBAAgB,EAAE,CAAA;CAC5D,GACG,QAAQ,GACR,EAAE,CAAA;AAEN,KAAK,2BAA2B,CAAC,OAAO,SAAS,gBAAgB,IAAI,IAAI,CACvE,gBAAgB,EAChB,SAAS,CACV,GACC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG;IACzB,OAAO,EAAE,CAAC,GAAG,qBAAqB,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAA;CACzE,CAAA;AAEH;;;;GAIG;AACH,MAAM,MAAM,eAAe,CACzB,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,IACjD,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,CAAC,CAAA;AAE9C;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,SAAS,gBAAgB,GAAG,gBAAgB,EAC5E,EAAE,EAAE,KAAK,EACT,GAAG,EAAE,gBAAgB,EACrB,OAAO,EAAE,OAAO,EAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,eAAe,CAAC,OAAO,CAAC,CAY1B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,EAAE,gBAAgB,GACpB,MAAM,EAAE,CAMV;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,GAAG,KAAK,CAezE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAE7D,IAAI,EAAE,IAAI,EACV,WAAW,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,GACtC,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,cAAc,CAAC,CAAC,CA4CzD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,WAAW,EAClB,eAAe,EAAE,OAAO,GACvB,gBAAgB,CAkClB;AAED,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA"}
package/dist/auth.js CHANGED
@@ -142,4 +142,5 @@ export function withEmailAuthDefaults(cfg, email, emailConfigured) {
142
142
  }
143
143
  return out;
144
144
  }
145
+ export { withPasswordDefaults } from './auth-password.js';
145
146
  //# sourceMappingURL=auth.js.map
package/dist/auth.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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","sourcesContent":["// src/auth.ts\nimport { betterAuth, type Auth, type BetterAuthPlugin } from 'better-auth'\nimport { drizzleAdapter } from 'better-auth/adapters/drizzle'\nimport { openAPI } from 'better-auth/plugins'\n\nimport type {\n AccessUserBase,\n AuthSessionResolver,\n SessionUserConfig,\n SessionUserSource,\n} from './access'\nimport type { BetterAuthConfig } from './config'\nimport type { AnyDb, Dialect } from './dialect'\nimport type { EmailFacade } from './email'\n\ntype ConfiguredAuthPlugins<TConfig extends BetterAuthConfig> = TConfig extends {\n plugins: infer TPlugins extends readonly BetterAuthPlugin[]\n}\n ? TPlugins\n : []\n\ntype WithBunderstackAuthDefaults<TConfig extends BetterAuthConfig> = Omit<\n BetterAuthConfig,\n 'plugins'\n> &\n Omit<TConfig, 'plugins'> & {\n plugins: [...ConfiguredAuthPlugins<TConfig>, ReturnType<typeof openAPI>]\n }\n\n/**\n * The public Better Auth instance retains every endpoint and model field from\n * the declared config. Bunderstack always installs OpenAPI at runtime, so its\n * endpoints are part of the public type even when the app omitted the plugin.\n */\nexport type BunderstackAuth<\n TConfig extends BetterAuthConfig = BetterAuthConfig,\n> = Auth<WithBunderstackAuthDefaults<TConfig>>\n\n/**\n * Build Better Auth while exposing its named plugin-aware public type. The\n * implementation accepts the resolved runtime config, while `TConfig` carries\n * the declaration that produced it; their relationship is asserted once here.\n */\nexport function createAuth<TConfig extends BetterAuthConfig = BetterAuthConfig>(\n db: AnyDb,\n cfg: BetterAuthConfig,\n dialect: Dialect,\n userSchema?: Record<string, unknown>,\n): BunderstackAuth<TConfig> {\n const hasOpenApi = cfg.plugins?.some((p: any) => p.id === 'open-api')\n const plugins = hasOpenApi ? cfg.plugins : [...(cfg.plugins || []), openAPI()]\n\n return betterAuth({\n ...cfg,\n plugins,\n database: drizzleAdapter(db as Parameters<typeof drizzleAdapter>[0], {\n provider: dialect === 'pg' ? 'pg' : 'sqlite',\n ...(userSchema ? { schema: userSchema } : {}),\n }),\n }) as unknown as BunderstackAuth<TConfig>\n}\n\n/**\n * better-auth resolves its models out of the app's own schema, so an app that\n * declares none has no auth at all. Session resolution must then short-circuit\n * to null: the drizzle adapter throws on a missing model, and any session\n * cookie for the host — a browser keeps those per host, not per port, so one\n * minted by another local app arrives here too — would break every request.\n */\nexport function missingAuthModels(\n schema: Record<string, unknown>,\n cfg: BetterAuthConfig,\n): string[] {\n const models = [\n cfg.user?.modelName ?? 'user',\n cfg.session?.modelName ?? 'session',\n ]\n return models.filter((model) => !schema[model])\n}\n\n/**\n * Defer building better-auth until someone touches the instance. From 1.7.6 on,\n * constructing it starts a schema check that logs, and every `auth.api` call\n * throws, when the auth tables are missing. An app without auth models never\n * builds one, yet `app.auth` / `ctx.auth` still work for code that reaches for\n * it on purpose. A proxy, not a getter: oRPC middleware and access scopes\n * spread the context, which would read a getter on every request.\n */\nexport function lazyAuth<TAuth extends object>(create: () => TAuth): TAuth {\n let instance: TAuth | undefined\n const resolve = () => {\n instance ??= create()\n return instance\n }\n return new Proxy({} as TAuth, {\n get: (_, key) => Reflect.get(resolve(), key),\n has: (_, key) => Reflect.has(resolve(), key),\n ownKeys: () => Reflect.ownKeys(resolve()),\n getOwnPropertyDescriptor: (_, key) => {\n const descriptor = Reflect.getOwnPropertyDescriptor(resolve(), key)\n return descriptor ? { ...descriptor, configurable: true } : undefined\n },\n })\n}\n\n/**\n * Adapt the raw better-auth instance to our internal {@link AuthSessionResolver}\n * contract. better-auth's `getSession` has a union return (a bare session, or a\n * `{ headers, response }` wrapper when `returnHeaders` is set); we only ever\n * call the bare form, so we narrow on `'user' in result` and map to our shape.\n * Keeping this adapter here means internal modules never depend on better-auth's\n * evolving types.\n */\nexport function toAuthSessionResolver<\n TExtra extends Record<string, unknown> = Record<never, never>,\n>(\n auth: Auth,\n sessionUser?: SessionUserConfig<TExtra>,\n): AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>> {\n return {\n api: {\n async getSession({ headers }) {\n const result = await auth.api.getSession({ headers })\n if (result && 'user' in result && result.user) {\n const session = 'session' in result ? result.session : null\n const activeOrganizationId =\n session &&\n 'activeOrganizationId' in session &&\n typeof session.activeOrganizationId === 'string'\n ? session.activeOrganizationId\n : null\n const role =\n 'role' in result.user && typeof result.user.role === 'string'\n ? result.user.role\n : undefined\n const emailVerified =\n 'emailVerified' in result.user && result.user.emailVerified === true\n const mapped = sessionUser?.mapUser(\n result.user as SessionUserSource,\n ) as Record<string, unknown> | undefined\n const extras = { ...mapped }\n delete extras.id\n delete extras.email\n delete extras.emailVerified\n delete extras.name\n delete extras.role\n return {\n user: {\n ...extras,\n id: result.user.id,\n email: result.user.email,\n emailVerified,\n name: result.user.name,\n ...(role ? { role } : {}),\n },\n session: session ? { activeOrganizationId } : null,\n }\n }\n return null\n },\n },\n } as AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>>\n}\n\n/**\n * Fill better-auth's email hooks from the bunderstack email facade. Only fills\n * gaps: user-supplied handlers always win, and nothing is injected when email\n * isn't configured. emailAndPassword is only touched when the user enabled it\n * (injecting it unasked would enable the feature).\n */\nexport function withEmailAuthDefaults(\n cfg: BetterAuthConfig,\n email: EmailFacade,\n emailConfigured: boolean,\n): BetterAuthConfig {\n if (!emailConfigured) return cfg\n const out: BetterAuthConfig = { ...cfg }\n\n if (\n cfg.emailAndPassword?.enabled &&\n !cfg.emailAndPassword.sendResetPassword\n ) {\n out.emailAndPassword = {\n ...cfg.emailAndPassword,\n sendResetPassword: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Reset your password',\n text: `Click the link to reset your password:\\n\\n${url}\\n\\nIf you didn't request this, you can ignore this email.`,\n })\n },\n }\n }\n\n if (!cfg.emailVerification?.sendVerificationEmail) {\n out.emailVerification = {\n ...cfg.emailVerification,\n sendVerificationEmail: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Verify your email',\n text: `Click the link to verify your email address:\\n\\n${url}`,\n })\n },\n }\n }\n\n return out\n}\n"]}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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","sourcesContent":["// src/auth.ts\nimport { betterAuth, type Auth, type BetterAuthPlugin } from 'better-auth'\nimport { drizzleAdapter } from 'better-auth/adapters/drizzle'\nimport { openAPI } from 'better-auth/plugins'\n\nimport type {\n AccessUserBase,\n AuthSessionResolver,\n SessionUserConfig,\n SessionUserSource,\n} from './access'\nimport type { BetterAuthConfig } from './config'\nimport type { AnyDb, Dialect } from './dialect'\nimport type { EmailFacade } from './email'\n\ntype ConfiguredAuthPlugins<TConfig extends BetterAuthConfig> = TConfig extends {\n plugins: infer TPlugins extends readonly BetterAuthPlugin[]\n}\n ? TPlugins\n : []\n\ntype WithBunderstackAuthDefaults<TConfig extends BetterAuthConfig> = Omit<\n BetterAuthConfig,\n 'plugins'\n> &\n Omit<TConfig, 'plugins'> & {\n plugins: [...ConfiguredAuthPlugins<TConfig>, ReturnType<typeof openAPI>]\n }\n\n/**\n * The public Better Auth instance retains every endpoint and model field from\n * the declared config. Bunderstack always installs OpenAPI at runtime, so its\n * endpoints are part of the public type even when the app omitted the plugin.\n */\nexport type BunderstackAuth<\n TConfig extends BetterAuthConfig = BetterAuthConfig,\n> = Auth<WithBunderstackAuthDefaults<TConfig>>\n\n/**\n * Build Better Auth while exposing its named plugin-aware public type. The\n * implementation accepts the resolved runtime config, while `TConfig` carries\n * the declaration that produced it; their relationship is asserted once here.\n */\nexport function createAuth<TConfig extends BetterAuthConfig = BetterAuthConfig>(\n db: AnyDb,\n cfg: BetterAuthConfig,\n dialect: Dialect,\n userSchema?: Record<string, unknown>,\n): BunderstackAuth<TConfig> {\n const hasOpenApi = cfg.plugins?.some((p: any) => p.id === 'open-api')\n const plugins = hasOpenApi ? cfg.plugins : [...(cfg.plugins || []), openAPI()]\n\n return betterAuth({\n ...cfg,\n plugins,\n database: drizzleAdapter(db as Parameters<typeof drizzleAdapter>[0], {\n provider: dialect === 'pg' ? 'pg' : 'sqlite',\n ...(userSchema ? { schema: userSchema } : {}),\n }),\n }) as unknown as BunderstackAuth<TConfig>\n}\n\n/**\n * better-auth resolves its models out of the app's own schema, so an app that\n * declares none has no auth at all. Session resolution must then short-circuit\n * to null: the drizzle adapter throws on a missing model, and any session\n * cookie for the host — a browser keeps those per host, not per port, so one\n * minted by another local app arrives here too — would break every request.\n */\nexport function missingAuthModels(\n schema: Record<string, unknown>,\n cfg: BetterAuthConfig,\n): string[] {\n const models = [\n cfg.user?.modelName ?? 'user',\n cfg.session?.modelName ?? 'session',\n ]\n return models.filter((model) => !schema[model])\n}\n\n/**\n * Defer building better-auth until someone touches the instance. From 1.7.6 on,\n * constructing it starts a schema check that logs, and every `auth.api` call\n * throws, when the auth tables are missing. An app without auth models never\n * builds one, yet `app.auth` / `ctx.auth` still work for code that reaches for\n * it on purpose. A proxy, not a getter: oRPC middleware and access scopes\n * spread the context, which would read a getter on every request.\n */\nexport function lazyAuth<TAuth extends object>(create: () => TAuth): TAuth {\n let instance: TAuth | undefined\n const resolve = () => {\n instance ??= create()\n return instance\n }\n return new Proxy({} as TAuth, {\n get: (_, key) => Reflect.get(resolve(), key),\n has: (_, key) => Reflect.has(resolve(), key),\n ownKeys: () => Reflect.ownKeys(resolve()),\n getOwnPropertyDescriptor: (_, key) => {\n const descriptor = Reflect.getOwnPropertyDescriptor(resolve(), key)\n return descriptor ? { ...descriptor, configurable: true } : undefined\n },\n })\n}\n\n/**\n * Adapt the raw better-auth instance to our internal {@link AuthSessionResolver}\n * contract. better-auth's `getSession` has a union return (a bare session, or a\n * `{ headers, response }` wrapper when `returnHeaders` is set); we only ever\n * call the bare form, so we narrow on `'user' in result` and map to our shape.\n * Keeping this adapter here means internal modules never depend on better-auth's\n * evolving types.\n */\nexport function toAuthSessionResolver<\n TExtra extends Record<string, unknown> = Record<never, never>,\n>(\n auth: Auth,\n sessionUser?: SessionUserConfig<TExtra>,\n): AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>> {\n return {\n api: {\n async getSession({ headers }) {\n const result = await auth.api.getSession({ headers })\n if (result && 'user' in result && result.user) {\n const session = 'session' in result ? result.session : null\n const activeOrganizationId =\n session &&\n 'activeOrganizationId' in session &&\n typeof session.activeOrganizationId === 'string'\n ? session.activeOrganizationId\n : null\n const role =\n 'role' in result.user && typeof result.user.role === 'string'\n ? result.user.role\n : undefined\n const emailVerified =\n 'emailVerified' in result.user && result.user.emailVerified === true\n const mapped = sessionUser?.mapUser(\n result.user as SessionUserSource,\n ) as Record<string, unknown> | undefined\n const extras = { ...mapped }\n delete extras.id\n delete extras.email\n delete extras.emailVerified\n delete extras.name\n delete extras.role\n return {\n user: {\n ...extras,\n id: result.user.id,\n email: result.user.email,\n emailVerified,\n name: result.user.name,\n ...(role ? { role } : {}),\n },\n session: session ? { activeOrganizationId } : null,\n }\n }\n return null\n },\n },\n } as AuthSessionResolver<Omit<TExtra, keyof AccessUserBase>>\n}\n\n/**\n * Fill better-auth's email hooks from the bunderstack email facade. Only fills\n * gaps: user-supplied handlers always win, and nothing is injected when email\n * isn't configured. emailAndPassword is only touched when the user enabled it\n * (injecting it unasked would enable the feature).\n */\nexport function withEmailAuthDefaults(\n cfg: BetterAuthConfig,\n email: EmailFacade,\n emailConfigured: boolean,\n): BetterAuthConfig {\n if (!emailConfigured) return cfg\n const out: BetterAuthConfig = { ...cfg }\n\n if (\n cfg.emailAndPassword?.enabled &&\n !cfg.emailAndPassword.sendResetPassword\n ) {\n out.emailAndPassword = {\n ...cfg.emailAndPassword,\n sendResetPassword: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Reset your password',\n text: `Click the link to reset your password:\\n\\n${url}\\n\\nIf you didn't request this, you can ignore this email.`,\n })\n },\n }\n }\n\n if (!cfg.emailVerification?.sendVerificationEmail) {\n out.emailVerification = {\n ...cfg.emailVerification,\n sendVerificationEmail: async ({ user, url }) => {\n await email.send({\n to: user.email,\n subject: 'Verify your email',\n text: `Click the link to verify your email address:\\n\\n${url}`,\n })\n },\n }\n }\n\n return out\n}\n\nexport { withPasswordDefaults } from './auth-password'\n"]}
package/dist/backend.d.ts CHANGED
@@ -5,12 +5,15 @@ import type { EnvConfigInput, ValidatedEnv } from './env.js';
5
5
  import type { BunderstackJobsBuilder, JobsDefs } from './jobs/index.js';
6
6
  import type { BunderstackManifest } from './manifest.js';
7
7
  import type { MessagingConfig } from './messaging/index.js';
8
+ import type { Platform } from './platform.js';
8
9
  import type { BunderstackApp, BucketNamesOf } from './runtime.js';
9
10
  import type { StorageConfigInput } from './storage/buckets.js';
10
11
  import type { TestMethod } from './testing/fixture.js';
11
12
  import { BACKEND_INTERNALS, type BackendInternals } from './backend-internals.js';
12
13
  export type StartOptions = {
13
14
  env?: Record<string, string | undefined>;
15
+ /** Host services. Missing ones use in-memory defaults. */
16
+ platform?: Partial<Platform>;
14
17
  };
15
18
  export type BunderstackBackend<TApp> = {
16
19
  inspect(options?: StartOptions): BunderstackManifest;
@@ -1 +1 @@
1
- {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
package/dist/backend.js CHANGED
@@ -44,7 +44,9 @@ export function bunderstack(declaration) {
44
44
  backend = {
45
45
  inspect: ({ env } = {}) => inspect(env ?? process.env)
46
46
  .manifest,
47
- start: async ({ env } = {}) => await start(env ?? process.env),
47
+ start: async ({ env, platform } = {}) => await start(env ?? process.env, {
48
+ platform,
49
+ }),
48
50
  test,
49
51
  [BACKEND_INTERNALS]: {
50
52
  envSchema,
@@ -1 +1 @@
1
- {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyRouter as AnyORPCRouter } from '@orpc/server'\n\nimport type {\n SessionUserConfig,\n SessionUserExtraOf,\n TableAccessInput,\n} from './access'\nimport type {\n BetterAuthConfig,\n BunderstackConfig,\n RealtimeConfigInput,\n} from './config'\nimport type { EnvConfigInput, ValidatedEnv } from './env'\nimport type { BunderstackJobsBuilder, JobsDefs } from './jobs'\nimport type { BunderstackManifest } from './manifest'\nimport type { MessagingConfig } from './messaging'\nimport type { BunderstackApp, BucketNamesOf, RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\nimport type { TestMethod, TestOptions } from './testing/fixture'\n\nimport { BACKEND_INTERNALS, type BackendInternals } from './backend-internals'\nimport { validateEnv } from './env'\nimport { assertHostedBlueprintFile } from './hosted-contract'\nimport { inspectConfig, type InspectedDefinition } from './inspect'\nimport { materializeBunderstack } from './runtime'\n\nexport type StartOptions = {\n env?: Record<string, string | undefined>\n}\n\nexport type BunderstackBackend<TApp> = {\n inspect(options?: StartOptions): BunderstackManifest\n start(options?: StartOptions): Promise<TApp>\n test: TestMethod<TApp>\n readonly [BACKEND_INTERNALS]: BackendInternals<TApp>\n}\n\nexport type BunderstackDefinitionConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n TMessaging extends MessagingConfig | undefined = undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n TSession extends SessionUserConfig<Record<string, unknown>> | undefined =\n undefined,\n> = Omit<\n BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter,\n TAuthConfig,\n TSession\n >,\n 'realtime' | 'messaging' | 'api'\n> & {\n /**\n * An inline `api` or `jobs` builder receives the open `MessagingConfig`, not\n * `TMessaging`. Naming `TMessaging` in a callback parameter makes TypeScript\n * fix it before it reads the sibling `messaging` property, and the whole\n * declaration then loses its inferred channels. Declare the builder in its\n * own module with an annotated parameter to type `ctx.messaging` exactly —\n * `defineApi({ schema, env, messaging })` and `BunderstackJobsBuilder` both\n * take the channel record.\n */\n realtime?: TRealtime\n messaging?: TMessaging\n api?:\n | TCustomApiRouter\n | ((\n builder: import('./api/builder').BunderstackApiBuilder<\n TSchema,\n ValidatedEnv<TEnv>,\n MessagingConfig,\n SessionUserExtraOf<TSession>\n >,\n ) => TCustomApiRouter)\n jobs?:\n | TJobsDefs\n | ((\n builder: BunderstackJobsBuilder<\n TSchema,\n ValidatedEnv<TEnv>,\n MessagingConfig\n >,\n ) => TJobsDefs)\n}\n\nexport function isBunderstackBackend(\n value: unknown,\n): value is BunderstackBackend<unknown> {\n return (\n typeof value === 'object' && value !== null && BACKEND_INTERNALS in value\n )\n}\n\n/**\n * A configuration slot that may read validated environment values. The shape a\n * slot produces reaches the manifest, so a callback belongs here to supply a\n * key, a URL, or a sender — never to change which channels, buckets, or tables\n * exist. Blueprint generation resolves the declaration against two accepted\n * environments and rejects a shape that differs between them.\n */\nexport type EnvAware<TEnv extends EnvConfigInput | undefined, T> =\n | T\n | ((env: ValidatedEnv<NoInfer<TEnv>>) => T)\n\nexport type DatabaseSlot = BunderstackConfig<\n Record<string, unknown>,\n undefined,\n undefined,\n undefined,\n undefined\n>['database']\n\n/**\n * The whole application in one object. `schema`, `access`, and the routers are\n * plain data; the slots that hold credentials also accept a function of the\n * validated environment.\n */\nexport type BunderstackDeclaration<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n TMessaging extends MessagingConfig | undefined = undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n TSession extends SessionUserConfig<Record<string, unknown>> | undefined =\n undefined,\n> = Omit<\n BunderstackDefinitionConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging,\n TAuthConfig,\n TSession\n >,\n 'database' | 'storage' | 'messaging' | 'realtime'\n> & {\n /** Declared environment. Its names reach the blueprint; its values never do. */\n env?: TEnv\n database: EnvAware<TEnv, DatabaseSlot>\n storage?: EnvAware<TEnv, TStorage>\n messaging?: EnvAware<TEnv, TMessaging>\n realtime?: EnvAware<TEnv, TRealtime>\n}\n\nexport function bunderstack<\n TSchema extends Record<string, unknown>,\n const TEnv extends EnvConfigInput | undefined = undefined,\n const TAccess extends Record<string, TableAccessInput> | undefined =\n undefined,\n const TStorage extends StorageConfigInput | undefined = undefined,\n const TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n const TRealtime extends RealtimeConfigInput | undefined = undefined,\n const TMessaging extends MessagingConfig | undefined = undefined,\n const TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n const TSession extends\n | SessionUserConfig<Record<string, unknown>>\n | undefined = undefined,\n>(\n declaration: BunderstackDeclaration<\n TSchema,\n TEnv,\n TAccess,\n TStorage,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging,\n TAuthConfig,\n TSession\n >,\n): BunderstackBackend<\n BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging,\n TAuthConfig\n >\n>\nexport function bunderstack(\n declaration: BunderstackDeclaration<Record<string, unknown>, any>,\n): BunderstackBackend<any> {\n const { env: envSchema, ...slots } = declaration as Record<\n string,\n unknown\n > & {\n env?: EnvConfigInput\n }\n const inspect = (source: Record<string, string | undefined>) => {\n const env = validateEnv(envSchema, { source })\n const resolve = (value: unknown) =>\n typeof value === 'function'\n ? (value as (given: typeof env) => unknown)(env)\n : value\n const config = {\n ...slots,\n database: resolve(slots.database),\n storage: resolve(slots.storage),\n messaging: resolve(slots.messaging),\n realtime: resolve(slots.realtime),\n }\n return inspectConfig(config as never, envSchema, env)\n }\n\n type App = BunderstackApp<any, any, any, any, any, any, any, any, any>\n\n const start = async (\n source: Record<string, string | undefined>,\n overrides: RuntimeOverrides = {},\n inspected: InspectedDefinition = inspect(source),\n ): Promise<App> => {\n if (source.BUNDERSTACK_BLUEPRINT_PATH) {\n await assertHostedBlueprintFile(\n inspected.manifest,\n source.BUNDERSTACK_BLUEPRINT_PATH,\n )\n }\n return materializeBunderstack(\n {\n ...inspected.config,\n jobs: inspected.jobsDefs,\n api: inspected.customApiRouter,\n } as never,\n source,\n overrides,\n inspected.env,\n ) as Promise<App>\n }\n\n let backend: BunderstackBackend<App>\n const test = (async (options) => {\n const testing = await import('./testing')\n return testing.createTestApp(backend, options)\n }) as TestMethod<App>\n test.configure = (options) =>\n (async (overrides: TestOptions = {}) => {\n const testing = await import('./testing')\n return testing.configureTestApp(backend, options)(overrides)\n }) as never\n backend = {\n inspect: ({ env } = {}) =>\n inspect(env ?? (process.env as Record<string, string | undefined>))\n .manifest,\n start: async ({ env } = {}) =>\n await start(env ?? (process.env as Record<string, string | undefined>)),\n test,\n [BACKEND_INTERNALS]: {\n envSchema,\n inspect,\n start,\n },\n }\n return backend\n}\n"]}
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyRouter as AnyORPCRouter } from '@orpc/server'\n\nimport type {\n SessionUserConfig,\n SessionUserExtraOf,\n TableAccessInput,\n} from './access'\nimport type {\n BetterAuthConfig,\n BunderstackConfig,\n RealtimeConfigInput,\n} from './config'\nimport type { EnvConfigInput, ValidatedEnv } from './env'\nimport type { BunderstackJobsBuilder, JobsDefs } from './jobs'\nimport type { BunderstackManifest } from './manifest'\nimport type { MessagingConfig } from './messaging'\nimport type { Platform } from './platform'\nimport type { BunderstackApp, BucketNamesOf, RuntimeOverrides } from './runtime'\nimport type { StorageConfigInput } from './storage/buckets'\nimport type { TestMethod, TestOptions } from './testing/fixture'\n\nimport { BACKEND_INTERNALS, type BackendInternals } from './backend-internals'\nimport { validateEnv } from './env'\nimport { assertHostedBlueprintFile } from './hosted-contract'\nimport { inspectConfig, type InspectedDefinition } from './inspect'\nimport { materializeBunderstack } from './runtime'\n\nexport type StartOptions = {\n env?: Record<string, string | undefined>\n /** Host services. Missing ones use in-memory defaults. */\n platform?: Partial<Platform>\n}\n\nexport type BunderstackBackend<TApp> = {\n inspect(options?: StartOptions): BunderstackManifest\n start(options?: StartOptions): Promise<TApp>\n test: TestMethod<TApp>\n readonly [BACKEND_INTERNALS]: BackendInternals<TApp>\n}\n\nexport type BunderstackDefinitionConfig<\n TSchema extends Record<string, unknown>,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TEnv extends EnvConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n TMessaging extends MessagingConfig | undefined = undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n TSession extends SessionUserConfig<Record<string, unknown>> | undefined =\n undefined,\n> = Omit<\n BunderstackConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TCustomApiRouter,\n TAuthConfig,\n TSession\n >,\n 'realtime' | 'messaging' | 'api'\n> & {\n /**\n * An inline `api` or `jobs` builder receives the open `MessagingConfig`, not\n * `TMessaging`. Naming `TMessaging` in a callback parameter makes TypeScript\n * fix it before it reads the sibling `messaging` property, and the whole\n * declaration then loses its inferred channels. Declare the builder in its\n * own module with an annotated parameter to type `ctx.messaging` exactly —\n * `defineApi({ schema, env, messaging })` and `BunderstackJobsBuilder` both\n * take the channel record.\n */\n realtime?: TRealtime\n messaging?: TMessaging\n api?:\n | TCustomApiRouter\n | ((\n builder: import('./api/builder').BunderstackApiBuilder<\n TSchema,\n ValidatedEnv<TEnv>,\n MessagingConfig,\n SessionUserExtraOf<TSession>\n >,\n ) => TCustomApiRouter)\n jobs?:\n | TJobsDefs\n | ((\n builder: BunderstackJobsBuilder<\n TSchema,\n ValidatedEnv<TEnv>,\n MessagingConfig\n >,\n ) => TJobsDefs)\n}\n\nexport function isBunderstackBackend(\n value: unknown,\n): value is BunderstackBackend<unknown> {\n return (\n typeof value === 'object' && value !== null && BACKEND_INTERNALS in value\n )\n}\n\n/**\n * A configuration slot that may read validated environment values. The shape a\n * slot produces reaches the manifest, so a callback belongs here to supply a\n * key, a URL, or a sender — never to change which channels, buckets, or tables\n * exist. Blueprint generation resolves the declaration against two accepted\n * environments and rejects a shape that differs between them.\n */\nexport type EnvAware<TEnv extends EnvConfigInput | undefined, T> =\n | T\n | ((env: ValidatedEnv<NoInfer<TEnv>>) => T)\n\nexport type DatabaseSlot = BunderstackConfig<\n Record<string, unknown>,\n undefined,\n undefined,\n undefined,\n undefined\n>['database']\n\n/**\n * The whole application in one object. `schema`, `access`, and the routers are\n * plain data; the slots that hold credentials also accept a function of the\n * validated environment.\n */\nexport type BunderstackDeclaration<\n TSchema extends Record<string, unknown>,\n TEnv extends EnvConfigInput | undefined = undefined,\n TAccess extends Record<string, TableAccessInput> | undefined = undefined,\n TStorage extends StorageConfigInput | undefined = undefined,\n TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n TRealtime = undefined,\n TMessaging extends MessagingConfig | undefined = undefined,\n TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n TSession extends SessionUserConfig<Record<string, unknown>> | undefined =\n undefined,\n> = Omit<\n BunderstackDefinitionConfig<\n TSchema,\n TAccess,\n TStorage,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging,\n TAuthConfig,\n TSession\n >,\n 'database' | 'storage' | 'messaging' | 'realtime'\n> & {\n /** Declared environment. Its names reach the blueprint; its values never do. */\n env?: TEnv\n database: EnvAware<TEnv, DatabaseSlot>\n storage?: EnvAware<TEnv, TStorage>\n messaging?: EnvAware<TEnv, TMessaging>\n realtime?: EnvAware<TEnv, TRealtime>\n}\n\nexport function bunderstack<\n TSchema extends Record<string, unknown>,\n const TEnv extends EnvConfigInput | undefined = undefined,\n const TAccess extends Record<string, TableAccessInput> | undefined =\n undefined,\n const TStorage extends StorageConfigInput | undefined = undefined,\n const TJobsDefs extends JobsDefs | undefined = undefined,\n TCustomApiRouter extends AnyORPCRouter | undefined = undefined,\n const TRealtime extends RealtimeConfigInput | undefined = undefined,\n const TMessaging extends MessagingConfig | undefined = undefined,\n const TAuthConfig extends BetterAuthConfig = BetterAuthConfig,\n const TSession extends\n | SessionUserConfig<Record<string, unknown>>\n | undefined = undefined,\n>(\n declaration: BunderstackDeclaration<\n TSchema,\n TEnv,\n TAccess,\n TStorage,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging,\n TAuthConfig,\n TSession\n >,\n): BunderstackBackend<\n BunderstackApp<\n TSchema,\n TAccess,\n BucketNamesOf<TStorage>,\n TEnv,\n TJobsDefs,\n TCustomApiRouter,\n TRealtime,\n TMessaging,\n TAuthConfig\n >\n>\nexport function bunderstack(\n declaration: BunderstackDeclaration<Record<string, unknown>, any>,\n): BunderstackBackend<any> {\n const { env: envSchema, ...slots } = declaration as Record<\n string,\n unknown\n > & {\n env?: EnvConfigInput\n }\n const inspect = (source: Record<string, string | undefined>) => {\n const env = validateEnv(envSchema, { source })\n const resolve = (value: unknown) =>\n typeof value === 'function'\n ? (value as (given: typeof env) => unknown)(env)\n : value\n const config = {\n ...slots,\n database: resolve(slots.database),\n storage: resolve(slots.storage),\n messaging: resolve(slots.messaging),\n realtime: resolve(slots.realtime),\n }\n return inspectConfig(config as never, envSchema, env)\n }\n\n type App = BunderstackApp<any, any, any, any, any, any, any, any, any>\n\n const start = async (\n source: Record<string, string | undefined>,\n overrides: RuntimeOverrides = {},\n inspected: InspectedDefinition = inspect(source),\n ): Promise<App> => {\n if (source.BUNDERSTACK_BLUEPRINT_PATH) {\n await assertHostedBlueprintFile(\n inspected.manifest,\n source.BUNDERSTACK_BLUEPRINT_PATH,\n )\n }\n return materializeBunderstack(\n {\n ...inspected.config,\n jobs: inspected.jobsDefs,\n api: inspected.customApiRouter,\n } as never,\n source,\n overrides,\n inspected.env,\n ) as Promise<App>\n }\n\n let backend: BunderstackBackend<App>\n const test = (async (options) => {\n const testing = await import('./testing')\n return testing.createTestApp(backend, options)\n }) as TestMethod<App>\n test.configure = (options) =>\n (async (overrides: TestOptions = {}) => {\n const testing = await import('./testing')\n return testing.configureTestApp(backend, options)(overrides)\n }) as never\n backend = {\n inspect: ({ env } = {}) =>\n inspect(env ?? (process.env as Record<string, string | undefined>))\n .manifest,\n start: async ({ env, platform } = {}) =>\n await start(env ?? (process.env as Record<string, string | undefined>), {\n platform,\n }),\n test,\n [BACKEND_INTERNALS]: {\n envSchema,\n inspect,\n start,\n },\n }\n return backend\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint-generator.d.ts","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"AAaA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAA;AAOpB,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,oBAAoB,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,qBAAa,mBAAoB,SAAQ,KAAK;;CAO7C;AAqED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CAuHlC"}
1
+ {"version":3,"file":"blueprint-generator.d.ts","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"AAaA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,aAAa,CAAA;AAOpB,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,oBAAoB,CAAA;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,qBAAa,mBAAoB,SAAQ,KAAK;;CAO7C;AAqED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,GAChC,OAAO,CAAC,uBAAuB,CAAC,CA0HlC"}
@@ -66,7 +66,7 @@ export async function generateBlueprint(options) {
66
66
  framework = 'solid';
67
67
  }
68
68
  requireScript(pkg, 'build', true);
69
- requireScript(pkg, 'start', true);
69
+ const hasStartScript = requireScript(pkg, 'start', false);
70
70
  const configuredEntry = pkg.bunderstack?.entry;
71
71
  const entry = requireRelativePath(options.entry ??
72
72
  (typeof configuredEntry === 'string'
@@ -108,7 +108,9 @@ export async function generateBlueprint(options) {
108
108
  }
109
109
  const manifest = firstManifest;
110
110
  const workerRequired = manifest.background.jobs.length > 0;
111
- requireScript(pkg, 'worker', workerRequired);
111
+ if (hasStartScript) {
112
+ requireScript(pkg, 'worker', workerRequired);
113
+ }
112
114
  const migrationsDirectory = normalizeProjectPath(directory, manifest.database.migrationsDirectory, 'migrationsDirectory');
113
115
  const migrationJournal = join(resolveWithin(directory, migrationsDirectory, 'migrationsDirectory'), 'meta', '_journal.json');
114
116
  const migrationMode = (await Bun.file(migrationJournal).exists())
@@ -123,6 +125,7 @@ export async function generateBlueprint(options) {
123
125
  entry,
124
126
  migrationMode,
125
127
  framework,
128
+ ...(hasStartScript ? {} : { runtime: 'worker' }),
126
129
  });
127
130
  const source = serializeBlueprint(blueprint);
128
131
  const existing = (await Bun.file(outputPath).exists())
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint-generator.js","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n mkdir,\n readFile,\n realpath,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { isBunderstackBackend } from './backend'\nimport { BACKEND_INTERNALS } from './backend-internals'\nimport {\n blueprintFromManifest,\n serializeBlueprint,\n type BunderstackBlueprint,\n} from './blueprint'\nimport { parseBlueprintYaml } from './blueprint'\nimport { createEnvProbeSources } from './env-probe'\nimport { assertManifestMatchesBlueprint } from './hosted-contract'\nimport { parseManifest } from './manifest'\nimport { diffManifests } from './manifest-diff'\n\nexport type GenerateBlueprintOptions = {\n directory: string\n entry?: string\n output?: string\n check?: boolean\n hostedCheck?: boolean\n}\n\nexport type GenerateBlueprintResult = {\n path: string\n blueprint: BunderstackBlueprint\n source: string\n changed: boolean\n}\n\nexport class BlueprintCheckError extends Error {\n constructor() {\n super(\n 'bunderstack.blueprint.yaml is missing or stale; run `bunderstack blueprint`',\n )\n this.name = 'BlueprintCheckError'\n }\n}\n\ntype AppPackage = {\n scripts?: Record<string, unknown>\n dependencies?: Record<string, unknown>\n devDependencies?: Record<string, unknown>\n bunderstack?: { entry?: unknown }\n}\n\nfunction requireRelativePath(value: string, label: string): string {\n const normalized = value.replaceAll('\\\\', '/')\n if (\n !normalized ||\n isAbsolute(normalized) ||\n normalized.split('/').some((part) => !part || part === '..')\n ) {\n throw new Error(\n `[bunderstack] ${label} must be a relative path without traversal`,\n )\n }\n return normalized\n}\n\nfunction resolveWithin(root: string, value: string, label: string): string {\n const path = resolve(root, requireRelativePath(value, label))\n const pathFromRoot = relative(root, path)\n if (\n pathFromRoot === '..' ||\n pathFromRoot.startsWith('../') ||\n isAbsolute(pathFromRoot)\n ) {\n throw new Error(\n `[bunderstack] ${label} must stay within the application directory`,\n )\n }\n return path\n}\n\nfunction normalizeProjectPath(\n root: string,\n value: string,\n label: string,\n): string {\n if (!isAbsolute(value)) return requireRelativePath(value, label)\n const pathFromRoot = relative(root, resolve(value)) || '.'\n return requireRelativePath(pathFromRoot, label)\n}\n\nfunction requireScript(\n pkg: AppPackage,\n name: 'build' | 'start' | 'worker',\n required: boolean,\n): boolean {\n const value = pkg.scripts?.[name]\n if (typeof value === 'string' && value.trim()) return true\n if (required)\n throw new Error(\n `[bunderstack] package.json requires a non-empty \"${name}\" script`,\n )\n return false\n}\n\nasync function packageVersion(): Promise<string> {\n const pkg = (await Bun.file(\n new URL('../package.json', import.meta.url),\n ).json()) as { version: string }\n return pkg.version\n}\n\nexport async function generateBlueprint(\n options: GenerateBlueprintOptions,\n): Promise<GenerateBlueprintResult> {\n const directory = await realpath(resolve(options.directory))\n const packagePath = join(directory, 'package.json')\n const pkg = JSON.parse(await readFile(packagePath, 'utf8')) as AppPackage\n const allDependencies = { ...pkg.dependencies, ...pkg.devDependencies }\n let framework: 'tanstack-start' | 'solid' | 'bun-ssr' = 'bun-ssr'\n if (typeof allDependencies['@tanstack/react-start'] === 'string') {\n framework = 'tanstack-start'\n } else if (\n typeof allDependencies['solid-js'] === 'string' ||\n typeof allDependencies['@solidjs/web'] === 'string'\n ) {\n framework = 'solid'\n }\n requireScript(pkg, 'build', true)\n requireScript(pkg, 'start', true)\n\n const configuredEntry = pkg.bunderstack?.entry\n const entry = requireRelativePath(\n options.entry ??\n (typeof configuredEntry === 'string'\n ? configuredEntry\n : 'src/bunderstack.ts'),\n 'entry',\n )\n const entryPath = resolveWithin(directory, entry, 'entry')\n if (!(await Bun.file(entryPath).exists())) {\n throw new Error(`[bunderstack] entry does not exist: ${entry}`)\n }\n const output = requireRelativePath(\n options.output ?? 'bunderstack.blueprint.yaml',\n 'output',\n )\n const outputPath = resolveWithin(directory, output, 'output')\n\n const module = (await import(\n `${pathToFileURL(entryPath).href}?blueprint=${Date.now()}`\n )) as { backend?: unknown }\n const backend = module.backend\n if (!isBunderstackBackend(backend)) {\n throw new Error(`[bunderstack] ${entry} must export backend`)\n }\n if (options.hostedCheck) {\n const file = Bun.file(outputPath)\n if (!(await file.exists())) {\n throw new Error(\n `[bunderstack] hosted blueprint does not exist: ${output}`,\n )\n }\n const source = await file.text()\n const manifest = parseManifest(backend.inspect({ env: process.env }))\n assertManifestMatchesBlueprint(manifest, source)\n return {\n path: outputPath,\n blueprint: parseBlueprintYaml(source),\n source,\n changed: false,\n }\n }\n const probeSources = createEnvProbeSources(\n backend[BACKEND_INTERNALS].envSchema,\n process.env as Record<string, string | undefined>,\n )\n const firstManifest = parseManifest(backend.inspect({ env: probeSources[0] }))\n const secondManifest = parseManifest(\n backend.inspect({ env: probeSources[1] }),\n )\n const differences = diffManifests(firstManifest, secondManifest)\n if (differences.length > 0) {\n throw new Error(\n '[bunderstack] environment-dependent blueprint shape:\\n' +\n differences.map(({ kind, path }) => ` - ${kind}: ${path}`).join('\\n'),\n )\n }\n const manifest = firstManifest\n const workerRequired = manifest.background.jobs.length > 0\n requireScript(pkg, 'worker', workerRequired)\n const migrationsDirectory = normalizeProjectPath(\n directory,\n manifest.database.migrationsDirectory,\n 'migrationsDirectory',\n )\n const migrationJournal = join(\n resolveWithin(directory, migrationsDirectory, 'migrationsDirectory'),\n 'meta',\n '_journal.json',\n )\n const migrationMode = (await Bun.file(migrationJournal).exists())\n ? 'migrations'\n : 'push'\n const blueprint = blueprintFromManifest({\n manifest: {\n ...manifest,\n database: { ...manifest.database, migrationsDirectory },\n },\n generatorVersion: await packageVersion(),\n entry,\n migrationMode,\n framework,\n })\n const source = serializeBlueprint(blueprint)\n const existing = (await Bun.file(outputPath).exists())\n ? await Bun.file(outputPath).text()\n : undefined\n if (options.check) {\n if (existing !== source) throw new BlueprintCheckError()\n return { path: outputPath, blueprint, source, changed: false }\n }\n if (existing === source)\n return { path: outputPath, blueprint, source, changed: false }\n await mkdir(dirname(outputPath), { recursive: true })\n const temporary = `${outputPath}.${process.pid}.${Date.now()}.tmp`\n try {\n await writeFile(temporary, source, { mode: 0o600 })\n await rename(temporary, outputPath)\n } finally {\n await rm(temporary, { force: true })\n }\n return { path: outputPath, blueprint, source, changed: true }\n}\n"]}
1
+ {"version":3,"file":"blueprint-generator.js","sourceRoot":"","sources":["../src/blueprint-generator.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n mkdir,\n readFile,\n realpath,\n rename,\n rm,\n writeFile,\n} from 'node:fs/promises'\nimport { dirname, isAbsolute, join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { isBunderstackBackend } from './backend'\nimport { BACKEND_INTERNALS } from './backend-internals'\nimport {\n blueprintFromManifest,\n serializeBlueprint,\n type BunderstackBlueprint,\n} from './blueprint'\nimport { parseBlueprintYaml } from './blueprint'\nimport { createEnvProbeSources } from './env-probe'\nimport { assertManifestMatchesBlueprint } from './hosted-contract'\nimport { parseManifest } from './manifest'\nimport { diffManifests } from './manifest-diff'\n\nexport type GenerateBlueprintOptions = {\n directory: string\n entry?: string\n output?: string\n check?: boolean\n hostedCheck?: boolean\n}\n\nexport type GenerateBlueprintResult = {\n path: string\n blueprint: BunderstackBlueprint\n source: string\n changed: boolean\n}\n\nexport class BlueprintCheckError extends Error {\n constructor() {\n super(\n 'bunderstack.blueprint.yaml is missing or stale; run `bunderstack blueprint`',\n )\n this.name = 'BlueprintCheckError'\n }\n}\n\ntype AppPackage = {\n scripts?: Record<string, unknown>\n dependencies?: Record<string, unknown>\n devDependencies?: Record<string, unknown>\n bunderstack?: { entry?: unknown }\n}\n\nfunction requireRelativePath(value: string, label: string): string {\n const normalized = value.replaceAll('\\\\', '/')\n if (\n !normalized ||\n isAbsolute(normalized) ||\n normalized.split('/').some((part) => !part || part === '..')\n ) {\n throw new Error(\n `[bunderstack] ${label} must be a relative path without traversal`,\n )\n }\n return normalized\n}\n\nfunction resolveWithin(root: string, value: string, label: string): string {\n const path = resolve(root, requireRelativePath(value, label))\n const pathFromRoot = relative(root, path)\n if (\n pathFromRoot === '..' ||\n pathFromRoot.startsWith('../') ||\n isAbsolute(pathFromRoot)\n ) {\n throw new Error(\n `[bunderstack] ${label} must stay within the application directory`,\n )\n }\n return path\n}\n\nfunction normalizeProjectPath(\n root: string,\n value: string,\n label: string,\n): string {\n if (!isAbsolute(value)) return requireRelativePath(value, label)\n const pathFromRoot = relative(root, resolve(value)) || '.'\n return requireRelativePath(pathFromRoot, label)\n}\n\nfunction requireScript(\n pkg: AppPackage,\n name: 'build' | 'start' | 'worker',\n required: boolean,\n): boolean {\n const value = pkg.scripts?.[name]\n if (typeof value === 'string' && value.trim()) return true\n if (required)\n throw new Error(\n `[bunderstack] package.json requires a non-empty \"${name}\" script`,\n )\n return false\n}\n\nasync function packageVersion(): Promise<string> {\n const pkg = (await Bun.file(\n new URL('../package.json', import.meta.url),\n ).json()) as { version: string }\n return pkg.version\n}\n\nexport async function generateBlueprint(\n options: GenerateBlueprintOptions,\n): Promise<GenerateBlueprintResult> {\n const directory = await realpath(resolve(options.directory))\n const packagePath = join(directory, 'package.json')\n const pkg = JSON.parse(await readFile(packagePath, 'utf8')) as AppPackage\n const allDependencies = { ...pkg.dependencies, ...pkg.devDependencies }\n let framework: 'tanstack-start' | 'solid' | 'bun-ssr' = 'bun-ssr'\n if (typeof allDependencies['@tanstack/react-start'] === 'string') {\n framework = 'tanstack-start'\n } else if (\n typeof allDependencies['solid-js'] === 'string' ||\n typeof allDependencies['@solidjs/web'] === 'string'\n ) {\n framework = 'solid'\n }\n requireScript(pkg, 'build', true)\n const hasStartScript = requireScript(pkg, 'start', false)\n\n const configuredEntry = pkg.bunderstack?.entry\n const entry = requireRelativePath(\n options.entry ??\n (typeof configuredEntry === 'string'\n ? configuredEntry\n : 'src/bunderstack.ts'),\n 'entry',\n )\n const entryPath = resolveWithin(directory, entry, 'entry')\n if (!(await Bun.file(entryPath).exists())) {\n throw new Error(`[bunderstack] entry does not exist: ${entry}`)\n }\n const output = requireRelativePath(\n options.output ?? 'bunderstack.blueprint.yaml',\n 'output',\n )\n const outputPath = resolveWithin(directory, output, 'output')\n\n const module = (await import(\n `${pathToFileURL(entryPath).href}?blueprint=${Date.now()}`\n )) as { backend?: unknown }\n const backend = module.backend\n if (!isBunderstackBackend(backend)) {\n throw new Error(`[bunderstack] ${entry} must export backend`)\n }\n if (options.hostedCheck) {\n const file = Bun.file(outputPath)\n if (!(await file.exists())) {\n throw new Error(\n `[bunderstack] hosted blueprint does not exist: ${output}`,\n )\n }\n const source = await file.text()\n const manifest = parseManifest(backend.inspect({ env: process.env }))\n assertManifestMatchesBlueprint(manifest, source)\n return {\n path: outputPath,\n blueprint: parseBlueprintYaml(source),\n source,\n changed: false,\n }\n }\n const probeSources = createEnvProbeSources(\n backend[BACKEND_INTERNALS].envSchema,\n process.env as Record<string, string | undefined>,\n )\n const firstManifest = parseManifest(backend.inspect({ env: probeSources[0] }))\n const secondManifest = parseManifest(\n backend.inspect({ env: probeSources[1] }),\n )\n const differences = diffManifests(firstManifest, secondManifest)\n if (differences.length > 0) {\n throw new Error(\n '[bunderstack] environment-dependent blueprint shape:\\n' +\n differences.map(({ kind, path }) => ` - ${kind}: ${path}`).join('\\n'),\n )\n }\n const manifest = firstManifest\n const workerRequired = manifest.background.jobs.length > 0\n if (hasStartScript) {\n requireScript(pkg, 'worker', workerRequired)\n }\n const migrationsDirectory = normalizeProjectPath(\n directory,\n manifest.database.migrationsDirectory,\n 'migrationsDirectory',\n )\n const migrationJournal = join(\n resolveWithin(directory, migrationsDirectory, 'migrationsDirectory'),\n 'meta',\n '_journal.json',\n )\n const migrationMode = (await Bun.file(migrationJournal).exists())\n ? 'migrations'\n : 'push'\n const blueprint = blueprintFromManifest({\n manifest: {\n ...manifest,\n database: { ...manifest.database, migrationsDirectory },\n },\n generatorVersion: await packageVersion(),\n entry,\n migrationMode,\n framework,\n ...(hasStartScript ? {} : { runtime: 'worker' }),\n })\n const source = serializeBlueprint(blueprint)\n const existing = (await Bun.file(outputPath).exists())\n ? await Bun.file(outputPath).text()\n : undefined\n if (options.check) {\n if (existing !== source) throw new BlueprintCheckError()\n return { path: outputPath, blueprint, source, changed: false }\n }\n if (existing === source)\n return { path: outputPath, blueprint, source, changed: false }\n await mkdir(dirname(outputPath), { recursive: true })\n const temporary = `${outputPath}.${process.pid}.${Date.now()}.tmp`\n try {\n await writeFile(temporary, source, { mode: 0o600 })\n await rename(temporary, outputPath)\n } finally {\n await rm(temporary, { force: true })\n }\n return { path: outputPath, blueprint, source, changed: true }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { BunderstackManifest } from './manifest.js';
2
2
  export type MigrationMode = 'migrations' | 'push';
3
+ export type ApplicationRuntime = 'server' | 'worker';
3
4
  export type ApplicationFramework = 'tanstack-start' | 'solid' | 'bun-ssr' | 'custom';
4
5
  /**
5
6
  * Blueprint form of an environment key. `sensitive` and `description` are
@@ -19,10 +20,11 @@ export type BunderstackBlueprint = {
19
20
  version: string;
20
21
  };
21
22
  application: {
23
+ runtime?: ApplicationRuntime;
22
24
  framework: ApplicationFramework;
23
25
  scripts: {
24
26
  build: 'build';
25
- start: 'start';
27
+ start?: 'start';
26
28
  worker?: 'worker';
27
29
  };
28
30
  };
@@ -63,6 +65,7 @@ export declare function blueprintFromManifest(args: {
63
65
  entry: string;
64
66
  migrationMode: MigrationMode;
65
67
  framework?: ApplicationFramework;
68
+ runtime?: ApplicationRuntime;
66
69
  }): BunderstackBlueprint;
67
70
  export declare function parseBlueprintYaml(source: string): BunderstackBlueprint;
68
71
  export declare function serializeBlueprint(value: BunderstackBlueprint): 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;;;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;QAC7B,SAAS,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAA;KAC5C,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;AAuID,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAgEnE;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,CAiDvB;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,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,CAAA;AACpD,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,OAAO,CAAC,EAAE,kBAAkB,CAAA;QAC5B,SAAS,EAAE,oBAAoB,CAAA;QAC/B,OAAO,EAAE;YAAE,KAAK,EAAE,OAAO,CAAC;YAAC,KAAK,CAAC,EAAE,OAAO,CAAC;YAAC,MAAM,CAAC,EAAE,QAAQ,CAAA;SAAE,CAAA;KAChE,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;QAC7B,SAAS,EAAE,mBAAmB,CAAC,WAAW,CAAC,CAAA;KAC5C,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;AAwID,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAiFnE;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;IAChC,OAAO,CAAC,EAAE,kBAAkB,CAAA;CAC7B,GAAG,oBAAoB,CAsDvB;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
@@ -28,10 +28,11 @@ const blueprintSchema = open({
28
28
  version: nonEmpty,
29
29
  }),
30
30
  application: open({
31
+ runtime: v.optional(v.picklist(['server', 'worker'])),
31
32
  framework: v.picklist(['tanstack-start', 'solid', 'bun-ssr', 'custom']),
32
33
  scripts: open({
33
34
  build: v.literal('build'),
34
- start: v.literal('start'),
35
+ start: v.optional(v.literal('start')),
35
36
  worker: v.optional(v.literal('worker')),
36
37
  }),
37
38
  }),
@@ -120,12 +121,24 @@ export function parseBlueprint(value) {
120
121
  if (!blueprint.resources.storage.buckets.some((bucket) => bucket.name === blueprint.resources.storage.defaultBucket)) {
121
122
  throw new Error('[bunderstack] storage defaultBucket must be declared in storage buckets');
122
123
  }
124
+ const runtime = blueprint.application.runtime ?? 'server';
123
125
  const workerRequired = blueprint.background.jobs.length > 0;
124
126
  if (blueprint.background.worker.required !== workerRequired) {
125
127
  throw new Error('[bunderstack] background worker.required must match declared queue jobs');
126
128
  }
127
- if (Boolean(blueprint.application.scripts.worker) !== workerRequired) {
128
- throw new Error('[bunderstack] application worker script must match declared queue jobs');
129
+ if (runtime === 'worker') {
130
+ if (blueprint.application.scripts.start !== undefined ||
131
+ blueprint.application.scripts.worker !== undefined) {
132
+ throw new Error('[bunderstack] worker runtime blueprint must only declare the build script');
133
+ }
134
+ }
135
+ else {
136
+ if (blueprint.application.scripts.start !== 'start') {
137
+ throw new Error('[bunderstack] server runtime blueprint must declare the start script');
138
+ }
139
+ if (Boolean(blueprint.application.scripts.worker) !== workerRequired) {
140
+ throw new Error('[bunderstack] application worker script must match declared queue jobs');
141
+ }
129
142
  }
130
143
  return blueprint;
131
144
  }
@@ -137,17 +150,21 @@ export function isSensitiveEnvVar(entry) {
137
150
  return entry.sensitive ?? entry.scope === 'server';
138
151
  }
139
152
  export function blueprintFromManifest(args) {
153
+ const runtime = args.runtime ?? 'server';
140
154
  const workerRequired = args.manifest.background.jobs.length > 0;
141
155
  return parseBlueprint({
142
156
  version: 1,
143
157
  generator: { name: 'bunderstack', version: args.generatorVersion },
144
158
  application: {
159
+ ...(args.runtime ? { runtime: args.runtime } : {}),
145
160
  framework: args.framework ?? 'tanstack-start',
146
- scripts: {
147
- build: 'build',
148
- start: 'start',
149
- ...(workerRequired ? { worker: 'worker' } : {}),
150
- },
161
+ scripts: runtime === 'worker'
162
+ ? { build: 'build' }
163
+ : {
164
+ build: 'build',
165
+ start: 'start',
166
+ ...(workerRequired ? { worker: 'worker' } : {}),
167
+ },
151
168
  },
152
169
  bunderstack: { entry: args.entry, manifestVersion: 4 },
153
170
  resources: {