@webstir-io/webstir 0.1.1 → 0.1.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.
- package/README.md +13 -0
- package/assets/deployment/docker/.dockerignore +7 -0
- package/assets/deployment/docker/Dockerfile +17 -0
- package/assets/deployment/docker/README.md +44 -0
- package/assets/deployment/docker/example.env +3 -0
- package/assets/features/client_nav/client_nav.ts +369 -264
- package/assets/features/client_nav/document_navigation.ts +344 -0
- package/assets/features/client_nav/form_enhancement.ts +275 -0
- package/assets/templates/api/src/backend/index.ts +71 -10
- package/assets/templates/api/src/backend/tsconfig.json +6 -1
- package/assets/templates/full/src/backend/index.ts +71 -10
- package/assets/templates/full/src/backend/module.ts +515 -0
- package/assets/templates/full/src/backend/tests/progressive-enhancement.test.ts +180 -0
- package/assets/templates/full/src/backend/tsconfig.json +6 -1
- package/assets/templates/full/src/frontend/app/scripts/features/client-nav.ts +574 -0
- package/assets/templates/full/src/frontend/app/scripts/features/document-navigation.ts +344 -0
- package/assets/templates/full/src/frontend/app/scripts/features/form-enhancement.ts +275 -0
- package/assets/templates/full/src/frontend/pages/home/index.css +8 -0
- package/assets/templates/full/src/frontend/pages/home/index.html +6 -1
- package/assets/templates/full/src/frontend/pages/home/tests/home.test.ts +12 -2
- package/assets/templates/spa/src/frontend/pages/home/tests/home.test.ts +10 -2
- package/package.json +31 -13
- package/scripts/check-feature-projections.mjs +87 -0
- package/scripts/check-full-demo-sync.mjs +89 -0
- package/scripts/check-package-install.mjs +537 -0
- package/scripts/check-standalone-install.mjs +221 -0
- package/scripts/pack-standalone.mjs +52 -28
- package/scripts/publish.sh +9 -0
- package/scripts/run-tests.mjs +99 -0
- package/scripts/sync-assets.mjs +175 -17
- package/src/add-backend-compat.ts +628 -0
- package/src/add-backend.ts +155 -27
- package/src/add.ts +111 -4
- package/src/agent.ts +393 -0
- package/src/api-watch.ts +7 -4
- package/src/backend-inspect.ts +70 -2
- package/src/backend-runtime.ts +22 -14
- package/src/build.ts +1 -3
- package/src/bun-generated-frontend-watch.ts +209 -0
- package/src/bun-globals.d.ts +23 -0
- package/src/bun-spa-document.ts +310 -0
- package/src/bun-spa-routes.ts +159 -0
- package/src/bun-spa-watch.ts +29 -0
- package/src/bun-ssg-watch.ts +304 -0
- package/src/cli.ts +381 -50
- package/src/compile-tests.ts +37 -29
- package/src/dev-server.ts +214 -143
- package/src/doctor.ts +164 -0
- package/src/enable-assets.ts +18 -1
- package/src/enable.ts +133 -41
- package/src/execute.ts +28 -4
- package/src/external-workspace.ts +178 -0
- package/src/format.ts +296 -17
- package/src/frontend-inspect.ts +32 -0
- package/src/frontend-watch.ts +27 -102
- package/src/full-watch.ts +13 -18
- package/src/index.ts +7 -0
- package/src/init-assets.ts +41 -11
- package/src/init.ts +85 -71
- package/src/inspect.ts +112 -0
- package/src/mcp/run-cli-json.ts +46 -0
- package/src/mcp/server.ts +307 -0
- package/src/operations.ts +176 -0
- package/src/providers.ts +20 -18
- package/src/refresh.ts +29 -3
- package/src/repair.ts +110 -43
- package/src/runtime-filter.ts +41 -0
- package/src/runtime.ts +1 -1
- package/src/smoke.ts +48 -16
- package/src/test.ts +54 -16
- package/src/testing-runtime.ts +273 -0
- package/src/types.ts +1 -4
- package/src/watch-events.ts +46 -17
- package/src/watch.ts +5 -1
- package/src/workspace-watcher.ts +10 -6
- package/src/workspace.ts +4 -2
- package/src/watch-daemon-client.ts +0 -171
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { access, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { constants as fsConstants } from 'node:fs';
|
|
7
|
+
|
|
8
|
+
const scriptRoot = path.dirname(new URL(import.meta.url).pathname);
|
|
9
|
+
const packageRoot = path.resolve(scriptRoot, '..');
|
|
10
|
+
const artifactsRoot = path.join(packageRoot, 'artifacts');
|
|
11
|
+
const repoRoot = path.resolve(packageRoot, '..', '..');
|
|
12
|
+
const moduleContractPackageRoot = path.join(repoRoot, 'packages', 'contracts', 'module-contract');
|
|
13
|
+
const testingContractPackageRoot = path.join(repoRoot, 'packages', 'contracts', 'testing-contract');
|
|
14
|
+
const backendPackageRoot = path.join(repoRoot, 'packages', 'tooling', 'webstir-backend');
|
|
15
|
+
const frontendPackageRoot = path.join(repoRoot, 'packages', 'tooling', 'webstir-frontend');
|
|
16
|
+
const testingPackageRoot = path.join(repoRoot, 'packages', 'tooling', 'webstir-testing');
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'webstir-standalone-install-'));
|
|
20
|
+
const consumerRoot = path.join(tempRoot, 'consumer');
|
|
21
|
+
const existingArtifacts = await listArtifacts(artifactsRoot);
|
|
22
|
+
const existingBackendTarballs = await listArtifacts(backendPackageRoot);
|
|
23
|
+
let tarballPath = null;
|
|
24
|
+
let backendTarballPath = null;
|
|
25
|
+
let keepWorkspace = false;
|
|
26
|
+
const localPackageSpecs = {
|
|
27
|
+
'@webstir-io/module-contract': `file:${moduleContractPackageRoot}`,
|
|
28
|
+
'@webstir-io/testing-contract': `file:${testingContractPackageRoot}`,
|
|
29
|
+
'@webstir-io/webstir-backend': `file:${backendPackageRoot}`,
|
|
30
|
+
'@webstir-io/webstir-frontend': `file:${frontendPackageRoot}`,
|
|
31
|
+
'@webstir-io/webstir-testing': `file:${testingPackageRoot}`,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await mkdir(consumerRoot, { recursive: true });
|
|
36
|
+
await writeConsumerManifest(consumerRoot, localPackageSpecs);
|
|
37
|
+
|
|
38
|
+
tarballPath = await packStandaloneTarball();
|
|
39
|
+
console.log(`[webstir][install-smoke] tarball ${tarballPath}`);
|
|
40
|
+
backendTarballPath = await packPackageTarball(backendPackageRoot);
|
|
41
|
+
console.log(`[webstir][install-smoke] backend tarball ${backendTarballPath}`);
|
|
42
|
+
|
|
43
|
+
await run(['bun', 'add', tarballPath], consumerRoot);
|
|
44
|
+
|
|
45
|
+
const cliPath = path.join(consumerRoot, 'node_modules', '.bin', 'webstir');
|
|
46
|
+
await assertExists(cliPath, 'installed CLI binary');
|
|
47
|
+
|
|
48
|
+
await run([cliPath, 'init', 'full', 'site'], consumerRoot);
|
|
49
|
+
|
|
50
|
+
const workspaceRoot = path.join(consumerRoot, 'site');
|
|
51
|
+
await assertExists(
|
|
52
|
+
path.join(workspaceRoot, 'package.json'),
|
|
53
|
+
'scaffolded workspace package.json',
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
await writeWorkspaceLocalPackageOverrides(workspaceRoot, localPackageSpecs);
|
|
57
|
+
await run(['bun', 'install'], workspaceRoot);
|
|
58
|
+
await installLocalBackendTarball(workspaceRoot, backendTarballPath);
|
|
59
|
+
await assertExists(
|
|
60
|
+
path.join(workspaceRoot, 'node_modules', '.bin', 'webstir-backend-deploy'),
|
|
61
|
+
'workspace deploy runner binary',
|
|
62
|
+
);
|
|
63
|
+
await run([cliPath, 'build', '--workspace', workspaceRoot], workspaceRoot);
|
|
64
|
+
|
|
65
|
+
await assertExists(
|
|
66
|
+
path.join(workspaceRoot, 'build', 'backend', 'index.js'),
|
|
67
|
+
'backend build output',
|
|
68
|
+
);
|
|
69
|
+
await assertExists(path.join(workspaceRoot, 'build', 'frontend'), 'frontend build output');
|
|
70
|
+
await assertExists(
|
|
71
|
+
path.join(workspaceRoot, '.webstir', 'backend-outputs.json'),
|
|
72
|
+
'backend outputs cache',
|
|
73
|
+
);
|
|
74
|
+
await assertExists(
|
|
75
|
+
path.join(workspaceRoot, '.webstir', 'backend-manifest-digest.json'),
|
|
76
|
+
'backend manifest digest',
|
|
77
|
+
);
|
|
78
|
+
await assertExists(
|
|
79
|
+
path.join(workspaceRoot, '.webstir', 'frontend-manifest.json'),
|
|
80
|
+
'frontend manifest',
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
console.log('[webstir][install-smoke] standalone install smoke passed');
|
|
84
|
+
} catch (error) {
|
|
85
|
+
keepWorkspace = (process.env.WEBSTIR_INSTALL_SMOKE_KEEP ?? '').toLowerCase() === '1';
|
|
86
|
+
if (keepWorkspace) {
|
|
87
|
+
console.error(`[webstir][install-smoke] preserving failed workspace at ${tempRoot}`);
|
|
88
|
+
}
|
|
89
|
+
throw error;
|
|
90
|
+
} finally {
|
|
91
|
+
if (tarballPath) {
|
|
92
|
+
await cleanupGeneratedTarball(tarballPath, existingArtifacts);
|
|
93
|
+
}
|
|
94
|
+
if (backendTarballPath) {
|
|
95
|
+
await cleanupGeneratedTarball(backendTarballPath, existingBackendTarballs);
|
|
96
|
+
}
|
|
97
|
+
if (!keepWorkspace) {
|
|
98
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function packStandaloneTarball() {
|
|
104
|
+
const output = await run(['bun', 'run', 'pack:standalone'], packageRoot);
|
|
105
|
+
const tarballPath = output
|
|
106
|
+
.split(/\r?\n/)
|
|
107
|
+
.map((line) => line.trim())
|
|
108
|
+
.filter((line) => line.endsWith('-standalone.tgz'))
|
|
109
|
+
.at(-1);
|
|
110
|
+
|
|
111
|
+
if (!tarballPath) {
|
|
112
|
+
throw new Error(`Unable to determine standalone tarball path from pack output:\n${output}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return path.resolve(packageRoot, tarballPath);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function packPackageTarball(root) {
|
|
119
|
+
const output = await run(['bun', 'pm', 'pack'], root);
|
|
120
|
+
const tarballPath = output
|
|
121
|
+
.split(/\r?\n/)
|
|
122
|
+
.map((line) => line.trim())
|
|
123
|
+
.filter((line) => line.endsWith('.tgz'))
|
|
124
|
+
.at(-1);
|
|
125
|
+
|
|
126
|
+
if (!tarballPath) {
|
|
127
|
+
throw new Error(`Unable to determine package tarball path from pack output:\n${output}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return path.resolve(root, tarballPath);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function installLocalBackendTarball(workspaceRoot, backendTarballPath) {
|
|
134
|
+
await run(['bun', 'remove', '@webstir-io/webstir-backend'], workspaceRoot);
|
|
135
|
+
await run(['bun', 'add', backendTarballPath], workspaceRoot);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function writeConsumerManifest(consumerRoot, localPackageSpecs) {
|
|
139
|
+
await writeFile(
|
|
140
|
+
path.join(consumerRoot, 'package.json'),
|
|
141
|
+
`${JSON.stringify(
|
|
142
|
+
{
|
|
143
|
+
name: 'webstir-standalone-smoke',
|
|
144
|
+
private: true,
|
|
145
|
+
overrides: localPackageSpecs,
|
|
146
|
+
},
|
|
147
|
+
null,
|
|
148
|
+
2,
|
|
149
|
+
)}\n`,
|
|
150
|
+
'utf8',
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function writeWorkspaceLocalPackageOverrides(workspaceRoot, localPackageSpecs) {
|
|
155
|
+
const packageJsonPath = path.join(workspaceRoot, 'package.json');
|
|
156
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8'));
|
|
157
|
+
packageJson.overrides = {
|
|
158
|
+
...(packageJson.overrides ?? {}),
|
|
159
|
+
...localPackageSpecs,
|
|
160
|
+
};
|
|
161
|
+
await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function assertExists(targetPath, label) {
|
|
165
|
+
try {
|
|
166
|
+
await access(targetPath, fsConstants.F_OK);
|
|
167
|
+
} catch {
|
|
168
|
+
throw new Error(`Expected ${label} at ${targetPath}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function listArtifacts(root) {
|
|
173
|
+
try {
|
|
174
|
+
return new Set(await readdir(root));
|
|
175
|
+
} catch {
|
|
176
|
+
return new Set();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function cleanupGeneratedTarball(tarballPath, existingArtifacts) {
|
|
181
|
+
const tarballName = path.basename(tarballPath);
|
|
182
|
+
if (existingArtifacts.has(tarballName)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
await rm(tarballPath, { force: true });
|
|
187
|
+
|
|
188
|
+
const remaining = await listArtifacts(artifactsRoot);
|
|
189
|
+
if (remaining.size === 0) {
|
|
190
|
+
await rm(artifactsRoot, { recursive: true, force: true });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function run(command, cwd) {
|
|
195
|
+
const proc = Bun.spawn({
|
|
196
|
+
cmd: command,
|
|
197
|
+
cwd,
|
|
198
|
+
stdout: 'pipe',
|
|
199
|
+
stderr: 'pipe',
|
|
200
|
+
env: process.env,
|
|
201
|
+
});
|
|
202
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
203
|
+
new Response(proc.stdout).text(),
|
|
204
|
+
new Response(proc.stderr).text(),
|
|
205
|
+
proc.exited,
|
|
206
|
+
]);
|
|
207
|
+
|
|
208
|
+
if (exitCode !== 0) {
|
|
209
|
+
const detail = [stdout.trim(), stderr.trim()].filter(Boolean).join('\n');
|
|
210
|
+
throw new Error(
|
|
211
|
+
`Command failed (${exitCode}): ${command.join(' ')}${detail ? `\n${detail}` : ''}`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return stdout;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
main().catch((error) => {
|
|
219
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
220
|
+
process.exit(1);
|
|
221
|
+
});
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
-
import { spawnSync } from 'node:child_process';
|
|
6
5
|
import { cp, mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
7
6
|
|
|
8
7
|
const scriptRoot = path.dirname(new URL(import.meta.url).pathname);
|
|
@@ -12,15 +11,18 @@ const outputRoot = path.join(packageRoot, 'artifacts');
|
|
|
12
11
|
|
|
13
12
|
const internalPackages = [
|
|
14
13
|
['@webstir-io/module-contract', path.join(repoRoot, 'packages', 'contracts', 'module-contract')],
|
|
15
|
-
[
|
|
14
|
+
[
|
|
15
|
+
'@webstir-io/testing-contract',
|
|
16
|
+
path.join(repoRoot, 'packages', 'contracts', 'testing-contract'),
|
|
17
|
+
],
|
|
16
18
|
['@webstir-io/webstir-frontend', path.join(repoRoot, 'packages', 'tooling', 'webstir-frontend')],
|
|
17
19
|
['@webstir-io/webstir-backend', path.join(repoRoot, 'packages', 'tooling', 'webstir-backend')],
|
|
18
20
|
['@webstir-io/webstir-testing', path.join(repoRoot, 'packages', 'tooling', 'webstir-testing')],
|
|
19
21
|
];
|
|
20
22
|
|
|
21
23
|
async function main() {
|
|
22
|
-
run('bun',
|
|
23
|
-
run('node',
|
|
24
|
+
await run(['bun', 'run', 'build:deps'], packageRoot);
|
|
25
|
+
await run(['node', 'scripts/sync-assets.mjs'], packageRoot);
|
|
24
26
|
|
|
25
27
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'webstir-standalone-pack-'));
|
|
26
28
|
const tarballRoot = path.join(tempRoot, 'tarballs');
|
|
@@ -42,6 +44,7 @@ async function main() {
|
|
|
42
44
|
|
|
43
45
|
const packageJson = JSON.parse(await readFile(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
44
46
|
const originalDependencies = { ...packageJson.dependencies };
|
|
47
|
+
const originalOverrides = packageJson.overrides ? { ...packageJson.overrides } : null;
|
|
45
48
|
packageJson.dependencies = Object.fromEntries(
|
|
46
49
|
Object.entries(originalDependencies).map(([name, version]) => {
|
|
47
50
|
const tarballPath = tarballs.get(name);
|
|
@@ -50,19 +53,38 @@ async function main() {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
return [name, `file:${path.relative(stageRoot, tarballPath).split(path.sep).join('/')}`];
|
|
53
|
-
})
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
packageJson.overrides = Object.fromEntries(
|
|
59
|
+
[...tarballs].map(([name, tarballPath]) => [
|
|
60
|
+
name,
|
|
61
|
+
`file:${path.relative(stageRoot, tarballPath).split(path.sep).join('/')}`,
|
|
62
|
+
]),
|
|
54
63
|
);
|
|
55
64
|
packageJson.bundledDependencies = Object.keys(packageJson.dependencies);
|
|
56
65
|
packageJson.files = ['assets', 'src', 'README.md'];
|
|
57
66
|
delete packageJson.scripts;
|
|
58
67
|
delete packageJson.devDependencies;
|
|
59
68
|
|
|
60
|
-
await writeFile(
|
|
69
|
+
await writeFile(
|
|
70
|
+
path.join(stageRoot, 'package.json'),
|
|
71
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
72
|
+
'utf8',
|
|
73
|
+
);
|
|
61
74
|
|
|
62
|
-
run('bun',
|
|
75
|
+
await run(['bun', 'install'], stageRoot);
|
|
63
76
|
packageJson.dependencies = originalDependencies;
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
if (originalOverrides) {
|
|
78
|
+
packageJson.overrides = originalOverrides;
|
|
79
|
+
} else {
|
|
80
|
+
delete packageJson.overrides;
|
|
81
|
+
}
|
|
82
|
+
await writeFile(
|
|
83
|
+
path.join(stageRoot, 'package.json'),
|
|
84
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
85
|
+
'utf8',
|
|
86
|
+
);
|
|
87
|
+
const tarballName = (await run(['bun', 'pm', 'pack'], stageRoot))
|
|
66
88
|
.split(/\r?\n/)
|
|
67
89
|
.map((line) => line.trim())
|
|
68
90
|
.filter((line) => line.endsWith('.tgz'))
|
|
@@ -74,6 +96,7 @@ async function main() {
|
|
|
74
96
|
|
|
75
97
|
const standaloneName = tarballName.replace(/\.tgz$/, '-standalone.tgz');
|
|
76
98
|
const destination = path.join(outputRoot, standaloneName);
|
|
99
|
+
await mkdir(path.dirname(destination), { recursive: true });
|
|
77
100
|
await rm(destination, { force: true });
|
|
78
101
|
await rename(path.join(stageRoot, tarballName), destination);
|
|
79
102
|
console.log(destination);
|
|
@@ -83,7 +106,7 @@ async function main() {
|
|
|
83
106
|
}
|
|
84
107
|
|
|
85
108
|
async function packPackage(cwd, outputDir) {
|
|
86
|
-
const tarballName = run('bun',
|
|
109
|
+
const tarballName = (await run(['bun', 'pm', 'pack'], cwd))
|
|
87
110
|
.split(/\r?\n/)
|
|
88
111
|
.map((line) => line.trim())
|
|
89
112
|
.filter((line) => line.endsWith('.tgz'))
|
|
@@ -99,26 +122,27 @@ async function packPackage(cwd, outputDir) {
|
|
|
99
122
|
return destination;
|
|
100
123
|
}
|
|
101
124
|
|
|
102
|
-
function run(command,
|
|
103
|
-
const
|
|
104
|
-
cwd,
|
|
105
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
106
|
-
encoding: 'utf8',
|
|
107
|
-
env: process.env,
|
|
108
|
-
});
|
|
125
|
+
async function run(command, cwd) {
|
|
126
|
+
const [program, ...args] = command;
|
|
109
127
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
try {
|
|
129
|
+
return await Bun.$.cwd(cwd)`${program} ${args}`.text();
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const exitCode =
|
|
132
|
+
typeof error === 'object' &&
|
|
133
|
+
error !== null &&
|
|
134
|
+
'exitCode' in error &&
|
|
135
|
+
typeof error.exitCode === 'number'
|
|
136
|
+
? error.exitCode
|
|
137
|
+
: null;
|
|
138
|
+
if (exitCode === null) {
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
|
|
142
|
+
throw new Error(`Command failed (${exitCode}): ${program} ${args.join(' ')}`, {
|
|
143
|
+
cause: error,
|
|
144
|
+
});
|
|
119
145
|
}
|
|
120
|
-
|
|
121
|
-
return result.stdout ?? '';
|
|
122
146
|
}
|
|
123
147
|
|
|
124
148
|
main().catch((error) => {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
+
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
7
|
+
MONOREPO_ROOT="$(git -C "$ROOT_DIR" rev-parse --show-toplevel)"
|
|
8
|
+
|
|
9
|
+
exec bun "$MONOREPO_ROOT/tools/release-package.mjs" --package-dir "$ROOT_DIR" "$@"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
8
|
+
const testsDir = path.join(packageRoot, 'tests');
|
|
9
|
+
|
|
10
|
+
const browserTestFile = 'tests/progressive-enhancement.browser.integration.test.ts';
|
|
11
|
+
const watchBrowserTestFiles = [
|
|
12
|
+
'tests/runtime-boundary.integration.test.ts',
|
|
13
|
+
'tests/bun-first-spa.integration.test.ts',
|
|
14
|
+
'tests/ssg-watch.integration.test.ts',
|
|
15
|
+
'tests/full-watch.integration.test.ts',
|
|
16
|
+
];
|
|
17
|
+
const publishModeFilter = 'publish mode';
|
|
18
|
+
const watchModeFilter = 'watch mode';
|
|
19
|
+
|
|
20
|
+
export function listCoreTestFiles() {
|
|
21
|
+
return readdirSync(testsDir)
|
|
22
|
+
.filter((file) => file.endsWith('.ts'))
|
|
23
|
+
.filter((file) => file !== path.basename(browserTestFile))
|
|
24
|
+
.filter((file) => !watchBrowserTestFiles.includes(path.posix.join('tests', file)))
|
|
25
|
+
.sort()
|
|
26
|
+
.map((file) => path.posix.join('tests', file));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function buildTestPlan(mode) {
|
|
30
|
+
const coreTests = {
|
|
31
|
+
label: 'core orchestrator tests',
|
|
32
|
+
args: ['test', '--bail=1', ...listCoreTestFiles()],
|
|
33
|
+
};
|
|
34
|
+
const publishBrowserTests = {
|
|
35
|
+
label: 'browser publish proofs',
|
|
36
|
+
args: ['test', '--bail=1', browserTestFile, '-t', publishModeFilter],
|
|
37
|
+
};
|
|
38
|
+
const integrationWatchBrowserTests = {
|
|
39
|
+
label: 'browser watch integration proofs',
|
|
40
|
+
args: ['test', '--bail=1', ...watchBrowserTestFiles],
|
|
41
|
+
};
|
|
42
|
+
const watchBrowserTests = {
|
|
43
|
+
label: 'browser watch proofs',
|
|
44
|
+
args: ['test', '--bail=1', browserTestFile, '-t', watchModeFilter],
|
|
45
|
+
};
|
|
46
|
+
const requiredPlan = [
|
|
47
|
+
coreTests,
|
|
48
|
+
publishBrowserTests,
|
|
49
|
+
integrationWatchBrowserTests,
|
|
50
|
+
watchBrowserTests,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
switch (mode) {
|
|
54
|
+
case 'required':
|
|
55
|
+
return requiredPlan;
|
|
56
|
+
case 'publish-browser':
|
|
57
|
+
return [publishBrowserTests];
|
|
58
|
+
case 'watch-browser':
|
|
59
|
+
return [integrationWatchBrowserTests, watchBrowserTests];
|
|
60
|
+
case 'all':
|
|
61
|
+
case 'with-watch-browser':
|
|
62
|
+
return requiredPlan;
|
|
63
|
+
default:
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Unknown orchestrator test mode "${mode}". Expected one of: required, publish-browser, watch-browser, with-watch-browser.`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function runStep(step) {
|
|
71
|
+
const result = spawnSync('bun', step.args, {
|
|
72
|
+
cwd: packageRoot,
|
|
73
|
+
stdio: 'inherit',
|
|
74
|
+
env: process.env,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (result.status !== 0) {
|
|
78
|
+
process.exit(result.status ?? 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function isCliInvocation() {
|
|
83
|
+
const entrypoint = process.argv[1];
|
|
84
|
+
if (!entrypoint) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return path.resolve(entrypoint) === fileURLToPath(import.meta.url);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (isCliInvocation()) {
|
|
92
|
+
const mode = process.argv[2] ?? 'required';
|
|
93
|
+
const plan = buildTestPlan(mode);
|
|
94
|
+
|
|
95
|
+
for (const step of plan) {
|
|
96
|
+
console.log(`[webstir][tests] ${step.label}`);
|
|
97
|
+
runStep(step);
|
|
98
|
+
}
|
|
99
|
+
}
|