@kodycodes/cli 1.23.1 → 1.23.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.
Files changed (39) hide show
  1. package/README.md +26 -0
  2. package/dist/dev/dev-bundler.d.ts +25 -0
  3. package/dist/dev/dev-bundler.js +51 -0
  4. package/dist/dev/dev-bundler.js.map +1 -0
  5. package/dist/dev/dev-events.d.ts +50 -0
  6. package/dist/dev/dev-events.js +81 -0
  7. package/dist/dev/dev-events.js.map +1 -0
  8. package/dist/dev/dev-protocol.d.ts +13 -0
  9. package/dist/dev/dev-protocol.js +14 -0
  10. package/dist/dev/dev-protocol.js.map +1 -0
  11. package/dist/dev/dev-watcher.d.ts +20 -0
  12. package/dist/dev/dev-watcher.js +108 -0
  13. package/dist/dev/dev-watcher.js.map +1 -0
  14. package/dist/dev/error-page.d.ts +21 -0
  15. package/dist/dev/error-page.js +64 -0
  16. package/dist/dev/error-page.js.map +1 -0
  17. package/dist/dev/process-reaper.d.ts +40 -0
  18. package/dist/dev/process-reaper.js +144 -0
  19. package/dist/dev/process-reaper.js.map +1 -0
  20. package/dist/dev/stack-trace.d.ts +11 -0
  21. package/dist/dev/stack-trace.js +56 -0
  22. package/dist/dev/stack-trace.js.map +1 -0
  23. package/dist/dev/workerd-process.d.ts +28 -0
  24. package/dist/dev/workerd-process.js +118 -0
  25. package/dist/dev/workerd-process.js.map +1 -0
  26. package/dist/dev-command.js +17 -4
  27. package/dist/dev-command.js.map +1 -1
  28. package/dist/help.js +5 -0
  29. package/dist/help.js.map +1 -1
  30. package/dist/local-bundle.d.ts +43 -3
  31. package/dist/local-bundle.js +94 -33
  32. package/dist/local-bundle.js.map +1 -1
  33. package/dist/package-app-dev.d.ts +16 -3
  34. package/dist/package-app-dev.js +389 -192
  35. package/dist/package-app-dev.js.map +1 -1
  36. package/dist/package-local-runtime-source.d.ts +8 -0
  37. package/dist/package-local-runtime-source.js +69 -5
  38. package/dist/package-local-runtime-source.js.map +1 -1
  39. package/package.json +3 -1
package/README.md CHANGED
@@ -240,6 +240,32 @@ kody execute --local --invoke kody:@scope/pkg/export --params '{}'
240
240
  kody search --target mock local
241
241
  ```
242
242
 
243
+ ### What `kody dev` does while it runs
244
+
245
+ - **Rebuilds never drop requests.** A save starts a new workerd, and the proxy
246
+ switches to it only once it is listening. The old one finishes its in-flight
247
+ requests and then stops. Requests that arrive mid-rebuild wait for it, so you
248
+ never get a `503` or the previous bundle after a save.
249
+ - **Watching survives editors and scripts.** chokidar watches the package
250
+ (skipping `node_modules`, dotfiles, and `*.map`). A 1s stat poll over the
251
+ files in the current bundle graph backs it up, so replaced files (`sed -i`,
252
+ `cp`, atomic saves) are always picked up.
253
+ - **Build errors** print `file:line:col: message` with a code frame. Until the
254
+ next good build, every request gets a `500` error page with the same text
255
+ (HTML for browsers, plain text otherwise, header `x-kody-dev-error: build`).
256
+ The last good build does not keep answering `200`.
257
+ - **Runtime errors**: an uncaught throw from the fetch handler returns a
258
+ dev-only `500` page with the stack mapped back to your `.ts` sources
259
+ (`x-kody-dev-error: runtime`). Runtime wrapper and native frames are
260
+ dropped. The terminal prints the same stack.
261
+ - **Logs**: app `console.*` output streams to the terminal tagged with the
262
+ request, e.g. `[GET /scores #12] saved 3 rows`. `warn` and `error` go to
263
+ stderr.
264
+ - **Cleanup**: Ctrl-C, `SIGTERM`, and `SIGHUP` stop workerd and remove the
265
+ `kody-dev-*` temp dir. If the CLI is killed outright, a small reaper process
266
+ does the same. Each start also sweeps dirs and orphaned workerd processes
267
+ left behind by dead CLI runs, including older CLI versions.
268
+
243
269
  ## Local execute
244
270
 
245
271
  `kody execute --local` runs the same execute module (default export called
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `kody dev` bundles of `kody.app.entry`: same esbuild options as one-shot
3
+ * local bundles, plus an in-memory source map (for mapping workerd stacks back
4
+ * to `.ts`) and the list of input files (for the watcher's stat poll).
5
+ */
6
+ import * as esbuild from 'esbuild';
7
+ export type DevBundle = {
8
+ code: string;
9
+ /** Source map JSON with sources relative to the package root. */
10
+ sourceMap: string | null;
11
+ /** Absolute paths of every non-node_modules input in the graph. */
12
+ inputs: Array<string>;
13
+ };
14
+ export type DevBundler = {
15
+ build: () => Promise<DevBundle>;
16
+ dispose: () => Promise<void>;
17
+ };
18
+ export declare function createDevBundler(input: {
19
+ entryPath: string;
20
+ root: string;
21
+ }): DevBundler;
22
+ export declare function readBundle(result: esbuild.BuildResult<{
23
+ metafile: true;
24
+ write: false;
25
+ }>, workingDir: string, entryPath: string): DevBundle;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `kody dev` bundles of `kody.app.entry`: same esbuild options as one-shot
3
+ * local bundles, plus an in-memory source map (for mapping workerd stacks back
4
+ * to `.ts`) and the list of input files (for the watcher's stat poll).
5
+ */
6
+ import * as esbuild from 'esbuild';
7
+ import { join, resolve } from 'node:path';
8
+ import { BundleError, createBundleError, localBundleOptions } from '../local-bundle.js';
9
+ export function createDevBundler(input) {
10
+ const entry = resolve(input.entryPath);
11
+ const base = localBundleOptions(entry);
12
+ const options = {
13
+ ...base,
14
+ sourcemap: 'external',
15
+ sourcesContent: false,
16
+ metafile: true,
17
+ // Never written (write: false); only anchors source map paths at the root.
18
+ outfile: join(input.root, '__kody_dev_bundle.js'),
19
+ };
20
+ const paths = { workingDir: base.absWorkingDir, displayRoot: input.root };
21
+ return {
22
+ build: async () => {
23
+ let result;
24
+ try {
25
+ result = await esbuild.build(options);
26
+ }
27
+ catch (error) {
28
+ throw createBundleError(input.entryPath, error, paths);
29
+ }
30
+ return readBundle(result, base.absWorkingDir, input.entryPath);
31
+ },
32
+ dispose: async () => { },
33
+ };
34
+ }
35
+ export function readBundle(result, workingDir, entryPath) {
36
+ const outputs = result.outputFiles ?? [];
37
+ const js = outputs.find((file) => file.path.endsWith('.js'));
38
+ const map = outputs.find((file) => file.path.endsWith('.js.map'));
39
+ if (!js) {
40
+ throw new BundleError(`Could not bundle ${entryPath}: esbuild produced no output.`, []);
41
+ }
42
+ const inputs = Object.keys(result.metafile?.inputs ?? {})
43
+ .filter((path) => !path.includes('node_modules') && !path.includes(':'))
44
+ .map((path) => resolve(workingDir, path));
45
+ return {
46
+ code: js.text.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, '\n'),
47
+ sourceMap: map?.text ?? null,
48
+ inputs,
49
+ };
50
+ }
51
+ //# sourceMappingURL=dev-bundler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-bundler.js","sourceRoot":"","sources":["../../src/dev/dev-bundler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAevF,MAAM,UAAU,gBAAgB,CAAC,KAA0C;IAC1E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACtC,MAAM,IAAI,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;IACtC,MAAM,OAAO,GAAG;QACf,GAAG,IAAI;QACP,SAAS,EAAE,UAAU;QACrB,cAAc,EAAE,KAAK;QACrB,QAAQ,EAAE,IAAI;QACd,2EAA2E;QAC3E,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,sBAAsB,CAAC;KAClB,CAAA;IAChC,MAAM,KAAK,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;IAEzE,OAAO;QACN,KAAK,EAAE,KAAK,IAAI,EAAE;YACjB,IAAI,MAA2C,CAAA;YAC/C,IAAI,CAAC;gBACJ,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;YACtC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,iBAAiB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;YACvD,CAAC;YACD,OAAO,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,SAAS,CAAC,CAAA;QAC/D,CAAC;QACD,OAAO,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;KACvB,CAAA;AACF,CAAC;AAED,MAAM,UAAU,UAAU,CACzB,MAA6D,EAC7D,UAAkB,EAClB,SAAiB;IAEjB,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,IAAI,EAAE,CAAA;IACxC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAA;IACjE,IAAI,CAAC,EAAE,EAAE,CAAC;QACT,MAAM,IAAI,WAAW,CAAC,oBAAoB,SAAS,+BAA+B,EAAE,EAAE,CAAC,CAAA;IACxF,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC;SACvD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;SACvE,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAA;IAC1C,OAAO;QACN,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,kCAAkC,EAAE,IAAI,CAAC;QAC/D,SAAS,EAAE,GAAG,EAAE,IAAI,IAAI,IAAI;QAC5B,MAAM;KACN,CAAA;AACF,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Events a running `kody dev` server reports: rebuilds, build and runtime
3
+ * errors, and app logs. Humans get {@link formatDevEvent}; agents get the
4
+ * same objects as NDJSON.
5
+ */
6
+ import { type BundleDiagnostic } from '../local-bundle.js';
7
+ export type DevRequestTag = {
8
+ id: string;
9
+ method: string;
10
+ path: string;
11
+ };
12
+ export type DevLogLevel = 'log' | 'info' | 'warn' | 'error' | 'debug';
13
+ export type DevEvent = {
14
+ type: 'rebuild_start';
15
+ files: Array<string>;
16
+ } | {
17
+ type: 'rebuild_ok';
18
+ ms: number;
19
+ buildId: number;
20
+ restarted: boolean;
21
+ } | {
22
+ type: 'build_error';
23
+ ms: number;
24
+ message: string;
25
+ file: string | null;
26
+ line: number | null;
27
+ col: number | null;
28
+ diagnostics: Array<BundleDiagnostic>;
29
+ } | {
30
+ type: 'runtime_error';
31
+ message: string;
32
+ stack: string;
33
+ request: DevRequestTag | null;
34
+ } | {
35
+ type: 'log';
36
+ source: 'app' | 'workerd';
37
+ level: DevLogLevel;
38
+ message: string;
39
+ request: DevRequestTag | null;
40
+ };
41
+ /** Parse one workerd stdout line; plain lines become untagged `log` events. */
42
+ export declare function parseWorkerdStdoutLine(line: string): Extract<DevEvent, {
43
+ type: 'log';
44
+ }>;
45
+ export declare function formatRequestTag(request: DevRequestTag | null): string;
46
+ /** One human-readable block for terminals. */
47
+ export declare function formatDevEvent(event: DevEvent): {
48
+ stream: 'stdout' | 'stderr';
49
+ text: string;
50
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Events a running `kody dev` server reports: rebuilds, build and runtime
3
+ * errors, and app logs. Humans get {@link formatDevEvent}; agents get the
4
+ * same objects as NDJSON.
5
+ */
6
+ import { formatBundleDiagnostic } from '../local-bundle.js';
7
+ import { appLogMarker } from './dev-protocol.js';
8
+ const logLevels = new Set(['log', 'info', 'warn', 'error', 'debug']);
9
+ /** Parse one workerd stdout line; plain lines become untagged `log` events. */
10
+ export function parseWorkerdStdoutLine(line) {
11
+ if (line.startsWith(appLogMarker)) {
12
+ try {
13
+ const parsed = JSON.parse(line.slice(appLogMarker.length));
14
+ const level = typeof parsed.level === 'string' && logLevels.has(parsed.level)
15
+ ? parsed.level
16
+ : 'log';
17
+ return {
18
+ type: 'log',
19
+ source: 'app',
20
+ level,
21
+ message: typeof parsed.message === 'string' ? parsed.message : String(parsed.message),
22
+ request: readRequestTag(parsed.request),
23
+ };
24
+ }
25
+ catch { }
26
+ }
27
+ return { type: 'log', source: 'app', level: 'log', message: line, request: null };
28
+ }
29
+ function readRequestTag(value) {
30
+ if (!value || typeof value !== 'object')
31
+ return null;
32
+ const record = value;
33
+ if (typeof record.id !== 'string')
34
+ return null;
35
+ return {
36
+ id: record.id,
37
+ method: typeof record.method === 'string' ? record.method : 'GET',
38
+ path: typeof record.path === 'string' ? record.path : '/',
39
+ };
40
+ }
41
+ export function formatRequestTag(request) {
42
+ return request ? `[${request.method} ${request.path} #${request.id}]` : '[app]';
43
+ }
44
+ /** One human-readable block for terminals. */
45
+ export function formatDevEvent(event) {
46
+ switch (event.type) {
47
+ case 'rebuild_start':
48
+ return {
49
+ stream: 'stderr',
50
+ text: event.files.length > 0 ? `Rebuilding (${event.files.join(', ')})…` : 'Building…',
51
+ };
52
+ case 'rebuild_ok':
53
+ return {
54
+ stream: 'stderr',
55
+ text: event.restarted
56
+ ? `Ready in ${event.ms}ms (build ${event.buildId})`
57
+ : `No bundle changes (${event.ms}ms)`,
58
+ };
59
+ case 'build_error':
60
+ return {
61
+ stream: 'stderr',
62
+ text: `Build failed${event.diagnostics.length > 0 ? `:\n${event.diagnostics.map(formatBundleDiagnostic).join('\n')}` : `: ${event.message}`}\nThe last good build is paused until this is fixed; requests get the error page.`,
63
+ };
64
+ case 'runtime_error':
65
+ return {
66
+ stream: 'stderr',
67
+ text: `${formatRequestTag(event.request)} Uncaught ${event.stack || event.message}`,
68
+ };
69
+ case 'log': {
70
+ const tag = event.source === 'workerd' ? '[workerd]' : formatRequestTag(event.request);
71
+ const level = event.level === 'log' || event.level === 'info' ? '' : `${event.level}: `;
72
+ const stream = event.level === 'error' || event.level === 'warn' ? 'stderr' : 'stdout';
73
+ return { stream, text: `${tag} ${level}${event.message}` };
74
+ }
75
+ default: {
76
+ const exhaustive = event;
77
+ throw new Error(`Unhandled dev event: ${JSON.stringify(exhaustive)}`);
78
+ }
79
+ }
80
+ }
81
+ //# sourceMappingURL=dev-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-events.js","sourceRoot":"","sources":["../../src/dev/dev-events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Wire constants shared by the `kody dev` proxy (Node) and the dev entry that
3
+ * runs inside workerd.
4
+ */
5
+ /**
6
+ * Prefix the dev entry puts on every captured `console.*` line it writes to
7
+ * workerd stdout. Control characters keep it from colliding with app output.
8
+ */
9
+ export declare const appLogMarker = "\u001Fkody-dev-log\u001F";
10
+ /** Proxy → workerd: short request id used to tag logs and errors. */
11
+ export declare const devRequestIdHeader = "x-kody-dev-request";
12
+ /** workerd → proxy: the response carries a serialized uncaught handler error. */
13
+ export declare const devErrorHeader = "x-kody-dev-error";
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Wire constants shared by the `kody dev` proxy (Node) and the dev entry that
3
+ * runs inside workerd.
4
+ */
5
+ /**
6
+ * Prefix the dev entry puts on every captured `console.*` line it writes to
7
+ * workerd stdout. Control characters keep it from colliding with app output.
8
+ */
9
+ export const appLogMarker = '\u001fkody-dev-log\u001f';
10
+ /** Proxy → workerd: short request id used to tag logs and errors. */
11
+ export const devRequestIdHeader = 'x-kody-dev-request';
12
+ /** workerd → proxy: the response carries a serialized uncaught handler error. */
13
+ export const devErrorHeader = 'x-kody-dev-error';
14
+ //# sourceMappingURL=dev-protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-protocol.js","sourceRoot":"","sources":["../../src/dev/dev-protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,0BAA0B,CAAA;AAEtD,qEAAqE;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAA;AAEtD,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAG,kBAAkB,CAAA"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * File watching for `kody dev`. chokidar watches the package tree (it re-arms
3
+ * per-directory watchers when editors or `sed -i` / `cp` replace a file with a
4
+ * new inode). A low-frequency stat poll over the files in the current bundle
5
+ * graph backs it up, so a missed event can never leave a stale bundle serving.
6
+ */
7
+ export type DevWatcher = {
8
+ ready: Promise<void>;
9
+ /** Replace the set of absolute paths the stat poll keeps an eye on. */
10
+ track: (paths: ReadonlyArray<string>) => void;
11
+ close: () => Promise<void>;
12
+ };
13
+ export declare function createDevWatcher(input: {
14
+ root: string;
15
+ onChange: (path: string) => void;
16
+ /** Extra root-relative path prefixes to ignore. */
17
+ ignore?: ReadonlyArray<string>;
18
+ pollIntervalMs?: number;
19
+ }): DevWatcher;
20
+ export declare function shouldIgnoreWatchPath(relativePath: string, ignorePrefixes?: ReadonlyArray<string>): boolean;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * File watching for `kody dev`. chokidar watches the package tree (it re-arms
3
+ * per-directory watchers when editors or `sed -i` / `cp` replace a file with a
4
+ * new inode). A low-frequency stat poll over the files in the current bundle
5
+ * graph backs it up, so a missed event can never leave a stale bundle serving.
6
+ */
7
+ import { watch } from 'chokidar';
8
+ import { stat } from 'node:fs/promises';
9
+ import { relative, sep } from 'node:path';
10
+ const defaultPollIntervalMs = 1000;
11
+ export function createDevWatcher(input) {
12
+ const ignorePrefixes = (input.ignore ?? []).map(normalizePrefix).filter(Boolean);
13
+ const ignored = (path) => shouldIgnoreWatchPath(relative(input.root, path), ignorePrefixes);
14
+ const watcher = watch(input.root, {
15
+ ignoreInitial: true,
16
+ ignored,
17
+ atomic: true,
18
+ });
19
+ watcher.on('all', (_event, path) => {
20
+ if (!ignored(path))
21
+ input.onChange(path);
22
+ });
23
+ // Init errors reject `ready` (awaited by start). Later errors are swallowed
24
+ // so a transient watch glitch cannot crash the process.
25
+ let readySettled = false;
26
+ const ready = new Promise((resolve, reject) => {
27
+ watcher.once('ready', () => {
28
+ readySettled = true;
29
+ resolve();
30
+ });
31
+ watcher.on('error', (error) => {
32
+ if (readySettled)
33
+ return;
34
+ readySettled = true;
35
+ reject(error instanceof Error ? error : new Error(String(error)));
36
+ });
37
+ });
38
+ let tracked = new Map();
39
+ let polling = false;
40
+ const poll = async () => {
41
+ if (polling)
42
+ return;
43
+ polling = true;
44
+ try {
45
+ for (const [path, previous] of tracked) {
46
+ const next = await fingerprint(path);
47
+ if (!tracked.has(path))
48
+ continue;
49
+ if (previous !== null && next !== previous) {
50
+ tracked.set(path, next);
51
+ input.onChange(path);
52
+ }
53
+ else if (previous === null) {
54
+ tracked.set(path, next);
55
+ }
56
+ }
57
+ }
58
+ finally {
59
+ polling = false;
60
+ }
61
+ };
62
+ const timer = setInterval(() => void poll(), input.pollIntervalMs ?? defaultPollIntervalMs);
63
+ timer.unref();
64
+ return {
65
+ ready,
66
+ track: (paths) => {
67
+ const next = new Map();
68
+ for (const path of paths) {
69
+ if (ignored(path))
70
+ continue;
71
+ next.set(path, tracked.get(path) ?? null);
72
+ }
73
+ tracked = next;
74
+ void poll();
75
+ },
76
+ close: async () => {
77
+ clearInterval(timer);
78
+ tracked = new Map();
79
+ await watcher.close();
80
+ },
81
+ };
82
+ }
83
+ async function fingerprint(path) {
84
+ try {
85
+ const info = await stat(path);
86
+ return `${info.ino}:${info.size}:${info.mtimeMs}`;
87
+ }
88
+ catch {
89
+ return 'missing';
90
+ }
91
+ }
92
+ function normalizePrefix(prefix) {
93
+ return prefix.replace(/^\.\//, '').replace(/\/+$/, '').split('/').join(sep);
94
+ }
95
+ export function shouldIgnoreWatchPath(relativePath, ignorePrefixes = []) {
96
+ if (!relativePath || relativePath === '.')
97
+ return false;
98
+ if (relativePath.startsWith('..'))
99
+ return true;
100
+ const segments = relativePath.split(sep);
101
+ if (segments.some((segment) => segment === 'node_modules' || segment.startsWith('.'))) {
102
+ return true;
103
+ }
104
+ if (relativePath.endsWith('.map'))
105
+ return true;
106
+ return ignorePrefixes.some((prefix) => relativePath === prefix || relativePath.startsWith(`${prefix}${sep}`));
107
+ }
108
+ //# sourceMappingURL=dev-watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-watcher.js","sourceRoot":"","sources":["../../src/dev/dev-watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Dev-only error responses served by the `kody dev` proxy for build failures
3
+ * and uncaught handler errors. HTML for browsers, plain text for everyone else
4
+ * (curl, agents, `kody dev fetch`).
5
+ */
6
+ import type { IncomingMessage, ServerResponse } from 'node:http';
7
+ export type DevErrorKind = 'build' | 'runtime';
8
+ export type DevErrorView = {
9
+ kind: DevErrorKind;
10
+ title: string;
11
+ /** Preformatted body: diagnostics with code frames, or a mapped stack. */
12
+ detail: string;
13
+ /** `METHOD /path` for runtime errors. */
14
+ request?: string;
15
+ };
16
+ export declare function wantsHtml(req: IncomingMessage): boolean;
17
+ export declare function sendDevError(req: IncomingMessage, res: ServerResponse, status: number, view: DevErrorView, options?: {
18
+ htmlTail?: string;
19
+ }): void;
20
+ export declare function renderDevErrorPage(view: DevErrorView): string;
21
+ export declare function escapeHtml(value: string): string;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Dev-only error responses served by the `kody dev` proxy for build failures
3
+ * and uncaught handler errors. HTML for browsers, plain text for everyone else
4
+ * (curl, agents, `kody dev fetch`).
5
+ */
6
+ export function wantsHtml(req) {
7
+ const accept = req.headers.accept ?? '';
8
+ return /\btext\/html\b/.test(accept);
9
+ }
10
+ export function sendDevError(req, res, status, view, options = {}) {
11
+ if (res.headersSent) {
12
+ res.end();
13
+ return;
14
+ }
15
+ const headers = {
16
+ 'cache-control': 'no-store',
17
+ 'x-kody-dev-error': view.kind,
18
+ };
19
+ if (wantsHtml(req)) {
20
+ res.writeHead(status, { ...headers, 'content-type': 'text/html; charset=utf-8' });
21
+ res.end(renderDevErrorPage(view) + (options.htmlTail ?? ''));
22
+ return;
23
+ }
24
+ res.writeHead(status, { ...headers, 'content-type': 'text/plain; charset=utf-8' });
25
+ res.end(`kody dev ${view.kind} error${view.request ? ` (${view.request})` : ''}\n\n${view.detail}\n`);
26
+ }
27
+ export function renderDevErrorPage(view) {
28
+ const label = view.kind === 'build' ? 'Build error' : 'Runtime error';
29
+ return `<!doctype html>
30
+ <html lang="en">
31
+ <head>
32
+ <meta charset="utf-8">
33
+ <meta name="viewport" content="width=device-width, initial-scale=1">
34
+ <title>${escapeHtml(label)} · kody dev</title>
35
+ <style>
36
+ :root { color-scheme: dark; }
37
+ body { margin: 0; font: 14px/1.5 ui-sans-serif, system-ui, sans-serif; background: #1a1015; color: #f5e9ee; }
38
+ main { max-width: 960px; margin: 0 auto; padding: 32px 24px; }
39
+ .label { display: inline-block; font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: #ff8fab; }
40
+ h1 { font-size: 20px; margin: 8px 0 20px; word-break: break-word; }
41
+ pre { margin: 0; padding: 16px; overflow: auto; background: #0e080b; border: 1px solid #3a2530; border-radius: 8px; font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre; }
42
+ p { color: #bfa9b3; font-size: 13px; }
43
+ </style>
44
+ </head>
45
+ <body>
46
+ <main>
47
+ <span class="label">${escapeHtml(label)}</span>
48
+ <h1>${escapeHtml(view.title)}</h1>${view.request ? `\n<p>${escapeHtml(view.request)}</p>` : ''}
49
+ <pre>${escapeHtml(view.detail)}</pre>
50
+ <p>Served by <code>kody dev</code> only; published apps never see this page.</p>
51
+ </main>
52
+ </body>
53
+ </html>
54
+ `;
55
+ }
56
+ export function escapeHtml(value) {
57
+ return value
58
+ .replace(/&/g, '&amp;')
59
+ .replace(/</g, '&lt;')
60
+ .replace(/>/g, '&gt;')
61
+ .replace(/"/g, '&quot;')
62
+ .replace(/'/g, '&#39;');
63
+ }
64
+ //# sourceMappingURL=error-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-page.js","sourceRoot":"","sources":["../../src/dev/error-page.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH,MAAM,UAAU,SAAS,CAAC,GAAoB;IAC7C,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAA;IACvC,OAAO,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAC3B,GAAoB,EACpB,GAAmB,EACnB,MAAc,EACd,IAAkB,EAClB,UAAiC,EAAE;IAEnC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QACrB,GAAG,CAAC,GAAG,EAAE,CAAA;QACT,OAAM;IACP,CAAC;IACD,MAAM,OAAO,GAAG;QACf,eAAe,EAAE,UAAU;QAC3B,kBAAkB,EAAE,IAAI,CAAC,IAAI;KAC7B,CAAA;IACD,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAA;QACjF,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAA;QAC5D,OAAM;IACP,CAAC;IACD,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,cAAc,EAAE,2BAA2B,EAAE,CAAC,CAAA;IAClF,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;AACtG,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAkB;IACpD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,eAAe,CAAA;IACrE,OAAO;;;;;SAKC,UAAU,CAAC,KAAK,CAAC;;;;;;;;;;;;;sBAaJ,UAAU,CAAC,KAAK,CAAC;MACjC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;OACvF,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC;;;;;CAK7B,CAAA;AACD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACvC,OAAO,KAAK;SACV,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AACzB,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Keeps `kody dev` from leaking workerd processes and `/tmp/kody-dev-*` dirs.
3
+ *
4
+ * - A tiny detached `sh` reaper holds a pipe from this process. It learns
5
+ * workerd pids over that pipe and, when the pipe closes for any reason
6
+ * (including SIGKILL of the CLI), kills those pids and removes the work dir.
7
+ * - On startup, {@link sweepStaleDevDirs} removes dirs and workerd processes
8
+ * left behind by dead CLI processes (including CLIs that predate the reaper).
9
+ */
10
+ export declare const devWorkDirPrefix = "kody-dev-";
11
+ export type ProcessReaper = {
12
+ track: (pid: number) => void;
13
+ untrack: (pid: number) => void;
14
+ close: () => void;
15
+ };
16
+ export declare function startProcessReaper(input: {
17
+ workDir: string;
18
+ }): Promise<ProcessReaper>;
19
+ type ListedProcess = {
20
+ pid: number;
21
+ ppid?: number;
22
+ name: string;
23
+ cmd?: string;
24
+ };
25
+ /**
26
+ * Remove `kody-dev-*` work dirs whose owner is gone and kill workerd processes
27
+ * still serving from them. Best effort; never throws.
28
+ */
29
+ export declare function sweepStaleDevDirs(input?: {
30
+ tmp?: string;
31
+ now?: number;
32
+ listProcesses?: () => Promise<Array<ListedProcess>>;
33
+ isAlive?: (pid: number) => boolean;
34
+ kill?: (pid: number) => void;
35
+ }): Promise<{
36
+ removed: Array<string>;
37
+ killed: Array<number>;
38
+ }>;
39
+ export declare function isProcessAlive(pid: number): boolean;
40
+ export {};