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
@@ -0,0 +1,290 @@
1
+ // `bunderstack dev` and `bunderstack build`. dev starts sqld, celld, and Vite
2
+ // with one command; build writes the SPA to dist/client and checks
3
+ // wrangler.json and bunderstack.blueprint.yaml.
4
+ import { watch } from 'node:fs';
5
+ import { mkdir, readdir, writeFile } from 'node:fs/promises';
6
+ import { createServer } from 'node:net';
7
+ import { dirname, join, resolve } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { generateBlueprint } from '../blueprint-generator.js';
10
+ import { runWranglerCommand } from '../workers/wrangler.js';
11
+ import { resolveBinary } from './binaries.js';
12
+ import { devSecret, devVars, readUserEnv } from './env.js';
13
+ import { ProcessGroup } from './processes.js';
14
+ const LOG_LINE = /^\S+Z\s+(TRACE|DEBUG|INFO|WARN|ERROR)\s+([\w:]+): (.*)$/;
15
+ /**
16
+ * celld --logs mixes the Worker's console output with its own INFO lines.
17
+ * Keep the Worker output (without the timestamp), warnings, errors, and the
18
+ * start summary.
19
+ */
20
+ export function celldLine(line) {
21
+ if (line.startsWith('<jemalloc>'))
22
+ return undefined;
23
+ const match = LOG_LINE.exec(line);
24
+ if (!match)
25
+ return line;
26
+ const [, level, target, message] = match;
27
+ if (target === 'cell_console')
28
+ return message;
29
+ if (target === 'celld::memory')
30
+ return undefined; // allocator notice on macOS
31
+ if (level === 'WARN' || level === 'ERROR')
32
+ return `${level} ${message}`;
33
+ return undefined;
34
+ }
35
+ export function planDev(input) {
36
+ const { directory, ports, binaries } = input;
37
+ const apiUrl = `http://127.0.0.1:${ports.api}`;
38
+ const appUrl = input.hasVite ? `http://localhost:${ports.app}` : apiUrl;
39
+ const external = input.userEnv.BUNDERSTACK_DATABASE_URL;
40
+ const databaseUrl = external || `http://127.0.0.1:${ports.db}`;
41
+ return {
42
+ appUrl,
43
+ apiUrl,
44
+ databaseUrl,
45
+ sqld: external
46
+ ? undefined
47
+ : {
48
+ name: 'sqld',
49
+ cmd: [
50
+ binaries.sqld,
51
+ '--http-listen-addr',
52
+ `127.0.0.1:${ports.db}`,
53
+ '-d',
54
+ join(input.stateDir, 'db'),
55
+ ],
56
+ cwd: directory,
57
+ },
58
+ worker: {
59
+ name: 'celld',
60
+ cmd: [
61
+ binaries.celld,
62
+ 'dev',
63
+ directory,
64
+ '--port',
65
+ String(ports.api),
66
+ // Shows the Worker's console output.
67
+ '--logs',
68
+ // sqld writes here; a rebuild per write would restart the Worker.
69
+ '--watch-ignore',
70
+ '.bunderstack/**',
71
+ '--watch-ignore',
72
+ 'dist/**',
73
+ ],
74
+ cwd: directory,
75
+ env: { CELLD_ESBUILD: binaries.esbuild },
76
+ filter: celldLine,
77
+ },
78
+ vite: input.hasVite
79
+ ? {
80
+ name: 'vite',
81
+ cmd: [
82
+ process.execPath,
83
+ 'x',
84
+ '--bun',
85
+ 'vite',
86
+ '--port',
87
+ String(ports.app),
88
+ '--strictPort',
89
+ ],
90
+ cwd: directory,
91
+ env: { BUNDERSTACK_DEV_API_URL: apiUrl },
92
+ }
93
+ : undefined,
94
+ };
95
+ }
96
+ async function hasViteConfig(directory) {
97
+ const files = await readdir(directory);
98
+ return files.some((file) => /^vite\.config\.[cm]?[jt]s$/.test(file));
99
+ }
100
+ async function freePort() {
101
+ return new Promise((resolvePort, reject) => {
102
+ const server = createServer();
103
+ server.listen(0, '127.0.0.1', () => {
104
+ const address = server.address();
105
+ server.close(() => typeof address === 'object' && address
106
+ ? resolvePort(address.port)
107
+ : reject(new Error('no free port')));
108
+ });
109
+ });
110
+ }
111
+ function canListen(port, hostname) {
112
+ try {
113
+ Bun.listen({ hostname, port, socket: { data() { } } }).stop(true);
114
+ return true;
115
+ }
116
+ catch (error) {
117
+ // No IPv6 on this machine: nothing can take the port there either.
118
+ return error.code === 'EADDRNOTAVAIL';
119
+ }
120
+ }
121
+ /**
122
+ * The first port from `start` that is free on 127.0.0.1 and on ::1. The
123
+ * browser opens `localhost`, which can resolve to either; Vite on ::1 next to
124
+ * another server on 127.0.0.1 would answer only some requests.
125
+ */
126
+ export async function firstFreePort(start) {
127
+ for (let port = start; port < start + 100; port++) {
128
+ if (canListen(port, '127.0.0.1') && canListen(port, '::1'))
129
+ return port;
130
+ }
131
+ throw new Error(`[bunderstack] no free port from ${start}`);
132
+ }
133
+ async function waitForHealth(url, timeoutMs, cancelled) {
134
+ const deadline = Date.now() + timeoutMs;
135
+ for (;;) {
136
+ if (cancelled())
137
+ return;
138
+ const ok = await fetch(url).then((res) => res.ok, () => false);
139
+ if (ok)
140
+ return;
141
+ if (Date.now() > deadline)
142
+ throw new Error(`timed out waiting for ${url}`);
143
+ await Bun.sleep(200);
144
+ }
145
+ }
146
+ function esbuildBinary() {
147
+ const pkg = Bun.resolveSync('esbuild/package.json', import.meta.dir);
148
+ return join(dirname(pkg), 'bin', 'esbuild');
149
+ }
150
+ const pushScript = fileURLToPath(new URL(import.meta.url.endsWith('.ts') ? './push.ts' : './push.js', import.meta.url));
151
+ export async function runDev(options) {
152
+ const directory = resolve(options.directory);
153
+ const stateDir = join(directory, '.bunderstack', 'dev');
154
+ const group = new ProcessGroup();
155
+ const say = (line) => group.log('dev', line);
156
+ const userEnv = await readUserEnv(directory);
157
+ const external = Boolean(userEnv.BUNDERSTACK_DATABASE_URL);
158
+ const [celld, sqld] = await Promise.all([
159
+ resolveBinary('celld', { log: say }),
160
+ external ? '' : resolveBinary('sqld', { log: say }),
161
+ ]);
162
+ const plan = planDev({
163
+ directory,
164
+ stateDir,
165
+ userEnv,
166
+ hasVite: await hasViteConfig(directory),
167
+ ports: {
168
+ // An explicit port is kept, and Vite fails when it is taken.
169
+ app: options.port ??
170
+ (process.env.PORT
171
+ ? Number(process.env.PORT)
172
+ : await firstFreePort(5173)),
173
+ api: await freePort(),
174
+ db: await freePort(),
175
+ },
176
+ binaries: { celld, sqld, esbuild: esbuildBinary() },
177
+ });
178
+ // Children run in their own process groups, so the terminal's Ctrl+C does
179
+ // not reach them; group.stop() passes it on.
180
+ let interrupted = false;
181
+ const interrupt = new Promise((resolveInterrupt) => {
182
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) {
183
+ process.once(signal, () => {
184
+ interrupted = true;
185
+ resolveInterrupt();
186
+ });
187
+ }
188
+ });
189
+ const cancelled = () => interrupted;
190
+ let watcher;
191
+ try {
192
+ await mkdir(stateDir, { recursive: true });
193
+ if (plan.sqld) {
194
+ group.start(plan.sqld);
195
+ await waitForHealth(`${plan.databaseUrl}/health`, 20_000, cancelled);
196
+ }
197
+ const authSecret = await devSecret(stateDir);
198
+ await writeFile(join(directory, '.dev.vars'), devVars({
199
+ userEnv,
200
+ databaseUrl: plan.databaseUrl,
201
+ appUrl: plan.appUrl,
202
+ authSecret,
203
+ }));
204
+ const pushEnv = {
205
+ ...userEnv,
206
+ BUNDERSTACK_DATABASE_URL: plan.databaseUrl,
207
+ AUTH_SECRET: userEnv.AUTH_SECRET || authSecret,
208
+ APP_URL: plan.appUrl,
209
+ };
210
+ const push = async () => {
211
+ const child = Bun.spawn([process.execPath, pushScript, directory], {
212
+ cwd: directory,
213
+ env: { ...process.env, ...pushEnv },
214
+ stdout: 'pipe',
215
+ stderr: 'pipe',
216
+ });
217
+ const [out, err, code] = await Promise.all([
218
+ new Response(child.stdout).text(),
219
+ new Response(child.stderr).text(),
220
+ child.exited,
221
+ ]);
222
+ // drizzle-kit draws a spinner with escape codes; keep the text only.
223
+ // oxlint-disable-next-line no-control-regex
224
+ const text = `${out}${err}`.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '');
225
+ for (const line of text.trim().split('\n')) {
226
+ if (line.trim())
227
+ group.log('push', line.trim());
228
+ }
229
+ if (code !== 0)
230
+ group.log('push', 'failed; fix the code and save again');
231
+ return code === 0;
232
+ };
233
+ // The first push also writes wrangler.json, which celld reads at start.
234
+ await push();
235
+ let timer;
236
+ let running = Promise.resolve(true);
237
+ watcher = watch(join(directory, 'src'), { recursive: true }, (_, file) => {
238
+ if (file && String(file).endsWith('routeTree.gen.ts'))
239
+ return;
240
+ clearTimeout(timer);
241
+ timer = setTimeout(() => {
242
+ running = running.then(push);
243
+ }, 300);
244
+ });
245
+ group.start(plan.worker);
246
+ await waitForHealth(`${plan.apiUrl}/api/health`, 60_000, cancelled).catch(() => say('the Worker did not answer /api/health yet; see the celld log'));
247
+ if (plan.vite && !interrupted)
248
+ group.start(plan.vite);
249
+ if (!interrupted)
250
+ say(`App: ${plan.appUrl}`);
251
+ const outcome = await Promise.race([group.exited, interrupt]);
252
+ if (outcome && !interrupted) {
253
+ say(`${outcome.name} exited with code ${outcome.code}; stopping`);
254
+ return 1;
255
+ }
256
+ return 0;
257
+ }
258
+ catch (error) {
259
+ say(error instanceof Error ? error.message : String(error));
260
+ return 1;
261
+ }
262
+ finally {
263
+ watcher?.close();
264
+ await group.stop();
265
+ }
266
+ }
267
+ export async function runBuild(options) {
268
+ const directory = resolve(options.directory);
269
+ if (await hasViteConfig(directory)) {
270
+ const vite = Bun.spawn([process.execPath, 'x', '--bun', 'vite', 'build'], {
271
+ cwd: directory,
272
+ stdout: 'inherit',
273
+ stderr: 'inherit',
274
+ });
275
+ if ((await vite.exited) !== 0)
276
+ return 1;
277
+ }
278
+ try {
279
+ await runWranglerCommand({ directory, check: true });
280
+ console.log('wrangler.json is current');
281
+ await generateBlueprint({ directory, check: true });
282
+ console.log('bunderstack.blueprint.yaml is current');
283
+ return 0;
284
+ }
285
+ catch (error) {
286
+ console.error(error instanceof Error ? error.message : String(error));
287
+ return 1;
288
+ }
289
+ }
290
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"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","sourcesContent":["// `bunderstack dev` and `bunderstack build`. dev starts sqld, celld, and Vite\n// with one command; build writes the SPA to dist/client and checks\n// wrangler.json and bunderstack.blueprint.yaml.\nimport { watch } from 'node:fs'\nimport { mkdir, readdir, writeFile } from 'node:fs/promises'\nimport { createServer } from 'node:net'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nimport { generateBlueprint } from '../blueprint-generator'\nimport { runWranglerCommand } from '../workers/wrangler'\nimport { resolveBinary } from './binaries'\nimport { devSecret, devVars, readUserEnv } from './env'\nimport { ProcessGroup, type ProcessSpec } from './processes'\n\nexport type DevPlan = {\n appUrl: string\n apiUrl: string\n databaseUrl: string\n sqld?: ProcessSpec\n worker: ProcessSpec\n vite?: ProcessSpec\n}\n\nconst LOG_LINE = /^\\S+Z\\s+(TRACE|DEBUG|INFO|WARN|ERROR)\\s+([\\w:]+): (.*)$/\n\n/**\n * celld --logs mixes the Worker's console output with its own INFO lines.\n * Keep the Worker output (without the timestamp), warnings, errors, and the\n * start summary.\n */\nexport function celldLine(line: string): string | undefined {\n if (line.startsWith('<jemalloc>')) return undefined\n const match = LOG_LINE.exec(line)\n if (!match) return line\n const [, level, target, message] = match as unknown as [\n string,\n string,\n string,\n string,\n ]\n if (target === 'cell_console') return message\n if (target === 'celld::memory') return undefined // allocator notice on macOS\n if (level === 'WARN' || level === 'ERROR') return `${level} ${message}`\n return undefined\n}\n\nexport function planDev(input: {\n directory: string\n stateDir: string\n userEnv: Record<string, string>\n hasVite: boolean\n ports: { app: number; api: number; db: number }\n binaries: { celld: string; sqld: string; esbuild: string }\n}): DevPlan {\n const { directory, ports, binaries } = input\n const apiUrl = `http://127.0.0.1:${ports.api}`\n const appUrl = input.hasVite ? `http://localhost:${ports.app}` : apiUrl\n const external = input.userEnv.BUNDERSTACK_DATABASE_URL\n const databaseUrl = external || `http://127.0.0.1:${ports.db}`\n return {\n appUrl,\n apiUrl,\n databaseUrl,\n sqld: external\n ? undefined\n : {\n name: 'sqld',\n cmd: [\n binaries.sqld,\n '--http-listen-addr',\n `127.0.0.1:${ports.db}`,\n '-d',\n join(input.stateDir, 'db'),\n ],\n cwd: directory,\n },\n worker: {\n name: 'celld',\n cmd: [\n binaries.celld,\n 'dev',\n directory,\n '--port',\n String(ports.api),\n // Shows the Worker's console output.\n '--logs',\n // sqld writes here; a rebuild per write would restart the Worker.\n '--watch-ignore',\n '.bunderstack/**',\n '--watch-ignore',\n 'dist/**',\n ],\n cwd: directory,\n env: { CELLD_ESBUILD: binaries.esbuild },\n filter: celldLine,\n },\n vite: input.hasVite\n ? {\n name: 'vite',\n cmd: [\n process.execPath,\n 'x',\n '--bun',\n 'vite',\n '--port',\n String(ports.app),\n '--strictPort',\n ],\n cwd: directory,\n env: { BUNDERSTACK_DEV_API_URL: apiUrl },\n }\n : undefined,\n }\n}\n\nasync function hasViteConfig(directory: string) {\n const files = await readdir(directory)\n return files.some((file) => /^vite\\.config\\.[cm]?[jt]s$/.test(file))\n}\n\nasync function freePort(): Promise<number> {\n return new Promise((resolvePort, reject) => {\n const server = createServer()\n server.listen(0, '127.0.0.1', () => {\n const address = server.address()\n server.close(() =>\n typeof address === 'object' && address\n ? resolvePort(address.port)\n : reject(new Error('no free port')),\n )\n })\n })\n}\n\nfunction canListen(port: number, hostname: string): boolean {\n try {\n Bun.listen({ hostname, port, socket: { data() {} } }).stop(true)\n return true\n } catch (error) {\n // No IPv6 on this machine: nothing can take the port there either.\n return (error as { code?: string }).code === 'EADDRNOTAVAIL'\n }\n}\n\n/**\n * The first port from `start` that is free on 127.0.0.1 and on ::1. The\n * browser opens `localhost`, which can resolve to either; Vite on ::1 next to\n * another server on 127.0.0.1 would answer only some requests.\n */\nexport async function firstFreePort(start: number): Promise<number> {\n for (let port = start; port < start + 100; port++) {\n if (canListen(port, '127.0.0.1') && canListen(port, '::1')) return port\n }\n throw new Error(`[bunderstack] no free port from ${start}`)\n}\n\nasync function waitForHealth(\n url: string,\n timeoutMs: number,\n cancelled: () => boolean,\n) {\n const deadline = Date.now() + timeoutMs\n for (;;) {\n if (cancelled()) return\n const ok = await fetch(url).then(\n (res) => res.ok,\n () => false,\n )\n if (ok) return\n if (Date.now() > deadline) throw new Error(`timed out waiting for ${url}`)\n await Bun.sleep(200)\n }\n}\n\nfunction esbuildBinary() {\n const pkg = Bun.resolveSync('esbuild/package.json', import.meta.dir)\n return join(dirname(pkg), 'bin', 'esbuild')\n}\n\nconst pushScript = fileURLToPath(\n new URL(\n import.meta.url.endsWith('.ts') ? './push.ts' : './push.js',\n import.meta.url,\n ),\n)\n\nexport async function runDev(options: {\n directory: string\n port?: number\n}): Promise<number> {\n const directory = resolve(options.directory)\n const stateDir = join(directory, '.bunderstack', 'dev')\n const group = new ProcessGroup()\n const say = (line: string) => group.log('dev', line)\n\n const userEnv = await readUserEnv(directory)\n const external = Boolean(userEnv.BUNDERSTACK_DATABASE_URL)\n const [celld, sqld] = await Promise.all([\n resolveBinary('celld', { log: say }),\n external ? '' : resolveBinary('sqld', { log: say }),\n ])\n const plan = planDev({\n directory,\n stateDir,\n userEnv,\n hasVite: await hasViteConfig(directory),\n ports: {\n // An explicit port is kept, and Vite fails when it is taken.\n app:\n options.port ??\n (process.env.PORT\n ? Number(process.env.PORT)\n : await firstFreePort(5173)),\n api: await freePort(),\n db: await freePort(),\n },\n binaries: { celld, sqld, esbuild: esbuildBinary() },\n })\n\n // Children run in their own process groups, so the terminal's Ctrl+C does\n // not reach them; group.stop() passes it on.\n let interrupted = false\n const interrupt = new Promise<void>((resolveInterrupt) => {\n for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) {\n process.once(signal, () => {\n interrupted = true\n resolveInterrupt()\n })\n }\n })\n const cancelled = () => interrupted\n let watcher: ReturnType<typeof watch> | undefined\n\n try {\n await mkdir(stateDir, { recursive: true })\n if (plan.sqld) {\n group.start(plan.sqld)\n await waitForHealth(`${plan.databaseUrl}/health`, 20_000, cancelled)\n }\n const authSecret = await devSecret(stateDir)\n await writeFile(\n join(directory, '.dev.vars'),\n devVars({\n userEnv,\n databaseUrl: plan.databaseUrl,\n appUrl: plan.appUrl,\n authSecret,\n }),\n )\n const pushEnv = {\n ...userEnv,\n BUNDERSTACK_DATABASE_URL: plan.databaseUrl,\n AUTH_SECRET: userEnv.AUTH_SECRET || authSecret,\n APP_URL: plan.appUrl,\n }\n const push = async () => {\n const child = Bun.spawn([process.execPath, pushScript, directory], {\n cwd: directory,\n env: { ...process.env, ...pushEnv },\n stdout: 'pipe',\n stderr: 'pipe',\n })\n const [out, err, code] = await Promise.all([\n new Response(child.stdout).text(),\n new Response(child.stderr).text(),\n child.exited,\n ])\n // drizzle-kit draws a spinner with escape codes; keep the text only.\n // oxlint-disable-next-line no-control-regex\n const text = `${out}${err}`.replace(/\\x1b\\[[0-9;]*[A-Za-z]/g, '')\n for (const line of text.trim().split('\\n')) {\n if (line.trim()) group.log('push', line.trim())\n }\n if (code !== 0) group.log('push', 'failed; fix the code and save again')\n return code === 0\n }\n // The first push also writes wrangler.json, which celld reads at start.\n await push()\n\n let timer: ReturnType<typeof setTimeout> | undefined\n let running = Promise.resolve(true)\n watcher = watch(join(directory, 'src'), { recursive: true }, (_, file) => {\n if (file && String(file).endsWith('routeTree.gen.ts')) return\n clearTimeout(timer)\n timer = setTimeout(() => {\n running = running.then(push)\n }, 300)\n })\n\n group.start(plan.worker)\n await waitForHealth(`${plan.apiUrl}/api/health`, 60_000, cancelled).catch(\n () => say('the Worker did not answer /api/health yet; see the celld log'),\n )\n if (plan.vite && !interrupted) group.start(plan.vite)\n if (!interrupted) say(`App: ${plan.appUrl}`)\n\n const outcome = await Promise.race([group.exited, interrupt])\n if (outcome && !interrupted) {\n say(`${outcome.name} exited with code ${outcome.code}; stopping`)\n return 1\n }\n return 0\n } catch (error) {\n say(error instanceof Error ? error.message : String(error))\n return 1\n } finally {\n watcher?.close()\n await group.stop()\n }\n}\n\nexport async function runBuild(options: {\n directory: string\n}): Promise<number> {\n const directory = resolve(options.directory)\n if (await hasViteConfig(directory)) {\n const vite = Bun.spawn([process.execPath, 'x', '--bun', 'vite', 'build'], {\n cwd: directory,\n stdout: 'inherit',\n stderr: 'inherit',\n })\n if ((await vite.exited) !== 0) return 1\n }\n try {\n await runWranglerCommand({ directory, check: true })\n console.log('wrangler.json is current')\n await generateBlueprint({ directory, check: true })\n console.log('bunderstack.blueprint.yaml is current')\n return 0\n } catch (error) {\n console.error(error instanceof Error ? error.message : String(error))\n return 1\n }\n}\n"]}
@@ -0,0 +1,37 @@
1
+ export type ProcessSpec = {
2
+ name: string;
3
+ cmd: string[];
4
+ cwd: string;
5
+ env?: Record<string, string>;
6
+ /** Rewrites an output line; `undefined` drops it. */
7
+ filter?: (line: string) => string | undefined;
8
+ };
9
+ export declare function prefixLines(chunk: string, pending: string): {
10
+ lines: string[];
11
+ pending: string;
12
+ };
13
+ export declare class ProcessGroup {
14
+ private write;
15
+ readonly exited: Promise<{
16
+ name: string;
17
+ code: number | null;
18
+ }>;
19
+ private children;
20
+ private resolveExit;
21
+ private stopping;
22
+ private width;
23
+ constructor(write?: (line: string) => void);
24
+ private label;
25
+ private names;
26
+ private labelIndex;
27
+ /** Writes one line with the name's prefix; the dev command logs here too. */
28
+ log(name: string, line: string): void;
29
+ start(spec: ProcessSpec): void;
30
+ /**
31
+ * SIGINT to each child's process group, as Ctrl+C in a terminal does, then
32
+ * SIGKILL to the groups after 3 s. `celld dev` runs a node process that
33
+ * ignores SIGTERM, so a signal to the leader alone leaves it running.
34
+ */
35
+ stop(): Promise<void>;
36
+ }
37
+ //# sourceMappingURL=processes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.d.ts","sourceRoot":"","sources":["../../src/dev/processes.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,EAAE,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,qDAAqD;IACrD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;CAC9C,CAAA;AAID,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAItC;AAED,qBAAa,YAAY;IAQrB,OAAO,CAAC,KAAK;IAPf,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAA;IAC/D,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,WAAW,CAAyD;IAC5E,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,KAAK,CAAI;gBAGP,KAAK,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkC;IAKrE,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,UAAU;IAKlB,6EAA6E;IAC7E,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAM9B,KAAK,CAAC,IAAI,EAAE,WAAW;IAqCvB;;;;OAIG;IACG,IAAI;CAmBX"}
@@ -0,0 +1,104 @@
1
+ const COLORS = [36, 35, 33, 32, 34];
2
+ export function prefixLines(chunk, pending) {
3
+ const parts = (pending + chunk).split(/\r?\n/);
4
+ const rest = parts.pop() ?? '';
5
+ return { lines: parts, pending: rest };
6
+ }
7
+ export class ProcessGroup {
8
+ write;
9
+ exited;
10
+ children = [];
11
+ resolveExit;
12
+ stopping = false;
13
+ width = 0;
14
+ constructor(write = (line) => console.log(line)) {
15
+ this.write = write;
16
+ this.exited = new Promise((resolve) => (this.resolveExit = resolve));
17
+ }
18
+ label(name) {
19
+ const text = `[${name}]`.padEnd(this.width + 2);
20
+ if (!process.stdout.isTTY)
21
+ return text;
22
+ const color = COLORS[this.labelIndex(name) % COLORS.length];
23
+ return `\x1b[${color}m${text}\x1b[0m`;
24
+ }
25
+ names = [];
26
+ labelIndex(name) {
27
+ if (!this.names.includes(name))
28
+ this.names.push(name);
29
+ return this.names.indexOf(name);
30
+ }
31
+ /** Writes one line with the name's prefix; the dev command logs here too. */
32
+ log(name, line) {
33
+ this.labelIndex(name);
34
+ this.width = Math.max(this.width, name.length);
35
+ this.write(`${this.label(name)} ${line}`);
36
+ }
37
+ start(spec) {
38
+ this.width = Math.max(this.width, spec.name.length);
39
+ const child = Bun.spawn(spec.cmd, {
40
+ cwd: spec.cwd,
41
+ env: { ...process.env, ...spec.env },
42
+ stdin: 'ignore',
43
+ // Own process group, so stop() reaches the child's children too.
44
+ detached: true,
45
+ stdout: 'pipe',
46
+ stderr: 'pipe',
47
+ });
48
+ this.children.push(child);
49
+ const emit = (line) => {
50
+ const shown = spec.filter ? spec.filter(line) : line;
51
+ if (shown !== undefined)
52
+ this.log(spec.name, shown);
53
+ };
54
+ const pump = async (stream) => {
55
+ let pending = '';
56
+ const decoder = new TextDecoder();
57
+ const reader = stream.getReader();
58
+ for (;;) {
59
+ const { done, value } = await reader.read();
60
+ if (done)
61
+ break;
62
+ const chunk = decoder.decode(value, { stream: true });
63
+ const result = prefixLines(chunk, pending);
64
+ pending = result.pending;
65
+ for (const line of result.lines)
66
+ emit(line);
67
+ }
68
+ if (pending)
69
+ emit(pending);
70
+ };
71
+ const output = Promise.all([pump(child.stdout), pump(child.stderr)]);
72
+ void child.exited.then(async (code) => {
73
+ await output.catch(() => { });
74
+ if (!this.stopping)
75
+ this.resolveExit({ name: spec.name, code });
76
+ });
77
+ }
78
+ /**
79
+ * SIGINT to each child's process group, as Ctrl+C in a terminal does, then
80
+ * SIGKILL to the groups after 3 s. `celld dev` runs a node process that
81
+ * ignores SIGTERM, so a signal to the leader alone leaves it running.
82
+ */
83
+ async stop() {
84
+ this.stopping = true;
85
+ const signal = (sig) => {
86
+ for (const child of this.children) {
87
+ try {
88
+ process.kill(-child.pid, sig);
89
+ }
90
+ catch {
91
+ // The group is gone already.
92
+ }
93
+ }
94
+ };
95
+ signal('SIGINT');
96
+ await Promise.race([
97
+ Promise.allSettled(this.children.map((child) => child.exited)),
98
+ Bun.sleep(3_000),
99
+ ]);
100
+ signal('SIGKILL');
101
+ await Promise.allSettled(this.children.map((child) => child.exited));
102
+ }
103
+ }
104
+ //# sourceMappingURL=processes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.js","sourceRoot":"","sources":["../../src/dev/processes.ts"],"names":[],"mappings":"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","sourcesContent":["// The child processes of `bunderstack dev`: one log with a prefix per process,\n// and one stop for all of them.\nimport type { Subprocess } from 'bun'\n\nexport type ProcessSpec = {\n name: string\n cmd: string[]\n cwd: string\n env?: Record<string, string>\n /** Rewrites an output line; `undefined` drops it. */\n filter?: (line: string) => string | undefined\n}\n\nconst COLORS = [36, 35, 33, 32, 34]\n\nexport function prefixLines(\n chunk: string,\n pending: string,\n): { lines: string[]; pending: string } {\n const parts = (pending + chunk).split(/\\r?\\n/)\n const rest = parts.pop() ?? ''\n return { lines: parts, pending: rest }\n}\n\nexport class ProcessGroup {\n readonly exited: Promise<{ name: string; code: number | null }>\n private children: Subprocess[] = []\n private resolveExit!: (value: { name: string; code: number | null }) => void\n private stopping = false\n private width = 0\n\n constructor(\n private write: (line: string) => void = (line) => console.log(line),\n ) {\n this.exited = new Promise((resolve) => (this.resolveExit = resolve))\n }\n\n private label(name: string) {\n const text = `[${name}]`.padEnd(this.width + 2)\n if (!process.stdout.isTTY) return text\n const color = COLORS[this.labelIndex(name) % COLORS.length]\n return `\\x1b[${color}m${text}\\x1b[0m`\n }\n\n private names: string[] = []\n private labelIndex(name: string) {\n if (!this.names.includes(name)) this.names.push(name)\n return this.names.indexOf(name)\n }\n\n /** Writes one line with the name's prefix; the dev command logs here too. */\n log(name: string, line: string) {\n this.labelIndex(name)\n this.width = Math.max(this.width, name.length)\n this.write(`${this.label(name)} ${line}`)\n }\n\n start(spec: ProcessSpec) {\n this.width = Math.max(this.width, spec.name.length)\n const child = Bun.spawn(spec.cmd, {\n cwd: spec.cwd,\n env: { ...process.env, ...spec.env },\n stdin: 'ignore',\n // Own process group, so stop() reaches the child's children too.\n detached: true,\n stdout: 'pipe',\n stderr: 'pipe',\n })\n this.children.push(child)\n const emit = (line: string) => {\n const shown = spec.filter ? spec.filter(line) : line\n if (shown !== undefined) this.log(spec.name, shown)\n }\n const pump = async (stream: ReadableStream<Uint8Array>) => {\n let pending = ''\n const decoder = new TextDecoder()\n const reader = stream.getReader()\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n const chunk = decoder.decode(value, { stream: true })\n const result = prefixLines(chunk, pending)\n pending = result.pending\n for (const line of result.lines) emit(line)\n }\n if (pending) emit(pending)\n }\n const output = Promise.all([pump(child.stdout), pump(child.stderr)])\n void child.exited.then(async (code) => {\n await output.catch(() => {})\n if (!this.stopping) this.resolveExit({ name: spec.name, code })\n })\n }\n\n /**\n * SIGINT to each child's process group, as Ctrl+C in a terminal does, then\n * SIGKILL to the groups after 3 s. `celld dev` runs a node process that\n * ignores SIGTERM, so a signal to the leader alone leaves it running.\n */\n async stop() {\n this.stopping = true\n const signal = (sig: NodeJS.Signals) => {\n for (const child of this.children) {\n try {\n process.kill(-child.pid, sig)\n } catch {\n // The group is gone already.\n }\n }\n }\n signal('SIGINT')\n await Promise.race([\n Promise.allSettled(this.children.map((child) => child.exited)),\n Bun.sleep(3_000),\n ])\n signal('SIGKILL')\n await Promise.allSettled(this.children.map((child) => child.exited))\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/dev/push.ts"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ // Subprocess of `bunderstack dev`: `bun push.ts <app directory>`. A fresh
2
+ // process sees the current app code on each run. It regenerates wrangler.json
3
+ // and bunderstack.blueprint.yaml, and pushes the schema (or applies committed
4
+ // migrations) to the dev database.
5
+ import { generateBlueprint } from '../blueprint-generator.js';
6
+ import { loadBackend, runWranglerCommand } from '../workers/wrangler.js';
7
+ const directory = process.argv[2];
8
+ if (!directory)
9
+ throw new Error('usage: push.ts <app directory>');
10
+ const wrangler = await runWranglerCommand({ directory });
11
+ if (wrangler.changed)
12
+ console.log('wrangler.json updated');
13
+ const blueprint = await generateBlueprint({ directory });
14
+ if (blueprint.changed)
15
+ console.log('bunderstack.blueprint.yaml updated');
16
+ const { backend } = await loadBackend(directory);
17
+ // Resolve from the app, so provision sees the same bunderstack instance as
18
+ // the backend it provisions.
19
+ const { provision } = (await import(Bun.resolveSync('bunderstack/provision-schema', directory)));
20
+ const app = (await backend.start({
21
+ env: process.env,
22
+ }));
23
+ try {
24
+ await provision(app);
25
+ console.log('schema is current');
26
+ }
27
+ finally {
28
+ await app.close();
29
+ }
30
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/dev/push.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,8EAA8E;AAC9E,8EAA8E;AAC9E,mCAAmC;AACnC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAErE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjC,IAAI,CAAC,SAAS;IAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;AAEjE,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACxD,IAAI,QAAQ,CAAC,OAAO;IAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;AAE1D,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;AACxD,IAAI,SAAS,CAAC,OAAO;IAAE,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAA;AAExE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,WAAW,CAAC,SAAS,CAAC,CAAA;AAChD,2EAA2E;AAC3E,6BAA6B;AAC7B,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,MAAM,CACjC,GAAG,CAAC,WAAW,CAAC,8BAA8B,EAAE,SAAS,CAAC,CAC3D,CAAyC,CAAA;AAC1C,MAAM,GAAG,GAAG,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC;IAC/B,GAAG,EAAE,OAAO,CAAC,GAAyC;CACvD,CAAC,CAA+B,CAAA;AACjC,IAAI,CAAC;IACH,MAAM,SAAS,CAAC,GAAG,CAAC,CAAA;IACpB,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAClC,CAAC;QAAS,CAAC;IACT,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;AACnB,CAAC","sourcesContent":["// Subprocess of `bunderstack dev`: `bun push.ts <app directory>`. A fresh\n// process sees the current app code on each run. It regenerates wrangler.json\n// and bunderstack.blueprint.yaml, and pushes the schema (or applies committed\n// migrations) to the dev database.\nimport { generateBlueprint } from '../blueprint-generator'\nimport { loadBackend, runWranglerCommand } from '../workers/wrangler'\n\nconst directory = process.argv[2]\nif (!directory) throw new Error('usage: push.ts <app directory>')\n\nconst wrangler = await runWranglerCommand({ directory })\nif (wrangler.changed) console.log('wrangler.json updated')\n\nconst blueprint = await generateBlueprint({ directory })\nif (blueprint.changed) console.log('bunderstack.blueprint.yaml updated')\n\nconst { backend } = await loadBackend(directory)\n// Resolve from the app, so provision sees the same bunderstack instance as\n// the backend it provisions.\nconst { provision } = (await import(\n Bun.resolveSync('bunderstack/provision-schema', directory)\n)) as typeof import('../provision-schema')\nconst app = (await backend.start({\n env: process.env as Record<string, string | undefined>,\n})) as { close(): Promise<void> }\ntry {\n await provision(app)\n console.log('schema is current')\n} finally {\n await app.close()\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"env-probe.d.ts","sourceRoot":"","sources":["../src/env-probe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAyB3C,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,GAAG,EAAE,MAAM;CAOxB;AAmCD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,cAAc,GAAG,SAAS,EACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC7C,SAAS;IACV,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,CAUA"}
1
+ {"version":3,"file":"env-probe.d.ts","sourceRoot":"","sources":["../src/env-probe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAwB3C,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,GAAG,EAAE,MAAM;CAOxB;AAmCD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,cAAc,GAAG,SAAS,EACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC7C,SAAS;IACV,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,CAUA"}
package/dist/env-probe.js CHANGED
@@ -16,7 +16,6 @@ const BASE_SOURCE = {
16
16
  NODE_ENV: 'production',
17
17
  AUTH_SECRET: 'bunderstack-blueprint-probe-secret',
18
18
  DATABASE_URL: 'file:./bunderstack-blueprint-probe.db',
19
- BUNDERSTACK_ROLE: 'all',
20
19
  };
21
20
  export class BlueprintProbeError extends Error {
22
21
  constructor(key) {
@@ -1 +1 @@
1
- {"version":3,"file":"env-probe.js","sourceRoot":"","sources":["../src/env-probe.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport type { EnvConfigInput } from './env'\n\nimport { validateStandardSchema } from './standard-schema'\n\nconst CANDIDATES: readonly (string | undefined)[] = [\n undefined,\n '',\n 'probe',\n 'true',\n 'false',\n '0',\n '1',\n 'https://example.invalid',\n 'file:./bunderstack-probe.db',\n 'probe@example.invalid',\n '00000000-0000-4000-8000-000000000000',\n]\n\nconst BASE_SOURCE = {\n NODE_ENV: 'production',\n AUTH_SECRET: 'bunderstack-blueprint-probe-secret',\n DATABASE_URL: 'file:./bunderstack-blueprint-probe.db',\n BUNDERSTACK_ROLE: 'all',\n} satisfies Record<string, string | undefined>\n\nexport class BlueprintProbeError extends Error {\n constructor(key: string) {\n super(\n `[bunderstack] cannot synthesize a valid blueprint probe for env key ${key}; ` +\n 'provide a valid value while generating the blueprint',\n )\n this.name = 'BlueprintProbeError'\n }\n}\n\nfunction identity(value: unknown): string {\n if (value === undefined) return 'undefined'\n if (typeof value === 'number' && Number.isNaN(value)) return 'number:NaN'\n try {\n return `${typeof value}:${JSON.stringify(value)}`\n } catch {\n return `${typeof value}:${String(value)}`\n }\n}\n\nfunction acceptedValues(\n key: string,\n schema: StandardSchemaV1,\n configured: string | undefined,\n): (string | undefined)[] {\n const accepted: (string | undefined)[] = []\n const outputs = new Set<string>()\n for (const candidate of [configured, ...CANDIDATES]) {\n try {\n const output = validateStandardSchema(schema, candidate, 'env')\n const id = identity(output)\n if (outputs.has(id)) continue\n outputs.add(id)\n accepted.push(candidate)\n if (accepted.length === 2) return accepted\n } catch {\n // Candidate failures are expected. The eventual error names only the key.\n }\n }\n if (accepted.length === 0) throw new BlueprintProbeError(key)\n return accepted\n}\n\nexport function createEnvProbeSources(\n envSchema: EnvConfigInput | undefined,\n configured: Record<string, string | undefined>,\n): readonly [\n Record<string, string | undefined>,\n Record<string, string | undefined>,\n] {\n const first: Record<string, string | undefined> = { ...BASE_SOURCE }\n const second: Record<string, string | undefined> = { ...BASE_SOURCE }\n const variables = { ...envSchema?.server, ...envSchema?.client }\n for (const [key, schema] of Object.entries(variables)) {\n const values = acceptedValues(key, schema, configured[key])\n first[key] = values[0]\n second[key] = values[1] ?? values[0]\n }\n return [first, second]\n}\n"]}
1
+ {"version":3,"file":"env-probe.js","sourceRoot":"","sources":["../src/env-probe.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec'\n\nimport type { EnvConfigInput } from './env'\n\nimport { validateStandardSchema } from './standard-schema'\n\nconst CANDIDATES: readonly (string | undefined)[] = [\n undefined,\n '',\n 'probe',\n 'true',\n 'false',\n '0',\n '1',\n 'https://example.invalid',\n 'file:./bunderstack-probe.db',\n 'probe@example.invalid',\n '00000000-0000-4000-8000-000000000000',\n]\n\nconst BASE_SOURCE = {\n NODE_ENV: 'production',\n AUTH_SECRET: 'bunderstack-blueprint-probe-secret',\n DATABASE_URL: 'file:./bunderstack-blueprint-probe.db',\n} satisfies Record<string, string | undefined>\n\nexport class BlueprintProbeError extends Error {\n constructor(key: string) {\n super(\n `[bunderstack] cannot synthesize a valid blueprint probe for env key ${key}; ` +\n 'provide a valid value while generating the blueprint',\n )\n this.name = 'BlueprintProbeError'\n }\n}\n\nfunction identity(value: unknown): string {\n if (value === undefined) return 'undefined'\n if (typeof value === 'number' && Number.isNaN(value)) return 'number:NaN'\n try {\n return `${typeof value}:${JSON.stringify(value)}`\n } catch {\n return `${typeof value}:${String(value)}`\n }\n}\n\nfunction acceptedValues(\n key: string,\n schema: StandardSchemaV1,\n configured: string | undefined,\n): (string | undefined)[] {\n const accepted: (string | undefined)[] = []\n const outputs = new Set<string>()\n for (const candidate of [configured, ...CANDIDATES]) {\n try {\n const output = validateStandardSchema(schema, candidate, 'env')\n const id = identity(output)\n if (outputs.has(id)) continue\n outputs.add(id)\n accepted.push(candidate)\n if (accepted.length === 2) return accepted\n } catch {\n // Candidate failures are expected. The eventual error names only the key.\n }\n }\n if (accepted.length === 0) throw new BlueprintProbeError(key)\n return accepted\n}\n\nexport function createEnvProbeSources(\n envSchema: EnvConfigInput | undefined,\n configured: Record<string, string | undefined>,\n): readonly [\n Record<string, string | undefined>,\n Record<string, string | undefined>,\n] {\n const first: Record<string, string | undefined> = { ...BASE_SOURCE }\n const second: Record<string, string | undefined> = { ...BASE_SOURCE }\n const variables = { ...envSchema?.server, ...envSchema?.client }\n for (const [key, schema] of Object.entries(variables)) {\n const values = acceptedValues(key, schema, configured[key])\n first[key] = values[0]\n second[key] = values[1] ?? values[0]\n }\n return [first, second]\n}\n"]}
package/dist/env.d.ts CHANGED
@@ -22,21 +22,18 @@ export type EnvConfigInput = {
22
22
  /** Value-free metadata per key, keyed by the key's own name. */
23
23
  meta?: Record<string, EnvVarMeta>;
24
24
  };
25
- export type BunderstackRole = 'all' | 'web' | 'worker';
26
25
  /** Vars bunderstack itself consumes, always validated. */
27
26
  export type BaseEnv = {
28
27
  NODE_ENV?: string;
29
28
  DATABASE_URL: string;
30
29
  DATABASE_AUTH_TOKEN?: string;
31
30
  AUTH_SECRET: string;
32
- REDIS_URL?: string;
33
31
  RESEND_API_KEY?: string;
34
32
  BUNDERSTACK_MESSAGING_CONFIG?: string;
35
33
  BUNDERSTACK_BLUEPRINT_PATH?: string;
36
34
  BUNDERHOST_ENVIRONMENT_ID?: string;
37
35
  /** Deployed commit SHA, injected by the platform. Reported by readiness. */
38
36
  BUNDERSTACK_REVISION?: string;
39
- BUNDERSTACK_ROLE: BunderstackRole;
40
37
  };
41
38
  type InferVars<T> = T extends Record<string, StandardSchemaV1> ? {
42
39
  [K in keyof T]: StandardSchemaV1.InferOutput<T[K]>;
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;;;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,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,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,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,CAqCpB;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,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,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,4BAA4B,CAAC,EAAE,MAAM,CAAA;IACrC,0BAA0B,CAAC,EAAE,MAAM,CAAA;IACnC,yBAAyB,CAAC,EAAE,MAAM,CAAA;IAClC,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAC9B,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,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,CA2BpB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,SAAS,cAAc,EACzD,SAAS,EAAE,IAAI,GACd,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAsB3B"}