@cosmicdrift/kumiko-dev-server 0.387.0 → 0.388.0
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/build-server-bundle.d.ts +1 -1
- package/dist/build-server-bundle.js +2 -2
- package/dist/create-kumiko-server-client-bundles.d.ts +21 -0
- package/dist/create-kumiko-server-client-bundles.js +97 -0
- package/dist/create-kumiko-server-html.d.ts +21 -0
- package/dist/create-kumiko-server-html.js +132 -0
- package/dist/create-kumiko-server-public-files.d.ts +6 -0
- package/dist/create-kumiko-server-public-files.js +96 -0
- package/dist/create-kumiko-server-reload.d.ts +14 -0
- package/dist/create-kumiko-server-reload.js +73 -0
- package/dist/create-kumiko-server-routes.d.ts +16 -0
- package/dist/create-kumiko-server-routes.js +206 -0
- package/dist/create-kumiko-server-shutdown.d.ts +14 -0
- package/dist/create-kumiko-server-shutdown.js +72 -0
- package/dist/create-kumiko-server-stack.d.ts +12 -0
- package/dist/create-kumiko-server-stack.js +101 -0
- package/dist/create-kumiko-server-stylesheet.d.ts +10 -0
- package/dist/create-kumiko-server-stylesheet.js +137 -0
- package/dist/create-kumiko-server-watcher.d.ts +22 -0
- package/dist/create-kumiko-server-watcher.js +155 -0
- package/dist/create-kumiko-server.d.ts +6 -20
- package/dist/create-kumiko-server.js +59 -1046
- package/dist/dev-server-runtime.d.ts +5 -0
- package/dist/dev-server-runtime.js +11 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/managed-files/app-jobs.js +1 -1
- package/dist/managed-files/check.js +1 -1
- package/dist/run-dev-app-auth.d.ts +38 -0
- package/dist/run-dev-app-auth.js +155 -0
- package/dist/run-dev-app-config.d.ts +26 -0
- package/dist/run-dev-app-config.js +185 -0
- package/dist/run-dev-app-server-options.d.ts +13 -0
- package/dist/run-dev-app-server-options.js +49 -0
- package/dist/run-dev-app.d.ts +4 -4
- package/dist/run-dev-app.js +86 -459
- package/dist/schema-apply.d.ts +1 -1
- package/dist/schema-apply.js +4 -4
- package/dist/write-server-lockfile.js +23 -16
- package/package.json +9 -9
- package/src/changes.json +30 -0
- package/templates/managed/ci-public.yml.template +4 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/// <reference types="bun-types" preserve="true" />
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { watch } from "node:fs/promises";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { rebuildClientBundles, } from "./create-kumiko-server-client-bundles.js";
|
|
6
|
+
import { broadcastReload } from "./create-kumiko-server-reload.js";
|
|
7
|
+
import { logInfo } from "./dev-server-runtime.js";
|
|
8
|
+
async function watchDir(dir, onChange, signal, watchEvents) {
|
|
9
|
+
// The server's stop() aborts the signal; without it, the for-await loop
|
|
10
|
+
// would continue until process exit. During test teardown, removing the
|
|
11
|
+
// temporary directory could then be classified as a restart and call
|
|
12
|
+
// process.exit(75), surfacing as an unhandled Vitest error.
|
|
13
|
+
const watcher = watchEvents(dir, { recursive: true, signal });
|
|
14
|
+
try {
|
|
15
|
+
for await (const ev of watcher) {
|
|
16
|
+
if (ev.filename)
|
|
17
|
+
onChange(ev.filename);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
// signal.abort() causes the async iterator to throw AbortError during
|
|
22
|
+
// expected teardown; other errors must propagate.
|
|
23
|
+
// skip: AbortSignal fired the abort, this is expected teardown not a real error
|
|
24
|
+
if (typeof err === "object" && err !== null && "name" in err && err.name === "AbortError")
|
|
25
|
+
return;
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const WATCHED_SOURCE_SUFFIXES = [".ts", ".tsx"];
|
|
30
|
+
const TEST_ONLY_SUFFIXES = [
|
|
31
|
+
".test.ts",
|
|
32
|
+
".test.tsx",
|
|
33
|
+
".integration.ts",
|
|
34
|
+
".integration.test.ts",
|
|
35
|
+
".e2e.ts",
|
|
36
|
+
];
|
|
37
|
+
const CLIENT_SUBDIRS = ["web", "admin", "public", "client"];
|
|
38
|
+
const CLIENT_ENTRY_SUFFIXES = ["/client.tsx", "/client.ts"];
|
|
39
|
+
function endsWithAny(filename, suffixes) {
|
|
40
|
+
return suffixes.some((suffix) => filename.endsWith(suffix));
|
|
41
|
+
}
|
|
42
|
+
function isTestOnlyFile(filename) {
|
|
43
|
+
return filename.includes("__tests__") || endsWithAny(filename, TEST_ONLY_SUFFIXES);
|
|
44
|
+
}
|
|
45
|
+
// Platform-independent: checks both POSIX and Windows separators. Matches
|
|
46
|
+
// `<sep><dir><sep>` and a leading `<dir><sep>` for watcher filenames
|
|
47
|
+
// supplied as relative paths.
|
|
48
|
+
function isInClientSubdir(filename) {
|
|
49
|
+
return CLIENT_SUBDIRS.some((dir) => filename.includes(`/${dir}/`) ||
|
|
50
|
+
filename.includes(`\\${dir}\\`) ||
|
|
51
|
+
filename.startsWith(`${dir}/`) ||
|
|
52
|
+
filename.startsWith(`${dir}\\`));
|
|
53
|
+
}
|
|
54
|
+
// Classifies a changed file as `hot-reload` for client-side changes,
|
|
55
|
+
// `restart` for server-side changes whose cached Bun modules must be
|
|
56
|
+
// reloaded, or `ignore` when the server is unaffected.
|
|
57
|
+
//
|
|
58
|
+
// Tests and non-TypeScript files are ignored. TypeScript files under
|
|
59
|
+
// `/web/`, `/admin/`, `/public/`, or `/client/`, as well as the client
|
|
60
|
+
// entry itself, trigger hot reload; other TypeScript files trigger a
|
|
61
|
+
// restart because they may contain schema or feature definitions.
|
|
62
|
+
//
|
|
63
|
+
// The multiple client directories are intentional: samples use different
|
|
64
|
+
// layouts. Restarting for a client-only edit would be slower, drop the
|
|
65
|
+
// ephemeral test database, and reseed it unnecessarily.
|
|
66
|
+
export function classifyChange(filename) {
|
|
67
|
+
if (!endsWithAny(filename, WATCHED_SOURCE_SUFFIXES))
|
|
68
|
+
return "ignore";
|
|
69
|
+
if (isTestOnlyFile(filename))
|
|
70
|
+
return "ignore";
|
|
71
|
+
if (isInClientSubdir(filename) || endsWithAny(filename, CLIENT_ENTRY_SUFFIXES)) {
|
|
72
|
+
return "hot-reload";
|
|
73
|
+
}
|
|
74
|
+
return "restart";
|
|
75
|
+
}
|
|
76
|
+
// The watcher reports paths relative to the watched directory, so an entry arrives as a bare
|
|
77
|
+
// "client.tsx" (or a custom name like "client-admin.tsx") that classifyChange alone reads as a
|
|
78
|
+
// server file and answers with a restart.
|
|
79
|
+
export function classifyWatchedChange(watchedDir, filename, entrySourceFiles) {
|
|
80
|
+
return entrySourceFiles.has(resolve(watchedDir, filename))
|
|
81
|
+
? "hot-reload"
|
|
82
|
+
: classifyChange(filename);
|
|
83
|
+
}
|
|
84
|
+
// Expands watchDirs patterns into concrete directories. Entries without
|
|
85
|
+
// `*` are resolved as ordinary paths; entries with `*` are glob-expanded
|
|
86
|
+
// and only directory matches are retained, so `"../../../packages/*/src"`
|
|
87
|
+
// replaces a list of hard-coded paths. Expansion runs once at boot,
|
|
88
|
+
// not while the watcher is running.
|
|
89
|
+
function expandWatchPatterns(patterns) {
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const p of patterns) {
|
|
92
|
+
if (!p.includes("*")) {
|
|
93
|
+
out.push(resolve(p));
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
// Only reachable under Bun (createKumikoServer is Bun-only); without Bun the
|
|
97
|
+
// call stack fails fast in Bun.serve anyway.
|
|
98
|
+
if (typeof Bun === "undefined")
|
|
99
|
+
throw new Error("expandWatchPatterns requires Bun.Glob");
|
|
100
|
+
const glob = new Bun.Glob(p);
|
|
101
|
+
const matches = Array.from(glob.scanSync({ onlyFiles: false, cwd: process.cwd() }));
|
|
102
|
+
for (const m of matches) {
|
|
103
|
+
const abs = resolve(m);
|
|
104
|
+
try {
|
|
105
|
+
if (statSync(abs).isDirectory())
|
|
106
|
+
out.push(abs);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// ignore unreadable matches, typically broken symlinks
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
// Because Bun caches module imports, a server-side change needs a process
|
|
116
|
+
// restart: exit code 75 (EX_TEMPFAIL) lets the `kumiko-dev` wrapper respawn.
|
|
117
|
+
async function handleWatchedChange(deps, dir, filename, entrySourceFiles) {
|
|
118
|
+
const action = classifyWatchedChange(dir, filename, entrySourceFiles);
|
|
119
|
+
// skip: file change classified as ignore (test/css/json), nothing to rebuild
|
|
120
|
+
if (action === "ignore")
|
|
121
|
+
return;
|
|
122
|
+
if (action === "restart") {
|
|
123
|
+
logInfo(`[kumiko-server] schema change in ${filename} — restarting (Bun caches imports, hot-reload reicht hier nicht). ` +
|
|
124
|
+
"The kumiko-dev wrapper (package.json's `dev` script) respawns automatically " +
|
|
125
|
+
"and re-syncs new r.entity() columns on the new boot; running this entry " +
|
|
126
|
+
"directly (not via kumiko-dev) exits here without restarting.");
|
|
127
|
+
await deps.stop();
|
|
128
|
+
process.exit(75);
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
await rebuildClientBundles(deps.entries, deps.buildBundle, deps.clientBundles);
|
|
132
|
+
logInfo(`[kumiko-server] rebuilt on ${filename}, broadcasting reload`);
|
|
133
|
+
broadcastReload(deps.reloadHub, filename);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// buildClient already logged the failure; keep serving the
|
|
137
|
+
// last good bundle until the next successful rebuild.
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Watches every entry directory plus the explicit watchDirs. Multi-entry
|
|
141
|
+
// setups often share one src/ directory, so the Set keeps one watcher per
|
|
142
|
+
// directory.
|
|
143
|
+
export function startDevFileWatchers(deps) {
|
|
144
|
+
// skip: without a client entry there is no bundle to rebuild, so watchDirs stay unwatched too
|
|
145
|
+
if (deps.entries.length === 0)
|
|
146
|
+
return;
|
|
147
|
+
const entryDirs = new Set();
|
|
148
|
+
for (const e of deps.entries)
|
|
149
|
+
entryDirs.add(resolve(e.sourceFile, ".."));
|
|
150
|
+
const entrySourceFiles = new Set(deps.entries.map((e) => resolve(e.sourceFile)));
|
|
151
|
+
const dirs = [...entryDirs, ...expandWatchPatterns(deps.watchDirs ?? [])];
|
|
152
|
+
for (const dir of dirs) {
|
|
153
|
+
void watchDir(dir, (filename) => handleWatchedChange(deps, dir, filename, entrySourceFiles), deps.signal, deps.watchDirectory ?? watch);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/// <reference types="bun-types" preserve="true" />
|
|
2
|
-
import {
|
|
2
|
+
import type { AuthRoutesConfig, ExtraRouteDefinition } from "@cosmicdrift/kumiko-framework/api";
|
|
3
3
|
import { type FeatureDefinition } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import type { ErrorDocsConfig } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import type { PageHeadResolver, PageHeadSystemQuery } from "@cosmicdrift/kumiko-renderer/headless/apex";
|
|
6
|
+
import type { SystemWireDeps } from "@cosmicdrift/kumiko-server-runtime/extra-routes-deps";
|
|
7
|
+
import type { TestStack, TestStackOptions } from "@cosmicdrift/kumiko-testing/stack";
|
|
8
|
+
import { type DirectoryWatchEvents } from "./create-kumiko-server-watcher.ts";
|
|
9
|
+
import { type BunServer } from "./dev-server-runtime.ts";
|
|
10
10
|
/** Multi-entry mode for apps that ship several separate bundles (e.g.
|
|
11
11
|
* publicstatus: `admin.<base>` loads the admin UI, otherwise the public
|
|
12
12
|
* page).
|
|
@@ -201,18 +201,4 @@ export type KumikoServerHandle = {
|
|
|
201
201
|
* its `bull:<prefix>-*` keys belong to this server alone on a shared Redis. */
|
|
202
202
|
readonly jobQueueNamePrefix: string;
|
|
203
203
|
};
|
|
204
|
-
export declare const STYLESHEET_WATCH_ENV = "KUMIKO_DEV_STYLESHEET_WATCH";
|
|
205
|
-
export declare const PROD_BUNDLES_ENV = "KUMIKO_DEV_PROD_BUNDLES";
|
|
206
|
-
export declare const DEFAULT_HTML = "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1,interactive-widget=resizes-content\" />\n <title>Kumiko</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/client.js\"></script>\n </body>\n</html>\n";
|
|
207
|
-
type DirectoryWatchEvents = (dir: string, options: {
|
|
208
|
-
readonly recursive: true;
|
|
209
|
-
readonly signal: AbortSignal;
|
|
210
|
-
}) => AsyncIterable<{
|
|
211
|
-
readonly filename: string | null;
|
|
212
|
-
}>;
|
|
213
|
-
export declare function classifyChange(filename: string): "restart" | "hot-reload" | "ignore";
|
|
214
|
-
export declare function classifyWatchedChange(watchedDir: string, filename: string, entrySourceFiles: ReadonlySet<string>): ReturnType<typeof classifyChange>;
|
|
215
|
-
export declare function resolveStylesheet(options: CreateKumikoServerOptions): string | undefined;
|
|
216
|
-
export declare function resolveStylesheetWatch(options: Pick<CreateKumikoServerOptions, "stylesheetWatch">, env?: Readonly<Record<string, string | undefined>>): boolean;
|
|
217
204
|
export declare function createKumikoServer(options: CreateKumikoServerOptions): Promise<KumikoServerHandle>;
|
|
218
|
-
export {};
|