@tyndall/dev 0.0.1
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 +37 -0
- package/dist/hmr.d.ts +57 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +384 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1424 -0
- package/dist/inspector.d.ts +29 -0
- package/dist/inspector.d.ts.map +1 -0
- package/dist/inspector.js +76 -0
- package/dist/watcher.d.ts +13 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +64 -0
- package/package.json +29 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const INSPECTOR_PATH = "/__hyper/inspector";
|
|
2
|
+
export declare const INSPECTOR_API_PATH = "/__hyper/inspector.json";
|
|
3
|
+
export type DevRequestKind = "route" | "asset" | "api" | "hmr" | "inspector" | "notfound" | "unknown";
|
|
4
|
+
export interface DevTimingEntry {
|
|
5
|
+
id: string;
|
|
6
|
+
method: string;
|
|
7
|
+
path: string;
|
|
8
|
+
status: number;
|
|
9
|
+
durationMs: number;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
routeId?: string;
|
|
12
|
+
kind: DevRequestKind;
|
|
13
|
+
}
|
|
14
|
+
export interface DevInspectorSnapshot {
|
|
15
|
+
startedAt: number;
|
|
16
|
+
now: number;
|
|
17
|
+
totalRequests: number;
|
|
18
|
+
entries: DevTimingEntry[];
|
|
19
|
+
}
|
|
20
|
+
export interface DevInspector {
|
|
21
|
+
recordRequest: (entry: Omit<DevTimingEntry, "id" | "timestamp">) => void;
|
|
22
|
+
getSnapshot: () => DevInspectorSnapshot;
|
|
23
|
+
}
|
|
24
|
+
export interface DevInspectorOptions {
|
|
25
|
+
maxEntries?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare const createDevInspector: (options?: DevInspectorOptions) => DevInspector;
|
|
28
|
+
export declare const renderInspectorHtml: (snapshot: DevInspectorSnapshot) => string;
|
|
29
|
+
//# sourceMappingURL=inspector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../src/inspector.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,uBAAuB,CAAC;AACnD,eAAO,MAAM,kBAAkB,4BAA4B,CAAC;AAE5D,MAAM,MAAM,cAAc,GACtB,OAAO,GACP,OAAO,GACP,KAAK,GACL,KAAK,GACL,WAAW,GACX,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,WAAW,CAAC,KAAK,IAAI,CAAC;IACzE,WAAW,EAAE,MAAM,oBAAoB,CAAC;CACzC;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,kBAAkB,GAAI,UAAS,mBAAwB,KAAG,YA6BtE,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,UAAU,oBAAoB,WAgDjE,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const INSPECTOR_PATH = "/__hyper/inspector";
|
|
2
|
+
export const INSPECTOR_API_PATH = "/__hyper/inspector.json";
|
|
3
|
+
export const createDevInspector = (options = {}) => {
|
|
4
|
+
const maxEntries = options.maxEntries ?? 100;
|
|
5
|
+
const startedAt = Date.now();
|
|
6
|
+
let totalRequests = 0;
|
|
7
|
+
const entries = [];
|
|
8
|
+
const recordRequest = (entry) => {
|
|
9
|
+
totalRequests += 1;
|
|
10
|
+
const next = {
|
|
11
|
+
...entry,
|
|
12
|
+
id: `${totalRequests}`,
|
|
13
|
+
timestamp: Date.now(),
|
|
14
|
+
};
|
|
15
|
+
entries.unshift(next);
|
|
16
|
+
// Keep only the most recent entries to avoid unbounded memory growth.
|
|
17
|
+
if (entries.length > maxEntries) {
|
|
18
|
+
entries.length = maxEntries;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
recordRequest,
|
|
23
|
+
getSnapshot: () => ({
|
|
24
|
+
startedAt,
|
|
25
|
+
now: Date.now(),
|
|
26
|
+
totalRequests,
|
|
27
|
+
entries: [...entries],
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
export const renderInspectorHtml = (snapshot) => {
|
|
32
|
+
const rows = snapshot.entries
|
|
33
|
+
.map((entry) => `
|
|
34
|
+
<tr>
|
|
35
|
+
<td>${entry.method}</td>
|
|
36
|
+
<td>${entry.path}</td>
|
|
37
|
+
<td>${entry.status}</td>
|
|
38
|
+
<td>${entry.kind}</td>
|
|
39
|
+
<td>${entry.routeId ?? "-"}</td>
|
|
40
|
+
<td>${entry.durationMs.toFixed(1)}ms</td>
|
|
41
|
+
</tr>
|
|
42
|
+
`)
|
|
43
|
+
.join("");
|
|
44
|
+
return `<!doctype html>
|
|
45
|
+
<html>
|
|
46
|
+
<head>
|
|
47
|
+
<meta charset="utf-8" />
|
|
48
|
+
<title>Hyper Dev Inspector</title>
|
|
49
|
+
<style>
|
|
50
|
+
body { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; padding: 24px; }
|
|
51
|
+
table { width: 100%; border-collapse: collapse; }
|
|
52
|
+
th, td { border-bottom: 1px solid #e2e2e2; padding: 6px 8px; text-align: left; font-size: 12px; }
|
|
53
|
+
th { background: #f7f7f7; }
|
|
54
|
+
</style>
|
|
55
|
+
</head>
|
|
56
|
+
<body>
|
|
57
|
+
<h1>Hyper Dev Inspector</h1>
|
|
58
|
+
<p>Requests: ${snapshot.totalRequests}</p>
|
|
59
|
+
<table>
|
|
60
|
+
<thead>
|
|
61
|
+
<tr>
|
|
62
|
+
<th>Method</th>
|
|
63
|
+
<th>Path</th>
|
|
64
|
+
<th>Status</th>
|
|
65
|
+
<th>Kind</th>
|
|
66
|
+
<th>Route</th>
|
|
67
|
+
<th>Duration</th>
|
|
68
|
+
</tr>
|
|
69
|
+
</thead>
|
|
70
|
+
<tbody>
|
|
71
|
+
${rows || "<tr><td colspan=\\\"6\\\">No requests yet.</td></tr>"}
|
|
72
|
+
</tbody>
|
|
73
|
+
</table>
|
|
74
|
+
</body>
|
|
75
|
+
</html>`;
|
|
76
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { watch as fsWatch } from "node:fs";
|
|
2
|
+
export interface WatcherOptions {
|
|
3
|
+
paths: string[];
|
|
4
|
+
debounceMs?: number;
|
|
5
|
+
onInvalidate: (changedPaths: string[]) => void;
|
|
6
|
+
watch?: typeof fsWatch;
|
|
7
|
+
}
|
|
8
|
+
export interface FileWatcher {
|
|
9
|
+
notifyChange: (filePath: string) => void;
|
|
10
|
+
close: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const createFileWatcher: (options: WatcherOptions) => FileWatcher;
|
|
13
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,MAAM,SAAS,CAAC;AAG3C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC/C,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAaD,eAAO,MAAM,iBAAiB,GAAI,SAAS,cAAc,KAAG,WAuD3D,CAAC"}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { watch as fsWatch } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
const normalizePath = (base, filePath) => {
|
|
4
|
+
if (typeof filePath === "string") {
|
|
5
|
+
return filePath.length > 0 ? join(base, filePath) : base;
|
|
6
|
+
}
|
|
7
|
+
if (filePath) {
|
|
8
|
+
const asString = filePath.toString();
|
|
9
|
+
return asString.length > 0 ? join(base, asString) : base;
|
|
10
|
+
}
|
|
11
|
+
return base;
|
|
12
|
+
};
|
|
13
|
+
export const createFileWatcher = (options) => {
|
|
14
|
+
const watchImpl = options.watch ?? fsWatch;
|
|
15
|
+
const debounceMs = options.debounceMs ?? 25;
|
|
16
|
+
const pending = new Set();
|
|
17
|
+
let timer = null;
|
|
18
|
+
const flush = () => {
|
|
19
|
+
if (timer) {
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
timer = null;
|
|
22
|
+
}
|
|
23
|
+
if (pending.size === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Important: sort to keep invalidation order deterministic in logs/tests.
|
|
27
|
+
const batch = Array.from(pending).sort();
|
|
28
|
+
pending.clear();
|
|
29
|
+
options.onInvalidate(batch);
|
|
30
|
+
};
|
|
31
|
+
const scheduleFlush = () => {
|
|
32
|
+
if (timer) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
timer = setTimeout(flush, debounceMs);
|
|
36
|
+
};
|
|
37
|
+
const notifyChange = (filePath) => {
|
|
38
|
+
pending.add(filePath);
|
|
39
|
+
scheduleFlush();
|
|
40
|
+
};
|
|
41
|
+
const watchers = options.paths.map((path) => {
|
|
42
|
+
try {
|
|
43
|
+
const watcher = watchImpl(path, { recursive: true }, (_event, filename) => {
|
|
44
|
+
notifyChange(normalizePath(path, filename));
|
|
45
|
+
});
|
|
46
|
+
return watcher;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
notifyChange,
|
|
54
|
+
close: () => {
|
|
55
|
+
if (timer) {
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
timer = null;
|
|
58
|
+
}
|
|
59
|
+
for (const watcher of watchers) {
|
|
60
|
+
watcher?.close();
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tyndall/dev",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"bun": "./src/index.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@tyndall/core": "workspace:*",
|
|
25
|
+
"@tyndall/dynamic-graph": "workspace:*",
|
|
26
|
+
"@tyndall/shared": "workspace:*",
|
|
27
|
+
"ws": "^8.18.0"
|
|
28
|
+
}
|
|
29
|
+
}
|