@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/src/cache/diff.ts
CHANGED
|
@@ -1,128 +1,134 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { mkdir } from 'node:fs/promises';
|
|
3
3
|
|
|
4
4
|
import type { ModuleManifest, ModuleDiagnostic } from '@webstir-io/module-contract';
|
|
5
5
|
|
|
6
6
|
import type { BackendBuildMode } from '../workspace.js';
|
|
7
|
+
import { readTextFile, writeTextFile } from '../utils/bun.js';
|
|
8
|
+
|
|
9
|
+
type DigestRouteLike = { readonly method?: string; readonly path?: string };
|
|
10
|
+
type DigestViewLike = { readonly path?: string };
|
|
7
11
|
|
|
8
12
|
export async function persistAndDiffOutputs(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
workspaceRoot: string,
|
|
14
|
+
_buildRoot: string,
|
|
15
|
+
outputs: Record<string, number> | undefined,
|
|
16
|
+
env: Record<string, string | undefined>,
|
|
17
|
+
diagnostics: ModuleDiagnostic[],
|
|
18
|
+
mode: BackendBuildMode,
|
|
15
19
|
): Promise<void> {
|
|
16
|
-
|
|
20
|
+
if (!outputs) return;
|
|
21
|
+
try {
|
|
22
|
+
const diagMax = resolveDiagMax(env);
|
|
23
|
+
const webstirDir = path.join(workspaceRoot, '.webstir');
|
|
24
|
+
const cachePath = path.join(webstirDir, 'backend-outputs.json');
|
|
25
|
+
await mkdir(webstirDir, { recursive: true });
|
|
26
|
+
|
|
27
|
+
let previous: Record<string, number> = {};
|
|
17
28
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const cachePath = path.join(webstirDir, 'backend-outputs.json');
|
|
21
|
-
await mkdir(webstirDir, { recursive: true });
|
|
22
|
-
|
|
23
|
-
let previous: Record<string, number> = {};
|
|
24
|
-
try {
|
|
25
|
-
const raw = await readFile(cachePath, 'utf8');
|
|
26
|
-
previous = JSON.parse(raw) as Record<string, number>;
|
|
27
|
-
} catch {
|
|
28
|
-
// first run or unreadable cache
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const changed: string[] = [];
|
|
32
|
-
for (const [rel, bytes] of Object.entries(outputs)) {
|
|
33
|
-
if (previous[rel] !== bytes) {
|
|
34
|
-
changed.push(rel);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
const removed = Object.keys(previous).filter((rel) => outputs[rel] === undefined);
|
|
38
|
-
|
|
39
|
-
if (changed.length + removed.length > 0) {
|
|
40
|
-
const list = changed.slice(0, diagMax).join(', ');
|
|
41
|
-
const omitted = changed.length > diagMax ? ` (+${changed.length - diagMax} more)` : '';
|
|
42
|
-
const removedInfo = removed.length > 0 ? `, removed=${removed.length}` : '';
|
|
43
|
-
diagnostics.push({
|
|
44
|
-
severity: 'info',
|
|
45
|
-
message: `[webstir-backend] ${mode}:changed ${changed.length} file(s): ${list}${omitted}${removedInfo}`
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
await writeFile(cachePath, JSON.stringify(outputs, null, 2), 'utf8');
|
|
29
|
+
const raw = await readTextFile(cachePath);
|
|
30
|
+
previous = JSON.parse(raw) as Record<string, number>;
|
|
50
31
|
} catch {
|
|
51
|
-
|
|
32
|
+
// first run or unreadable cache
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const changed: string[] = [];
|
|
36
|
+
for (const [rel, bytes] of Object.entries(outputs)) {
|
|
37
|
+
if (previous[rel] !== bytes) {
|
|
38
|
+
changed.push(rel);
|
|
39
|
+
}
|
|
52
40
|
}
|
|
41
|
+
const removed = Object.keys(previous).filter((rel) => outputs[rel] === undefined);
|
|
42
|
+
|
|
43
|
+
if (changed.length + removed.length > 0) {
|
|
44
|
+
const list = changed.slice(0, diagMax).join(', ');
|
|
45
|
+
const omitted = changed.length > diagMax ? ` (+${changed.length - diagMax} more)` : '';
|
|
46
|
+
const removedInfo = removed.length > 0 ? `, removed=${removed.length}` : '';
|
|
47
|
+
diagnostics.push({
|
|
48
|
+
severity: 'info',
|
|
49
|
+
message: `[webstir-backend] ${mode}:changed ${changed.length} file(s): ${list}${omitted}${removedInfo}`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await writeTextFile(cachePath, JSON.stringify(outputs, null, 2));
|
|
54
|
+
} catch {
|
|
55
|
+
// ignore cache errors
|
|
56
|
+
}
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
export async function persistAndDiffManifest(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
workspaceRoot: string,
|
|
61
|
+
manifest: ModuleManifest,
|
|
62
|
+
env: Record<string, string | undefined>,
|
|
63
|
+
diagnostics: ModuleDiagnostic[],
|
|
60
64
|
): Promise<void> {
|
|
65
|
+
try {
|
|
66
|
+
const diagMax = resolveDiagMax(env);
|
|
67
|
+
const webstirDir = path.join(workspaceRoot, '.webstir');
|
|
68
|
+
const cachePath = path.join(webstirDir, 'backend-manifest-digest.json');
|
|
69
|
+
await mkdir(webstirDir, { recursive: true });
|
|
70
|
+
|
|
71
|
+
const routeKeys = Array.isArray(manifest.routes)
|
|
72
|
+
? (manifest.routes as readonly DigestRouteLike[]).map(
|
|
73
|
+
(route) => `${(route.method ?? '').toUpperCase()} ${route.path ?? ''}`,
|
|
74
|
+
)
|
|
75
|
+
: [];
|
|
76
|
+
const viewPaths = Array.isArray(manifest.views)
|
|
77
|
+
? (manifest.views as readonly DigestViewLike[]).map((view) => `${view.path ?? ''}`)
|
|
78
|
+
: [];
|
|
79
|
+
const caps = Array.isArray(manifest.capabilities) ? manifest.capabilities : [];
|
|
80
|
+
|
|
81
|
+
type Digest = { routes: string[]; views: string[]; capabilities: string[] };
|
|
82
|
+
let previous: Digest | undefined;
|
|
61
83
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const routeKeys = Array.isArray(manifest.routes)
|
|
68
|
-
? (manifest.routes as any[]).map((r) => `${(r.method ?? '').toUpperCase()} ${r.path ?? ''}`)
|
|
69
|
-
: [];
|
|
70
|
-
const viewPaths = Array.isArray(manifest.views)
|
|
71
|
-
? (manifest.views as any[]).map((v) => `${v.path ?? ''}`)
|
|
72
|
-
: [];
|
|
73
|
-
const caps = Array.isArray(manifest.capabilities) ? manifest.capabilities : [];
|
|
74
|
-
|
|
75
|
-
type Digest = { routes: string[]; views: string[]; capabilities: string[] };
|
|
76
|
-
let previous: Digest | undefined;
|
|
77
|
-
try {
|
|
78
|
-
const raw = await readFile(cachePath, 'utf8');
|
|
79
|
-
previous = JSON.parse(raw) as Digest;
|
|
80
|
-
} catch {
|
|
81
|
-
// first run; no diff
|
|
82
|
-
}
|
|
84
|
+
const raw = await readTextFile(cachePath);
|
|
85
|
+
previous = JSON.parse(raw) as Digest;
|
|
86
|
+
} catch {
|
|
87
|
+
// first run; no diff
|
|
88
|
+
}
|
|
83
89
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
diagnostics.push({ severity: 'info', message: msg });
|
|
114
|
-
}
|
|
90
|
+
if (previous) {
|
|
91
|
+
const prevRoutes = new Set(previous.routes);
|
|
92
|
+
const prevViews = new Set(previous.views);
|
|
93
|
+
const nextRoutes = new Set(routeKeys);
|
|
94
|
+
const nextViews = new Set(viewPaths);
|
|
95
|
+
|
|
96
|
+
const addedRoutes: string[] = [];
|
|
97
|
+
const removedRoutes: string[] = [];
|
|
98
|
+
const addedViews: string[] = [];
|
|
99
|
+
const removedViews: string[] = [];
|
|
100
|
+
|
|
101
|
+
for (const r of nextRoutes) if (!prevRoutes.has(r)) addedRoutes.push(r);
|
|
102
|
+
for (const r of prevRoutes) if (!nextRoutes.has(r)) removedRoutes.push(r);
|
|
103
|
+
for (const v of nextViews) if (!prevViews.has(v)) addedViews.push(v);
|
|
104
|
+
for (const v of prevViews) if (!nextViews.has(v)) removedViews.push(v);
|
|
105
|
+
|
|
106
|
+
if (addedRoutes.length + removedRoutes.length + addedViews.length + removedViews.length > 0) {
|
|
107
|
+
const list = (items: string[]) => items.slice(0, diagMax).join(', ');
|
|
108
|
+
const routeDelta = `routes +${addedRoutes.length}/-${removedRoutes.length}`;
|
|
109
|
+
const viewDelta = `views +${addedViews.length}/-${removedViews.length}`;
|
|
110
|
+
let msg = `[webstir-backend] manifest changed: ${routeDelta}; ${viewDelta}`;
|
|
111
|
+
const details: string[] = [];
|
|
112
|
+
if (addedRoutes.length > 0) details.push(`added routes: ${list(addedRoutes)}`);
|
|
113
|
+
if (removedRoutes.length > 0) details.push(`removed routes: ${list(removedRoutes)}`);
|
|
114
|
+
if (addedViews.length > 0) details.push(`added views: ${list(addedViews)}`);
|
|
115
|
+
if (removedViews.length > 0) details.push(`removed views: ${list(removedViews)}`);
|
|
116
|
+
if (details.length > 0) {
|
|
117
|
+
msg += ` — ${details.join(' | ')}`;
|
|
115
118
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
await writeFile(cachePath, JSON.stringify(digest, null, 2), 'utf8');
|
|
119
|
-
} catch {
|
|
120
|
-
// ignore cache errors
|
|
119
|
+
diagnostics.push({ severity: 'info', message: msg });
|
|
120
|
+
}
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
const digest: Digest = { routes: routeKeys, views: viewPaths, capabilities: caps };
|
|
124
|
+
await writeTextFile(cachePath, JSON.stringify(digest, null, 2));
|
|
125
|
+
} catch {
|
|
126
|
+
// ignore cache errors
|
|
127
|
+
}
|
|
122
128
|
}
|
|
123
129
|
|
|
124
130
|
function resolveDiagMax(env: Record<string, string | undefined>, fallback = 50): number {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
131
|
+
const raw = env?.WEBSTIR_BACKEND_DIAG_MAX;
|
|
132
|
+
const n = typeof raw === 'string' ? parseInt(raw, 10) : NaN;
|
|
133
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
128
134
|
}
|
package/src/cache/reporters.ts
CHANGED
|
@@ -4,38 +4,38 @@ import type { BackendBuildMode } from '../workspace.js';
|
|
|
4
4
|
import { persistAndDiffManifest, persistAndDiffOutputs } from './diff.js';
|
|
5
5
|
|
|
6
6
|
export interface CacheReporter {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
readonly diffOutputs: (
|
|
8
|
+
outputs: Record<string, number> | undefined,
|
|
9
|
+
mode: BackendBuildMode,
|
|
10
|
+
) => Promise<void>;
|
|
11
|
+
readonly diffManifest: (manifest: ModuleManifest) => Promise<void>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function createCacheReporter(options: {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
readonly workspaceRoot: string;
|
|
16
|
+
readonly buildRoot: string;
|
|
17
|
+
readonly env: Record<string, string | undefined>;
|
|
18
|
+
readonly diagnostics: ModuleDiagnostic[];
|
|
19
19
|
}): CacheReporter {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const { workspaceRoot, buildRoot, env, diagnostics } = options;
|
|
21
|
+
const diagnosticsTarget = shouldLogCacheDiffs(env) ? diagnostics : [];
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
return {
|
|
24
|
+
async diffOutputs(outputs, mode) {
|
|
25
|
+
await persistAndDiffOutputs(workspaceRoot, buildRoot, outputs, env, diagnosticsTarget, mode);
|
|
26
|
+
},
|
|
27
|
+
async diffManifest(manifest) {
|
|
28
|
+
await persistAndDiffManifest(workspaceRoot, manifest, env, diagnosticsTarget);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
function shouldLogCacheDiffs(env: Record<string, string | undefined>): boolean {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
const raw = env?.WEBSTIR_BACKEND_CACHE_LOG;
|
|
35
|
+
if (!raw) return true;
|
|
36
|
+
const normalized = raw.trim().toLowerCase();
|
|
37
|
+
if (['off', '0', 'false', 'quiet', 'silent', 'skip'].includes(normalized)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
41
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { startPublishedWorkspaceServer } from './runtime/deploy.js';
|
|
6
|
+
|
|
7
|
+
async function main(argv: readonly string[]): Promise<void> {
|
|
8
|
+
const args = parseArgs(argv);
|
|
9
|
+
if (args.help) {
|
|
10
|
+
printHelp();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const workspaceRoot = path.resolve(args.workspace ?? process.cwd());
|
|
15
|
+
const server = await startPublishedWorkspaceServer({
|
|
16
|
+
workspaceRoot,
|
|
17
|
+
host: args.host,
|
|
18
|
+
port: args.port,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const shutdown = async (signal: NodeJS.Signals): Promise<void> => {
|
|
22
|
+
process.stderr.write(`[webstir-backend-deploy] received ${signal}, stopping.\n`);
|
|
23
|
+
await server.stop();
|
|
24
|
+
process.exit(0);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
process.on('SIGINT', () => void shutdown('SIGINT'));
|
|
28
|
+
process.on('SIGTERM', () => void shutdown('SIGTERM'));
|
|
29
|
+
|
|
30
|
+
process.stdout.write(
|
|
31
|
+
`[webstir-backend-deploy] serving ${server.mode} workspace at ${server.origin}\n`,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
await new Promise<void>(() => {
|
|
35
|
+
// Keep the process alive until it receives a signal.
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function parseArgs(argv: readonly string[]): {
|
|
40
|
+
readonly workspace?: string;
|
|
41
|
+
readonly host?: string;
|
|
42
|
+
readonly port?: number;
|
|
43
|
+
readonly help: boolean;
|
|
44
|
+
} {
|
|
45
|
+
let workspace: string | undefined;
|
|
46
|
+
let host: string | undefined;
|
|
47
|
+
let port: number | undefined;
|
|
48
|
+
let help = false;
|
|
49
|
+
|
|
50
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
51
|
+
const arg = argv[index];
|
|
52
|
+
if (!arg) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (arg === '--help' || arg === '-h') {
|
|
57
|
+
help = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (arg === '--workspace') {
|
|
62
|
+
const next = argv[index + 1];
|
|
63
|
+
if (!next) {
|
|
64
|
+
throw new Error('Missing value for --workspace.');
|
|
65
|
+
}
|
|
66
|
+
workspace = next;
|
|
67
|
+
index += 1;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (arg === '--host') {
|
|
72
|
+
const next = argv[index + 1];
|
|
73
|
+
if (!next) {
|
|
74
|
+
throw new Error('Missing value for --host.');
|
|
75
|
+
}
|
|
76
|
+
host = next;
|
|
77
|
+
index += 1;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (arg === '--port') {
|
|
82
|
+
const next = argv[index + 1];
|
|
83
|
+
if (!next) {
|
|
84
|
+
throw new Error('Missing value for --port.');
|
|
85
|
+
}
|
|
86
|
+
const parsed = Number(next);
|
|
87
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
88
|
+
throw new Error(`Invalid value for --port: ${next}`);
|
|
89
|
+
}
|
|
90
|
+
port = parsed;
|
|
91
|
+
index += 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { workspace, host, port, help };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function printHelp(): void {
|
|
102
|
+
process.stdout.write(`Usage: webstir-backend-deploy [--workspace <path>] [--host <host>] [--port <port>]
|
|
103
|
+
|
|
104
|
+
Starts a published api or full Webstir workspace with a single Bun entrypoint.
|
|
105
|
+
`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main(process.argv.slice(2)).catch((error) => {
|
|
109
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
});
|
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
import type { ModuleDiagnostic } from '@webstir-io/module-contract';
|
|
2
2
|
|
|
3
|
-
export function pushEntryBucketSummary(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
3
|
+
export function pushEntryBucketSummary(
|
|
4
|
+
diagnostics: ModuleDiagnostic[],
|
|
5
|
+
entryPoints: readonly string[],
|
|
6
|
+
): void {
|
|
7
|
+
try {
|
|
8
|
+
const server = entryPoints.filter(
|
|
9
|
+
(p) => p === 'index.js' || (/(^|\/)index\.js$/.test(p) && !/^(functions|jobs)\//.test(p)),
|
|
10
|
+
).length;
|
|
11
|
+
const functionsCount = entryPoints.filter((p) => p.startsWith('functions/')).length;
|
|
12
|
+
const jobsCount = entryPoints.filter((p) => p.startsWith('jobs/')).length;
|
|
13
|
+
diagnostics.push({
|
|
14
|
+
severity: 'info',
|
|
15
|
+
message: `[webstir-backend] entries by bucket: server=${server} functions=${functionsCount} jobs=${jobsCount}`,
|
|
16
|
+
});
|
|
17
|
+
} catch {
|
|
18
|
+
// best-effort only
|
|
19
|
+
}
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
type Severity = 'info' | 'warn' | 'error';
|
|
20
23
|
|
|
21
24
|
export function normalizeLogLevel(value: unknown): Severity {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
if (typeof value !== 'string') return 'info';
|
|
26
|
+
const v = value.toLowerCase();
|
|
27
|
+
if (v === 'error' || v === 'warn' || v === 'info') return v;
|
|
28
|
+
return 'info';
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
export function filterDiagnostics(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
export function filterDiagnostics(
|
|
32
|
+
list: readonly ModuleDiagnostic[],
|
|
33
|
+
min: Severity,
|
|
34
|
+
): readonly ModuleDiagnostic[] {
|
|
35
|
+
const rank = (s: Severity) => (s === 'error' ? 3 : s === 'warn' ? 2 : 1);
|
|
36
|
+
const threshold = rank(min);
|
|
37
|
+
return list.filter((d) => rank(d.severity as Severity) >= threshold);
|
|
32
38
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,13 @@
|
|
|
1
|
+
export { runAddJob, runAddRoute, runUpdateRouteContract } from './add.js';
|
|
2
|
+
export type { AddJobOptions, AddRouteOptions, UpdateRouteContractOptions } from './add.js';
|
|
1
3
|
export { backendProvider } from './provider.js';
|
|
2
4
|
export { startBackendWatch } from './watch.js';
|
|
5
|
+
export { getBackendScaffoldAssets } from './scaffold/assets.js';
|
|
6
|
+
export { createDefaultBunBackendBootstrap, startBunBackend } from './runtime/bun.js';
|
|
7
|
+
export type {
|
|
8
|
+
BunRuntimeEnvLike,
|
|
9
|
+
DefaultBunBackendBootstrapOptions,
|
|
10
|
+
MetricsTracker,
|
|
11
|
+
RuntimeLogger,
|
|
12
|
+
} from './runtime/bun.js';
|
|
13
|
+
export { startPublishedWorkspaceServer } from './runtime/deploy.js';
|