@webstir-io/webstir-backend 0.1.15 → 0.1.16
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.
- package/README.md +106 -79
- package/dist/add.d.ts +59 -0
- package/dist/add.js +626 -0
- package/dist/build/artifacts.d.ts +115 -1
- package/dist/build/artifacts.js +4 -4
- package/dist/build/entries.js +1 -1
- package/dist/build/pipeline.d.ts +33 -1
- package/dist/build/pipeline.js +307 -65
- package/dist/cache/diff.js +9 -8
- package/dist/cache/reporters.js +1 -1
- package/dist/deploy-cli.d.ts +2 -0
- package/dist/deploy-cli.js +86 -0
- package/dist/diagnostics/summary.js +2 -2
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/manifest/pipeline.js +103 -32
- package/dist/provider.js +35 -17
- package/dist/runtime/bun.d.ts +51 -0
- package/dist/runtime/bun.js +499 -0
- package/dist/runtime/core.d.ts +141 -0
- package/dist/runtime/core.js +316 -0
- package/dist/runtime/deploy-backend.d.ts +20 -0
- package/dist/runtime/deploy-backend.js +175 -0
- package/dist/runtime/deploy-shared.d.ts +43 -0
- package/dist/runtime/deploy-shared.js +75 -0
- package/dist/runtime/deploy-static.d.ts +2 -0
- package/dist/runtime/deploy-static.js +161 -0
- package/dist/runtime/deploy.d.ts +3 -0
- package/dist/runtime/deploy.js +91 -0
- package/dist/runtime/forms.d.ts +73 -0
- package/dist/runtime/forms.js +236 -0
- package/dist/runtime/request-hooks.d.ts +47 -0
- package/dist/runtime/request-hooks.js +102 -0
- package/dist/runtime/session-metadata.d.ts +13 -0
- package/dist/runtime/session-metadata.js +98 -0
- package/dist/runtime/session-runtime.d.ts +28 -0
- package/dist/runtime/session-runtime.js +180 -0
- package/dist/runtime/session.d.ts +83 -0
- package/dist/runtime/session.js +396 -0
- package/dist/runtime/views.d.ts +74 -0
- package/dist/runtime/views.js +221 -0
- package/dist/scaffold/assets.js +25 -21
- package/dist/testing/context.js +1 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +100 -56
- package/dist/utils/bun.d.ts +2 -0
- package/dist/utils/bun.js +13 -0
- package/dist/watch.d.ts +13 -1
- package/dist/watch.js +345 -97
- package/dist/workspace.d.ts +8 -0
- package/dist/workspace.js +44 -3
- package/package.json +49 -14
- package/scripts/publish.sh +2 -92
- package/scripts/smoke.mjs +282 -107
- package/scripts/update-contract.sh +12 -10
- package/src/add.ts +964 -0
- package/src/build/artifacts.ts +49 -46
- package/src/build/entries.ts +12 -12
- package/src/build/pipeline.ts +779 -403
- package/src/cache/diff.ts +111 -105
- package/src/cache/reporters.ts +26 -26
- package/src/deploy-cli.ts +111 -0
- package/src/diagnostics/summary.ts +28 -22
- package/src/index.ts +11 -0
- package/src/manifest/pipeline.ts +328 -215
- package/src/provider.ts +115 -98
- package/src/runtime/bun.ts +793 -0
- package/src/runtime/core.ts +598 -0
- package/src/runtime/deploy-backend.ts +239 -0
- package/src/runtime/deploy-shared.ts +136 -0
- package/src/runtime/deploy-static.ts +191 -0
- package/src/runtime/deploy.ts +143 -0
- package/src/runtime/forms.ts +364 -0
- package/src/runtime/request-hooks.ts +165 -0
- package/src/runtime/session-metadata.ts +135 -0
- package/src/runtime/session-runtime.ts +267 -0
- package/src/runtime/session.ts +642 -0
- package/src/runtime/views.ts +385 -0
- package/src/scaffold/assets.ts +77 -73
- package/src/testing/context.js +8 -9
- package/src/testing/context.ts +9 -9
- package/src/testing/index.d.ts +14 -3
- package/src/testing/index.js +254 -175
- package/src/testing/index.ts +298 -195
- package/src/testing/types.d.ts +18 -19
- package/src/testing/types.ts +18 -18
- package/src/utils/bun.ts +26 -0
- package/src/watch.ts +503 -99
- package/src/workspace.ts +59 -3
- package/templates/backend/.env.example +15 -0
- package/templates/backend/auth/adapter.ts +335 -36
- package/templates/backend/db/connection.ts +190 -65
- package/templates/backend/db/migrate.ts +149 -43
- package/templates/backend/db/types.d.ts +1 -1
- package/templates/backend/env.ts +132 -20
- package/templates/backend/functions/hello/index.ts +1 -2
- package/templates/backend/index.ts +15 -508
- package/templates/backend/jobs/nightly/index.ts +1 -1
- package/templates/backend/jobs/runtime.ts +24 -11
- package/templates/backend/jobs/scheduler.ts +208 -46
- package/templates/backend/module.ts +227 -13
- package/templates/backend/observability/logger.ts +2 -12
- package/templates/backend/observability/metrics.ts +8 -5
- package/templates/backend/session/sqlite.ts +152 -0
- package/templates/backend/session/store.ts +45 -0
- package/templates/backend/tsconfig.json +1 -1
- package/tests/add.test.js +327 -0
- package/tests/authAdapter.test.js +315 -0
- package/tests/bundlerParity.test.js +217 -0
- package/tests/cacheReporter.test.js +10 -10
- package/tests/dbConnection.test.js +209 -0
- package/tests/deploy.test.js +357 -0
- package/tests/envLoader.test.js +271 -17
- package/tests/integration.test.js +2432 -3
- package/tests/jobsScheduler.test.js +253 -0
- package/tests/manifest.test.js +287 -12
- package/tests/migrationRunner.test.js +249 -0
- package/tests/sessionScaffoldStore.test.js +752 -0
- package/tests/sessionStore.test.js +490 -0
- package/tests/testing.test.js +252 -0
- package/tests/watch.test.js +192 -32
- package/tsconfig.json +3 -10
- package/templates/backend/server/fastify.ts +0 -288
package/dist/build/pipeline.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { mkdir, rm } from 'node:fs/promises';
|
|
3
5
|
import { spawn } from 'node:child_process';
|
|
4
6
|
import { performance } from 'node:perf_hooks';
|
|
5
7
|
import { build as esbuild, context as esbuildContext } from 'esbuild';
|
|
@@ -14,57 +16,76 @@ if (typeof process !== 'undefined' && typeof process.once === 'function') {
|
|
|
14
16
|
export async function runBackendBuildPipeline(options) {
|
|
15
17
|
const { sourceRoot, buildRoot, tsconfigPath, diagnostics, incremental, mode } = options;
|
|
16
18
|
const env = options.env ?? {};
|
|
19
|
+
const bundler = options.bundler ??
|
|
20
|
+
resolveBackendBundler({
|
|
21
|
+
env,
|
|
22
|
+
incremental,
|
|
23
|
+
diagnostics,
|
|
24
|
+
});
|
|
17
25
|
console.info(`[webstir-backend] ${mode}:tsc start`);
|
|
18
26
|
if (shouldTypeCheck(mode, env)) {
|
|
19
27
|
await runTypeCheck(tsconfigPath, env, diagnostics);
|
|
20
28
|
}
|
|
21
29
|
else {
|
|
22
|
-
diagnostics.push({
|
|
30
|
+
diagnostics.push({
|
|
31
|
+
severity: 'info',
|
|
32
|
+
message: '[webstir-backend] type-check skipped by WEBSTIR_BACKEND_TYPECHECK',
|
|
33
|
+
});
|
|
23
34
|
}
|
|
24
35
|
console.info(`[webstir-backend] ${mode}:tsc done`);
|
|
25
36
|
const entryPoints = await discoverEntryPoints(sourceRoot);
|
|
26
37
|
if (entryPoints.length === 0) {
|
|
27
38
|
diagnostics.push({
|
|
28
39
|
severity: 'warn',
|
|
29
|
-
message: `No backend entry points found under ${sourceRoot} (expected index.* or functions/*/index.* or jobs/*/index.*)
|
|
40
|
+
message: `No backend entry points found under ${sourceRoot} (expected index.* or functions/*/index.* or jobs/*/index.*).`,
|
|
30
41
|
});
|
|
31
42
|
}
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
if (bundler === 'bun') {
|
|
44
|
+
await resetBuildRoot(buildRoot);
|
|
45
|
+
}
|
|
46
|
+
console.info(`[webstir-backend] ${mode}:${bundler} start`);
|
|
47
|
+
const outputs = bundler === 'bun'
|
|
48
|
+
? await runBunBuild({
|
|
49
|
+
sourceRoot,
|
|
50
|
+
buildRoot,
|
|
51
|
+
tsconfigPath,
|
|
52
|
+
mode,
|
|
53
|
+
env,
|
|
54
|
+
incremental,
|
|
55
|
+
diagnostics,
|
|
56
|
+
entryPoints,
|
|
57
|
+
})
|
|
58
|
+
: await runEsbuild({
|
|
59
|
+
sourceRoot,
|
|
60
|
+
buildRoot,
|
|
61
|
+
tsconfigPath,
|
|
62
|
+
mode,
|
|
63
|
+
env,
|
|
64
|
+
incremental,
|
|
65
|
+
diagnostics,
|
|
66
|
+
entryPoints,
|
|
67
|
+
});
|
|
68
|
+
console.info(`[webstir-backend] ${mode}:${bundler} done`);
|
|
69
|
+
await ensureModuleDefinitionBuild({
|
|
34
70
|
sourceRoot,
|
|
35
71
|
buildRoot,
|
|
36
72
|
tsconfigPath,
|
|
37
73
|
mode,
|
|
38
74
|
env,
|
|
39
|
-
incremental,
|
|
40
75
|
diagnostics,
|
|
41
|
-
entryPoints
|
|
42
76
|
});
|
|
43
|
-
console.info(`[webstir-backend] ${mode}:esbuild done`);
|
|
44
|
-
const moduleSource = await discoverModuleDefinitionSource(sourceRoot);
|
|
45
|
-
if (moduleSource) {
|
|
46
|
-
await buildModuleDefinition({
|
|
47
|
-
sourceFile: moduleSource,
|
|
48
|
-
sourceRoot,
|
|
49
|
-
buildRoot,
|
|
50
|
-
tsconfigPath,
|
|
51
|
-
mode,
|
|
52
|
-
env,
|
|
53
|
-
diagnostics
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
77
|
const includePublishSourcemaps = mode === 'publish' && shouldEmitPublishSourcemaps(env);
|
|
57
78
|
return {
|
|
58
79
|
entryPoints,
|
|
59
80
|
outputs,
|
|
60
|
-
includePublishSourcemaps
|
|
81
|
+
includePublishSourcemaps,
|
|
61
82
|
};
|
|
62
83
|
}
|
|
63
84
|
async function runTypeCheck(tsconfigPath, env, diagnostics) {
|
|
64
85
|
if (!existsSync(tsconfigPath)) {
|
|
65
86
|
diagnostics.push({
|
|
66
87
|
severity: 'warn',
|
|
67
|
-
message: `TypeScript config not found at ${tsconfigPath}; skipping type-check
|
|
88
|
+
message: `TypeScript config not found at ${tsconfigPath}; skipping type-check.`,
|
|
68
89
|
});
|
|
69
90
|
return;
|
|
70
91
|
}
|
|
@@ -73,8 +94,8 @@ async function runTypeCheck(tsconfigPath, env, diagnostics) {
|
|
|
73
94
|
stdio: 'pipe',
|
|
74
95
|
env: {
|
|
75
96
|
...process.env,
|
|
76
|
-
...env
|
|
77
|
-
}
|
|
97
|
+
...env,
|
|
98
|
+
},
|
|
78
99
|
});
|
|
79
100
|
let stdout = '';
|
|
80
101
|
let stderr = '';
|
|
@@ -85,9 +106,12 @@ async function runTypeCheck(tsconfigPath, env, diagnostics) {
|
|
|
85
106
|
stderr += chunk.toString();
|
|
86
107
|
});
|
|
87
108
|
child.on('error', (err) => {
|
|
88
|
-
const code =
|
|
109
|
+
const code = err.code;
|
|
89
110
|
if (code === 'ENOENT') {
|
|
90
|
-
diagnostics.push({
|
|
111
|
+
diagnostics.push({
|
|
112
|
+
severity: 'warn',
|
|
113
|
+
message: 'TypeScript compiler (tsc) not found in PATH; skipping type-check.',
|
|
114
|
+
});
|
|
91
115
|
resolve();
|
|
92
116
|
return;
|
|
93
117
|
}
|
|
@@ -101,7 +125,7 @@ async function runTypeCheck(tsconfigPath, env, diagnostics) {
|
|
|
101
125
|
diagnostics.push({
|
|
102
126
|
severity: 'error',
|
|
103
127
|
message: `Type checking failed (exit code ${code}).`,
|
|
104
|
-
file: tsconfigPath
|
|
128
|
+
file: tsconfigPath,
|
|
105
129
|
});
|
|
106
130
|
if (stderr.trim()) {
|
|
107
131
|
diagnostics.push({ severity: 'error', message: stderr.trim() });
|
|
@@ -124,6 +148,32 @@ export function shouldTypeCheck(mode, env) {
|
|
|
124
148
|
}
|
|
125
149
|
return true;
|
|
126
150
|
}
|
|
151
|
+
export function resolveBackendBundler(options) {
|
|
152
|
+
const requestedBundler = normalizeBackendBundler(options.env?.WEBSTIR_BACKEND_BUNDLER);
|
|
153
|
+
if (requestedBundler !== 'bun') {
|
|
154
|
+
return 'esbuild';
|
|
155
|
+
}
|
|
156
|
+
if (options.incremental) {
|
|
157
|
+
options.diagnostics?.push({
|
|
158
|
+
severity: 'info',
|
|
159
|
+
message: '[webstir-backend] WEBSTIR_BACKEND_BUNDLER=bun requested for an incremental build; falling back to esbuild.',
|
|
160
|
+
});
|
|
161
|
+
return 'esbuild';
|
|
162
|
+
}
|
|
163
|
+
if (!getBunBuild()) {
|
|
164
|
+
options.diagnostics?.push({
|
|
165
|
+
severity: 'warn',
|
|
166
|
+
message: '[webstir-backend] WEBSTIR_BACKEND_BUNDLER=bun requested outside a Bun runtime; falling back to esbuild.',
|
|
167
|
+
});
|
|
168
|
+
return 'esbuild';
|
|
169
|
+
}
|
|
170
|
+
return 'bun';
|
|
171
|
+
}
|
|
172
|
+
function normalizeBackendBundler(rawBundler) {
|
|
173
|
+
return typeof rawBundler === 'string' && rawBundler.trim().toLowerCase() === 'bun'
|
|
174
|
+
? 'bun'
|
|
175
|
+
: 'esbuild';
|
|
176
|
+
}
|
|
127
177
|
function shouldEmitPublishSourcemaps(env) {
|
|
128
178
|
const flag = env?.WEBSTIR_BACKEND_SOURCEMAPS;
|
|
129
179
|
if (typeof flag !== 'string') {
|
|
@@ -139,36 +189,74 @@ async function discoverModuleDefinitionSource(sourceRoot) {
|
|
|
139
189
|
cwd: sourceRoot,
|
|
140
190
|
absolute: true,
|
|
141
191
|
nodir: true,
|
|
142
|
-
dot: false
|
|
192
|
+
dot: false,
|
|
143
193
|
});
|
|
144
194
|
if (matches.length > 0) {
|
|
145
195
|
return matches[0];
|
|
146
196
|
}
|
|
147
197
|
}
|
|
198
|
+
const indexPatterns = ['index.{ts,tsx,js,mjs}'];
|
|
199
|
+
for (const pattern of indexPatterns) {
|
|
200
|
+
const matches = await glob(pattern, {
|
|
201
|
+
cwd: sourceRoot,
|
|
202
|
+
absolute: true,
|
|
203
|
+
nodir: true,
|
|
204
|
+
dot: false,
|
|
205
|
+
});
|
|
206
|
+
for (const candidate of matches) {
|
|
207
|
+
if (await sourceExportsNamedModuleDefinition(candidate)) {
|
|
208
|
+
return candidate;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
148
212
|
return undefined;
|
|
149
213
|
}
|
|
214
|
+
async function sourceExportsNamedModuleDefinition(sourceFile) {
|
|
215
|
+
try {
|
|
216
|
+
const source = await readFile(sourceFile, 'utf8');
|
|
217
|
+
return (/\bexport\s+(const|let|var)\s+module\b/.test(source) ||
|
|
218
|
+
/\bexport\s*\{\s*module\b/.test(source));
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
export async function ensureModuleDefinitionBuild(options) {
|
|
225
|
+
const moduleSource = await discoverModuleDefinitionSource(options.sourceRoot);
|
|
226
|
+
if (!moduleSource) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
await buildModuleDefinition({
|
|
230
|
+
sourceFile: moduleSource,
|
|
231
|
+
sourceRoot: options.sourceRoot,
|
|
232
|
+
buildRoot: options.buildRoot,
|
|
233
|
+
tsconfigPath: options.tsconfigPath,
|
|
234
|
+
mode: options.mode,
|
|
235
|
+
env: options.env,
|
|
236
|
+
diagnostics: options.diagnostics,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
150
239
|
async function buildModuleDefinition(options) {
|
|
151
|
-
const { sourceFile,
|
|
240
|
+
const { sourceFile, buildRoot, tsconfigPath, mode, env, diagnostics } = options;
|
|
152
241
|
const isProduction = mode === 'publish';
|
|
153
242
|
const nodeEnv = env?.NODE_ENV ?? (isProduction ? 'production' : 'development');
|
|
154
243
|
const emitPublishSourcemaps = isProduction && shouldEmitPublishSourcemaps(env);
|
|
155
244
|
const define = {
|
|
156
|
-
'process.env.NODE_ENV': JSON.stringify(nodeEnv)
|
|
245
|
+
'process.env.NODE_ENV': JSON.stringify(nodeEnv),
|
|
157
246
|
};
|
|
158
247
|
try {
|
|
159
248
|
await esbuild({
|
|
160
249
|
entryPoints: [sourceFile],
|
|
161
|
-
bundle:
|
|
250
|
+
bundle: true,
|
|
251
|
+
packages: 'external',
|
|
162
252
|
platform: 'node',
|
|
163
253
|
target: 'node20',
|
|
164
254
|
format: 'esm',
|
|
165
255
|
sourcemap: isProduction ? emitPublishSourcemaps : true,
|
|
166
|
-
|
|
167
|
-
outbase: sourceRoot,
|
|
168
|
-
entryNames: '[dir]/[name]',
|
|
256
|
+
outfile: path.join(buildRoot, 'module.js'),
|
|
169
257
|
tsconfig: existsSync(tsconfigPath) ? tsconfigPath : undefined,
|
|
170
258
|
define,
|
|
171
|
-
logLevel: 'silent'
|
|
259
|
+
logLevel: 'silent',
|
|
172
260
|
});
|
|
173
261
|
}
|
|
174
262
|
catch (error) {
|
|
@@ -194,22 +282,43 @@ export async function buildSupportFile(options) {
|
|
|
194
282
|
const nodeEnv = env?.NODE_ENV ?? (isProduction ? 'production' : 'development');
|
|
195
283
|
const emitPublishSourcemaps = isProduction && shouldEmitPublishSourcemaps(env);
|
|
196
284
|
const define = {
|
|
197
|
-
'process.env.NODE_ENV': JSON.stringify(nodeEnv)
|
|
285
|
+
'process.env.NODE_ENV': JSON.stringify(nodeEnv),
|
|
198
286
|
};
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
target: 'node20',
|
|
205
|
-
format: 'esm',
|
|
206
|
-
sourcemap: isProduction ? emitPublishSourcemaps : true,
|
|
207
|
-
outdir: buildRoot,
|
|
208
|
-
outbase: sourceRoot,
|
|
209
|
-
tsconfig: existsSync(tsconfigPath) ? tsconfigPath : undefined,
|
|
210
|
-
define,
|
|
211
|
-
logLevel: 'silent'
|
|
287
|
+
const bundler = options.bundler ??
|
|
288
|
+
resolveBackendBundler({
|
|
289
|
+
env,
|
|
290
|
+
incremental: false,
|
|
291
|
+
diagnostics,
|
|
212
292
|
});
|
|
293
|
+
const diagMax = readDiagMax(env, 50);
|
|
294
|
+
try {
|
|
295
|
+
if (bundler === 'bun') {
|
|
296
|
+
const result = await runBunCompile({
|
|
297
|
+
entryPoints: [sourceFile],
|
|
298
|
+
sourceRoot,
|
|
299
|
+
buildRoot,
|
|
300
|
+
tsconfigPath,
|
|
301
|
+
define,
|
|
302
|
+
minify: false,
|
|
303
|
+
includeSourceMaps: isProduction ? emitPublishSourcemaps : true,
|
|
304
|
+
});
|
|
305
|
+
ensureBunCompileSucceeded(result, diagnostics, `${mode}:bun:support`, diagMax);
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
await esbuild({
|
|
309
|
+
entryPoints: [sourceFile],
|
|
310
|
+
bundle: false,
|
|
311
|
+
platform: 'node',
|
|
312
|
+
target: 'node20',
|
|
313
|
+
format: 'esm',
|
|
314
|
+
sourcemap: isProduction ? emitPublishSourcemaps : true,
|
|
315
|
+
outdir: buildRoot,
|
|
316
|
+
outbase: sourceRoot,
|
|
317
|
+
tsconfig: existsSync(tsconfigPath) ? tsconfigPath : undefined,
|
|
318
|
+
define,
|
|
319
|
+
logLevel: 'silent',
|
|
320
|
+
});
|
|
321
|
+
}
|
|
213
322
|
}
|
|
214
323
|
catch (error) {
|
|
215
324
|
if (error instanceof Error) {
|
|
@@ -234,13 +343,9 @@ async function runEsbuild(options) {
|
|
|
234
343
|
}
|
|
235
344
|
const entrySignature = useIncremental ? createEntrySignature(entryPoints) : undefined;
|
|
236
345
|
const nodeEnv = env?.NODE_ENV ?? (isProduction ? 'production' : 'development');
|
|
237
|
-
const diagMax = (
|
|
238
|
-
const raw = env?.WEBSTIR_BACKEND_DIAG_MAX;
|
|
239
|
-
const n = typeof raw === 'string' ? parseInt(raw, 10) : NaN;
|
|
240
|
-
return Number.isFinite(n) && n > 0 ? n : 50;
|
|
241
|
-
})();
|
|
346
|
+
const diagMax = readDiagMax(env, 50);
|
|
242
347
|
const define = {
|
|
243
|
-
'process.env.NODE_ENV': JSON.stringify(nodeEnv)
|
|
348
|
+
'process.env.NODE_ENV': JSON.stringify(nodeEnv),
|
|
244
349
|
};
|
|
245
350
|
const emitPublishSourcemaps = isProduction && shouldEmitPublishSourcemaps(env);
|
|
246
351
|
const start = performance.now();
|
|
@@ -267,7 +372,7 @@ async function runEsbuild(options) {
|
|
|
267
372
|
tsconfig: existsSync(tsconfigPath) ? tsconfigPath : undefined,
|
|
268
373
|
define,
|
|
269
374
|
logLevel: 'silent',
|
|
270
|
-
metafile: true
|
|
375
|
+
metafile: true,
|
|
271
376
|
});
|
|
272
377
|
}
|
|
273
378
|
else if (useIncremental && incrementalKey && entrySignature) {
|
|
@@ -292,11 +397,11 @@ async function runEsbuild(options) {
|
|
|
292
397
|
tsconfig: existsSync(tsconfigPath) ? tsconfigPath : undefined,
|
|
293
398
|
define,
|
|
294
399
|
logLevel: 'silent',
|
|
295
|
-
metafile: true
|
|
400
|
+
metafile: true,
|
|
296
401
|
});
|
|
297
402
|
incrementalBuildCache.set(incrementalKey, {
|
|
298
403
|
entrySignature,
|
|
299
|
-
context: ctx
|
|
404
|
+
context: ctx,
|
|
300
405
|
});
|
|
301
406
|
result = await ctx.rebuild();
|
|
302
407
|
}
|
|
@@ -317,7 +422,7 @@ async function runEsbuild(options) {
|
|
|
317
422
|
tsconfig: existsSync(tsconfigPath) ? tsconfigPath : undefined,
|
|
318
423
|
define,
|
|
319
424
|
logLevel: 'silent',
|
|
320
|
-
metafile: true
|
|
425
|
+
metafile: true,
|
|
321
426
|
});
|
|
322
427
|
}
|
|
323
428
|
const warnCount = result.warnings?.length ?? 0;
|
|
@@ -327,14 +432,14 @@ async function runEsbuild(options) {
|
|
|
327
432
|
if (warnCount > diagMax) {
|
|
328
433
|
diagnostics.push({
|
|
329
434
|
severity: 'info',
|
|
330
|
-
message: `[webstir-backend] ${isProduction ? 'publish:esbuild' : `${mode}:esbuild`} ... ${warnCount - diagMax} more warning(s) omitted
|
|
435
|
+
message: `[webstir-backend] ${isProduction ? 'publish:esbuild' : `${mode}:esbuild`} ... ${warnCount - diagMax} more warning(s) omitted`,
|
|
331
436
|
});
|
|
332
437
|
}
|
|
333
438
|
const end = performance.now();
|
|
334
439
|
const reuseSuffix = reusedIncremental ? ' (incremental)' : '';
|
|
335
440
|
diagnostics.push({
|
|
336
441
|
severity: 'info',
|
|
337
|
-
message: `[webstir-backend] ${isProduction ? 'publish:esbuild' : `${mode}:esbuild`} 0 error(s), ${warnCount} warning(s) in ${(end - start).toFixed(1)}ms${reuseSuffix}
|
|
442
|
+
message: `[webstir-backend] ${isProduction ? 'publish:esbuild' : `${mode}:esbuild`} 0 error(s), ${warnCount} warning(s) in ${(end - start).toFixed(1)}ms${reuseSuffix}`,
|
|
338
443
|
});
|
|
339
444
|
return collectOutputSizes(result.metafile, buildRoot);
|
|
340
445
|
}
|
|
@@ -353,12 +458,21 @@ async function runEsbuild(options) {
|
|
|
353
458
|
diagnostics.push({ severity: 'warn', message: formatEsbuildMessage(w) });
|
|
354
459
|
}
|
|
355
460
|
if (errs.length > diagMax) {
|
|
356
|
-
diagnostics.push({
|
|
461
|
+
diagnostics.push({
|
|
462
|
+
severity: 'info',
|
|
463
|
+
message: `[webstir-backend] ${mode}:esbuild ... ${errs.length - diagMax} more error(s) omitted`,
|
|
464
|
+
});
|
|
357
465
|
}
|
|
358
466
|
if (warns.length > diagMax) {
|
|
359
|
-
diagnostics.push({
|
|
467
|
+
diagnostics.push({
|
|
468
|
+
severity: 'info',
|
|
469
|
+
message: `[webstir-backend] ${mode}:esbuild ... ${warns.length - diagMax} more warning(s) omitted`,
|
|
470
|
+
});
|
|
360
471
|
}
|
|
361
|
-
diagnostics.push({
|
|
472
|
+
diagnostics.push({
|
|
473
|
+
severity: 'info',
|
|
474
|
+
message: `[webstir-backend] ${mode}:esbuild ${errs.length} error(s), ${warns.length} warning(s) in ${(end - start).toFixed(1)}ms`,
|
|
475
|
+
});
|
|
362
476
|
}
|
|
363
477
|
else if (error instanceof Error) {
|
|
364
478
|
diagnostics.push({ severity: 'error', message: error.message });
|
|
@@ -369,6 +483,130 @@ async function runEsbuild(options) {
|
|
|
369
483
|
throw new Error('esbuild failed.');
|
|
370
484
|
}
|
|
371
485
|
}
|
|
486
|
+
async function runBunBuild(options) {
|
|
487
|
+
const { sourceRoot, buildRoot, tsconfigPath, mode, env, diagnostics, entryPoints } = options;
|
|
488
|
+
const isProduction = mode === 'publish';
|
|
489
|
+
if (!entryPoints || entryPoints.length === 0) {
|
|
490
|
+
return undefined;
|
|
491
|
+
}
|
|
492
|
+
const nodeEnv = env?.NODE_ENV ?? (isProduction ? 'production' : 'development');
|
|
493
|
+
const diagMax = readDiagMax(env, 50);
|
|
494
|
+
const define = {
|
|
495
|
+
'process.env.NODE_ENV': JSON.stringify(nodeEnv),
|
|
496
|
+
};
|
|
497
|
+
const emitPublishSourcemaps = isProduction && shouldEmitPublishSourcemaps(env);
|
|
498
|
+
const start = performance.now();
|
|
499
|
+
try {
|
|
500
|
+
const result = await runBunCompile({
|
|
501
|
+
entryPoints,
|
|
502
|
+
sourceRoot,
|
|
503
|
+
buildRoot,
|
|
504
|
+
tsconfigPath,
|
|
505
|
+
define,
|
|
506
|
+
minify: isProduction,
|
|
507
|
+
includeSourceMaps: isProduction ? emitPublishSourcemaps : true,
|
|
508
|
+
});
|
|
509
|
+
const { errorCount, warningCount } = pushBunLogs(diagnostics, result.logs, `${mode}:bun`, diagMax);
|
|
510
|
+
const end = performance.now();
|
|
511
|
+
diagnostics.push({
|
|
512
|
+
severity: 'info',
|
|
513
|
+
message: `[webstir-backend] ${mode}:bun ${errorCount} error(s), ${warningCount} warning(s) in ${(end - start).toFixed(1)}ms`,
|
|
514
|
+
});
|
|
515
|
+
if (!result.success || errorCount > 0) {
|
|
516
|
+
throw new Error('bun build failed.');
|
|
517
|
+
}
|
|
518
|
+
return collectBunOutputSizes(result.outputs, buildRoot);
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
const end = performance.now();
|
|
522
|
+
if (error instanceof Error) {
|
|
523
|
+
diagnostics.push({ severity: 'error', message: error.message });
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
diagnostics.push({ severity: 'error', message: String(error) });
|
|
527
|
+
}
|
|
528
|
+
diagnostics.push({
|
|
529
|
+
severity: 'info',
|
|
530
|
+
message: `[webstir-backend] ${mode}:bun failed in ${(end - start).toFixed(1)}ms`,
|
|
531
|
+
});
|
|
532
|
+
throw new Error('bun build failed.');
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function runBunCompile(options) {
|
|
536
|
+
const build = getBunBuild();
|
|
537
|
+
if (!build) {
|
|
538
|
+
throw new Error('Bun.build() is not available in the current runtime.');
|
|
539
|
+
}
|
|
540
|
+
return await build({
|
|
541
|
+
entrypoints: [...options.entryPoints],
|
|
542
|
+
root: options.sourceRoot,
|
|
543
|
+
outdir: options.buildRoot,
|
|
544
|
+
target: 'node',
|
|
545
|
+
format: 'esm',
|
|
546
|
+
splitting: false,
|
|
547
|
+
packages: 'external',
|
|
548
|
+
minify: options.minify,
|
|
549
|
+
sourcemap: options.includeSourceMaps ? 'linked' : 'none',
|
|
550
|
+
tsconfig: existsSync(options.tsconfigPath) ? options.tsconfigPath : undefined,
|
|
551
|
+
define: options.define,
|
|
552
|
+
throw: false,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
function ensureBunCompileSucceeded(result, diagnostics, label, diagMax) {
|
|
556
|
+
const { errorCount } = pushBunLogs(diagnostics, result.logs, label, diagMax);
|
|
557
|
+
if (!result.success || errorCount > 0) {
|
|
558
|
+
throw new Error('bun build failed.');
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
function pushBunLogs(diagnostics, logs, label, diagMax) {
|
|
562
|
+
const entries = Array.isArray(logs) ? logs : [];
|
|
563
|
+
const errorLogs = entries.filter((log) => log.level === 'error');
|
|
564
|
+
const warningLogs = entries.filter((log) => log.level === 'warning');
|
|
565
|
+
for (const entry of errorLogs.slice(0, diagMax)) {
|
|
566
|
+
diagnostics.push({ severity: 'error', message: formatEsbuildMessage(entry) });
|
|
567
|
+
}
|
|
568
|
+
for (const entry of warningLogs.slice(0, diagMax)) {
|
|
569
|
+
diagnostics.push({ severity: 'warn', message: formatEsbuildMessage(entry) });
|
|
570
|
+
}
|
|
571
|
+
if (errorLogs.length > diagMax) {
|
|
572
|
+
diagnostics.push({
|
|
573
|
+
severity: 'info',
|
|
574
|
+
message: `[webstir-backend] ${label} ... ${errorLogs.length - diagMax} more error(s) omitted`,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
if (warningLogs.length > diagMax) {
|
|
578
|
+
diagnostics.push({
|
|
579
|
+
severity: 'info',
|
|
580
|
+
message: `[webstir-backend] ${label} ... ${warningLogs.length - diagMax} more warning(s) omitted`,
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
return {
|
|
584
|
+
errorCount: errorLogs.length,
|
|
585
|
+
warningCount: warningLogs.length,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function getBunBuild() {
|
|
589
|
+
const runtime = globalThis;
|
|
590
|
+
const build = runtime.Bun?.build;
|
|
591
|
+
return typeof build === 'function' ? build.bind(runtime.Bun) : undefined;
|
|
592
|
+
}
|
|
593
|
+
function collectBunOutputSizes(outputs, buildRoot) {
|
|
594
|
+
const collected = {};
|
|
595
|
+
for (const output of outputs ?? []) {
|
|
596
|
+
const rel = path.relative(buildRoot, output.path);
|
|
597
|
+
collected[rel] = typeof output.size === 'number' ? output.size : 0;
|
|
598
|
+
}
|
|
599
|
+
return collected;
|
|
600
|
+
}
|
|
601
|
+
async function resetBuildRoot(buildRoot) {
|
|
602
|
+
await rm(buildRoot, { recursive: true, force: true });
|
|
603
|
+
await mkdir(buildRoot, { recursive: true });
|
|
604
|
+
}
|
|
605
|
+
function readDiagMax(env, fallback) {
|
|
606
|
+
const raw = env?.WEBSTIR_BACKEND_DIAG_MAX;
|
|
607
|
+
const n = typeof raw === 'string' ? parseInt(raw, 10) : NaN;
|
|
608
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
609
|
+
}
|
|
372
610
|
function createIncrementalKey(mode, buildRoot) {
|
|
373
611
|
return `${mode}:${path.resolve(buildRoot)}`;
|
|
374
612
|
}
|
|
@@ -414,8 +652,12 @@ function isEsbuildFailure(error) {
|
|
|
414
652
|
return typeof error === 'object' && error !== null && ('errors' in error || 'warnings' in error);
|
|
415
653
|
}
|
|
416
654
|
export function formatEsbuildMessage(msg) {
|
|
417
|
-
const text = typeof msg
|
|
418
|
-
|
|
655
|
+
const text = typeof msg.message === 'string'
|
|
656
|
+
? msg.message
|
|
657
|
+
: typeof msg.text === 'string'
|
|
658
|
+
? msg.text
|
|
659
|
+
: String(msg);
|
|
660
|
+
const loc = msg.location ?? msg.position;
|
|
419
661
|
if (loc && typeof loc.file === 'string') {
|
|
420
662
|
const position = typeof loc.line === 'number' ? `${loc.line}:${loc.column ?? 1}` : '1:1';
|
|
421
663
|
return `${loc.file}:${position} ${text}`;
|
package/dist/cache/diff.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
|
+
import { readTextFile, writeTextFile } from '../utils/bun.js';
|
|
3
4
|
export async function persistAndDiffOutputs(workspaceRoot, _buildRoot, outputs, env, diagnostics, mode) {
|
|
4
5
|
if (!outputs)
|
|
5
6
|
return;
|
|
@@ -10,7 +11,7 @@ export async function persistAndDiffOutputs(workspaceRoot, _buildRoot, outputs,
|
|
|
10
11
|
await mkdir(webstirDir, { recursive: true });
|
|
11
12
|
let previous = {};
|
|
12
13
|
try {
|
|
13
|
-
const raw = await
|
|
14
|
+
const raw = await readTextFile(cachePath);
|
|
14
15
|
previous = JSON.parse(raw);
|
|
15
16
|
}
|
|
16
17
|
catch {
|
|
@@ -29,10 +30,10 @@ export async function persistAndDiffOutputs(workspaceRoot, _buildRoot, outputs,
|
|
|
29
30
|
const removedInfo = removed.length > 0 ? `, removed=${removed.length}` : '';
|
|
30
31
|
diagnostics.push({
|
|
31
32
|
severity: 'info',
|
|
32
|
-
message: `[webstir-backend] ${mode}:changed ${changed.length} file(s): ${list}${omitted}${removedInfo}
|
|
33
|
+
message: `[webstir-backend] ${mode}:changed ${changed.length} file(s): ${list}${omitted}${removedInfo}`,
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
|
-
await
|
|
36
|
+
await writeTextFile(cachePath, JSON.stringify(outputs, null, 2));
|
|
36
37
|
}
|
|
37
38
|
catch {
|
|
38
39
|
// ignore cache errors
|
|
@@ -45,15 +46,15 @@ export async function persistAndDiffManifest(workspaceRoot, manifest, env, diagn
|
|
|
45
46
|
const cachePath = path.join(webstirDir, 'backend-manifest-digest.json');
|
|
46
47
|
await mkdir(webstirDir, { recursive: true });
|
|
47
48
|
const routeKeys = Array.isArray(manifest.routes)
|
|
48
|
-
? manifest.routes.map((
|
|
49
|
+
? manifest.routes.map((route) => `${(route.method ?? '').toUpperCase()} ${route.path ?? ''}`)
|
|
49
50
|
: [];
|
|
50
51
|
const viewPaths = Array.isArray(manifest.views)
|
|
51
|
-
? manifest.views.map((
|
|
52
|
+
? manifest.views.map((view) => `${view.path ?? ''}`)
|
|
52
53
|
: [];
|
|
53
54
|
const caps = Array.isArray(manifest.capabilities) ? manifest.capabilities : [];
|
|
54
55
|
let previous;
|
|
55
56
|
try {
|
|
56
|
-
const raw = await
|
|
57
|
+
const raw = await readTextFile(cachePath);
|
|
57
58
|
previous = JSON.parse(raw);
|
|
58
59
|
}
|
|
59
60
|
catch {
|
|
@@ -101,7 +102,7 @@ export async function persistAndDiffManifest(workspaceRoot, manifest, env, diagn
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
const digest = { routes: routeKeys, views: viewPaths, capabilities: caps };
|
|
104
|
-
await
|
|
105
|
+
await writeTextFile(cachePath, JSON.stringify(digest, null, 2));
|
|
105
106
|
}
|
|
106
107
|
catch {
|
|
107
108
|
// ignore cache errors
|
package/dist/cache/reporters.js
CHANGED