@hyperframes/inspector 0.8.155 → 0.8.157

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/server.js ADDED
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module"; const require = createRequire(import.meta.url);
3
+
4
+ // src/server.ts
5
+ import { createServer } from "node:http";
6
+ import { readFileSync, realpathSync } from "node:fs";
7
+ import { pathToFileURL } from "node:url";
8
+ import { Inspector } from "./service.js";
9
+ var version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
10
+ var MAX_BODY = 64e3;
11
+ var BROWSER_MISSING = "Set HF_INSPECT_CHROME";
12
+ function codeOf(message) {
13
+ return /^[a-z][a-z0-9_-]*(?=:|$)/.exec(message)?.[0] ?? "invalid_request";
14
+ }
15
+ function send(res, status, message) {
16
+ res.writeHead(status).end(JSON.stringify({ error: message, code: codeOf(message) }));
17
+ }
18
+ function failureOf(error) {
19
+ if (error instanceof TypeError) return { status: 500, message: "internal_error" };
20
+ const message = error instanceof Error ? error.message : "internal_error";
21
+ if (message.startsWith(BROWSER_MISSING)) return { status: 503, message: "browser_unavailable" };
22
+ if (!(error instanceof Error)) return { status: 500, message: "internal_error" };
23
+ return { status: message === "no-composition" ? 404 : 400, message };
24
+ }
25
+ function loopback(host) {
26
+ if (!host) return true;
27
+ const name = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "").toLowerCase();
28
+ return name === "127.0.0.1" || name === "localhost" || name === "::1";
29
+ }
30
+ function readBody(req) {
31
+ return new Promise((resolve, reject) => {
32
+ const chunks = [];
33
+ let size = 0;
34
+ let settled = false;
35
+ const finish = (value) => {
36
+ if (settled) return;
37
+ settled = true;
38
+ resolve(value);
39
+ };
40
+ req.on("data", (chunk) => {
41
+ if (settled) return;
42
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
43
+ size += bytes.length;
44
+ if (size > MAX_BODY) {
45
+ settled = true;
46
+ const done = () => resolve("too_large");
47
+ req.on("end", done);
48
+ req.on("error", done);
49
+ req.resume();
50
+ if (req.readableEnded) done();
51
+ return;
52
+ }
53
+ chunks.push(bytes);
54
+ });
55
+ req.on("end", () => finish(Buffer.concat(chunks)));
56
+ req.on("error", (error) => {
57
+ if (!settled) reject(error);
58
+ });
59
+ });
60
+ }
61
+ function createInspectorServer(project) {
62
+ const inspector = new Inspector(project);
63
+ const server = createServer(async (req, res) => {
64
+ res.setHeader("Content-Type", "application/json");
65
+ if (!loopback(req.headers.host)) {
66
+ send(res, 403, "host_not_allowed");
67
+ return;
68
+ }
69
+ if (req.headers.origin) {
70
+ send(res, 403, "browser_origin_not_allowed");
71
+ return;
72
+ }
73
+ if (req.method === "GET" && req.url === "/info") {
74
+ res.end(
75
+ JSON.stringify({
76
+ project: inspector.project,
77
+ version,
78
+ capability: "inspection",
79
+ stats: inspector.stats
80
+ })
81
+ );
82
+ return;
83
+ }
84
+ if (req.method !== "POST" || !["/inspect", "/timeline", "/element-map"].includes(req.url ?? "")) {
85
+ send(res, 404, "not_found");
86
+ return;
87
+ }
88
+ try {
89
+ const body = await readBody(req);
90
+ if (body === "too_large") {
91
+ send(res, 400, "request_too_large");
92
+ return;
93
+ }
94
+ const request = JSON.parse(body.toString("utf8") || "{}");
95
+ if (req.url === "/element-map") res.end(JSON.stringify(await inspector.elementMap()));
96
+ else if (req.url === "/timeline") res.end(JSON.stringify(await inspector.timeline(request)));
97
+ else res.end(JSON.stringify(await inspector.inspect(request)));
98
+ } catch (error) {
99
+ const failure = failureOf(error);
100
+ send(res, failure.status, failure.message);
101
+ }
102
+ });
103
+ return { inspector, server };
104
+ }
105
+ function runCli() {
106
+ const project = process.argv[2];
107
+ if (!project)
108
+ throw new Error("Usage: server PROJECT [PORT] (bun src/server.ts, or node dist/server.js)");
109
+ const { inspector, server } = createInspectorServer(project);
110
+ server.listen(Number(process.argv[3] ?? 0), "127.0.0.1", () => {
111
+ const address = server.address();
112
+ if (address && typeof address !== "string")
113
+ console.log(
114
+ JSON.stringify({
115
+ url: `http://127.0.0.1:${address.port}`,
116
+ project: inspector.project
117
+ })
118
+ );
119
+ });
120
+ async function close() {
121
+ server.close();
122
+ await inspector.dispose();
123
+ }
124
+ process.once("SIGINT", () => {
125
+ void close();
126
+ });
127
+ process.once("SIGTERM", () => {
128
+ void close();
129
+ });
130
+ }
131
+ function launchedAsCli(entry) {
132
+ if (!entry) return false;
133
+ try {
134
+ return import.meta.url === pathToFileURL(realpathSync(entry)).href;
135
+ } catch {
136
+ return false;
137
+ }
138
+ }
139
+ if (launchedAsCli(process.argv[1])) runCli();
140
+ export {
141
+ createInspectorServer
142
+ };