@treeseed/core 0.6.16 → 0.6.18
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/dist/dev-watch.d.ts +25 -0
- package/dist/dev-watch.js +161 -0
- package/dist/dev.d.ts +6 -14
- package/dist/dev.js +186 -208
- package/package.json +2 -2
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export type TreeseedDevWatchEntry = {
|
|
2
|
+
kind: 'tenant' | 'package' | 'sdk';
|
|
3
|
+
root: string;
|
|
4
|
+
};
|
|
5
|
+
export type TreeseedDevWatchChange = {
|
|
6
|
+
changedPaths: string[];
|
|
7
|
+
tenantChanged: boolean;
|
|
8
|
+
tenantApiChanged: boolean;
|
|
9
|
+
packageChanged: boolean;
|
|
10
|
+
sdkChanged: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type TreeseedDevWatchController = {
|
|
13
|
+
stop: () => void;
|
|
14
|
+
rebaseline: () => void;
|
|
15
|
+
};
|
|
16
|
+
export type TreeseedDevWatchStarter = (input: {
|
|
17
|
+
watchEntries: TreeseedDevWatchEntry[];
|
|
18
|
+
onChange: (change: TreeseedDevWatchChange) => void | Promise<void>;
|
|
19
|
+
}) => TreeseedDevWatchController;
|
|
20
|
+
export declare function shouldIgnoreWatchPath(filePath: string, rootPath: string): boolean;
|
|
21
|
+
export declare function classifyChanges(changedPaths: string[], watchEntries: TreeseedDevWatchEntry[]): TreeseedDevWatchChange;
|
|
22
|
+
export declare function startPollingWatch({ watchEntries, onChange }: Parameters<TreeseedDevWatchStarter>[0]): {
|
|
23
|
+
stop(): void;
|
|
24
|
+
rebaseline(): void;
|
|
25
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { relative, resolve, sep } from "node:path";
|
|
3
|
+
const WATCH_INTERVAL_MS = 900;
|
|
4
|
+
const WATCH_DEBOUNCE_MS = 350;
|
|
5
|
+
function shouldIgnoreWatchPath(filePath, rootPath) {
|
|
6
|
+
const rel = relative(rootPath, filePath);
|
|
7
|
+
if (!rel || rel.startsWith(`..${sep}`) || rel === "..") {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
const normalized = rel.split(sep).join("/");
|
|
11
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
12
|
+
const basename = segments.at(-1) ?? normalized;
|
|
13
|
+
const ignoredSegments = /* @__PURE__ */ new Set([".git", "node_modules", ".astro", ".wrangler", ".local", ".treeseed", "dist", "coverage"]);
|
|
14
|
+
if (segments.some((segment) => ignoredSegments.has(segment))) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (normalized === "books" || normalized.startsWith("books/") || normalized === "__treeseed" || normalized.startsWith("__treeseed/") || normalized.startsWith("public/books/") || normalized.startsWith("public/__treeseed/")) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return basename.startsWith(".ts-run-") && basename.endsWith(".mjs") || basename.endsWith(".log") || basename.endsWith(".pid") || basename.endsWith(".sock") || basename.endsWith(".tmp") || basename.endsWith(".temp") || basename.endsWith(".sqlite") || basename.includes(".sqlite-") || basename.endsWith(".db-journal") || basename.endsWith(".db-wal") || basename.endsWith(".db-shm");
|
|
21
|
+
}
|
|
22
|
+
function collectRootSnapshot(rootPath, snapshot) {
|
|
23
|
+
if (!existsSync(rootPath)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const stats = statSync(rootPath);
|
|
27
|
+
if (stats.isFile()) {
|
|
28
|
+
snapshot.set(rootPath, `${stats.mtimeMs}:${stats.size}`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
for (const entry of readdirSync(rootPath, { withFileTypes: true })) {
|
|
32
|
+
const fullPath = resolve(rootPath, entry.name);
|
|
33
|
+
if (shouldIgnoreWatchPath(fullPath, rootPath)) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (entry.isDirectory()) {
|
|
37
|
+
collectDirectorySnapshot(fullPath, rootPath, snapshot);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const entryStats = statSync(fullPath);
|
|
41
|
+
snapshot.set(fullPath, `${entryStats.mtimeMs}:${entryStats.size}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function collectDirectorySnapshot(directoryPath, rootPath, snapshot) {
|
|
45
|
+
if (shouldIgnoreWatchPath(directoryPath, rootPath)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
for (const entry of readdirSync(directoryPath, { withFileTypes: true })) {
|
|
49
|
+
const fullPath = resolve(directoryPath, entry.name);
|
|
50
|
+
if (shouldIgnoreWatchPath(fullPath, rootPath)) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (entry.isDirectory()) {
|
|
54
|
+
collectDirectorySnapshot(fullPath, rootPath, snapshot);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const stats = statSync(fullPath);
|
|
58
|
+
snapshot.set(fullPath, `${stats.mtimeMs}:${stats.size}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function collectSnapshot(entries) {
|
|
62
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
collectRootSnapshot(entry.root, snapshot);
|
|
65
|
+
}
|
|
66
|
+
return snapshot;
|
|
67
|
+
}
|
|
68
|
+
function diffSnapshots(previousSnapshot, nextSnapshot) {
|
|
69
|
+
const changed = /* @__PURE__ */ new Set();
|
|
70
|
+
for (const [filePath, signature] of nextSnapshot.entries()) {
|
|
71
|
+
if (previousSnapshot.get(filePath) !== signature) {
|
|
72
|
+
changed.add(filePath);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const filePath of previousSnapshot.keys()) {
|
|
76
|
+
if (!nextSnapshot.has(filePath)) {
|
|
77
|
+
changed.add(filePath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [...changed];
|
|
81
|
+
}
|
|
82
|
+
function classifyChanges(changedPaths, watchEntries) {
|
|
83
|
+
function matchesEntry(filePath, entry) {
|
|
84
|
+
return filePath === entry.root || filePath.startsWith(`${entry.root}${sep}`);
|
|
85
|
+
}
|
|
86
|
+
function isTenantApiInput(filePath) {
|
|
87
|
+
const normalized = filePath.split(sep).join("/");
|
|
88
|
+
return normalized.includes("/src/api/") || normalized.endsWith("/src/api") || normalized.endsWith("/treeseed.site.yaml") || normalized.endsWith("/treeseed.config.ts") || normalized.endsWith("/package.json") || normalized.endsWith("/tsconfig.json");
|
|
89
|
+
}
|
|
90
|
+
const tenantChanged = changedPaths.some(
|
|
91
|
+
(filePath) => watchEntries.some((entry) => entry.kind === "tenant" && matchesEntry(filePath, entry))
|
|
92
|
+
);
|
|
93
|
+
return {
|
|
94
|
+
changedPaths,
|
|
95
|
+
sdkChanged: changedPaths.some(
|
|
96
|
+
(filePath) => watchEntries.some((entry) => entry.kind === "sdk" && matchesEntry(filePath, entry))
|
|
97
|
+
),
|
|
98
|
+
packageChanged: changedPaths.some(
|
|
99
|
+
(filePath) => watchEntries.some((entry) => entry.kind === "package" && matchesEntry(filePath, entry))
|
|
100
|
+
),
|
|
101
|
+
tenantChanged,
|
|
102
|
+
tenantApiChanged: tenantChanged && changedPaths.some(isTenantApiInput)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function startPollingWatch({ watchEntries, onChange }) {
|
|
106
|
+
let previousSnapshot = collectSnapshot(watchEntries);
|
|
107
|
+
let queuedPaths = [];
|
|
108
|
+
let debounceTimer = null;
|
|
109
|
+
let running = false;
|
|
110
|
+
const intervalId = setInterval(() => {
|
|
111
|
+
const nextSnapshot = collectSnapshot(watchEntries);
|
|
112
|
+
const changedPaths = diffSnapshots(previousSnapshot, nextSnapshot);
|
|
113
|
+
previousSnapshot = nextSnapshot;
|
|
114
|
+
if (changedPaths.length === 0) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
queuedPaths.push(...changedPaths);
|
|
118
|
+
if (debounceTimer) {
|
|
119
|
+
clearTimeout(debounceTimer);
|
|
120
|
+
}
|
|
121
|
+
debounceTimer = setTimeout(() => {
|
|
122
|
+
void flush();
|
|
123
|
+
}, WATCH_DEBOUNCE_MS);
|
|
124
|
+
}, WATCH_INTERVAL_MS);
|
|
125
|
+
async function flush() {
|
|
126
|
+
if (running || queuedPaths.length === 0) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const changedPaths = [...new Set(queuedPaths)];
|
|
130
|
+
queuedPaths = [];
|
|
131
|
+
running = true;
|
|
132
|
+
try {
|
|
133
|
+
await onChange(classifyChanges(changedPaths, watchEntries));
|
|
134
|
+
} finally {
|
|
135
|
+
previousSnapshot = collectSnapshot(watchEntries);
|
|
136
|
+
running = false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function clearDebounce() {
|
|
140
|
+
if (debounceTimer) {
|
|
141
|
+
clearTimeout(debounceTimer);
|
|
142
|
+
debounceTimer = null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
stop() {
|
|
147
|
+
clearDebounce();
|
|
148
|
+
clearInterval(intervalId);
|
|
149
|
+
},
|
|
150
|
+
rebaseline() {
|
|
151
|
+
clearDebounce();
|
|
152
|
+
queuedPaths = [];
|
|
153
|
+
previousSnapshot = collectSnapshot(watchEntries);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
classifyChanges,
|
|
159
|
+
shouldIgnoreWatchPath,
|
|
160
|
+
startPollingWatch
|
|
161
|
+
};
|
package/dist/dev.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ChildProcess, SpawnOptions } from 'node:child_process';
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { type TreeseedDevWatchEntry, type TreeseedDevWatchStarter } from './dev-watch';
|
|
3
4
|
export declare const TREESEED_DEFAULT_WEB_HOST = "127.0.0.1";
|
|
4
5
|
export declare const TREESEED_DEFAULT_WEB_PORT = 4321;
|
|
5
6
|
export declare const TREESEED_DEFAULT_API_HOST = "127.0.0.1";
|
|
@@ -30,6 +31,7 @@ export type TreeseedIntegratedDevOptions = {
|
|
|
30
31
|
teamId?: string;
|
|
31
32
|
readinessTimeoutMs?: number;
|
|
32
33
|
processReadyGraceMs?: number;
|
|
34
|
+
shutdownGraceMs?: number;
|
|
33
35
|
};
|
|
34
36
|
export type TreeseedIntegratedDevCommand = {
|
|
35
37
|
id: 'web' | 'api' | 'manager' | 'worker';
|
|
@@ -39,10 +41,7 @@ export type TreeseedIntegratedDevCommand = {
|
|
|
39
41
|
cwd: string;
|
|
40
42
|
env: NodeJS.ProcessEnv;
|
|
41
43
|
};
|
|
42
|
-
export type TreeseedIntegratedDevWatchEntry =
|
|
43
|
-
kind: 'tenant' | 'package' | 'sdk';
|
|
44
|
-
root: string;
|
|
45
|
-
};
|
|
44
|
+
export type TreeseedIntegratedDevWatchEntry = TreeseedDevWatchEntry;
|
|
46
45
|
export type TreeseedIntegratedDevSetupStep = {
|
|
47
46
|
id: string;
|
|
48
47
|
label: string;
|
|
@@ -77,22 +76,15 @@ type SpawnLike = (command: string, args: string[], options: SpawnOptions) => Chi
|
|
|
77
76
|
type SpawnSyncLike = typeof spawnSync;
|
|
78
77
|
type SignalRegistrar = (signal: NodeJS.Signals, handler: () => void) => () => void;
|
|
79
78
|
type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
|
|
80
|
-
type
|
|
81
|
-
|
|
82
|
-
tenantChanged: boolean;
|
|
83
|
-
packageChanged: boolean;
|
|
84
|
-
sdkChanged: boolean;
|
|
85
|
-
};
|
|
86
|
-
type WatchStarter = (input: {
|
|
87
|
-
watchEntries: TreeseedIntegratedDevWatchEntry[];
|
|
88
|
-
onChange: (change: WatchChange) => void | Promise<void>;
|
|
89
|
-
}) => () => void;
|
|
79
|
+
type ProcessKiller = (pid: number, signal: NodeJS.Signals) => void;
|
|
80
|
+
type WatchStarter = TreeseedDevWatchStarter;
|
|
90
81
|
type TreeseedIntegratedDevDependencies = {
|
|
91
82
|
spawn: SpawnLike;
|
|
92
83
|
spawnSync: SpawnSyncLike;
|
|
93
84
|
onSignal: SignalRegistrar;
|
|
94
85
|
prepareEnvironment: (tenantRoot: string) => void;
|
|
95
86
|
fetch: FetchLike;
|
|
87
|
+
killProcess: ProcessKiller;
|
|
96
88
|
write: (line: string, stream: 'stdout' | 'stderr') => void;
|
|
97
89
|
openBrowser: (url: string) => void | Promise<void>;
|
|
98
90
|
startWatch: WatchStarter;
|
package/dist/dev.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { spawn, spawnSync } from "node:child_process";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
-
import { dirname,
|
|
4
|
+
import { dirname, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { setTimeout as delay } from "node:timers/promises";
|
|
7
7
|
import {
|
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
findNearestTreeseedWorkspaceRoot,
|
|
12
12
|
resolveTreeseedToolBinary
|
|
13
13
|
} from "@treeseed/sdk/workflow-support";
|
|
14
|
+
import {
|
|
15
|
+
startPollingWatch
|
|
16
|
+
} from "./dev-watch.js";
|
|
14
17
|
const require2 = createRequire(import.meta.url);
|
|
15
18
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
16
19
|
const TREESEED_DEFAULT_WEB_HOST = "127.0.0.1";
|
|
@@ -19,10 +22,10 @@ const TREESEED_DEFAULT_API_HOST = "127.0.0.1";
|
|
|
19
22
|
const TREESEED_DEFAULT_API_PORT = 3e3;
|
|
20
23
|
const TREESEED_DEFAULT_MANAGER_PORT = 3100;
|
|
21
24
|
const DEV_RELOAD_FILE = "public/__treeseed/dev-reload.json";
|
|
22
|
-
const WATCH_INTERVAL_MS = 900;
|
|
23
|
-
const WATCH_DEBOUNCE_MS = 350;
|
|
24
25
|
const DEFAULT_READINESS_TIMEOUT_MS = 9e4;
|
|
25
26
|
const DEFAULT_PROCESS_READY_GRACE_MS = 1200;
|
|
27
|
+
const DEFAULT_SHUTDOWN_GRACE_MS = 2500;
|
|
28
|
+
const DEFAULT_KILL_GRACE_MS = 500;
|
|
26
29
|
function resolvePackageRoot(packageName, tenantRoot) {
|
|
27
30
|
const resolvedPath = require2.resolve(packageName, {
|
|
28
31
|
paths: [tenantRoot, packageRoot, process.cwd()]
|
|
@@ -91,9 +94,6 @@ function resolveTenantApiEntrypoint(tenantRoot, runTsPath) {
|
|
|
91
94
|
}
|
|
92
95
|
return null;
|
|
93
96
|
}
|
|
94
|
-
function withWatchArgs(args, watchPaths) {
|
|
95
|
-
return watchPaths.flatMap((watchPath) => ["--watch-path", watchPath]).concat(args);
|
|
96
|
-
}
|
|
97
97
|
function normalizePort(value, fallback) {
|
|
98
98
|
return Number.isInteger(value) && Number(value) > 0 ? Number(value) : fallback;
|
|
99
99
|
}
|
|
@@ -115,21 +115,30 @@ function webUrlFor(host, port) {
|
|
|
115
115
|
function createWatchEntries(tenantRoot, sdkPackageRoot) {
|
|
116
116
|
const entries = [
|
|
117
117
|
{ kind: "tenant", root: resolve(tenantRoot, "src") },
|
|
118
|
+
{ kind: "tenant", root: resolve(tenantRoot, "content") },
|
|
118
119
|
{ kind: "tenant", root: resolve(tenantRoot, "public") },
|
|
119
120
|
{ kind: "tenant", root: resolve(tenantRoot, "astro.config.ts") },
|
|
120
|
-
{ kind: "tenant", root: resolve(tenantRoot, "
|
|
121
|
+
{ kind: "tenant", root: resolve(tenantRoot, "astro.config.mjs") },
|
|
122
|
+
{ kind: "tenant", root: resolve(tenantRoot, "treeseed.site.yaml") },
|
|
123
|
+
{ kind: "tenant", root: resolve(tenantRoot, "treeseed.config.ts") },
|
|
124
|
+
{ kind: "tenant", root: resolve(tenantRoot, "package.json") },
|
|
125
|
+
{ kind: "tenant", root: resolve(tenantRoot, "tsconfig.json") }
|
|
121
126
|
];
|
|
122
127
|
if (!packageRoot.split(sep).includes("node_modules")) {
|
|
123
128
|
entries.push(
|
|
124
129
|
{ kind: "package", root: resolve(packageRoot, "src") },
|
|
125
|
-
{ kind: "package", root: resolve(packageRoot, "scripts") },
|
|
130
|
+
{ kind: "package", root: resolve(packageRoot, "scripts", "dev-platform.ts") },
|
|
131
|
+
{ kind: "package", root: resolve(packageRoot, "scripts", "build-tenant-worker.ts") },
|
|
132
|
+
{ kind: "package", root: resolve(packageRoot, "scripts", "run-ts.mjs") },
|
|
126
133
|
{ kind: "package", root: resolve(packageRoot, "package.json") }
|
|
127
134
|
);
|
|
128
135
|
}
|
|
129
136
|
if (!sdkPackageRoot.split(sep).includes("node_modules")) {
|
|
130
137
|
entries.push(
|
|
131
138
|
{ kind: "sdk", root: resolve(sdkPackageRoot, "src") },
|
|
132
|
-
{ kind: "sdk", root: resolve(sdkPackageRoot, "scripts") },
|
|
139
|
+
{ kind: "sdk", root: resolve(sdkPackageRoot, "scripts", "tenant-astro-command.ts") },
|
|
140
|
+
{ kind: "sdk", root: resolve(sdkPackageRoot, "scripts", "tenant-d1-migrate-local.ts") },
|
|
141
|
+
{ kind: "sdk", root: resolve(sdkPackageRoot, "scripts", "run-ts.mjs") },
|
|
133
142
|
{ kind: "sdk", root: resolve(sdkPackageRoot, "package.json") }
|
|
134
143
|
);
|
|
135
144
|
}
|
|
@@ -247,13 +256,6 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
247
256
|
"dist/services/worker.js"
|
|
248
257
|
);
|
|
249
258
|
const watchEntries = watch ? createWatchEntries(tenantRoot, sdkPackageRoot) : [];
|
|
250
|
-
const watchPaths = [
|
|
251
|
-
resolve(packageRoot, existsSync(resolve(packageRoot, "src")) ? "src" : "dist"),
|
|
252
|
-
resolve(tenantRoot, "src"),
|
|
253
|
-
resolve(tenantRoot, "public"),
|
|
254
|
-
resolve(tenantRoot, "treeseed.site.yaml"),
|
|
255
|
-
resolve(tenantRoot, "astro.config.ts")
|
|
256
|
-
];
|
|
257
259
|
const sharedEnv = {
|
|
258
260
|
...mergedEnv,
|
|
259
261
|
TREESEED_LOCAL_DEV_MODE: mergedEnv.TREESEED_LOCAL_DEV_MODE ?? "cloudflare",
|
|
@@ -284,7 +286,7 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
284
286
|
id: "api",
|
|
285
287
|
label: "Hono API",
|
|
286
288
|
command: apiEntrypoint.command,
|
|
287
|
-
args:
|
|
289
|
+
args: apiEntrypoint.args,
|
|
288
290
|
cwd: tenantRoot,
|
|
289
291
|
env: {
|
|
290
292
|
...sharedEnv,
|
|
@@ -297,7 +299,7 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
297
299
|
id: "manager",
|
|
298
300
|
label: "Manager",
|
|
299
301
|
command: managerEntrypoint.command,
|
|
300
|
-
args:
|
|
302
|
+
args: managerEntrypoint.args,
|
|
301
303
|
cwd: tenantRoot,
|
|
302
304
|
env: {
|
|
303
305
|
...sharedEnv,
|
|
@@ -311,7 +313,7 @@ function createTreeseedIntegratedDevPlan(options = {}) {
|
|
|
311
313
|
id: "worker",
|
|
312
314
|
label: "Worker",
|
|
313
315
|
command: workerEntrypoint.command,
|
|
314
|
-
args:
|
|
316
|
+
args: workerEntrypoint.args,
|
|
315
317
|
cwd: tenantRoot,
|
|
316
318
|
env: sharedEnv
|
|
317
319
|
});
|
|
@@ -367,15 +369,57 @@ function defaultPrepareEnvironment(tenantRoot) {
|
|
|
367
369
|
applyTreeseedEnvironmentToProcess({ tenantRoot, scope: "local", override: true });
|
|
368
370
|
assertTreeseedCommandEnvironment({ tenantRoot, scope: "local", purpose: "dev" });
|
|
369
371
|
}
|
|
370
|
-
function
|
|
371
|
-
|
|
372
|
+
function defaultKillProcess(pid, signal) {
|
|
373
|
+
process.kill(pid, signal);
|
|
374
|
+
}
|
|
375
|
+
function createManagedDevProcess(command, child) {
|
|
376
|
+
let resolveExit = () => {
|
|
377
|
+
};
|
|
378
|
+
const exitPromise = new Promise((resolvePromise) => {
|
|
379
|
+
resolveExit = resolvePromise;
|
|
380
|
+
});
|
|
381
|
+
return {
|
|
382
|
+
id: command.id,
|
|
383
|
+
command,
|
|
384
|
+
child,
|
|
385
|
+
pid: typeof child.pid === "number" ? child.pid : null,
|
|
386
|
+
exited: false,
|
|
387
|
+
intentionalStop: false,
|
|
388
|
+
exitCode: null,
|
|
389
|
+
exitSignal: null,
|
|
390
|
+
resolveExit,
|
|
391
|
+
exitPromise
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function signalManagedProcess(managed, signal, killProcess) {
|
|
395
|
+
if (managed.pid != null && process.platform !== "win32") {
|
|
396
|
+
try {
|
|
397
|
+
killProcess(-managed.pid, signal);
|
|
398
|
+
return;
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (typeof managed.child.kill !== "function") {
|
|
372
403
|
return;
|
|
373
404
|
}
|
|
374
405
|
try {
|
|
375
|
-
child.kill(signal);
|
|
406
|
+
managed.child.kill(signal);
|
|
376
407
|
} catch {
|
|
377
408
|
}
|
|
378
409
|
}
|
|
410
|
+
async function stopManagedProcess(managed, signal, killProcess, graceMs) {
|
|
411
|
+
managed.intentionalStop = true;
|
|
412
|
+
signalManagedProcess(managed, signal, killProcess);
|
|
413
|
+
if (!managed.exited) {
|
|
414
|
+
await Promise.race([managed.exitPromise, delay(Math.max(0, graceMs))]);
|
|
415
|
+
}
|
|
416
|
+
if (signal !== "SIGKILL") {
|
|
417
|
+
signalManagedProcess(managed, "SIGKILL", killProcess);
|
|
418
|
+
if (!managed.exited) {
|
|
419
|
+
await Promise.race([managed.exitPromise, delay(DEFAULT_KILL_GRACE_MS)]);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
379
423
|
function writeDevReloadStamp(projectRoot) {
|
|
380
424
|
const outputPath = resolve(projectRoot, DEV_RELOAD_FILE);
|
|
381
425
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
@@ -393,131 +437,6 @@ function writeDevReloadStamp(projectRoot) {
|
|
|
393
437
|
"utf8"
|
|
394
438
|
);
|
|
395
439
|
}
|
|
396
|
-
function shouldIgnoreWatchPath(filePath, rootPath) {
|
|
397
|
-
const rel = relative(rootPath, filePath);
|
|
398
|
-
if (!rel || rel.startsWith(`..${sep}`) || rel === "..") {
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
const normalized = rel.split(sep).join("/");
|
|
402
|
-
return normalized === ".git" || normalized.startsWith(".git/") || normalized === "node_modules" || normalized.startsWith("node_modules/") || normalized === ".astro" || normalized.startsWith(".astro/") || normalized === ".wrangler" || normalized.startsWith(".wrangler/") || normalized === ".local" || normalized.startsWith(".local/") || normalized === ".treeseed" || normalized.startsWith(".treeseed/") || normalized === "dist" || normalized.startsWith("dist/") || normalized === "coverage" || normalized.startsWith("coverage/") || normalized.startsWith("public/books/") || normalized.startsWith("public/__treeseed/");
|
|
403
|
-
}
|
|
404
|
-
function collectRootSnapshot(rootPath, snapshot) {
|
|
405
|
-
if (!existsSync(rootPath)) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
const stats = statSync(rootPath);
|
|
409
|
-
if (stats.isFile()) {
|
|
410
|
-
snapshot.set(rootPath, `${stats.mtimeMs}:${stats.size}`);
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
for (const entry of readdirSync(rootPath, { withFileTypes: true })) {
|
|
414
|
-
const fullPath = resolve(rootPath, entry.name);
|
|
415
|
-
if (shouldIgnoreWatchPath(fullPath, rootPath)) {
|
|
416
|
-
continue;
|
|
417
|
-
}
|
|
418
|
-
if (entry.isDirectory()) {
|
|
419
|
-
collectDirectorySnapshot(fullPath, rootPath, snapshot);
|
|
420
|
-
continue;
|
|
421
|
-
}
|
|
422
|
-
const entryStats = statSync(fullPath);
|
|
423
|
-
snapshot.set(fullPath, `${entryStats.mtimeMs}:${entryStats.size}`);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
function collectDirectorySnapshot(directoryPath, rootPath, snapshot) {
|
|
427
|
-
if (shouldIgnoreWatchPath(directoryPath, rootPath)) {
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
for (const entry of readdirSync(directoryPath, { withFileTypes: true })) {
|
|
431
|
-
const fullPath = resolve(directoryPath, entry.name);
|
|
432
|
-
if (shouldIgnoreWatchPath(fullPath, rootPath)) {
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
if (entry.isDirectory()) {
|
|
436
|
-
collectDirectorySnapshot(fullPath, rootPath, snapshot);
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
const stats = statSync(fullPath);
|
|
440
|
-
snapshot.set(fullPath, `${stats.mtimeMs}:${stats.size}`);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
function collectSnapshot(entries) {
|
|
444
|
-
const snapshot = /* @__PURE__ */ new Map();
|
|
445
|
-
for (const entry of entries) {
|
|
446
|
-
collectRootSnapshot(entry.root, snapshot);
|
|
447
|
-
}
|
|
448
|
-
return snapshot;
|
|
449
|
-
}
|
|
450
|
-
function diffSnapshots(previousSnapshot, nextSnapshot) {
|
|
451
|
-
const changed = /* @__PURE__ */ new Set();
|
|
452
|
-
for (const [filePath, signature] of nextSnapshot.entries()) {
|
|
453
|
-
if (previousSnapshot.get(filePath) !== signature) {
|
|
454
|
-
changed.add(filePath);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
for (const filePath of previousSnapshot.keys()) {
|
|
458
|
-
if (!nextSnapshot.has(filePath)) {
|
|
459
|
-
changed.add(filePath);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return [...changed];
|
|
463
|
-
}
|
|
464
|
-
function classifyChanges(changedPaths, watchEntries) {
|
|
465
|
-
function matchesEntry(filePath, entry) {
|
|
466
|
-
return filePath === entry.root || filePath.startsWith(`${entry.root}${sep}`);
|
|
467
|
-
}
|
|
468
|
-
return {
|
|
469
|
-
changedPaths,
|
|
470
|
-
sdkChanged: changedPaths.some(
|
|
471
|
-
(filePath) => watchEntries.some((entry) => entry.kind === "sdk" && matchesEntry(filePath, entry))
|
|
472
|
-
),
|
|
473
|
-
packageChanged: changedPaths.some(
|
|
474
|
-
(filePath) => watchEntries.some((entry) => entry.kind === "package" && matchesEntry(filePath, entry))
|
|
475
|
-
),
|
|
476
|
-
tenantChanged: changedPaths.some(
|
|
477
|
-
(filePath) => watchEntries.some((entry) => entry.kind === "tenant" && matchesEntry(filePath, entry))
|
|
478
|
-
)
|
|
479
|
-
};
|
|
480
|
-
}
|
|
481
|
-
function startPollingWatch({ watchEntries, onChange }) {
|
|
482
|
-
let previousSnapshot = collectSnapshot(watchEntries);
|
|
483
|
-
let queuedPaths = [];
|
|
484
|
-
let debounceTimer = null;
|
|
485
|
-
let running = false;
|
|
486
|
-
const intervalId = setInterval(() => {
|
|
487
|
-
const nextSnapshot = collectSnapshot(watchEntries);
|
|
488
|
-
const changedPaths = diffSnapshots(previousSnapshot, nextSnapshot);
|
|
489
|
-
previousSnapshot = nextSnapshot;
|
|
490
|
-
if (changedPaths.length === 0) {
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
queuedPaths.push(...changedPaths);
|
|
494
|
-
if (debounceTimer) {
|
|
495
|
-
clearTimeout(debounceTimer);
|
|
496
|
-
}
|
|
497
|
-
debounceTimer = setTimeout(() => {
|
|
498
|
-
void flush();
|
|
499
|
-
}, WATCH_DEBOUNCE_MS);
|
|
500
|
-
}, WATCH_INTERVAL_MS);
|
|
501
|
-
async function flush() {
|
|
502
|
-
if (running || queuedPaths.length === 0) {
|
|
503
|
-
return;
|
|
504
|
-
}
|
|
505
|
-
const changedPaths = [...new Set(queuedPaths)];
|
|
506
|
-
queuedPaths = [];
|
|
507
|
-
running = true;
|
|
508
|
-
try {
|
|
509
|
-
await onChange(classifyChanges(changedPaths, watchEntries));
|
|
510
|
-
} finally {
|
|
511
|
-
running = false;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
return () => {
|
|
515
|
-
if (debounceTimer) {
|
|
516
|
-
clearTimeout(debounceTimer);
|
|
517
|
-
}
|
|
518
|
-
clearInterval(intervalId);
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
440
|
function defaultWrite(line, stream) {
|
|
522
441
|
const target = stream === "stderr" ? process.stderr : process.stdout;
|
|
523
442
|
target.write(line);
|
|
@@ -738,6 +657,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
738
657
|
const spawnSyncProcess = deps.spawnSync ?? spawnSync;
|
|
739
658
|
const onSignal = deps.onSignal ?? defaultSignalRegistrar;
|
|
740
659
|
const fetchFn = deps.fetch ?? globalThis.fetch.bind(globalThis);
|
|
660
|
+
const killProcess = deps.killProcess ?? defaultKillProcess;
|
|
741
661
|
const openBrowser = deps.openBrowser ?? defaultOpenBrowser;
|
|
742
662
|
const startWatch = deps.startWatch ?? startPollingWatch;
|
|
743
663
|
const prepareEnvironment = deps.prepareEnvironment ?? defaultPrepareEnvironment;
|
|
@@ -762,33 +682,47 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
762
682
|
}
|
|
763
683
|
const children = /* @__PURE__ */ new Map();
|
|
764
684
|
const commandsById = new Map(plan.commands.map((command) => [command.id, command]));
|
|
685
|
+
const requiredSurfaceIds = new Set(plan.readyChecks.filter((check) => check.required).map((check) => check.id));
|
|
765
686
|
const exited = /* @__PURE__ */ new Map();
|
|
766
|
-
let
|
|
687
|
+
let watchController = null;
|
|
767
688
|
let settled = false;
|
|
768
|
-
let
|
|
689
|
+
let readinessComplete = false;
|
|
690
|
+
let restartInProgress = false;
|
|
691
|
+
const shutdownGraceMs = options.shutdownGraceMs ?? DEFAULT_SHUTDOWN_GRACE_MS;
|
|
769
692
|
return await new Promise((resolveExitCode) => {
|
|
770
693
|
const disposers = [
|
|
771
694
|
onSignal("SIGINT", () => finalize(130)),
|
|
772
695
|
onSignal("SIGTERM", () => finalize(143))
|
|
773
696
|
];
|
|
774
|
-
function
|
|
697
|
+
function stopWatching() {
|
|
698
|
+
if (!watchController) {
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
watchController.stop();
|
|
702
|
+
watchController = null;
|
|
703
|
+
}
|
|
704
|
+
function finalize(exitCode) {
|
|
775
705
|
if (settled) {
|
|
776
706
|
return;
|
|
777
707
|
}
|
|
778
708
|
settled = true;
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
}
|
|
709
|
+
void finalizeAsync(exitCode);
|
|
710
|
+
}
|
|
711
|
+
async function finalizeAsync(exitCode) {
|
|
712
|
+
stopWatching();
|
|
713
|
+
await Promise.all(
|
|
714
|
+
[...children.values()].map((managed) => stopManagedProcess(managed, "SIGTERM", killProcess, shutdownGraceMs))
|
|
715
|
+
);
|
|
716
|
+
children.clear();
|
|
788
717
|
for (const dispose of disposers) {
|
|
789
718
|
dispose();
|
|
790
719
|
}
|
|
791
|
-
emitEvent(
|
|
720
|
+
emitEvent(
|
|
721
|
+
options,
|
|
722
|
+
write,
|
|
723
|
+
{ type: "shutdown", exitCode, message: `Dev runtime stopped with exit code ${exitCode}.` },
|
|
724
|
+
exitCode === 0 ? "stdout" : "stderr"
|
|
725
|
+
);
|
|
792
726
|
resolveExitCode(exitCode);
|
|
793
727
|
}
|
|
794
728
|
function spawnCommand(command) {
|
|
@@ -802,26 +736,46 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
802
736
|
const child = spawnProcess(command.command, command.args, {
|
|
803
737
|
cwd: command.cwd,
|
|
804
738
|
env: command.env,
|
|
805
|
-
stdio: options.stdio ?? ["
|
|
806
|
-
detached:
|
|
739
|
+
stdio: options.stdio ?? ["ignore", "pipe", "pipe"],
|
|
740
|
+
detached: true
|
|
807
741
|
});
|
|
808
|
-
|
|
742
|
+
const managed = createManagedDevProcess(command, child);
|
|
743
|
+
children.set(command.id, managed);
|
|
809
744
|
attachPrefixedLogReader(child, command.id, options, write);
|
|
810
745
|
child.on("exit", (code, signal) => {
|
|
811
|
-
|
|
746
|
+
managed.exited = true;
|
|
747
|
+
managed.exitCode = code;
|
|
748
|
+
managed.exitSignal = signal;
|
|
749
|
+
managed.resolveExit();
|
|
812
750
|
exited.set(command.id, { code, signal });
|
|
813
|
-
if (
|
|
751
|
+
if (managed.intentionalStop || settled) {
|
|
814
752
|
return;
|
|
815
753
|
}
|
|
816
754
|
const exitCode = signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : code ?? 0;
|
|
755
|
+
const required = requiredSurfaceIds.has(command.id);
|
|
756
|
+
if (required) {
|
|
757
|
+
emitEvent(options, write, {
|
|
758
|
+
type: "error",
|
|
759
|
+
surface: command.id,
|
|
760
|
+
exitCode,
|
|
761
|
+
signal,
|
|
762
|
+
message: `${command.label} exited unexpectedly during ${readinessComplete ? "supervision" : "startup"} with ${signal ?? exitCode}.`
|
|
763
|
+
});
|
|
764
|
+
finalize(exitCode === 0 ? 1 : exitCode);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
const status = exitCode === 0 ? "idle" : "degraded";
|
|
817
768
|
emitEvent(options, write, {
|
|
818
|
-
type:
|
|
769
|
+
type: "error",
|
|
819
770
|
surface: command.id,
|
|
820
771
|
exitCode,
|
|
821
772
|
signal,
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
773
|
+
status,
|
|
774
|
+
message: readinessComplete ? `${command.label} exited with ${signal ?? exitCode}; continuing because it is not a required surface.` : `${command.label} exited during startup with ${signal ?? exitCode}; continuing because it is not a required surface.`
|
|
775
|
+
}, status === "idle" ? "stdout" : "stderr");
|
|
776
|
+
void stopManagedProcess(managed, "SIGTERM", killProcess, 0).finally(() => {
|
|
777
|
+
children.delete(command.id);
|
|
778
|
+
});
|
|
825
779
|
});
|
|
826
780
|
return child;
|
|
827
781
|
}
|
|
@@ -831,17 +785,64 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
831
785
|
return;
|
|
832
786
|
}
|
|
833
787
|
const current = children.get(id);
|
|
834
|
-
restarting = true;
|
|
835
788
|
if (current) {
|
|
836
|
-
|
|
837
|
-
await delay(350);
|
|
789
|
+
await stopManagedProcess(current, "SIGTERM", killProcess, Math.min(shutdownGraceMs, 500));
|
|
838
790
|
}
|
|
839
791
|
children.delete(id);
|
|
840
792
|
exited.delete(id);
|
|
841
|
-
|
|
793
|
+
if (settled) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
842
796
|
spawnCommand(command);
|
|
843
797
|
emitEvent(options, write, { type: "restart", surface: id, message: `Restarted ${command.label}.` });
|
|
844
798
|
}
|
|
799
|
+
function startLiveWatch() {
|
|
800
|
+
if (watchController || plan.watchEntries.length === 0 || plan.feedbackMode === "off" || settled) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
watchController = startWatch({
|
|
804
|
+
watchEntries: plan.watchEntries,
|
|
805
|
+
onChange: async (change) => {
|
|
806
|
+
if (settled) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
if (restartInProgress) {
|
|
810
|
+
watchController?.rebaseline();
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
restartInProgress = true;
|
|
814
|
+
try {
|
|
815
|
+
emitEvent(options, write, {
|
|
816
|
+
type: "restart",
|
|
817
|
+
message: `Detected ${change.changedPaths.length} development change${change.changedPaths.length === 1 ? "" : "s"}.`,
|
|
818
|
+
detail: {
|
|
819
|
+
tenantChanged: change.tenantChanged,
|
|
820
|
+
tenantApiChanged: change.tenantApiChanged,
|
|
821
|
+
packageChanged: change.packageChanged,
|
|
822
|
+
sdkChanged: change.sdkChanged
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
if (change.packageChanged || change.sdkChanged) {
|
|
826
|
+
await Promise.all([
|
|
827
|
+
restartCommand("api"),
|
|
828
|
+
restartCommand("manager"),
|
|
829
|
+
restartCommand("worker")
|
|
830
|
+
]);
|
|
831
|
+
} else if (change.tenantApiChanged) {
|
|
832
|
+
await restartCommand("api");
|
|
833
|
+
}
|
|
834
|
+
if (plan.feedbackMode === "live") {
|
|
835
|
+
writeDevReloadStamp(plan.tenantRoot);
|
|
836
|
+
emitEvent(options, write, { type: "reload", message: "Wrote browser reload stamp." });
|
|
837
|
+
}
|
|
838
|
+
} finally {
|
|
839
|
+
watchController?.rebaseline();
|
|
840
|
+
restartInProgress = false;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
watchController.rebaseline();
|
|
845
|
+
}
|
|
845
846
|
async function waitForReadiness() {
|
|
846
847
|
const readinessTimeoutMs = options.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;
|
|
847
848
|
const processReadyGraceMs = options.processReadyGraceMs ?? DEFAULT_PROCESS_READY_GRACE_MS;
|
|
@@ -854,7 +855,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
854
855
|
ready = await waitForHttpReady(fetchFn, check.url, readinessTimeoutMs);
|
|
855
856
|
} else {
|
|
856
857
|
await delay(processReadyGraceMs);
|
|
857
|
-
ready = !exited.has(check.id);
|
|
858
|
+
ready = !exited.has(check.id) && children.has(check.id);
|
|
858
859
|
}
|
|
859
860
|
if (settled) {
|
|
860
861
|
return;
|
|
@@ -866,7 +867,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
866
867
|
url: check.url,
|
|
867
868
|
message: `${check.label} did not become ready${check.url ? ` at ${check.url}` : ""}.`
|
|
868
869
|
});
|
|
869
|
-
finalize(1
|
|
870
|
+
finalize(1);
|
|
870
871
|
return;
|
|
871
872
|
}
|
|
872
873
|
emitEvent(options, write, {
|
|
@@ -877,6 +878,7 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
877
878
|
message: `${check.label} is ${ready ? "ready" : "degraded"}.`
|
|
878
879
|
});
|
|
879
880
|
}
|
|
881
|
+
readinessComplete = true;
|
|
880
882
|
if (plan.webUrl) {
|
|
881
883
|
emitEvent(options, write, { type: "ready", url: plan.webUrl, message: `Treeseed dev ready at ${plan.webUrl}.` });
|
|
882
884
|
}
|
|
@@ -894,43 +896,19 @@ async function runTreeseedIntegratedDev(options = {}, deps = {}) {
|
|
|
894
896
|
});
|
|
895
897
|
}
|
|
896
898
|
}
|
|
899
|
+
startLiveWatch();
|
|
897
900
|
}
|
|
898
901
|
for (const command of plan.commands) {
|
|
899
902
|
spawnCommand(command);
|
|
900
903
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
emitEvent(options, write, {
|
|
909
|
-
type: "restart",
|
|
910
|
-
message: `Detected ${change.changedPaths.length} change${change.changedPaths.length === 1 ? "" : "s"}.`,
|
|
911
|
-
detail: {
|
|
912
|
-
tenantChanged: change.tenantChanged,
|
|
913
|
-
packageChanged: change.packageChanged,
|
|
914
|
-
sdkChanged: change.sdkChanged
|
|
915
|
-
}
|
|
916
|
-
});
|
|
917
|
-
if (change.packageChanged || change.sdkChanged) {
|
|
918
|
-
await Promise.all([
|
|
919
|
-
restartCommand("api"),
|
|
920
|
-
restartCommand("manager"),
|
|
921
|
-
restartCommand("worker")
|
|
922
|
-
]);
|
|
923
|
-
} else if (change.tenantChanged) {
|
|
924
|
-
await restartCommand("api");
|
|
925
|
-
}
|
|
926
|
-
if (plan.feedbackMode === "live") {
|
|
927
|
-
writeDevReloadStamp(plan.tenantRoot);
|
|
928
|
-
emitEvent(options, write, { type: "reload", message: "Wrote browser reload stamp." });
|
|
929
|
-
}
|
|
930
|
-
}
|
|
904
|
+
void waitForReadiness().catch((error) => {
|
|
905
|
+
emitEvent(options, write, {
|
|
906
|
+
type: "error",
|
|
907
|
+
message: "Dev readiness failed.",
|
|
908
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
931
909
|
});
|
|
932
|
-
|
|
933
|
-
|
|
910
|
+
finalize(1);
|
|
911
|
+
});
|
|
934
912
|
});
|
|
935
913
|
}
|
|
936
914
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@treeseed/core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.18",
|
|
4
4
|
"description": "Treeseed integrated platform starter for Astro/Starlight web runtimes and Hono API runtimes.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"repository": {
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@astrojs/sitemap": "3.7.0",
|
|
77
77
|
"@astrojs/starlight": "0.37.6",
|
|
78
78
|
"@tailwindcss/vite": "^4.1.4",
|
|
79
|
-
"@treeseed/sdk": "0.6.
|
|
79
|
+
"@treeseed/sdk": "0.6.16",
|
|
80
80
|
"astro": "^5.6.1",
|
|
81
81
|
"esbuild": "^0.28.0",
|
|
82
82
|
"hono": "^4.8.2",
|