@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
package/scripts/sync-assets.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { cp, mkdir, rm } from 'node:fs/promises';
|
|
3
|
+
import { cp, mkdir, mkdtemp, readdir, readFile, rm } from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
import { fileURLToPath } from 'node:url';
|
|
6
7
|
|
|
@@ -8,8 +9,12 @@ const here = path.dirname(fileURLToPath(import.meta.url));
|
|
|
8
9
|
const packageRoot = path.resolve(here, '..');
|
|
9
10
|
const repoRoot = path.resolve(packageRoot, '..', '..');
|
|
10
11
|
const assetsRoot = path.join(packageRoot, 'assets');
|
|
11
|
-
const
|
|
12
|
-
const
|
|
12
|
+
const resourcesRoot = path.join(packageRoot, 'resources');
|
|
13
|
+
const templateSourcesRoot = path.join(resourcesRoot, 'templates');
|
|
14
|
+
const deploymentSourcesRoot = path.join(resourcesRoot, 'deployment');
|
|
15
|
+
const dotnetRoot = path.join(repoRoot, 'orchestrators', 'dotnet');
|
|
16
|
+
const demosRoot = path.join(repoRoot, 'examples', 'demos');
|
|
17
|
+
const checkOnly = process.argv.includes('--check');
|
|
13
18
|
|
|
14
19
|
const rootAssets = [
|
|
15
20
|
'Errors.404.html',
|
|
@@ -23,47 +28,87 @@ const modeTemplates = [
|
|
|
23
28
|
{
|
|
24
29
|
mode: 'ssg',
|
|
25
30
|
roots: [
|
|
26
|
-
{
|
|
31
|
+
{
|
|
32
|
+
source: path.join(templateSourcesRoot, 'ssg', 'src', 'frontend'),
|
|
33
|
+
target: path.join('src', 'frontend'),
|
|
34
|
+
},
|
|
27
35
|
],
|
|
28
36
|
},
|
|
29
37
|
{
|
|
30
38
|
mode: 'spa',
|
|
31
39
|
roots: [
|
|
32
|
-
{
|
|
33
|
-
|
|
40
|
+
{
|
|
41
|
+
source: path.join(templateSourcesRoot, 'spa', 'src', 'frontend'),
|
|
42
|
+
target: path.join('src', 'frontend'),
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
source: path.join(templateSourcesRoot, 'spa', 'src', 'shared'),
|
|
46
|
+
target: path.join('src', 'shared'),
|
|
47
|
+
},
|
|
34
48
|
],
|
|
35
49
|
},
|
|
36
50
|
{
|
|
37
51
|
mode: 'api',
|
|
38
52
|
roots: [
|
|
39
|
-
{
|
|
40
|
-
|
|
53
|
+
{
|
|
54
|
+
source: path.join(templateSourcesRoot, 'api', 'src', 'backend'),
|
|
55
|
+
target: path.join('src', 'backend'),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
source: path.join(templateSourcesRoot, 'api', 'src', 'shared'),
|
|
59
|
+
target: path.join('src', 'shared'),
|
|
60
|
+
},
|
|
41
61
|
],
|
|
42
62
|
},
|
|
43
63
|
{
|
|
44
64
|
mode: 'full',
|
|
45
65
|
roots: [
|
|
46
|
-
{
|
|
47
|
-
|
|
48
|
-
|
|
66
|
+
{
|
|
67
|
+
source: path.join(templateSourcesRoot, 'full', 'src', 'frontend'),
|
|
68
|
+
target: path.join('src', 'frontend'),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
source: path.join(templateSourcesRoot, 'full', 'src', 'backend'),
|
|
72
|
+
target: path.join('src', 'backend'),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
source: path.join(templateSourcesRoot, 'full', 'src', 'shared'),
|
|
76
|
+
target: path.join('src', 'shared'),
|
|
77
|
+
},
|
|
49
78
|
],
|
|
50
79
|
},
|
|
51
80
|
];
|
|
52
81
|
|
|
53
82
|
const features = [
|
|
54
|
-
{ source: path.join(
|
|
55
|
-
{ source: path.join(
|
|
56
|
-
{ source: path.join(
|
|
57
|
-
{ source: path.join(
|
|
83
|
+
{ source: path.join(packageRoot, 'resources', 'features', 'router'), target: 'router' },
|
|
84
|
+
{ source: path.join(packageRoot, 'resources', 'features', 'client_nav'), target: 'client_nav' },
|
|
85
|
+
{ source: path.join(packageRoot, 'resources', 'features', 'search'), target: 'search' },
|
|
86
|
+
{ source: path.join(packageRoot, 'resources', 'features', 'content_nav'), target: 'content_nav' },
|
|
58
87
|
];
|
|
59
88
|
|
|
60
89
|
async function main() {
|
|
61
|
-
|
|
90
|
+
assertNoLegacyAssetReads();
|
|
91
|
+
if (checkOnly) {
|
|
92
|
+
await assertAssetsInSync();
|
|
93
|
+
console.log('[webstir] assets OK');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await materializeAssets(assetsRoot);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function materializeAssets(targetAssetsRoot) {
|
|
101
|
+
const templatesRoot = path.join(targetAssetsRoot, 'templates');
|
|
102
|
+
const featuresRoot = path.join(targetAssetsRoot, 'features');
|
|
103
|
+
const deploymentRoot = path.join(targetAssetsRoot, 'deployment');
|
|
104
|
+
|
|
105
|
+
await rm(targetAssetsRoot, { recursive: true, force: true });
|
|
62
106
|
await mkdir(templatesRoot, { recursive: true });
|
|
63
107
|
await mkdir(featuresRoot, { recursive: true });
|
|
108
|
+
await mkdir(deploymentRoot, { recursive: true });
|
|
64
109
|
|
|
65
110
|
for (const relativePath of rootAssets) {
|
|
66
|
-
const sourcePath = path.join(
|
|
111
|
+
const sourcePath = path.join(templateSourcesRoot, 'shared', relativePath);
|
|
67
112
|
const targetPath = path.join(templatesRoot, 'shared', relativePath);
|
|
68
113
|
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
69
114
|
await cp(sourcePath, targetPath, { recursive: true });
|
|
@@ -82,6 +127,119 @@ async function main() {
|
|
|
82
127
|
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
83
128
|
await cp(feature.source, targetPath, { recursive: true });
|
|
84
129
|
}
|
|
130
|
+
|
|
131
|
+
await cp(deploymentSourcesRoot, deploymentRoot, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function assertAssetsInSync() {
|
|
135
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'webstir-assets-'));
|
|
136
|
+
const expectedAssetsRoot = path.join(tempRoot, 'assets');
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await materializeAssets(expectedAssetsRoot);
|
|
140
|
+
const differences = await collectDirectoryDifferences(expectedAssetsRoot, assetsRoot);
|
|
141
|
+
if (differences.length === 0) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
throw new Error(
|
|
146
|
+
[
|
|
147
|
+
'Generated Bun assets are out of sync with orchestrators/bun/resources.',
|
|
148
|
+
'Run `bun run --filter @webstir-io/webstir build` or `bun scripts/sync-assets.mjs` from orchestrators/bun.',
|
|
149
|
+
...differences.map((difference) => ` - ${difference}`),
|
|
150
|
+
].join('\n'),
|
|
151
|
+
);
|
|
152
|
+
} finally {
|
|
153
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function collectDirectoryDifferences(expectedRoot, actualRoot) {
|
|
158
|
+
const [expectedEntries, actualEntries] = await Promise.all([
|
|
159
|
+
collectEntries(expectedRoot),
|
|
160
|
+
collectEntries(actualRoot),
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
const differences = [];
|
|
164
|
+
const paths = [...new Set([...expectedEntries.keys(), ...actualEntries.keys()])].sort();
|
|
165
|
+
|
|
166
|
+
for (const relativePath of paths) {
|
|
167
|
+
const expected = expectedEntries.get(relativePath);
|
|
168
|
+
const actual = actualEntries.get(relativePath);
|
|
169
|
+
|
|
170
|
+
if (!expected) {
|
|
171
|
+
differences.push(`unexpected ${actual.type} in assets: ${relativePath}`);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!actual) {
|
|
176
|
+
differences.push(`missing ${expected.type} in assets: ${relativePath}`);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (expected.type !== actual.type) {
|
|
181
|
+
differences.push(
|
|
182
|
+
`type mismatch for ${relativePath}: expected ${expected.type}, found ${actual.type}`,
|
|
183
|
+
);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (expected.type === 'file' && !expected.content.equals(actual.content)) {
|
|
188
|
+
differences.push(`content mismatch in assets: ${relativePath}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return differences;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function collectEntries(root, currentPath = root, entries = new Map()) {
|
|
196
|
+
const children = await readdir(currentPath, { withFileTypes: true });
|
|
197
|
+
|
|
198
|
+
for (const child of children) {
|
|
199
|
+
const childPath = path.join(currentPath, child.name);
|
|
200
|
+
const relativePath = path.relative(root, childPath);
|
|
201
|
+
|
|
202
|
+
if (child.isDirectory()) {
|
|
203
|
+
entries.set(relativePath, { type: 'directory' });
|
|
204
|
+
await collectEntries(root, childPath, entries);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (child.isFile()) {
|
|
209
|
+
entries.set(relativePath, { type: 'file', content: await readFile(childPath) });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return entries;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function assertNoLegacyAssetReads() {
|
|
217
|
+
const sources = [
|
|
218
|
+
...rootAssets.map((relativePath) => path.join(templateSourcesRoot, 'shared', relativePath)),
|
|
219
|
+
...modeTemplates.flatMap((template) => template.roots.map((root) => root.source)),
|
|
220
|
+
...features.map((feature) => feature.source),
|
|
221
|
+
deploymentSourcesRoot,
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
assertSourcesOutsideRoot(dotnetRoot, 'orchestrators/dotnet', sources);
|
|
225
|
+
assertSourcesOutsideRoot(demosRoot, 'examples/demos', sources);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function assertSourcesOutsideRoot(disallowedRoot, label, sources) {
|
|
229
|
+
const violations = sources.filter((source) => isInside(disallowedRoot, source));
|
|
230
|
+
if (violations.length === 0) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const details = violations.map((source) => ` - ${path.relative(repoRoot, source)}`).join('\n');
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Bun asset sync cannot read active assets from ${label}.\nMove the source into orchestrators/bun/resources first.\n${details}`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function isInside(root, candidate) {
|
|
241
|
+
const relative = path.relative(root, path.resolve(candidate));
|
|
242
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
85
243
|
}
|
|
86
244
|
|
|
87
245
|
await main();
|