@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/scaffold/assets.js
CHANGED
|
@@ -7,71 +7,75 @@ export async function getBackendScaffoldAssets() {
|
|
|
7
7
|
return [
|
|
8
8
|
{
|
|
9
9
|
sourcePath: path.join(templatesRoot, 'tsconfig.json'),
|
|
10
|
-
targetPath: path.join('src', 'backend', 'tsconfig.json')
|
|
10
|
+
targetPath: path.join('src', 'backend', 'tsconfig.json'),
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
sourcePath: path.join(templatesRoot, 'index.ts'),
|
|
14
|
-
targetPath: path.join('src', 'backend', 'index.ts')
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
sourcePath: path.join(templatesRoot, 'server', 'fastify.ts'),
|
|
18
|
-
targetPath: path.join('src', 'backend', 'server', 'fastify.ts')
|
|
14
|
+
targetPath: path.join('src', 'backend', 'index.ts'),
|
|
19
15
|
},
|
|
20
16
|
{
|
|
21
17
|
sourcePath: path.join(templatesRoot, 'module.ts'),
|
|
22
|
-
targetPath: path.join('src', 'backend', 'module.ts')
|
|
18
|
+
targetPath: path.join('src', 'backend', 'module.ts'),
|
|
23
19
|
},
|
|
24
20
|
{
|
|
25
21
|
sourcePath: path.join(templatesRoot, 'auth', 'adapter.ts'),
|
|
26
|
-
targetPath: path.join('src', 'backend', 'auth', 'adapter.ts')
|
|
22
|
+
targetPath: path.join('src', 'backend', 'auth', 'adapter.ts'),
|
|
27
23
|
},
|
|
28
24
|
{
|
|
29
25
|
sourcePath: path.join(templatesRoot, 'observability', 'logger.ts'),
|
|
30
|
-
targetPath: path.join('src', 'backend', 'observability', 'logger.ts')
|
|
26
|
+
targetPath: path.join('src', 'backend', 'observability', 'logger.ts'),
|
|
31
27
|
},
|
|
32
28
|
{
|
|
33
29
|
sourcePath: path.join(templatesRoot, 'observability', 'metrics.ts'),
|
|
34
|
-
targetPath: path.join('src', 'backend', 'observability', 'metrics.ts')
|
|
30
|
+
targetPath: path.join('src', 'backend', 'observability', 'metrics.ts'),
|
|
35
31
|
},
|
|
36
32
|
{
|
|
37
33
|
sourcePath: path.join(templatesRoot, 'env.ts'),
|
|
38
|
-
targetPath: path.join('src', 'backend', 'env.ts')
|
|
34
|
+
targetPath: path.join('src', 'backend', 'env.ts'),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
sourcePath: path.join(templatesRoot, 'session', 'store.ts'),
|
|
38
|
+
targetPath: path.join('src', 'backend', 'session', 'store.ts'),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
sourcePath: path.join(templatesRoot, 'session', 'sqlite.ts'),
|
|
42
|
+
targetPath: path.join('src', 'backend', 'session', 'sqlite.ts'),
|
|
39
43
|
},
|
|
40
44
|
{
|
|
41
45
|
sourcePath: path.join(templatesRoot, 'functions', 'hello', 'index.ts'),
|
|
42
|
-
targetPath: path.join('src', 'backend', 'functions', 'hello', 'index.ts')
|
|
46
|
+
targetPath: path.join('src', 'backend', 'functions', 'hello', 'index.ts'),
|
|
43
47
|
},
|
|
44
48
|
{
|
|
45
49
|
sourcePath: path.join(templatesRoot, 'jobs', 'nightly', 'index.ts'),
|
|
46
|
-
targetPath: path.join('src', 'backend', 'jobs', 'nightly', 'index.ts')
|
|
50
|
+
targetPath: path.join('src', 'backend', 'jobs', 'nightly', 'index.ts'),
|
|
47
51
|
},
|
|
48
52
|
{
|
|
49
53
|
sourcePath: path.join(templatesRoot, 'jobs', 'runtime.ts'),
|
|
50
|
-
targetPath: path.join('src', 'backend', 'jobs', 'runtime.ts')
|
|
54
|
+
targetPath: path.join('src', 'backend', 'jobs', 'runtime.ts'),
|
|
51
55
|
},
|
|
52
56
|
{
|
|
53
57
|
sourcePath: path.join(templatesRoot, 'jobs', 'scheduler.ts'),
|
|
54
|
-
targetPath: path.join('src', 'backend', 'jobs', 'scheduler.ts')
|
|
58
|
+
targetPath: path.join('src', 'backend', 'jobs', 'scheduler.ts'),
|
|
55
59
|
},
|
|
56
60
|
{
|
|
57
61
|
sourcePath: path.join(templatesRoot, 'db', 'connection.ts'),
|
|
58
|
-
targetPath: path.join('src', 'backend', 'db', 'connection.ts')
|
|
62
|
+
targetPath: path.join('src', 'backend', 'db', 'connection.ts'),
|
|
59
63
|
},
|
|
60
64
|
{
|
|
61
65
|
sourcePath: path.join(templatesRoot, 'db', 'migrate.ts'),
|
|
62
|
-
targetPath: path.join('src', 'backend', 'db', 'migrate.ts')
|
|
66
|
+
targetPath: path.join('src', 'backend', 'db', 'migrate.ts'),
|
|
63
67
|
},
|
|
64
68
|
{
|
|
65
69
|
sourcePath: path.join(templatesRoot, 'db', 'migrations', '0001-example.ts'),
|
|
66
|
-
targetPath: path.join('src', 'backend', 'db', 'migrations', '0001-example.ts')
|
|
70
|
+
targetPath: path.join('src', 'backend', 'db', 'migrations', '0001-example.ts'),
|
|
67
71
|
},
|
|
68
72
|
{
|
|
69
73
|
sourcePath: path.join(templatesRoot, 'db', 'types.d.ts'),
|
|
70
|
-
targetPath: path.join('src', 'backend', 'db', 'types.d.ts')
|
|
74
|
+
targetPath: path.join('src', 'backend', 'db', 'types.d.ts'),
|
|
71
75
|
},
|
|
72
76
|
{
|
|
73
77
|
sourcePath: path.join(templatesRoot, '.env.example'),
|
|
74
|
-
targetPath: path.join('.env.example')
|
|
75
|
-
}
|
|
78
|
+
targetPath: path.join('.env.example'),
|
|
79
|
+
},
|
|
76
80
|
];
|
|
77
81
|
}
|
package/dist/testing/context.js
CHANGED
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getBackendTestContext, setBackendTestContext } from './context.js';
|
|
2
2
|
import type { BackendTestCallback, BackendTestHarness, BackendTestHarnessOptions } from './types.js';
|
|
3
|
-
export type { BackendTestCallback, BackendTestContext, BackendTestHarness, BackendTestHarnessOptions } from './types.js';
|
|
3
|
+
export type { BackendTestCallback, BackendTestContext, BackendTestHarness, BackendTestHarnessOptions, } from './types.js';
|
|
4
4
|
export { getBackendTestContext, setBackendTestContext };
|
|
5
5
|
export declare function createBackendTestHarness(options?: BackendTestHarnessOptions): Promise<BackendTestHarness>;
|
|
6
6
|
export declare function backendTest(name: string, callback: BackendTestCallback): void;
|
package/dist/testing/index.js
CHANGED
|
@@ -2,67 +2,68 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { once } from 'node:events';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { readFile } from 'node:fs/promises';
|
|
5
|
-
import net from 'node:net';
|
|
6
5
|
import path from 'node:path';
|
|
7
6
|
import { getBackendTestContext, setBackendTestContext } from './context.js';
|
|
7
|
+
import { resolveWorkspaceRoot } from '../workspace.js';
|
|
8
8
|
const DEFAULT_PORT = 4100;
|
|
9
9
|
const DEFAULT_READY_TEXT = 'API server running';
|
|
10
10
|
const DEFAULT_READY_TIMEOUT_MS = 15_000;
|
|
11
11
|
export { getBackendTestContext, setBackendTestContext };
|
|
12
12
|
export async function createBackendTestHarness(options = {}) {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
const resolvedEnv = { ...process.env, ...(options.env ?? {}) };
|
|
14
|
+
const workspaceRoot = resolveWorkspaceRoot({
|
|
15
|
+
workspaceRoot: options.workspaceRoot,
|
|
16
|
+
env: resolvedEnv,
|
|
17
|
+
});
|
|
18
|
+
const buildRoot = resolveWorkspacePath(workspaceRoot, options.buildRoot ??
|
|
19
|
+
resolvedEnv.WEBSTIR_BACKEND_BUILD_ROOT ??
|
|
20
|
+
path.join(workspaceRoot, 'build', 'backend'));
|
|
21
|
+
const entry = resolveWorkspacePath(workspaceRoot, options.entry ?? resolvedEnv.WEBSTIR_BACKEND_TEST_ENTRY ?? path.join(buildRoot, 'index.js'));
|
|
22
|
+
const manifestPath = resolveWorkspacePath(workspaceRoot, options.manifestPath ??
|
|
23
|
+
resolvedEnv.WEBSTIR_BACKEND_TEST_MANIFEST ??
|
|
24
|
+
path.join(workspaceRoot, '.webstir', 'backend-manifest.json'));
|
|
25
|
+
const readyText = options.readyText ?? resolvedEnv.WEBSTIR_BACKEND_TEST_READY ?? DEFAULT_READY_TEXT;
|
|
26
|
+
const readyTimeoutMs = options.readyTimeoutMs ??
|
|
27
|
+
readInt(resolvedEnv.WEBSTIR_BACKEND_TEST_READY_TIMEOUT, DEFAULT_READY_TIMEOUT_MS);
|
|
21
28
|
if (!existsSync(entry)) {
|
|
22
29
|
throw new Error(`Backend test entry not found at ${entry}. Run the backend build before executing backend tests.`);
|
|
23
30
|
}
|
|
24
|
-
const requestedPort = options.port ?? readInt(process.env.WEBSTIR_BACKEND_TEST_PORT, DEFAULT_PORT);
|
|
25
|
-
const port = await findOpenPort(requestedPort);
|
|
26
|
-
const env = createRuntimeEnv({
|
|
27
|
-
workspaceRoot,
|
|
28
|
-
port,
|
|
29
|
-
overrides: options.env
|
|
30
|
-
});
|
|
31
31
|
const manifest = await loadManifest(manifestPath);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
const requestedPort = options.port ?? readInt(resolvedEnv.WEBSTIR_BACKEND_TEST_PORT, DEFAULT_PORT);
|
|
33
|
+
const { child, env, port } = await startBackendTestProcess({
|
|
34
|
+
workspaceRoot,
|
|
35
|
+
entry,
|
|
36
|
+
requestedPort,
|
|
37
|
+
baseEnv: resolvedEnv,
|
|
38
|
+
overrides: options.env,
|
|
39
|
+
readyText,
|
|
40
|
+
readyTimeoutMs,
|
|
36
41
|
});
|
|
37
|
-
try {
|
|
38
|
-
await waitForReady(child, readyText, readyTimeoutMs);
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
await stopProcess(child);
|
|
42
|
-
throw error;
|
|
43
|
-
}
|
|
44
42
|
const baseUrl = new URL(env.API_BASE_URL ?? `http://127.0.0.1:${port}`);
|
|
45
43
|
const context = {
|
|
46
44
|
baseUrl: baseUrl.toString(),
|
|
47
45
|
url: baseUrl,
|
|
48
46
|
port,
|
|
49
47
|
manifest,
|
|
50
|
-
routes: Array.isArray(manifest?.routes)
|
|
48
|
+
routes: Array.isArray(manifest?.routes)
|
|
49
|
+
? manifest.routes
|
|
50
|
+
: [],
|
|
51
51
|
env,
|
|
52
52
|
request: async (pathOrUrl = '/', init) => {
|
|
53
53
|
const target = toUrl(baseUrl, pathOrUrl);
|
|
54
54
|
return await fetch(target, init);
|
|
55
|
-
}
|
|
55
|
+
},
|
|
56
56
|
};
|
|
57
57
|
return {
|
|
58
58
|
context,
|
|
59
59
|
async stop() {
|
|
60
60
|
await stopProcess(child);
|
|
61
|
-
}
|
|
61
|
+
},
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
export function backendTest(name, callback) {
|
|
65
|
-
const globalTest = globalThis
|
|
65
|
+
const globalTest = globalThis
|
|
66
|
+
.test;
|
|
66
67
|
if (typeof globalTest !== 'function') {
|
|
67
68
|
throw new Error('backendTest() requires the @webstir-io/webstir-testing runtime.');
|
|
68
69
|
}
|
|
@@ -89,28 +90,42 @@ function readInt(value, fallback) {
|
|
|
89
90
|
const parsed = Number.parseInt(value, 10);
|
|
90
91
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
91
92
|
}
|
|
92
|
-
async function
|
|
93
|
-
let candidate =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
throw new Error(`Unable to find an open port for backend tests (tried starting at ${start}).`);
|
|
102
|
-
}
|
|
103
|
-
function isPortAvailable(port) {
|
|
104
|
-
return new Promise((resolve) => {
|
|
105
|
-
const server = net.createServer();
|
|
106
|
-
server.once('error', () => {
|
|
107
|
-
server.close(() => resolve(false));
|
|
93
|
+
async function startBackendTestProcess(options) {
|
|
94
|
+
let candidate = options.requestedPort;
|
|
95
|
+
let lastError = null;
|
|
96
|
+
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
97
|
+
const env = createRuntimeEnv({
|
|
98
|
+
workspaceRoot: options.workspaceRoot,
|
|
99
|
+
port: candidate,
|
|
100
|
+
baseEnv: options.baseEnv,
|
|
101
|
+
overrides: options.overrides,
|
|
108
102
|
});
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
const child = spawn(process.execPath, [options.entry], {
|
|
104
|
+
cwd: options.workspaceRoot,
|
|
105
|
+
env,
|
|
106
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
111
107
|
});
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
const output = captureChildOutput(child);
|
|
109
|
+
try {
|
|
110
|
+
await waitForReady(child, options.readyText, options.readyTimeoutMs);
|
|
111
|
+
output.stop();
|
|
112
|
+
return { child, env, port: candidate };
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
const captured = output.read();
|
|
116
|
+
output.stop();
|
|
117
|
+
await stopProcess(child);
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
const failure = new Error(`Backend test server did not become ready on port ${candidate}.\nstdout:\n${captured.stdout}\nstderr:\n${captured.stderr}\nerror:\n${message}`);
|
|
120
|
+
if (attempt < 9 && indicatesPortInUse(captured.stdout, captured.stderr, message)) {
|
|
121
|
+
lastError = failure;
|
|
122
|
+
candidate += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
throw failure;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw lastError ?? new Error('Backend test server did not become ready.');
|
|
114
129
|
}
|
|
115
130
|
function createRuntimeEnv(options) {
|
|
116
131
|
const overrides = {};
|
|
@@ -119,16 +134,45 @@ function createRuntimeEnv(options) {
|
|
|
119
134
|
overrides[key] = value;
|
|
120
135
|
}
|
|
121
136
|
}
|
|
122
|
-
const baseUrl = overrides.API_BASE_URL ??
|
|
137
|
+
const baseUrl = overrides.API_BASE_URL ?? options.baseEnv.API_BASE_URL ?? `http://127.0.0.1:${options.port}`;
|
|
123
138
|
return {
|
|
124
|
-
...
|
|
139
|
+
...options.baseEnv,
|
|
125
140
|
...overrides,
|
|
126
141
|
PORT: String(options.port),
|
|
127
142
|
API_BASE_URL: baseUrl,
|
|
128
|
-
NODE_ENV: overrides.NODE_ENV ??
|
|
143
|
+
NODE_ENV: overrides.NODE_ENV ?? options.baseEnv.NODE_ENV ?? 'test',
|
|
129
144
|
WORKSPACE_ROOT: options.workspaceRoot,
|
|
130
|
-
WEBSTIR_BACKEND_TEST_RUN: '1'
|
|
145
|
+
WEBSTIR_BACKEND_TEST_RUN: '1',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function captureChildOutput(child) {
|
|
149
|
+
let stdout = '';
|
|
150
|
+
let stderr = '';
|
|
151
|
+
const onStdout = (chunk) => {
|
|
152
|
+
stdout += chunk.toString();
|
|
131
153
|
};
|
|
154
|
+
const onStderr = (chunk) => {
|
|
155
|
+
stderr += chunk.toString();
|
|
156
|
+
};
|
|
157
|
+
child.stdout?.on('data', onStdout);
|
|
158
|
+
child.stderr?.on('data', onStderr);
|
|
159
|
+
return {
|
|
160
|
+
stop() {
|
|
161
|
+
child.stdout?.off('data', onStdout);
|
|
162
|
+
child.stderr?.off('data', onStderr);
|
|
163
|
+
},
|
|
164
|
+
read() {
|
|
165
|
+
return { stdout, stderr };
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function indicatesPortInUse(stdout, stderr, message) {
|
|
170
|
+
return [stdout, stderr, message].some((value) => value.includes('EADDRINUSE') ||
|
|
171
|
+
value.includes('address already in use') ||
|
|
172
|
+
value.includes('Failed to listen at 127.0.0.1'));
|
|
173
|
+
}
|
|
174
|
+
function resolveWorkspacePath(workspaceRoot, value) {
|
|
175
|
+
return path.isAbsolute(value) ? path.resolve(value) : path.resolve(workspaceRoot, value);
|
|
132
176
|
}
|
|
133
177
|
async function loadManifest(manifestPath) {
|
|
134
178
|
try {
|
|
@@ -148,7 +192,7 @@ async function waitForReady(child, readyText, timeoutMs) {
|
|
|
148
192
|
.split('|')
|
|
149
193
|
.map((token) => token.trim())
|
|
150
194
|
.filter(Boolean);
|
|
151
|
-
const readinessMatches = (line) =>
|
|
195
|
+
const readinessMatches = (line) => normalized.length === 0 ? line.length > 0 : normalized.some((token) => line.includes(token));
|
|
152
196
|
await new Promise((resolve, reject) => {
|
|
153
197
|
const cleanup = () => {
|
|
154
198
|
child.stdout?.off('data', onStdout);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function getBunRuntime() {
|
|
2
|
+
const runtime = globalThis;
|
|
3
|
+
if (typeof runtime.Bun?.file === 'function' && typeof runtime.Bun?.write === 'function') {
|
|
4
|
+
return runtime.Bun;
|
|
5
|
+
}
|
|
6
|
+
throw new Error('[webstir-backend] Bun runtime is required for package-level IO.');
|
|
7
|
+
}
|
|
8
|
+
export async function readTextFile(filePath) {
|
|
9
|
+
return await getBunRuntime().file(filePath).text();
|
|
10
|
+
}
|
|
11
|
+
export async function writeTextFile(filePath, contents) {
|
|
12
|
+
await getBunRuntime().write(filePath, contents);
|
|
13
|
+
}
|
package/dist/watch.d.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
export interface WatchHandle {
|
|
2
2
|
stop(): Promise<void>;
|
|
3
3
|
}
|
|
4
|
+
export interface BackendWatchEvent {
|
|
5
|
+
readonly type: 'build-start' | 'build-complete';
|
|
6
|
+
readonly succeeded?: boolean;
|
|
7
|
+
readonly errorCount?: number;
|
|
8
|
+
readonly warningCount?: number;
|
|
9
|
+
readonly durationMs?: number;
|
|
10
|
+
readonly bunBenchmarkSucceeded?: boolean;
|
|
11
|
+
readonly bunBenchmarkErrorCount?: number;
|
|
12
|
+
readonly bunBenchmarkWarningCount?: number;
|
|
13
|
+
readonly bunBenchmarkDurationMs?: number;
|
|
14
|
+
}
|
|
4
15
|
export interface StartWatchOptions {
|
|
5
|
-
readonly workspaceRoot
|
|
16
|
+
readonly workspaceRoot?: string;
|
|
6
17
|
readonly env?: Record<string, string | undefined>;
|
|
18
|
+
readonly onEvent?: (event: BackendWatchEvent) => void | Promise<void>;
|
|
7
19
|
}
|
|
8
20
|
export declare function startBackendWatch(options: StartWatchOptions): Promise<WatchHandle>;
|