@solidrt/cli 0.0.50 → 0.0.52
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/AGENTS.md +79 -36
- package/README.md +86 -15
- package/agents/assets.md +46 -0
- package/agents/debugging.md +294 -0
- package/dist/console.srtapp +94123 -73
- package/dist/server.js +3936 -0
- package/package.json +11 -10
- package/src/android/docs.md +21 -0
- package/src/android/main.ts +286 -0
- package/src/{bundler.ts → bundle/bundler.ts} +163 -76
- package/src/bundle/docs.md +12 -0
- package/src/bundle/main.ts +203 -0
- package/src/check/docs.md +10 -0
- package/src/check/main.ts +85 -0
- package/src/{commands/check.ts → check/typecheck.ts} +11 -34
- package/src/client/docs.md +9 -0
- package/src/client/main.ts +33 -0
- package/src/console/docs.md +14 -0
- package/src/console/main.ts +21 -0
- package/src/demo/docs.md +27 -0
- package/src/demo/main.ts +67 -0
- package/src/init/docs.md +11 -0
- package/src/{commands/init.ts → init/main.ts} +31 -25
- package/src/init/scaffold/AGENTS.md +98 -0
- package/src/init/scaffold/package.json +23 -0
- package/{scaffold → src/init/scaffold}/templates/components/index.tsx +2 -3
- package/{scaffold → src/init/scaffold}/templates/default/index.tsx +2 -3
- package/src/lib/args.ts +194 -0
- package/src/{artifacts.ts → lib/artifacts.ts} +41 -1
- package/src/{dev-dir.ts → lib/dev-dir.ts} +10 -8
- package/src/{fonts.ts → lib/fonts.ts} +12 -26
- package/src/lib/mode.ts +77 -0
- package/src/{project.ts → lib/project.ts} +109 -61
- package/src/lib/registry.ts +120 -0
- package/src/lib/server-bundle.ts +24 -0
- package/src/lib/usage.ts +117 -0
- package/src/lib/util.ts +36 -0
- package/src/main.ts +109 -31
- package/src/mcp/docs.md +22 -0
- package/src/mcp/main.ts +719 -0
- package/src/pack/docs.md +18 -0
- package/src/{pack-folder.ts → pack/layout.ts} +13 -22
- package/src/pack/main.ts +86 -0
- package/src/pack/trailer.ts +97 -0
- package/src/render/docs.md +19 -0
- package/src/render/main.ts +49 -0
- package/src/server/args.ts +126 -0
- package/src/server/binaries.ts +47 -0
- package/src/server/config.ts +54 -0
- package/{server → src/server}/control.ts +246 -78
- package/src/server/docs.md +51 -0
- package/src/server/line-editor.ts +200 -0
- package/src/server/main.ts +473 -0
- package/src/server/mode.ts +92 -0
- package/src/server/rebuild.ts +90 -0
- package/src/server/registry.ts +138 -0
- package/src/server/remap.ts +60 -0
- package/src/server/repl.ts +223 -0
- package/src/server/state.ts +54 -0
- package/{server → src/server}/tsconfig.json +1 -1
- package/{server → src/server}/tunnel.ts +6 -6
- package/src/server/watcher.ts +121 -0
- package/src/tool/main.ts +70 -0
- package/src/types/bundle.d.ts +24 -0
- package/src/types/control.d.ts +90 -0
- package/src/types/registry.d.ts +16 -0
- package/scaffold/AGENTS.md +0 -630
- package/scaffold/package.json +0 -22
- package/scaffold/templates/components/icon.tsx +0 -48
- package/scaffold/templates/default/icon.tsx +0 -48
- package/server/main.ts +0 -308
- package/server/rebuild.ts +0 -68
- package/server/remap.ts +0 -47
- package/server/state.ts +0 -93
- package/src/args.ts +0 -212
- package/src/bundle-cli.ts +0 -13
- package/src/commands/bundle.ts +0 -76
- package/src/commands/client.ts +0 -34
- package/src/commands/mcp.ts +0 -603
- package/src/commands/pack.ts +0 -65
- package/src/commands/render.ts +0 -24
- package/src/commands/server.ts +0 -73
- package/src/dev-android.ts +0 -176
- package/src/dev-client.ts +0 -29
- package/src/dev-server.ts +0 -302
- package/src/packer.ts +0 -103
- package/src/repl.ts +0 -233
- package/src/util.ts +0 -121
- package/src/watcher.ts +0 -69
- /package/src/{untyped-deps.d.ts → bundle/untyped-deps.d.ts} +0 -0
- /package/src/{prompt.ts → init/prompt.ts} +0 -0
- /package/{scaffold → src/init/scaffold}/gitignore +0 -0
- /package/{scaffold → src/init/scaffold}/icon.svg +0 -0
- /package/{scaffold → src/init/scaffold}/mcp.json +0 -0
- /package/{scaffold → src/init/scaffold}/tsconfig.json +0 -0
- /package/{server → src/server}/cache.ts +0 -0
- /package/{server → src/server}/proxy.ts +0 -0
- /package/{server → src/server}/qr.ts +0 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,3936 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
function __accessProp(key) {
|
|
7
|
+
return this[key];
|
|
8
|
+
}
|
|
9
|
+
var __toESMCache_node;
|
|
10
|
+
var __toESMCache_esm;
|
|
11
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
12
|
+
var canCache = mod != null && typeof mod === "object";
|
|
13
|
+
if (canCache) {
|
|
14
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
15
|
+
var cached = cache.get(mod);
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
}
|
|
19
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
20
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
21
|
+
if (mod && typeof mod === "object" || typeof mod === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
31
|
+
return to;
|
|
32
|
+
};
|
|
33
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
|
+
|
|
35
|
+
// ../../node_modules/.bun/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js
|
|
36
|
+
var require_resolve_uri_umd = __commonJS(function(exports, module) {
|
|
37
|
+
(function(global, factory) {
|
|
38
|
+
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.resolveURI = factory());
|
|
39
|
+
})(exports, function() {
|
|
40
|
+
const schemeRegex = /^[\w+.-]+:\/\//;
|
|
41
|
+
const urlRegex = /^([\w+.-]+:)\/\/([^@/#?]*@)?([^:/#?]*)(:\d+)?(\/[^#?]*)?(\?[^#]*)?(#.*)?/;
|
|
42
|
+
const fileRegex = /^file:(?:\/\/((?![a-z]:)[^/#?]*)?)?(\/?[^#?]*)(\?[^#]*)?(#.*)?/i;
|
|
43
|
+
function isAbsoluteUrl(input) {
|
|
44
|
+
return schemeRegex.test(input);
|
|
45
|
+
}
|
|
46
|
+
function isSchemeRelativeUrl(input) {
|
|
47
|
+
return input.startsWith("//");
|
|
48
|
+
}
|
|
49
|
+
function isAbsolutePath(input) {
|
|
50
|
+
return input.startsWith("/");
|
|
51
|
+
}
|
|
52
|
+
function isFileUrl(input) {
|
|
53
|
+
return input.startsWith("file:");
|
|
54
|
+
}
|
|
55
|
+
function isRelative(input) {
|
|
56
|
+
return /^[.?#]/.test(input);
|
|
57
|
+
}
|
|
58
|
+
function parseAbsoluteUrl(input) {
|
|
59
|
+
const match = urlRegex.exec(input);
|
|
60
|
+
return makeUrl(match[1], match[2] || "", match[3], match[4] || "", match[5] || "/", match[6] || "", match[7] || "");
|
|
61
|
+
}
|
|
62
|
+
function parseFileUrl(input) {
|
|
63
|
+
const match = fileRegex.exec(input);
|
|
64
|
+
const path = match[2];
|
|
65
|
+
return makeUrl("file:", "", match[1] || "", "", isAbsolutePath(path) ? path : "/" + path, match[3] || "", match[4] || "");
|
|
66
|
+
}
|
|
67
|
+
function makeUrl(scheme, user, host, port, path, query, hash) {
|
|
68
|
+
return {
|
|
69
|
+
scheme,
|
|
70
|
+
user,
|
|
71
|
+
host,
|
|
72
|
+
port,
|
|
73
|
+
path,
|
|
74
|
+
query,
|
|
75
|
+
hash,
|
|
76
|
+
type: 7
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function parseUrl(input) {
|
|
80
|
+
if (isSchemeRelativeUrl(input)) {
|
|
81
|
+
const url2 = parseAbsoluteUrl("http:" + input);
|
|
82
|
+
url2.scheme = "";
|
|
83
|
+
url2.type = 6;
|
|
84
|
+
return url2;
|
|
85
|
+
}
|
|
86
|
+
if (isAbsolutePath(input)) {
|
|
87
|
+
const url2 = parseAbsoluteUrl("http://foo.com" + input);
|
|
88
|
+
url2.scheme = "";
|
|
89
|
+
url2.host = "";
|
|
90
|
+
url2.type = 5;
|
|
91
|
+
return url2;
|
|
92
|
+
}
|
|
93
|
+
if (isFileUrl(input))
|
|
94
|
+
return parseFileUrl(input);
|
|
95
|
+
if (isAbsoluteUrl(input))
|
|
96
|
+
return parseAbsoluteUrl(input);
|
|
97
|
+
const url = parseAbsoluteUrl("http://foo.com/" + input);
|
|
98
|
+
url.scheme = "";
|
|
99
|
+
url.host = "";
|
|
100
|
+
url.type = input ? input.startsWith("?") ? 3 : input.startsWith("#") ? 2 : 4 : 1;
|
|
101
|
+
return url;
|
|
102
|
+
}
|
|
103
|
+
function stripPathFilename(path) {
|
|
104
|
+
if (path.endsWith("/.."))
|
|
105
|
+
return path;
|
|
106
|
+
const index = path.lastIndexOf("/");
|
|
107
|
+
return path.slice(0, index + 1);
|
|
108
|
+
}
|
|
109
|
+
function mergePaths(url, base) {
|
|
110
|
+
normalizePath(base, base.type);
|
|
111
|
+
if (url.path === "/") {
|
|
112
|
+
url.path = base.path;
|
|
113
|
+
} else {
|
|
114
|
+
url.path = stripPathFilename(base.path) + url.path;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function normalizePath(url, type) {
|
|
118
|
+
const rel = type <= 4;
|
|
119
|
+
const pieces = url.path.split("/");
|
|
120
|
+
let pointer = 1;
|
|
121
|
+
let positive = 0;
|
|
122
|
+
let addTrailingSlash = false;
|
|
123
|
+
for (let i = 1;i < pieces.length; i++) {
|
|
124
|
+
const piece = pieces[i];
|
|
125
|
+
if (!piece) {
|
|
126
|
+
addTrailingSlash = true;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
addTrailingSlash = false;
|
|
130
|
+
if (piece === ".")
|
|
131
|
+
continue;
|
|
132
|
+
if (piece === "..") {
|
|
133
|
+
if (positive) {
|
|
134
|
+
addTrailingSlash = true;
|
|
135
|
+
positive--;
|
|
136
|
+
pointer--;
|
|
137
|
+
} else if (rel) {
|
|
138
|
+
pieces[pointer++] = piece;
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
pieces[pointer++] = piece;
|
|
143
|
+
positive++;
|
|
144
|
+
}
|
|
145
|
+
let path = "";
|
|
146
|
+
for (let i = 1;i < pointer; i++) {
|
|
147
|
+
path += "/" + pieces[i];
|
|
148
|
+
}
|
|
149
|
+
if (!path || addTrailingSlash && !path.endsWith("/..")) {
|
|
150
|
+
path += "/";
|
|
151
|
+
}
|
|
152
|
+
url.path = path;
|
|
153
|
+
}
|
|
154
|
+
function resolve(input, base) {
|
|
155
|
+
if (!input && !base)
|
|
156
|
+
return "";
|
|
157
|
+
const url = parseUrl(input);
|
|
158
|
+
let inputType = url.type;
|
|
159
|
+
if (base && inputType !== 7) {
|
|
160
|
+
const baseUrl = parseUrl(base);
|
|
161
|
+
const baseType = baseUrl.type;
|
|
162
|
+
switch (inputType) {
|
|
163
|
+
case 1:
|
|
164
|
+
url.hash = baseUrl.hash;
|
|
165
|
+
case 2:
|
|
166
|
+
url.query = baseUrl.query;
|
|
167
|
+
case 3:
|
|
168
|
+
case 4:
|
|
169
|
+
mergePaths(url, baseUrl);
|
|
170
|
+
case 5:
|
|
171
|
+
url.user = baseUrl.user;
|
|
172
|
+
url.host = baseUrl.host;
|
|
173
|
+
url.port = baseUrl.port;
|
|
174
|
+
case 6:
|
|
175
|
+
url.scheme = baseUrl.scheme;
|
|
176
|
+
}
|
|
177
|
+
if (baseType > inputType)
|
|
178
|
+
inputType = baseType;
|
|
179
|
+
}
|
|
180
|
+
normalizePath(url, inputType);
|
|
181
|
+
const queryHash = url.query + url.hash;
|
|
182
|
+
switch (inputType) {
|
|
183
|
+
case 2:
|
|
184
|
+
case 3:
|
|
185
|
+
return queryHash;
|
|
186
|
+
case 4: {
|
|
187
|
+
const path = url.path.slice(1);
|
|
188
|
+
if (!path)
|
|
189
|
+
return queryHash || ".";
|
|
190
|
+
if (isRelative(base || input) && !isRelative(path)) {
|
|
191
|
+
return "./" + path + queryHash;
|
|
192
|
+
}
|
|
193
|
+
return path + queryHash;
|
|
194
|
+
}
|
|
195
|
+
case 5:
|
|
196
|
+
return url.path + queryHash;
|
|
197
|
+
default:
|
|
198
|
+
return url.scheme + "//" + url.user + url.host + url.port + url.path + queryHash;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return resolve;
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// src/server/main.ts
|
|
206
|
+
import { serve } from "flux:http";
|
|
207
|
+
import { file as file7, realpath as realpath4 } from "flux:fs";
|
|
208
|
+
import { on as onSignal } from "flux:process";
|
|
209
|
+
import { join as join6, resolveWithin } from "flux:path";
|
|
210
|
+
import { command as command2 } from "flux:subprocess";
|
|
211
|
+
import { interfaces, probe } from "flux:net";
|
|
212
|
+
|
|
213
|
+
// src/server/state.ts
|
|
214
|
+
var state = {
|
|
215
|
+
config: undefined,
|
|
216
|
+
clients: new Map,
|
|
217
|
+
nextClientId: 0,
|
|
218
|
+
generation: Date.now(),
|
|
219
|
+
currentReload: null,
|
|
220
|
+
currentMaps: null,
|
|
221
|
+
serverUrl: "",
|
|
222
|
+
stats: false,
|
|
223
|
+
userInputMuted: false,
|
|
224
|
+
watchPaused: false,
|
|
225
|
+
captureStartMs: 0,
|
|
226
|
+
captureLastAt: 0,
|
|
227
|
+
captureChain: Promise.resolve()
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// src/server/args.ts
|
|
231
|
+
import { argv } from "flux:process";
|
|
232
|
+
function fail(message) {
|
|
233
|
+
throw new Error(message);
|
|
234
|
+
}
|
|
235
|
+
function integer(name, raw, min, max) {
|
|
236
|
+
let value = Number(raw);
|
|
237
|
+
if (!/^\d+$/.test(raw) || value < min || value > max) {
|
|
238
|
+
fail(`Invalid --${name} value "${raw}": expected an integer between ${min} and ${max}`);
|
|
239
|
+
}
|
|
240
|
+
return value;
|
|
241
|
+
}
|
|
242
|
+
function parseArgs(raw = argv) {
|
|
243
|
+
let sep = raw.indexOf("--");
|
|
244
|
+
let own = sep === -1 ? raw : raw.slice(0, sep);
|
|
245
|
+
let args = {
|
|
246
|
+
entry: undefined,
|
|
247
|
+
project: false,
|
|
248
|
+
file: false,
|
|
249
|
+
lan: false,
|
|
250
|
+
proxyHttp: false,
|
|
251
|
+
tunnel: false,
|
|
252
|
+
stats: false,
|
|
253
|
+
minify: false,
|
|
254
|
+
port: undefined,
|
|
255
|
+
capture: undefined,
|
|
256
|
+
client: null,
|
|
257
|
+
dataRoot: undefined,
|
|
258
|
+
size: undefined,
|
|
259
|
+
appArgs: sep === -1 ? [] : raw.slice(sep + 1)
|
|
260
|
+
};
|
|
261
|
+
let value = (name, i) => {
|
|
262
|
+
let v = own[i];
|
|
263
|
+
if (v === undefined || v.startsWith("--"))
|
|
264
|
+
fail(`--${name} needs a value`);
|
|
265
|
+
return v;
|
|
266
|
+
};
|
|
267
|
+
for (let i = 0;i < own.length; i++) {
|
|
268
|
+
let arg = own[i];
|
|
269
|
+
if (!arg.startsWith("--")) {
|
|
270
|
+
if (args.entry !== undefined)
|
|
271
|
+
fail(`Unexpected argument: ${arg}`);
|
|
272
|
+
args.entry = arg;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
let name = arg.slice(2);
|
|
276
|
+
switch (name) {
|
|
277
|
+
case "project":
|
|
278
|
+
args.project = true;
|
|
279
|
+
break;
|
|
280
|
+
case "file":
|
|
281
|
+
args.file = true;
|
|
282
|
+
break;
|
|
283
|
+
case "lan":
|
|
284
|
+
args.lan = true;
|
|
285
|
+
break;
|
|
286
|
+
case "proxy-http":
|
|
287
|
+
args.proxyHttp = true;
|
|
288
|
+
break;
|
|
289
|
+
case "tunnel":
|
|
290
|
+
args.tunnel = true;
|
|
291
|
+
break;
|
|
292
|
+
case "stats":
|
|
293
|
+
args.stats = true;
|
|
294
|
+
break;
|
|
295
|
+
case "minify":
|
|
296
|
+
args.minify = true;
|
|
297
|
+
break;
|
|
298
|
+
case "port":
|
|
299
|
+
args.port = integer(name, value(name, ++i), 1, 65535);
|
|
300
|
+
break;
|
|
301
|
+
case "capture":
|
|
302
|
+
args.capture = value(name, ++i);
|
|
303
|
+
break;
|
|
304
|
+
case "client":
|
|
305
|
+
args.client = integer(name, value(name, ++i), 0, Number.MAX_SAFE_INTEGER);
|
|
306
|
+
break;
|
|
307
|
+
case "data-root":
|
|
308
|
+
args.dataRoot = value(name, ++i);
|
|
309
|
+
break;
|
|
310
|
+
case "size":
|
|
311
|
+
args.size = value(name, ++i);
|
|
312
|
+
break;
|
|
313
|
+
default:
|
|
314
|
+
fail(`Unknown flag: ${arg}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return args;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/server/mode.ts
|
|
321
|
+
import { file, realpath } from "flux:fs";
|
|
322
|
+
import { join } from "flux:path";
|
|
323
|
+
var DEFAULT_ENTRY = "src/index.tsx";
|
|
324
|
+
var ENTRY_EXTENSIONS = [".tsx", ".jsx", ".ts", ".js"];
|
|
325
|
+
async function resolveMode(args) {
|
|
326
|
+
let cwd = await realpath(".");
|
|
327
|
+
let hasPkg = await file(join(cwd, "package.json")).exists();
|
|
328
|
+
let source = args.entry;
|
|
329
|
+
if (source !== undefined && !ENTRY_EXTENSIONS.some((ext) => source.endsWith(ext))) {
|
|
330
|
+
fail(`Not an app entry: ${source} (expected .tsx, .jsx, .ts, .js or .srt.js)`);
|
|
331
|
+
}
|
|
332
|
+
if (source !== undefined && !await file(source).exists())
|
|
333
|
+
fail(`Entry not found: ${source}`);
|
|
334
|
+
if (args.file && args.project)
|
|
335
|
+
fail("--file and --project exclude each other");
|
|
336
|
+
if (source === undefined) {
|
|
337
|
+
if (args.file || args.project)
|
|
338
|
+
fail("--file and --project need an entry file");
|
|
339
|
+
if (!hasPkg)
|
|
340
|
+
fail(`No package.json in ${cwd}. Run from the project root, or pass a file to use on its own.`);
|
|
341
|
+
let declared = await declaredEntry(cwd);
|
|
342
|
+
let entry2 = join(cwd, declared ?? DEFAULT_ENTRY);
|
|
343
|
+
if (!await file(entry2).exists()) {
|
|
344
|
+
fail(`Entry not found: ${entry2}${declared ? "" : ' (set "solidrt": { "entry": ... } in package.json)'}`);
|
|
345
|
+
}
|
|
346
|
+
return { mode: "project", key: cwd, projectDir: cwd, entry: entry2 };
|
|
347
|
+
}
|
|
348
|
+
let entry = await realpath(source);
|
|
349
|
+
if (hasPkg && !args.file && !args.project) {
|
|
350
|
+
fail(`${cwd} is a project (it has a package.json) and ${source} is a file: pass --project to use the project with this entry, or --file to use the file on its own.`);
|
|
351
|
+
}
|
|
352
|
+
if (hasPkg && args.project)
|
|
353
|
+
return { mode: "project", key: cwd, projectDir: cwd, entry };
|
|
354
|
+
if (!hasPkg && args.project)
|
|
355
|
+
fail(`--project needs a package.json in ${cwd}`);
|
|
356
|
+
return { mode: "file", key: entry, projectDir: null, entry };
|
|
357
|
+
}
|
|
358
|
+
async function declaredEntry(dir) {
|
|
359
|
+
let pkg = await file(join(dir, "package.json")).json().catch(() => fail(`Unreadable package.json in ${dir}`));
|
|
360
|
+
let config = pkg?.solidrt;
|
|
361
|
+
if (config === undefined)
|
|
362
|
+
return;
|
|
363
|
+
if (typeof config !== "object" || config === null || Array.isArray(config)) {
|
|
364
|
+
fail('"solidrt" in package.json must be an object');
|
|
365
|
+
}
|
|
366
|
+
if ("entry" in config && typeof config.entry !== "string")
|
|
367
|
+
fail('"solidrt": "entry" must be a string');
|
|
368
|
+
return config.entry;
|
|
369
|
+
}
|
|
370
|
+
function sourceDirOf(mode) {
|
|
371
|
+
return dirname(mode.entry);
|
|
372
|
+
}
|
|
373
|
+
function dirname(path) {
|
|
374
|
+
let i = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
375
|
+
return i <= 0 ? i === 0 ? path.slice(0, 1) : "." : path.slice(0, i);
|
|
376
|
+
}
|
|
377
|
+
function absolute(path, base) {
|
|
378
|
+
return /^([A-Za-z]:)?[\\/]/.test(path) ? path : join(base, path);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/server/binaries.ts
|
|
382
|
+
import { file as file2 } from "flux:fs";
|
|
383
|
+
import { join as join2 } from "flux:path";
|
|
384
|
+
import { arch, env, platform } from "flux:process";
|
|
385
|
+
var TRIPLES = {
|
|
386
|
+
"linux-x64": "linux-x64-gnu",
|
|
387
|
+
"linux-arm64": "linux-arm64-gnu",
|
|
388
|
+
"darwin-arm64": "darwin-arm64",
|
|
389
|
+
"win32-x64": "win32-x64-msvc"
|
|
390
|
+
};
|
|
391
|
+
function platformDir() {
|
|
392
|
+
if (env.SRT_PLATFORM_DIR)
|
|
393
|
+
return env.SRT_PLATFORM_DIR;
|
|
394
|
+
let triple = TRIPLES[`${platform}-${arch}`];
|
|
395
|
+
return env.SRT_HOME && triple ? join2(env.SRT_HOME, "dist", triple) : null;
|
|
396
|
+
}
|
|
397
|
+
async function requireBinary(name) {
|
|
398
|
+
let dir = platformDir();
|
|
399
|
+
let path = dir ? join2(dir, name + (platform === "win32" ? ".exe" : "")) : null;
|
|
400
|
+
if (path && await file2(path).exists())
|
|
401
|
+
return path;
|
|
402
|
+
fail(`Could not find the ${name} binary${dir ? ` in ${dir}` : ""}. Run through srt, or set SRT_HOME to a SolidRT checkout built with make client.`);
|
|
403
|
+
}
|
|
404
|
+
function srtCommand() {
|
|
405
|
+
let cli = env.SRT_CLI ?? (env.SRT_HOME ? join2(env.SRT_HOME, "packages", "cli") : undefined);
|
|
406
|
+
if (!cli)
|
|
407
|
+
fail("Could not find srt: set SRT_CLI to the @solidrt/cli package root, or SRT_HOME to a SolidRT checkout.");
|
|
408
|
+
return [env.SRT_BUN ?? "bun", join2(cli, "bin", "srt")];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/server/cache.ts
|
|
412
|
+
import { Database } from "flux:sqlite";
|
|
413
|
+
import { dir } from "flux:fs";
|
|
414
|
+
import { join as join3 } from "flux:path";
|
|
415
|
+
var CACHE_FILE = "http-cache.db";
|
|
416
|
+
var db = null;
|
|
417
|
+
var enabled = false;
|
|
418
|
+
async function initCache(opts) {
|
|
419
|
+
await dir(opts.dir).create();
|
|
420
|
+
let d = await Database.open(join3(opts.dir, CACHE_FILE), "rw+");
|
|
421
|
+
await d.exec(`CREATE TABLE IF NOT EXISTS entries (
|
|
422
|
+
key TEXT PRIMARY KEY,
|
|
423
|
+
method TEXT NOT NULL,
|
|
424
|
+
url TEXT NOT NULL,
|
|
425
|
+
status INTEGER NOT NULL,
|
|
426
|
+
headers TEXT NOT NULL,
|
|
427
|
+
body BLOB NOT NULL,
|
|
428
|
+
cached_at INTEGER NOT NULL
|
|
429
|
+
)`);
|
|
430
|
+
db = d;
|
|
431
|
+
enabled = true;
|
|
432
|
+
}
|
|
433
|
+
function keyFor(method, url) {
|
|
434
|
+
return method + `
|
|
435
|
+
` + url;
|
|
436
|
+
}
|
|
437
|
+
function cacheableMethod(method) {
|
|
438
|
+
return method === "GET" || method === "HEAD";
|
|
439
|
+
}
|
|
440
|
+
function hasNoStore(headerVal) {
|
|
441
|
+
if (!headerVal)
|
|
442
|
+
return false;
|
|
443
|
+
return /(^|,)\s*no-store(\s*,|$)/i.test(headerVal);
|
|
444
|
+
}
|
|
445
|
+
function hasNoCache(headerVal) {
|
|
446
|
+
if (!headerVal)
|
|
447
|
+
return false;
|
|
448
|
+
return /(^|,)\s*no-cache(\s*,|$)/i.test(headerVal);
|
|
449
|
+
}
|
|
450
|
+
function shouldConsider(method, reqHeaders) {
|
|
451
|
+
if (!enabled)
|
|
452
|
+
return { skip: true };
|
|
453
|
+
if (!cacheableMethod(method))
|
|
454
|
+
return { skip: true };
|
|
455
|
+
if (reqHeaders.has("authorization"))
|
|
456
|
+
return { skip: true };
|
|
457
|
+
if (hasNoStore(reqHeaders.get("cache-control")))
|
|
458
|
+
return { skip: true };
|
|
459
|
+
return { skip: false };
|
|
460
|
+
}
|
|
461
|
+
function isBypass(reqHeaders) {
|
|
462
|
+
if (reqHeaders.get("x-srt-cache")?.toLowerCase() === "bypass")
|
|
463
|
+
return true;
|
|
464
|
+
if (hasNoCache(reqHeaders.get("cache-control")))
|
|
465
|
+
return true;
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
async function get(method, url) {
|
|
469
|
+
if (!db || !enabled)
|
|
470
|
+
return null;
|
|
471
|
+
let row = await db.query("SELECT method, url, status, headers, body, cached_at FROM entries WHERE key = ?").get([keyFor(method, url)]);
|
|
472
|
+
if (!row)
|
|
473
|
+
return null;
|
|
474
|
+
return {
|
|
475
|
+
method: row.method,
|
|
476
|
+
url: row.url,
|
|
477
|
+
status: row.status,
|
|
478
|
+
headers: JSON.parse(row.headers),
|
|
479
|
+
body: row.body,
|
|
480
|
+
cachedAt: row.cached_at
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
async function put(method, url, status, headers, body) {
|
|
484
|
+
if (!db || !enabled)
|
|
485
|
+
return;
|
|
486
|
+
if (status < 200 || status >= 300)
|
|
487
|
+
return;
|
|
488
|
+
if (hasNoStore(headers["cache-control"] ?? null))
|
|
489
|
+
return;
|
|
490
|
+
await db.run(`INSERT OR REPLACE INTO entries
|
|
491
|
+
(key, method, url, status, headers, body, cached_at)
|
|
492
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`, [keyFor(method, url), method, url, status, JSON.stringify(headers), body, Date.now()]);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/server/proxy.ts
|
|
496
|
+
function headersToObject(h) {
|
|
497
|
+
let out = {};
|
|
498
|
+
h.forEach((v, k) => {
|
|
499
|
+
out[k] = v;
|
|
500
|
+
});
|
|
501
|
+
return out;
|
|
502
|
+
}
|
|
503
|
+
async function handleProxy(req) {
|
|
504
|
+
let target = req.headers.get("x-srt-proxy-url");
|
|
505
|
+
if (!target) {
|
|
506
|
+
return new Response("Missing X-SRT-Proxy-Url", { status: 400 });
|
|
507
|
+
}
|
|
508
|
+
let forwardHeaders = new Headers(req.headers);
|
|
509
|
+
forwardHeaders.delete("host");
|
|
510
|
+
forwardHeaders.delete("x-srt-proxy-url");
|
|
511
|
+
forwardHeaders.delete("x-srt-cache");
|
|
512
|
+
forwardHeaders.delete("content-length");
|
|
513
|
+
let cacheStatus = "skip";
|
|
514
|
+
let cacheable = !shouldConsider(req.method, req.headers).skip;
|
|
515
|
+
let bypass = cacheable && isBypass(req.headers);
|
|
516
|
+
if (cacheable && !bypass) {
|
|
517
|
+
let hit = await get(req.method, target);
|
|
518
|
+
if (hit) {
|
|
519
|
+
console.log(`[cli] proxy ${req.method} ${target} [cache hit]`);
|
|
520
|
+
let respHeaders = new Headers(hit.headers);
|
|
521
|
+
respHeaders.set("x-srt-cache", "hit");
|
|
522
|
+
return new Response(hit.body, { status: hit.status, headers: respHeaders });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
let hasBody = req.method !== "GET" && req.method !== "HEAD";
|
|
526
|
+
if (cacheable) {
|
|
527
|
+
cacheStatus = bypass ? "bypass" : "miss";
|
|
528
|
+
console.log(`[cli] proxy ${req.method} ${target} [${cacheStatus}]`);
|
|
529
|
+
} else {
|
|
530
|
+
console.log(`[cli] proxy ${req.method} ${target}`);
|
|
531
|
+
}
|
|
532
|
+
try {
|
|
533
|
+
let upstream = await fetch(target, {
|
|
534
|
+
method: req.method,
|
|
535
|
+
headers: forwardHeaders,
|
|
536
|
+
body: hasBody ? await req.bytes() : undefined
|
|
537
|
+
});
|
|
538
|
+
let respHeaders = new Headers(upstream.headers);
|
|
539
|
+
respHeaders.delete("content-encoding");
|
|
540
|
+
respHeaders.delete("transfer-encoding");
|
|
541
|
+
let bodyBytes = await upstream.bytes();
|
|
542
|
+
if (cacheable) {
|
|
543
|
+
await put(req.method, target, upstream.status, headersToObject(respHeaders), bodyBytes);
|
|
544
|
+
respHeaders.set("x-srt-cache", cacheStatus);
|
|
545
|
+
}
|
|
546
|
+
return new Response(bodyBytes, {
|
|
547
|
+
status: upstream.status,
|
|
548
|
+
statusText: upstream.statusText,
|
|
549
|
+
headers: respHeaders
|
|
550
|
+
});
|
|
551
|
+
} catch (e) {
|
|
552
|
+
console.log(`[cli] proxy error ${target}: ${String(e)}`);
|
|
553
|
+
return new Response(`Proxy error: ${String(e)}`, { status: 502 });
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/server/rebuild.ts
|
|
558
|
+
import { command } from "flux:subprocess";
|
|
559
|
+
import { dir as dir3, file as file3 } from "flux:fs";
|
|
560
|
+
|
|
561
|
+
// src/server/watcher.ts
|
|
562
|
+
import { dir as dir2 } from "flux:fs";
|
|
563
|
+
var DEBOUNCE_MS = 100;
|
|
564
|
+
var SOURCE_EXT = /\.(tsx?|jsx?)$/;
|
|
565
|
+
var offs = [];
|
|
566
|
+
var timer = null;
|
|
567
|
+
var changed = new Set;
|
|
568
|
+
var building = false;
|
|
569
|
+
var dirty = false;
|
|
570
|
+
function basename(path) {
|
|
571
|
+
return path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1);
|
|
572
|
+
}
|
|
573
|
+
function isToolingPath(path, root) {
|
|
574
|
+
let rel = path.startsWith(root) ? path.slice(root.length) : path;
|
|
575
|
+
return rel.split(/[\\/]/).some((part) => part.startsWith(".") || part === "node_modules");
|
|
576
|
+
}
|
|
577
|
+
function armWatcher(inputs) {
|
|
578
|
+
stopWatcher();
|
|
579
|
+
let config = state.config;
|
|
580
|
+
let hit = (path) => onChange(path);
|
|
581
|
+
try {
|
|
582
|
+
if (inputs) {
|
|
583
|
+
let byDir = new Map;
|
|
584
|
+
for (let input of inputs) {
|
|
585
|
+
let parent = dirname(input);
|
|
586
|
+
let names = byDir.get(parent);
|
|
587
|
+
if (!names)
|
|
588
|
+
byDir.set(parent, names = new Set);
|
|
589
|
+
names.add(basename(input));
|
|
590
|
+
}
|
|
591
|
+
for (let [parent, names] of byDir) {
|
|
592
|
+
offs.push(dir2(parent).watch((e) => names.has(basename(e.path)) && hit(e.path)));
|
|
593
|
+
}
|
|
594
|
+
} else {
|
|
595
|
+
let root = config.sourceDir;
|
|
596
|
+
offs.push(dir2(root).watch((e) => SOURCE_EXT.test(e.path) && !isToolingPath(e.path, root) && hit(e.path), { recursive: true }));
|
|
597
|
+
}
|
|
598
|
+
if (config.projectDir) {
|
|
599
|
+
let assets = `${config.projectDir}/assets`;
|
|
600
|
+
dir2(assets).exists().then((exists) => {
|
|
601
|
+
if (exists && offs.length)
|
|
602
|
+
offs.push(dir2(assets).watch((e) => !isToolingPath(e.path, assets) && hit(e.path), { recursive: true }));
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
} catch (e) {
|
|
606
|
+
console.error(`[cli] Watch failed: ${e instanceof Error ? e.message : e}`);
|
|
607
|
+
stopWatcher();
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function stopWatcher() {
|
|
611
|
+
for (let off of offs)
|
|
612
|
+
off();
|
|
613
|
+
offs = [];
|
|
614
|
+
if (timer !== null)
|
|
615
|
+
clearTimeout(timer);
|
|
616
|
+
timer = null;
|
|
617
|
+
changed.clear();
|
|
618
|
+
}
|
|
619
|
+
function onChange(path) {
|
|
620
|
+
if (state.watchPaused)
|
|
621
|
+
return;
|
|
622
|
+
changed.add(path);
|
|
623
|
+
if (building) {
|
|
624
|
+
dirty = true;
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (timer !== null)
|
|
628
|
+
clearTimeout(timer);
|
|
629
|
+
timer = setTimeout(() => void rebuild(), DEBOUNCE_MS);
|
|
630
|
+
}
|
|
631
|
+
async function rebuild() {
|
|
632
|
+
timer = null;
|
|
633
|
+
building = true;
|
|
634
|
+
let root = state.config.projectDir ?? state.config.sourceDir;
|
|
635
|
+
let names = [...changed].map((p) => p.startsWith(root) ? p.slice(root.length + 1) : p);
|
|
636
|
+
changed.clear();
|
|
637
|
+
console.log(`[cli] Change detected: ${names.slice(0, 3).join(", ")}${names.length > 3 ? ` (+${names.length - 3})` : ""}`);
|
|
638
|
+
let error = await rebuildAndBroadcast();
|
|
639
|
+
if (error) {
|
|
640
|
+
console.error(error);
|
|
641
|
+
showBuildFailure();
|
|
642
|
+
}
|
|
643
|
+
building = false;
|
|
644
|
+
if (dirty) {
|
|
645
|
+
dirty = false;
|
|
646
|
+
onChange(names[0] ?? root);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/server/rebuild.ts
|
|
651
|
+
function buildReload(code, manifest) {
|
|
652
|
+
let config = state.config;
|
|
653
|
+
return {
|
|
654
|
+
type: "reload",
|
|
655
|
+
proxyHttp: config.proxyHttp,
|
|
656
|
+
args: config.args,
|
|
657
|
+
...manifest ? { manifest } : {},
|
|
658
|
+
code
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
var BSOD_TRIGGER = `throw new Error("SolidRT: build failed")`;
|
|
662
|
+
function latchAndSend(text) {
|
|
663
|
+
state.currentReload = text;
|
|
664
|
+
for (let [ws, info] of state.clients) {
|
|
665
|
+
info.timeScale = 1;
|
|
666
|
+
ws.send(text);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
function showBuildFailure() {
|
|
670
|
+
state.currentMaps = null;
|
|
671
|
+
latchAndSend(JSON.stringify(buildReload(BSOD_TRIGGER)));
|
|
672
|
+
}
|
|
673
|
+
async function rebuildAndBroadcast() {
|
|
674
|
+
let config = state.config;
|
|
675
|
+
let args = [...config.srt.slice(1), "bundle", "--json", "--dev", ...config.entryArgs];
|
|
676
|
+
if (config.minify)
|
|
677
|
+
args.push("--minify");
|
|
678
|
+
let result = await command(config.srt[0], args, { cwd: config.cwd }).output();
|
|
679
|
+
if (!result.success) {
|
|
680
|
+
let stderr = typeof result.stderr === "string" ? result.stderr : "";
|
|
681
|
+
armWatcher(null);
|
|
682
|
+
return `Rebuild failed:
|
|
683
|
+
${stderr.trim()}`;
|
|
684
|
+
}
|
|
685
|
+
let bundle;
|
|
686
|
+
try {
|
|
687
|
+
bundle = JSON.parse(typeof result.stdout === "string" ? result.stdout : "");
|
|
688
|
+
} catch {
|
|
689
|
+
armWatcher(null);
|
|
690
|
+
return "Rebuild failed: unreadable bundler output";
|
|
691
|
+
}
|
|
692
|
+
armWatcher(bundle.inputs ?? null);
|
|
693
|
+
let maps = {};
|
|
694
|
+
if (bundle.map)
|
|
695
|
+
maps.main = bundle.map;
|
|
696
|
+
for (let isolate of bundle.isolates ?? []) {
|
|
697
|
+
let path = `${config.cacheDir}/isolates/${isolate.id}.js`;
|
|
698
|
+
await dir3(path.slice(0, path.lastIndexOf("/"))).create();
|
|
699
|
+
await file3(path).write(isolate.code);
|
|
700
|
+
if (isolate.map)
|
|
701
|
+
maps[isolate.id] = isolate.map;
|
|
702
|
+
}
|
|
703
|
+
state.currentMaps = Object.keys(maps).length ? maps : null;
|
|
704
|
+
latchAndSend(JSON.stringify(buildReload(bundle.code ?? "", bundle.manifest)));
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// ../../node_modules/.bun/@jridgewell+sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs
|
|
709
|
+
var comma = 44;
|
|
710
|
+
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
711
|
+
var intToChar = new Uint8Array(64);
|
|
712
|
+
var charToInt = new Uint8Array(128);
|
|
713
|
+
for (let i = 0;i < chars.length; i++) {
|
|
714
|
+
const c = chars.charCodeAt(i);
|
|
715
|
+
intToChar[i] = c;
|
|
716
|
+
charToInt[c] = i;
|
|
717
|
+
}
|
|
718
|
+
function decodeInteger(reader, relative) {
|
|
719
|
+
let value = 0;
|
|
720
|
+
let shift = 0;
|
|
721
|
+
let integer2 = 0;
|
|
722
|
+
do {
|
|
723
|
+
const c = reader.next();
|
|
724
|
+
integer2 = charToInt[c];
|
|
725
|
+
value |= (integer2 & 31) << shift;
|
|
726
|
+
shift += 5;
|
|
727
|
+
} while (integer2 & 32);
|
|
728
|
+
const shouldNegate = value & 1;
|
|
729
|
+
value >>>= 1;
|
|
730
|
+
if (shouldNegate) {
|
|
731
|
+
value = -2147483648 | -value;
|
|
732
|
+
}
|
|
733
|
+
return relative + value;
|
|
734
|
+
}
|
|
735
|
+
function hasMoreVlq(reader, max) {
|
|
736
|
+
if (reader.pos >= max)
|
|
737
|
+
return false;
|
|
738
|
+
return reader.peek() !== comma;
|
|
739
|
+
}
|
|
740
|
+
var bufLength = 1024 * 16;
|
|
741
|
+
var StringReader = class {
|
|
742
|
+
constructor(buffer) {
|
|
743
|
+
this.pos = 0;
|
|
744
|
+
this.buffer = buffer;
|
|
745
|
+
}
|
|
746
|
+
next() {
|
|
747
|
+
return this.buffer.charCodeAt(this.pos++);
|
|
748
|
+
}
|
|
749
|
+
peek() {
|
|
750
|
+
return this.buffer.charCodeAt(this.pos);
|
|
751
|
+
}
|
|
752
|
+
indexOf(char) {
|
|
753
|
+
const { buffer, pos } = this;
|
|
754
|
+
const idx = buffer.indexOf(char, pos);
|
|
755
|
+
return idx === -1 ? buffer.length : idx;
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
function decode(mappings) {
|
|
759
|
+
const { length } = mappings;
|
|
760
|
+
const reader = new StringReader(mappings);
|
|
761
|
+
const decoded = [];
|
|
762
|
+
let genColumn = 0;
|
|
763
|
+
let sourcesIndex = 0;
|
|
764
|
+
let sourceLine = 0;
|
|
765
|
+
let sourceColumn = 0;
|
|
766
|
+
let namesIndex = 0;
|
|
767
|
+
do {
|
|
768
|
+
const semi = reader.indexOf(";");
|
|
769
|
+
const line = [];
|
|
770
|
+
let sorted = true;
|
|
771
|
+
let lastCol = 0;
|
|
772
|
+
genColumn = 0;
|
|
773
|
+
while (reader.pos < semi) {
|
|
774
|
+
let seg;
|
|
775
|
+
genColumn = decodeInteger(reader, genColumn);
|
|
776
|
+
if (genColumn < lastCol)
|
|
777
|
+
sorted = false;
|
|
778
|
+
lastCol = genColumn;
|
|
779
|
+
if (hasMoreVlq(reader, semi)) {
|
|
780
|
+
sourcesIndex = decodeInteger(reader, sourcesIndex);
|
|
781
|
+
sourceLine = decodeInteger(reader, sourceLine);
|
|
782
|
+
sourceColumn = decodeInteger(reader, sourceColumn);
|
|
783
|
+
if (hasMoreVlq(reader, semi)) {
|
|
784
|
+
namesIndex = decodeInteger(reader, namesIndex);
|
|
785
|
+
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn, namesIndex];
|
|
786
|
+
} else {
|
|
787
|
+
seg = [genColumn, sourcesIndex, sourceLine, sourceColumn];
|
|
788
|
+
}
|
|
789
|
+
} else {
|
|
790
|
+
seg = [genColumn];
|
|
791
|
+
}
|
|
792
|
+
line.push(seg);
|
|
793
|
+
reader.pos++;
|
|
794
|
+
}
|
|
795
|
+
if (!sorted)
|
|
796
|
+
sort(line);
|
|
797
|
+
decoded.push(line);
|
|
798
|
+
reader.pos = semi + 1;
|
|
799
|
+
} while (reader.pos <= length);
|
|
800
|
+
return decoded;
|
|
801
|
+
}
|
|
802
|
+
function sort(line) {
|
|
803
|
+
line.sort(sortComparator);
|
|
804
|
+
}
|
|
805
|
+
function sortComparator(a, b) {
|
|
806
|
+
return a[0] - b[0];
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// ../../node_modules/.bun/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs
|
|
810
|
+
var import_resolve_uri = __toESM(require_resolve_uri_umd(), 1);
|
|
811
|
+
function stripFilename(path) {
|
|
812
|
+
if (!path)
|
|
813
|
+
return "";
|
|
814
|
+
const index = path.lastIndexOf("/");
|
|
815
|
+
return path.slice(0, index + 1);
|
|
816
|
+
}
|
|
817
|
+
function resolver(mapUrl, sourceRoot) {
|
|
818
|
+
const from = stripFilename(mapUrl);
|
|
819
|
+
const prefix = sourceRoot ? sourceRoot + "/" : "";
|
|
820
|
+
return (source) => import_resolve_uri.default(prefix + (source || ""), from);
|
|
821
|
+
}
|
|
822
|
+
var COLUMN = 0;
|
|
823
|
+
var SOURCES_INDEX = 1;
|
|
824
|
+
var SOURCE_LINE = 2;
|
|
825
|
+
var SOURCE_COLUMN = 3;
|
|
826
|
+
var NAMES_INDEX = 4;
|
|
827
|
+
function maybeSort(mappings, owned) {
|
|
828
|
+
const unsortedIndex = nextUnsortedSegmentLine(mappings, 0);
|
|
829
|
+
if (unsortedIndex === mappings.length)
|
|
830
|
+
return mappings;
|
|
831
|
+
if (!owned)
|
|
832
|
+
mappings = mappings.slice();
|
|
833
|
+
for (let i = unsortedIndex;i < mappings.length; i = nextUnsortedSegmentLine(mappings, i + 1)) {
|
|
834
|
+
mappings[i] = sortSegments(mappings[i], owned);
|
|
835
|
+
}
|
|
836
|
+
return mappings;
|
|
837
|
+
}
|
|
838
|
+
function nextUnsortedSegmentLine(mappings, start) {
|
|
839
|
+
for (let i = start;i < mappings.length; i++) {
|
|
840
|
+
if (!isSorted(mappings[i]))
|
|
841
|
+
return i;
|
|
842
|
+
}
|
|
843
|
+
return mappings.length;
|
|
844
|
+
}
|
|
845
|
+
function isSorted(line) {
|
|
846
|
+
for (let j = 1;j < line.length; j++) {
|
|
847
|
+
if (line[j][COLUMN] < line[j - 1][COLUMN]) {
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return true;
|
|
852
|
+
}
|
|
853
|
+
function sortSegments(line, owned) {
|
|
854
|
+
if (!owned)
|
|
855
|
+
line = line.slice();
|
|
856
|
+
return line.sort(sortComparator2);
|
|
857
|
+
}
|
|
858
|
+
function sortComparator2(a, b) {
|
|
859
|
+
return a[COLUMN] - b[COLUMN];
|
|
860
|
+
}
|
|
861
|
+
var found = false;
|
|
862
|
+
function binarySearch(haystack, needle, low, high) {
|
|
863
|
+
while (low <= high) {
|
|
864
|
+
const mid = low + (high - low >> 1);
|
|
865
|
+
const cmp = haystack[mid][COLUMN] - needle;
|
|
866
|
+
if (cmp === 0) {
|
|
867
|
+
found = true;
|
|
868
|
+
return mid;
|
|
869
|
+
}
|
|
870
|
+
if (cmp < 0) {
|
|
871
|
+
low = mid + 1;
|
|
872
|
+
} else {
|
|
873
|
+
high = mid - 1;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
found = false;
|
|
877
|
+
return low - 1;
|
|
878
|
+
}
|
|
879
|
+
function upperBound(haystack, needle, index) {
|
|
880
|
+
for (let i = index + 1;i < haystack.length; index = i++) {
|
|
881
|
+
if (haystack[i][COLUMN] !== needle)
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
return index;
|
|
885
|
+
}
|
|
886
|
+
function lowerBound(haystack, needle, index) {
|
|
887
|
+
for (let i = index - 1;i >= 0; index = i--) {
|
|
888
|
+
if (haystack[i][COLUMN] !== needle)
|
|
889
|
+
break;
|
|
890
|
+
}
|
|
891
|
+
return index;
|
|
892
|
+
}
|
|
893
|
+
function memoizedState() {
|
|
894
|
+
return {
|
|
895
|
+
lastKey: -1,
|
|
896
|
+
lastNeedle: -1,
|
|
897
|
+
lastIndex: -1
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
function memoizedBinarySearch(haystack, needle, state2, key) {
|
|
901
|
+
const { lastKey, lastNeedle, lastIndex } = state2;
|
|
902
|
+
let low = 0;
|
|
903
|
+
let high = haystack.length - 1;
|
|
904
|
+
if (key === lastKey) {
|
|
905
|
+
if (needle === lastNeedle) {
|
|
906
|
+
found = lastIndex !== -1 && haystack[lastIndex][COLUMN] === needle;
|
|
907
|
+
return lastIndex;
|
|
908
|
+
}
|
|
909
|
+
if (needle >= lastNeedle) {
|
|
910
|
+
low = lastIndex === -1 ? 0 : lastIndex;
|
|
911
|
+
} else {
|
|
912
|
+
high = lastIndex;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
state2.lastKey = key;
|
|
916
|
+
state2.lastNeedle = needle;
|
|
917
|
+
return state2.lastIndex = binarySearch(haystack, needle, low, high);
|
|
918
|
+
}
|
|
919
|
+
function parse(map) {
|
|
920
|
+
return typeof map === "string" ? JSON.parse(map) : map;
|
|
921
|
+
}
|
|
922
|
+
var LINE_GTR_ZERO = "`line` must be greater than 0 (lines start at line 1)";
|
|
923
|
+
var COL_GTR_EQ_ZERO = "`column` must be greater than or equal to 0 (columns start at column 0)";
|
|
924
|
+
var LEAST_UPPER_BOUND = -1;
|
|
925
|
+
var GREATEST_LOWER_BOUND = 1;
|
|
926
|
+
var TraceMap = class {
|
|
927
|
+
constructor(map, mapUrl) {
|
|
928
|
+
const isString = typeof map === "string";
|
|
929
|
+
if (!isString && map._decodedMemo)
|
|
930
|
+
return map;
|
|
931
|
+
const parsed = parse(map);
|
|
932
|
+
const { version, file: file4, names, sourceRoot, sources, sourcesContent } = parsed;
|
|
933
|
+
this.version = version;
|
|
934
|
+
this.file = file4;
|
|
935
|
+
this.names = names || [];
|
|
936
|
+
this.sourceRoot = sourceRoot;
|
|
937
|
+
this.sources = sources;
|
|
938
|
+
this.sourcesContent = sourcesContent;
|
|
939
|
+
this.ignoreList = parsed.ignoreList || parsed.x_google_ignoreList || undefined;
|
|
940
|
+
const resolve = resolver(mapUrl, sourceRoot);
|
|
941
|
+
this.resolvedSources = sources.map(resolve);
|
|
942
|
+
const { mappings } = parsed;
|
|
943
|
+
if (typeof mappings === "string") {
|
|
944
|
+
this._encoded = mappings;
|
|
945
|
+
this._decoded = undefined;
|
|
946
|
+
} else if (Array.isArray(mappings)) {
|
|
947
|
+
this._encoded = undefined;
|
|
948
|
+
this._decoded = maybeSort(mappings, isString);
|
|
949
|
+
} else if (parsed.sections) {
|
|
950
|
+
throw new Error(`TraceMap passed sectioned source map, please use FlattenMap export instead`);
|
|
951
|
+
} else {
|
|
952
|
+
throw new Error(`invalid source map: ${JSON.stringify(parsed)}`);
|
|
953
|
+
}
|
|
954
|
+
this._decodedMemo = memoizedState();
|
|
955
|
+
this._bySources = undefined;
|
|
956
|
+
this._bySourceMemos = undefined;
|
|
957
|
+
}
|
|
958
|
+
};
|
|
959
|
+
function cast(map) {
|
|
960
|
+
return map;
|
|
961
|
+
}
|
|
962
|
+
function decodedMappings(map) {
|
|
963
|
+
var _a;
|
|
964
|
+
return (_a = cast(map))._decoded || (_a._decoded = decode(cast(map)._encoded));
|
|
965
|
+
}
|
|
966
|
+
function originalPositionFor(map, needle) {
|
|
967
|
+
let { line, column, bias } = needle;
|
|
968
|
+
line--;
|
|
969
|
+
if (line < 0)
|
|
970
|
+
throw new Error(LINE_GTR_ZERO);
|
|
971
|
+
if (column < 0)
|
|
972
|
+
throw new Error(COL_GTR_EQ_ZERO);
|
|
973
|
+
const decoded = decodedMappings(map);
|
|
974
|
+
if (line >= decoded.length)
|
|
975
|
+
return OMapping(null, null, null, null);
|
|
976
|
+
const segments = decoded[line];
|
|
977
|
+
const index = traceSegmentInternal(segments, cast(map)._decodedMemo, line, column, bias || GREATEST_LOWER_BOUND);
|
|
978
|
+
if (index === -1)
|
|
979
|
+
return OMapping(null, null, null, null);
|
|
980
|
+
const segment = segments[index];
|
|
981
|
+
if (segment.length === 1)
|
|
982
|
+
return OMapping(null, null, null, null);
|
|
983
|
+
const { names, resolvedSources } = map;
|
|
984
|
+
return OMapping(resolvedSources[segment[SOURCES_INDEX]], segment[SOURCE_LINE] + 1, segment[SOURCE_COLUMN], segment.length === 5 ? names[segment[NAMES_INDEX]] : null);
|
|
985
|
+
}
|
|
986
|
+
function OMapping(source, line, column, name) {
|
|
987
|
+
return { source, line, column, name };
|
|
988
|
+
}
|
|
989
|
+
function traceSegmentInternal(segments, memo, line, column, bias) {
|
|
990
|
+
let index = memoizedBinarySearch(segments, column, memo, line);
|
|
991
|
+
if (found) {
|
|
992
|
+
index = (bias === LEAST_UPPER_BOUND ? upperBound : lowerBound)(segments, column, index);
|
|
993
|
+
} else if (bias === LEAST_UPPER_BOUND)
|
|
994
|
+
index++;
|
|
995
|
+
if (index === -1 || index === segments.length)
|
|
996
|
+
return -1;
|
|
997
|
+
return index;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// src/server/remap.ts
|
|
1001
|
+
var cached = new Map;
|
|
1002
|
+
function tracerFor(name, text) {
|
|
1003
|
+
let entry = cached.get(name);
|
|
1004
|
+
if (!entry || entry.text !== text) {
|
|
1005
|
+
let tracer = null;
|
|
1006
|
+
try {
|
|
1007
|
+
tracer = new TraceMap(JSON.parse(text));
|
|
1008
|
+
} catch {}
|
|
1009
|
+
entry = { text, tracer };
|
|
1010
|
+
cached.set(name, entry);
|
|
1011
|
+
}
|
|
1012
|
+
return entry.tracer;
|
|
1013
|
+
}
|
|
1014
|
+
var REGEX_SPECIALS = /[.*+?^${}()|[\]\\]/g;
|
|
1015
|
+
function remapPositions(text, maps) {
|
|
1016
|
+
if (!maps)
|
|
1017
|
+
return text;
|
|
1018
|
+
for (let [name, map] of Object.entries(maps)) {
|
|
1019
|
+
if (!text.includes(name + ":"))
|
|
1020
|
+
continue;
|
|
1021
|
+
let t = tracerFor(name, map);
|
|
1022
|
+
if (!t)
|
|
1023
|
+
continue;
|
|
1024
|
+
let pattern = new RegExp(`(^|[^\\w/.$-])${name.replace(REGEX_SPECIALS, "\\$&")}:(\\d+)(?::(\\d+))?\\b`, "g");
|
|
1025
|
+
text = text.replace(pattern, (frame, prefix, line, column) => {
|
|
1026
|
+
let pos = originalPositionFor(t, {
|
|
1027
|
+
line: parseInt(line, 10),
|
|
1028
|
+
column: column ? Math.max(parseInt(column, 10) - 1, 0) : 0
|
|
1029
|
+
});
|
|
1030
|
+
if (pos.source == null || pos.line == null)
|
|
1031
|
+
return frame;
|
|
1032
|
+
return `${prefix}${pos.source}:${pos.line}:${(pos.column ?? 0) + 1}`;
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
return text;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// src/server/control.ts
|
|
1039
|
+
import { file as file4, realpath as realpath2 } from "flux:fs";
|
|
1040
|
+
var LOG_CAP = 2000;
|
|
1041
|
+
var QUERY_TIMEOUT_MS = 5000;
|
|
1042
|
+
var MAX_WAIT_MS = 30000;
|
|
1043
|
+
var logs = [];
|
|
1044
|
+
var logSeq = 0;
|
|
1045
|
+
var logWaiters = [];
|
|
1046
|
+
var nextQueryId = 1;
|
|
1047
|
+
var pendingQueries = new Map;
|
|
1048
|
+
function sleep(ms) {
|
|
1049
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1050
|
+
}
|
|
1051
|
+
function appendLog(client, level, text) {
|
|
1052
|
+
logs.push({ seq: ++logSeq, at: Date.now(), client, level, text: remapPositions(text, state.currentMaps) });
|
|
1053
|
+
if (logs.length > LOG_CAP)
|
|
1054
|
+
logs.splice(0, logs.length - LOG_CAP);
|
|
1055
|
+
let waiters = logWaiters;
|
|
1056
|
+
logWaiters = [];
|
|
1057
|
+
for (let wake of waiters)
|
|
1058
|
+
wake();
|
|
1059
|
+
}
|
|
1060
|
+
function resolveQuery(msg) {
|
|
1061
|
+
if (typeof msg.id !== "number")
|
|
1062
|
+
return;
|
|
1063
|
+
let resolve = pendingQueries.get(msg.id);
|
|
1064
|
+
if (resolve) {
|
|
1065
|
+
pendingQueries.delete(msg.id);
|
|
1066
|
+
resolve(msg);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
function clientList(withAddress = false) {
|
|
1070
|
+
return [...state.clients.entries()].map(([ws, info]) => ({
|
|
1071
|
+
id: info.id,
|
|
1072
|
+
platform: info.platform,
|
|
1073
|
+
version: info.version,
|
|
1074
|
+
profile: info.profile,
|
|
1075
|
+
capabilities: info.capabilities,
|
|
1076
|
+
queries: info.queries,
|
|
1077
|
+
stats: info.stats,
|
|
1078
|
+
timeScale: info.timeScale,
|
|
1079
|
+
clientDir: info.clientDir,
|
|
1080
|
+
pid: info.pid,
|
|
1081
|
+
execPath: info.execPath,
|
|
1082
|
+
host: info.host,
|
|
1083
|
+
os: info.os,
|
|
1084
|
+
kernel: info.kernel,
|
|
1085
|
+
videoDriver: info.videoDriver,
|
|
1086
|
+
gpu: info.gpu,
|
|
1087
|
+
...withAddress ? { address: ws.remoteAddr ?? null } : {}
|
|
1088
|
+
}));
|
|
1089
|
+
}
|
|
1090
|
+
function findClient(param) {
|
|
1091
|
+
let entries = [...state.clients.entries()];
|
|
1092
|
+
if (param === undefined) {
|
|
1093
|
+
if (entries.length === 1)
|
|
1094
|
+
return { ws: entries[0][0] };
|
|
1095
|
+
if (entries.length === 0)
|
|
1096
|
+
return { error: Response.json({ error: "No connected clients" }, { status: 503 }) };
|
|
1097
|
+
return { error: Response.json({ error: "Multiple clients connected; pass a client id (list_clients has the ids)" }, { status: 400 }) };
|
|
1098
|
+
}
|
|
1099
|
+
let id = parseInt(param, 10);
|
|
1100
|
+
let entry = entries.find(([, info]) => info.id === id);
|
|
1101
|
+
if (!entry) {
|
|
1102
|
+
let ids = entries.map(([, info]) => info.id);
|
|
1103
|
+
return {
|
|
1104
|
+
error: Response.json({
|
|
1105
|
+
error: `Client ${param} is gone (connected ids: ${ids.length ? ids.join(", ") : "none"}). ` + "Ids reset when the dev server restarts; call list_clients for current ones."
|
|
1106
|
+
}, { status: 404 })
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
return { ws: entry[0] };
|
|
1110
|
+
}
|
|
1111
|
+
function parseRect(query) {
|
|
1112
|
+
let params = ["x", "y", "width", "height"].map((k) => query.get(k));
|
|
1113
|
+
if (params.every((v) => v === undefined))
|
|
1114
|
+
return;
|
|
1115
|
+
let [x, y, width, height] = params.map((v) => parseInt(v ?? "", 10));
|
|
1116
|
+
if (![x, y, width, height].every(Number.isFinite))
|
|
1117
|
+
return Response.json({ error: "Crop rect requires all of x, y, width, height" }, { status: 400 });
|
|
1118
|
+
return { x, y, width, height };
|
|
1119
|
+
}
|
|
1120
|
+
function parseFormat(query) {
|
|
1121
|
+
let param = query.get("format");
|
|
1122
|
+
if (param === undefined || param === "png")
|
|
1123
|
+
return;
|
|
1124
|
+
if (param === "raw")
|
|
1125
|
+
return "raw";
|
|
1126
|
+
return Response.json({ error: 'Format must be "png" or "raw"' }, { status: 400 });
|
|
1127
|
+
}
|
|
1128
|
+
function parseScale(query) {
|
|
1129
|
+
let param = query.get("scale");
|
|
1130
|
+
if (param === undefined)
|
|
1131
|
+
return;
|
|
1132
|
+
let scale = parseInt(param, 10);
|
|
1133
|
+
if (!Number.isFinite(scale) || scale < 1 || scale > 8)
|
|
1134
|
+
return Response.json({ error: "Scale must be an integer between 1 and 8" }, { status: 400 });
|
|
1135
|
+
return scale === 1 ? undefined : scale;
|
|
1136
|
+
}
|
|
1137
|
+
async function queryClient(ws, kind, extra, timeoutMs = QUERY_TIMEOUT_MS) {
|
|
1138
|
+
let id = nextQueryId++;
|
|
1139
|
+
let reply = new Promise((resolve) => {
|
|
1140
|
+
pendingQueries.set(id, resolve);
|
|
1141
|
+
});
|
|
1142
|
+
ws.send(JSON.stringify({ type: "query", kind, id, ...extra }));
|
|
1143
|
+
let msg = await Promise.race([reply, sleep(timeoutMs)]);
|
|
1144
|
+
pendingQueries.delete(id);
|
|
1145
|
+
if (!msg)
|
|
1146
|
+
return {
|
|
1147
|
+
error: Response.json({ error: "Query timed out: the client is connected but did not answer (JS thread busy or app wedged?)" }, { status: 504 })
|
|
1148
|
+
};
|
|
1149
|
+
if (msg.error)
|
|
1150
|
+
return { error: Response.json({ error: remapPositions(String(msg.error), state.currentMaps) }, { status: 502 }) };
|
|
1151
|
+
return { data: msg.data };
|
|
1152
|
+
}
|
|
1153
|
+
async function handleQuery(query, kind, extra, timeoutMs = QUERY_TIMEOUT_MS) {
|
|
1154
|
+
let target = findClient(query.get("client"));
|
|
1155
|
+
if ("error" in target)
|
|
1156
|
+
return target.error;
|
|
1157
|
+
let result = await queryClient(target.ws, kind, extra, timeoutMs);
|
|
1158
|
+
return "error" in result ? result.error : Response.json(result.data);
|
|
1159
|
+
}
|
|
1160
|
+
function collapseRepeats(entries) {
|
|
1161
|
+
let out = [];
|
|
1162
|
+
for (let e of entries) {
|
|
1163
|
+
let last = out[out.length - 1];
|
|
1164
|
+
if (last && last.client === e.client && last.level === e.level && last.text === e.text) {
|
|
1165
|
+
last.repeats = (last.repeats ?? 1) + 1;
|
|
1166
|
+
last.seq = e.seq;
|
|
1167
|
+
last.at = e.at;
|
|
1168
|
+
} else {
|
|
1169
|
+
out.push({ ...e });
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
return out;
|
|
1173
|
+
}
|
|
1174
|
+
async function handleLogs(query) {
|
|
1175
|
+
let since = parseInt(query.get("since") ?? "0", 10) || 0;
|
|
1176
|
+
let wait = Math.min(parseInt(query.get("wait") ?? "0", 10) || 0, MAX_WAIT_MS);
|
|
1177
|
+
let levels = query.get("level")?.split(",").map((l) => l.trim()).filter(Boolean);
|
|
1178
|
+
let contains = query.get("contains")?.toLowerCase();
|
|
1179
|
+
let client;
|
|
1180
|
+
let clientParam = query.get("client");
|
|
1181
|
+
if (clientParam !== undefined) {
|
|
1182
|
+
client = parseInt(clientParam, 10);
|
|
1183
|
+
if (!Number.isFinite(client))
|
|
1184
|
+
return Response.json({ error: "Client must be a numeric id" }, { status: 400 });
|
|
1185
|
+
}
|
|
1186
|
+
let select = () => logs.filter((e) => e.seq > since && (client === undefined || e.client === client) && (!levels || levels.length === 0 || levels.includes(e.level)) && (!contains || e.text.toLowerCase().includes(contains)));
|
|
1187
|
+
let entries = select();
|
|
1188
|
+
let deadline = Date.now() + wait;
|
|
1189
|
+
while (entries.length === 0 && Date.now() < deadline) {
|
|
1190
|
+
await new Promise((resolve) => {
|
|
1191
|
+
let timer2 = setTimeout(resolve, deadline - Date.now());
|
|
1192
|
+
logWaiters.push(() => {
|
|
1193
|
+
clearTimeout(timer2);
|
|
1194
|
+
resolve();
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1197
|
+
entries = select();
|
|
1198
|
+
}
|
|
1199
|
+
let body = { entries: collapseRepeats(entries), latest: logSeq, generation: state.generation };
|
|
1200
|
+
return Response.json(body);
|
|
1201
|
+
}
|
|
1202
|
+
function isUnder(path, root) {
|
|
1203
|
+
return path.length > root.length && path.startsWith(root) && (path[root.length] === "/" || path[root.length] === "\\");
|
|
1204
|
+
}
|
|
1205
|
+
async function loadEntry(requested) {
|
|
1206
|
+
if (!ENTRY_EXTENSIONS.some((ext) => requested.endsWith(ext))) {
|
|
1207
|
+
return { error: `Not an app entry: ${requested} (expected .tsx, .jsx, .ts, .js or .srt.js)`, status: 400 };
|
|
1208
|
+
}
|
|
1209
|
+
let config = state.config;
|
|
1210
|
+
let path = absolute(requested, config.projectDir ?? config.sourceDir);
|
|
1211
|
+
if (!await file4(path).exists())
|
|
1212
|
+
return { error: `Entry not found: ${path}`, status: 400 };
|
|
1213
|
+
let entry = await realpath2(path);
|
|
1214
|
+
if (config.projectDir && !isUnder(entry, config.projectDir)) {
|
|
1215
|
+
return {
|
|
1216
|
+
error: `Entry is outside the project: ${entry} is not under ${config.projectDir}. A project server only bundles sources inside its project; start srt for that file on its own.`,
|
|
1217
|
+
status: 400
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
config.entry = entry;
|
|
1221
|
+
config.entryArgs[0] = entry;
|
|
1222
|
+
if (!config.projectDir) {
|
|
1223
|
+
config.sourceDir = dirname(entry);
|
|
1224
|
+
config.cwd = config.sourceDir;
|
|
1225
|
+
}
|
|
1226
|
+
console.log(`[cli] Loading ${entry}`);
|
|
1227
|
+
let error = await rebuildAndBroadcast();
|
|
1228
|
+
if (error)
|
|
1229
|
+
return { error, status: 502 };
|
|
1230
|
+
return { entry };
|
|
1231
|
+
}
|
|
1232
|
+
function setUserInputMuted(on) {
|
|
1233
|
+
if (on !== state.userInputMuted) {
|
|
1234
|
+
console.log(on ? "[cli] User input muted on every client" : "[cli] User input unmuted");
|
|
1235
|
+
}
|
|
1236
|
+
state.userInputMuted = on;
|
|
1237
|
+
let text = JSON.stringify({ type: "mute", active: on });
|
|
1238
|
+
for (let ws of state.clients.keys())
|
|
1239
|
+
ws.send(text);
|
|
1240
|
+
}
|
|
1241
|
+
function setWatchActive(on) {
|
|
1242
|
+
if (on === state.watchPaused) {
|
|
1243
|
+
console.log(on ? "[cli] Reload on save resumed" : "[cli] Reload on save paused");
|
|
1244
|
+
}
|
|
1245
|
+
state.watchPaused = !on;
|
|
1246
|
+
}
|
|
1247
|
+
function setStats(on, ws) {
|
|
1248
|
+
let text = JSON.stringify({ type: "stats", stats: on });
|
|
1249
|
+
let targets = ws ? [ws] : [...state.clients.keys()];
|
|
1250
|
+
if (!ws)
|
|
1251
|
+
state.stats = on;
|
|
1252
|
+
for (let target of targets) {
|
|
1253
|
+
let info = state.clients.get(target);
|
|
1254
|
+
if (info)
|
|
1255
|
+
info.stats = on;
|
|
1256
|
+
target.send(text);
|
|
1257
|
+
}
|
|
1258
|
+
return targets.length;
|
|
1259
|
+
}
|
|
1260
|
+
async function handleControl(req, path, query) {
|
|
1261
|
+
switch (path) {
|
|
1262
|
+
case "/__control__/clients":
|
|
1263
|
+
let body = {
|
|
1264
|
+
generation: state.generation,
|
|
1265
|
+
key: state.config.key,
|
|
1266
|
+
mode: state.config.mode,
|
|
1267
|
+
entry: state.config.entry,
|
|
1268
|
+
projectDir: state.config.projectDir,
|
|
1269
|
+
userInputMuted: state.userInputMuted,
|
|
1270
|
+
watchPaused: state.watchPaused,
|
|
1271
|
+
clients: clientList()
|
|
1272
|
+
};
|
|
1273
|
+
return Response.json(body);
|
|
1274
|
+
case "/__control__/logs":
|
|
1275
|
+
return handleLogs(query);
|
|
1276
|
+
case "/__control__/tree": {
|
|
1277
|
+
let extra = {};
|
|
1278
|
+
let root = parseInt(query.get("root") ?? "", 10);
|
|
1279
|
+
if (Number.isFinite(root))
|
|
1280
|
+
extra.root = root;
|
|
1281
|
+
let depth = parseInt(query.get("depth") ?? "", 10);
|
|
1282
|
+
if (Number.isFinite(depth))
|
|
1283
|
+
extra.depth = depth;
|
|
1284
|
+
let q = query.get("query");
|
|
1285
|
+
if (q)
|
|
1286
|
+
extra.query = q;
|
|
1287
|
+
if (query.get("props") === "true")
|
|
1288
|
+
extra.props = true;
|
|
1289
|
+
return handleQuery(query, "tree", extra);
|
|
1290
|
+
}
|
|
1291
|
+
case "/__control__/stats": {
|
|
1292
|
+
if (req.method === "POST") {
|
|
1293
|
+
let active = query.get("active");
|
|
1294
|
+
if (active !== "true" && active !== "false") {
|
|
1295
|
+
return Response.json({ error: "Stats requires ?active=true or ?active=false" }, { status: 400 });
|
|
1296
|
+
}
|
|
1297
|
+
let on = active === "true";
|
|
1298
|
+
let target = query.get("client");
|
|
1299
|
+
let ws;
|
|
1300
|
+
if (target !== undefined) {
|
|
1301
|
+
let found2 = findClient(target);
|
|
1302
|
+
if ("error" in found2)
|
|
1303
|
+
return found2.error;
|
|
1304
|
+
ws = found2.ws;
|
|
1305
|
+
}
|
|
1306
|
+
let body2 = { ok: true, active: on, clients: setStats(on, ws) };
|
|
1307
|
+
return Response.json(body2);
|
|
1308
|
+
}
|
|
1309
|
+
let extra = {};
|
|
1310
|
+
let windowMs = parseInt(query.get("window") ?? "", 10);
|
|
1311
|
+
if (Number.isFinite(windowMs))
|
|
1312
|
+
extra.windowMs = windowMs;
|
|
1313
|
+
return handleQuery(query, "stats", extra);
|
|
1314
|
+
}
|
|
1315
|
+
case "/__control__/snapshot": {
|
|
1316
|
+
let nodeId = parseInt(query.get("node") ?? "", 10);
|
|
1317
|
+
if (!Number.isFinite(nodeId))
|
|
1318
|
+
return Response.json({ error: "Snapshot requires ?node=<id>" }, { status: 400 });
|
|
1319
|
+
let extra = { nodeId };
|
|
1320
|
+
let rect = parseRect(query);
|
|
1321
|
+
if (rect instanceof Response)
|
|
1322
|
+
return rect;
|
|
1323
|
+
if (rect)
|
|
1324
|
+
extra.rect = rect;
|
|
1325
|
+
let scale = parseScale(query);
|
|
1326
|
+
if (scale instanceof Response)
|
|
1327
|
+
return scale;
|
|
1328
|
+
if (scale)
|
|
1329
|
+
extra.scale = scale;
|
|
1330
|
+
let format = parseFormat(query);
|
|
1331
|
+
if (format instanceof Response)
|
|
1332
|
+
return format;
|
|
1333
|
+
if (format)
|
|
1334
|
+
extra.format = format;
|
|
1335
|
+
return handleQuery(query, "snapshot", extra);
|
|
1336
|
+
}
|
|
1337
|
+
case "/__control__/gpu": {
|
|
1338
|
+
let label = query.get("label");
|
|
1339
|
+
return handleQuery(query, "gpu", label === undefined ? undefined : { label });
|
|
1340
|
+
}
|
|
1341
|
+
case "/__control__/debug": {
|
|
1342
|
+
if (req.method !== "POST")
|
|
1343
|
+
return handleQuery(query, "debug_list");
|
|
1344
|
+
let name = query.get("name");
|
|
1345
|
+
if (!name)
|
|
1346
|
+
return Response.json({ error: "Debug call requires ?name=<command>" }, { status: 400 });
|
|
1347
|
+
let args = null;
|
|
1348
|
+
try {
|
|
1349
|
+
args = await req.json();
|
|
1350
|
+
} catch {}
|
|
1351
|
+
return handleQuery(query, "debug_call", { name, args });
|
|
1352
|
+
}
|
|
1353
|
+
case "/__control__/texture": {
|
|
1354
|
+
let textureId = parseInt(query.get("id") ?? "", 10);
|
|
1355
|
+
if (!Number.isFinite(textureId))
|
|
1356
|
+
return Response.json({ error: "Texture requires ?id=<textureId>" }, { status: 400 });
|
|
1357
|
+
let extra = { textureId };
|
|
1358
|
+
let rect = parseRect(query);
|
|
1359
|
+
if (rect instanceof Response)
|
|
1360
|
+
return rect;
|
|
1361
|
+
if (rect)
|
|
1362
|
+
extra.rect = rect;
|
|
1363
|
+
let scale = parseScale(query);
|
|
1364
|
+
if (scale instanceof Response)
|
|
1365
|
+
return scale;
|
|
1366
|
+
if (scale)
|
|
1367
|
+
extra.scale = scale;
|
|
1368
|
+
let format = parseFormat(query);
|
|
1369
|
+
if (format instanceof Response)
|
|
1370
|
+
return format;
|
|
1371
|
+
if (format)
|
|
1372
|
+
extra.format = format;
|
|
1373
|
+
return handleQuery(query, "texture", extra);
|
|
1374
|
+
}
|
|
1375
|
+
case "/__control__/clock": {
|
|
1376
|
+
if (req.method !== "POST")
|
|
1377
|
+
return Response.json({ error: "Clock requires POST" }, { status: 405 });
|
|
1378
|
+
let extra = {};
|
|
1379
|
+
let scaleParam = query.get("scale");
|
|
1380
|
+
if (scaleParam !== undefined) {
|
|
1381
|
+
let scale = parseFloat(scaleParam);
|
|
1382
|
+
if (!Number.isFinite(scale) || scale < 0)
|
|
1383
|
+
return Response.json({ error: "Clock scale must be a number >= 0" }, { status: 400 });
|
|
1384
|
+
extra.scale = scale;
|
|
1385
|
+
}
|
|
1386
|
+
let stepParam = query.get("step");
|
|
1387
|
+
if (stepParam !== undefined) {
|
|
1388
|
+
let step = parseInt(stepParam, 10);
|
|
1389
|
+
if (!Number.isFinite(step) || step < 1 || step > 1000)
|
|
1390
|
+
return Response.json({ error: "Clock step must be an integer between 1 and 1000" }, { status: 400 });
|
|
1391
|
+
extra.step = step;
|
|
1392
|
+
}
|
|
1393
|
+
if (!("scale" in extra) && !("step" in extra))
|
|
1394
|
+
return Response.json({ error: "Clock requires ?scale=<x> or ?step=<n>" }, { status: 400 });
|
|
1395
|
+
let target = findClient(query.get("client"));
|
|
1396
|
+
if ("error" in target)
|
|
1397
|
+
return target.error;
|
|
1398
|
+
let result = await queryClient(target.ws, "clock", extra);
|
|
1399
|
+
if ("error" in result)
|
|
1400
|
+
return result.error;
|
|
1401
|
+
let info = state.clients.get(target.ws);
|
|
1402
|
+
if (info && typeof result.data?.scale === "number")
|
|
1403
|
+
info.timeScale = result.data.scale;
|
|
1404
|
+
return Response.json(result.data);
|
|
1405
|
+
}
|
|
1406
|
+
case "/__control__/input": {
|
|
1407
|
+
if (req.method !== "POST")
|
|
1408
|
+
return Response.json({ error: "Input requires POST" }, { status: 405 });
|
|
1409
|
+
let body2 = null;
|
|
1410
|
+
try {
|
|
1411
|
+
body2 = await req.json();
|
|
1412
|
+
} catch {}
|
|
1413
|
+
let events = body2?.events;
|
|
1414
|
+
if (!Array.isArray(events) || events.length === 0)
|
|
1415
|
+
return Response.json({ error: "Input requires a body {events: [...]} with at least one event" }, { status: 400 });
|
|
1416
|
+
if (events.length > 200)
|
|
1417
|
+
return Response.json({ error: "Input sequences are capped at 200 events" }, { status: 400 });
|
|
1418
|
+
let totalMs = 0;
|
|
1419
|
+
for (let e of events) {
|
|
1420
|
+
if (typeof e !== "object" || e === null)
|
|
1421
|
+
return Response.json({ error: "Each event must be an object" }, { status: 400 });
|
|
1422
|
+
for (let f of ["delayMs", "holdMs"]) {
|
|
1423
|
+
let v = e[f];
|
|
1424
|
+
if (v !== undefined) {
|
|
1425
|
+
if (typeof v !== "number" || !Number.isInteger(v) || v < 0 || v > 5000)
|
|
1426
|
+
return Response.json({ error: `Event ${f} must be an integer between 0 and 5000` }, { status: 400 });
|
|
1427
|
+
totalMs += v;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
if (totalMs > 30000)
|
|
1432
|
+
return Response.json({ error: "Input sequence too long: delays and holds total over 30000 ms" }, { status: 400 });
|
|
1433
|
+
return handleQuery(query, "input", { events }, QUERY_TIMEOUT_MS + totalMs);
|
|
1434
|
+
}
|
|
1435
|
+
case "/__control__/buffer": {
|
|
1436
|
+
let bufferId = parseInt(query.get("id") ?? "", 10);
|
|
1437
|
+
if (!Number.isFinite(bufferId))
|
|
1438
|
+
return Response.json({ error: "Buffer requires ?id=<bufferId>" }, { status: 400 });
|
|
1439
|
+
let extra = { bufferId };
|
|
1440
|
+
let byteOffset = parseInt(query.get("offset") ?? "", 10);
|
|
1441
|
+
if (Number.isFinite(byteOffset))
|
|
1442
|
+
extra.byteOffset = byteOffset;
|
|
1443
|
+
let length = parseInt(query.get("length") ?? "", 10);
|
|
1444
|
+
if (Number.isFinite(length))
|
|
1445
|
+
extra.length = length;
|
|
1446
|
+
let as = query.get("as");
|
|
1447
|
+
if (as !== undefined)
|
|
1448
|
+
extra.as = as;
|
|
1449
|
+
return handleQuery(query, "buffer", extra);
|
|
1450
|
+
}
|
|
1451
|
+
case "/__control__/reload": {
|
|
1452
|
+
if (req.method !== "POST")
|
|
1453
|
+
return Response.json({ error: "Reload requires POST" }, { status: 405 });
|
|
1454
|
+
let error = await rebuildAndBroadcast();
|
|
1455
|
+
if (error)
|
|
1456
|
+
return Response.json({ error }, { status: 502 });
|
|
1457
|
+
let body2 = { ok: true, clients: state.clients.size };
|
|
1458
|
+
return Response.json(body2);
|
|
1459
|
+
}
|
|
1460
|
+
case "/__control__/load": {
|
|
1461
|
+
if (req.method !== "POST")
|
|
1462
|
+
return Response.json({ error: "Load requires POST" }, { status: 405 });
|
|
1463
|
+
let requested = (await req.json().catch(() => null))?.entry;
|
|
1464
|
+
if (typeof requested !== "string" || !requested) {
|
|
1465
|
+
return Response.json({ error: "Load requires { entry: <source path> }" }, { status: 400 });
|
|
1466
|
+
}
|
|
1467
|
+
let result = await loadEntry(requested);
|
|
1468
|
+
if ("error" in result)
|
|
1469
|
+
return Response.json({ error: result.error }, { status: result.status });
|
|
1470
|
+
let body2 = { ok: true, entry: result.entry, clients: state.clients.size };
|
|
1471
|
+
return Response.json(body2);
|
|
1472
|
+
}
|
|
1473
|
+
case "/__control__/mute": {
|
|
1474
|
+
if (req.method !== "POST")
|
|
1475
|
+
return Response.json({ error: "Mute requires POST" }, { status: 405 });
|
|
1476
|
+
let active = query.get("active");
|
|
1477
|
+
if (active !== "true" && active !== "false") {
|
|
1478
|
+
return Response.json({ error: "Mute requires ?active=true or ?active=false" }, { status: 400 });
|
|
1479
|
+
}
|
|
1480
|
+
let on = active === "true";
|
|
1481
|
+
setUserInputMuted(on);
|
|
1482
|
+
let body2 = { ok: true, active: on, clients: state.clients.size };
|
|
1483
|
+
return Response.json(body2);
|
|
1484
|
+
}
|
|
1485
|
+
case "/__control__/watch": {
|
|
1486
|
+
if (req.method !== "POST")
|
|
1487
|
+
return Response.json({ error: "Watch requires POST" }, { status: 405 });
|
|
1488
|
+
let active = query.get("active");
|
|
1489
|
+
if (active !== "true" && active !== "false") {
|
|
1490
|
+
return Response.json({ error: "Watch requires ?active=true or ?active=false" }, { status: 400 });
|
|
1491
|
+
}
|
|
1492
|
+
let on = active === "true";
|
|
1493
|
+
setWatchActive(on);
|
|
1494
|
+
let body2 = { ok: true, active: on };
|
|
1495
|
+
return Response.json(body2);
|
|
1496
|
+
}
|
|
1497
|
+
default:
|
|
1498
|
+
return Response.json({ error: "Unknown control endpoint" }, { status: 404 });
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
// ../../node_modules/.bun/qrcode-generator@2.0.4/node_modules/qrcode-generator/dist/qrcode.mjs
|
|
1503
|
+
var qrcode = function(typeNumber, errorCorrectionLevel) {
|
|
1504
|
+
const PAD0 = 236;
|
|
1505
|
+
const PAD1 = 17;
|
|
1506
|
+
let _typeNumber = typeNumber;
|
|
1507
|
+
const _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel];
|
|
1508
|
+
let _modules = null;
|
|
1509
|
+
let _moduleCount = 0;
|
|
1510
|
+
let _dataCache = null;
|
|
1511
|
+
const _dataList = [];
|
|
1512
|
+
const _this = {};
|
|
1513
|
+
const makeImpl = function(test, maskPattern) {
|
|
1514
|
+
_moduleCount = _typeNumber * 4 + 17;
|
|
1515
|
+
_modules = function(moduleCount) {
|
|
1516
|
+
const modules = new Array(moduleCount);
|
|
1517
|
+
for (let row = 0;row < moduleCount; row += 1) {
|
|
1518
|
+
modules[row] = new Array(moduleCount);
|
|
1519
|
+
for (let col = 0;col < moduleCount; col += 1) {
|
|
1520
|
+
modules[row][col] = null;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
return modules;
|
|
1524
|
+
}(_moduleCount);
|
|
1525
|
+
setupPositionProbePattern(0, 0);
|
|
1526
|
+
setupPositionProbePattern(_moduleCount - 7, 0);
|
|
1527
|
+
setupPositionProbePattern(0, _moduleCount - 7);
|
|
1528
|
+
setupPositionAdjustPattern();
|
|
1529
|
+
setupTimingPattern();
|
|
1530
|
+
setupTypeInfo(test, maskPattern);
|
|
1531
|
+
if (_typeNumber >= 7) {
|
|
1532
|
+
setupTypeNumber(test);
|
|
1533
|
+
}
|
|
1534
|
+
if (_dataCache == null) {
|
|
1535
|
+
_dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList);
|
|
1536
|
+
}
|
|
1537
|
+
mapData(_dataCache, maskPattern);
|
|
1538
|
+
};
|
|
1539
|
+
const setupPositionProbePattern = function(row, col) {
|
|
1540
|
+
for (let r = -1;r <= 7; r += 1) {
|
|
1541
|
+
if (row + r <= -1 || _moduleCount <= row + r)
|
|
1542
|
+
continue;
|
|
1543
|
+
for (let c = -1;c <= 7; c += 1) {
|
|
1544
|
+
if (col + c <= -1 || _moduleCount <= col + c)
|
|
1545
|
+
continue;
|
|
1546
|
+
if (0 <= r && r <= 6 && (c == 0 || c == 6) || 0 <= c && c <= 6 && (r == 0 || r == 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) {
|
|
1547
|
+
_modules[row + r][col + c] = true;
|
|
1548
|
+
} else {
|
|
1549
|
+
_modules[row + r][col + c] = false;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
const getBestMaskPattern = function() {
|
|
1555
|
+
let minLostPoint = 0;
|
|
1556
|
+
let pattern = 0;
|
|
1557
|
+
for (let i = 0;i < 8; i += 1) {
|
|
1558
|
+
makeImpl(true, i);
|
|
1559
|
+
const lostPoint = QRUtil.getLostPoint(_this);
|
|
1560
|
+
if (i == 0 || minLostPoint > lostPoint) {
|
|
1561
|
+
minLostPoint = lostPoint;
|
|
1562
|
+
pattern = i;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return pattern;
|
|
1566
|
+
};
|
|
1567
|
+
const setupTimingPattern = function() {
|
|
1568
|
+
for (let r = 8;r < _moduleCount - 8; r += 1) {
|
|
1569
|
+
if (_modules[r][6] != null) {
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1572
|
+
_modules[r][6] = r % 2 == 0;
|
|
1573
|
+
}
|
|
1574
|
+
for (let c = 8;c < _moduleCount - 8; c += 1) {
|
|
1575
|
+
if (_modules[6][c] != null) {
|
|
1576
|
+
continue;
|
|
1577
|
+
}
|
|
1578
|
+
_modules[6][c] = c % 2 == 0;
|
|
1579
|
+
}
|
|
1580
|
+
};
|
|
1581
|
+
const setupPositionAdjustPattern = function() {
|
|
1582
|
+
const pos = QRUtil.getPatternPosition(_typeNumber);
|
|
1583
|
+
for (let i = 0;i < pos.length; i += 1) {
|
|
1584
|
+
for (let j = 0;j < pos.length; j += 1) {
|
|
1585
|
+
const row = pos[i];
|
|
1586
|
+
const col = pos[j];
|
|
1587
|
+
if (_modules[row][col] != null) {
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
for (let r = -2;r <= 2; r += 1) {
|
|
1591
|
+
for (let c = -2;c <= 2; c += 1) {
|
|
1592
|
+
if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) {
|
|
1593
|
+
_modules[row + r][col + c] = true;
|
|
1594
|
+
} else {
|
|
1595
|
+
_modules[row + r][col + c] = false;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
};
|
|
1602
|
+
const setupTypeNumber = function(test) {
|
|
1603
|
+
const bits = QRUtil.getBCHTypeNumber(_typeNumber);
|
|
1604
|
+
for (let i = 0;i < 18; i += 1) {
|
|
1605
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1606
|
+
_modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
|
|
1607
|
+
}
|
|
1608
|
+
for (let i = 0;i < 18; i += 1) {
|
|
1609
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1610
|
+
_modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
const setupTypeInfo = function(test, maskPattern) {
|
|
1614
|
+
const data = _errorCorrectionLevel << 3 | maskPattern;
|
|
1615
|
+
const bits = QRUtil.getBCHTypeInfo(data);
|
|
1616
|
+
for (let i = 0;i < 15; i += 1) {
|
|
1617
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1618
|
+
if (i < 6) {
|
|
1619
|
+
_modules[i][8] = mod;
|
|
1620
|
+
} else if (i < 8) {
|
|
1621
|
+
_modules[i + 1][8] = mod;
|
|
1622
|
+
} else {
|
|
1623
|
+
_modules[_moduleCount - 15 + i][8] = mod;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
for (let i = 0;i < 15; i += 1) {
|
|
1627
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1628
|
+
if (i < 8) {
|
|
1629
|
+
_modules[8][_moduleCount - i - 1] = mod;
|
|
1630
|
+
} else if (i < 9) {
|
|
1631
|
+
_modules[8][15 - i - 1 + 1] = mod;
|
|
1632
|
+
} else {
|
|
1633
|
+
_modules[8][15 - i - 1] = mod;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
_modules[_moduleCount - 8][8] = !test;
|
|
1637
|
+
};
|
|
1638
|
+
const mapData = function(data, maskPattern) {
|
|
1639
|
+
let inc = -1;
|
|
1640
|
+
let row = _moduleCount - 1;
|
|
1641
|
+
let bitIndex = 7;
|
|
1642
|
+
let byteIndex = 0;
|
|
1643
|
+
const maskFunc = QRUtil.getMaskFunction(maskPattern);
|
|
1644
|
+
for (let col = _moduleCount - 1;col > 0; col -= 2) {
|
|
1645
|
+
if (col == 6)
|
|
1646
|
+
col -= 1;
|
|
1647
|
+
while (true) {
|
|
1648
|
+
for (let c = 0;c < 2; c += 1) {
|
|
1649
|
+
if (_modules[row][col - c] == null) {
|
|
1650
|
+
let dark = false;
|
|
1651
|
+
if (byteIndex < data.length) {
|
|
1652
|
+
dark = (data[byteIndex] >>> bitIndex & 1) == 1;
|
|
1653
|
+
}
|
|
1654
|
+
const mask = maskFunc(row, col - c);
|
|
1655
|
+
if (mask) {
|
|
1656
|
+
dark = !dark;
|
|
1657
|
+
}
|
|
1658
|
+
_modules[row][col - c] = dark;
|
|
1659
|
+
bitIndex -= 1;
|
|
1660
|
+
if (bitIndex == -1) {
|
|
1661
|
+
byteIndex += 1;
|
|
1662
|
+
bitIndex = 7;
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
row += inc;
|
|
1667
|
+
if (row < 0 || _moduleCount <= row) {
|
|
1668
|
+
row -= inc;
|
|
1669
|
+
inc = -inc;
|
|
1670
|
+
break;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
};
|
|
1675
|
+
const createBytes = function(buffer, rsBlocks) {
|
|
1676
|
+
let offset = 0;
|
|
1677
|
+
let maxDcCount = 0;
|
|
1678
|
+
let maxEcCount = 0;
|
|
1679
|
+
const dcdata = new Array(rsBlocks.length);
|
|
1680
|
+
const ecdata = new Array(rsBlocks.length);
|
|
1681
|
+
for (let r = 0;r < rsBlocks.length; r += 1) {
|
|
1682
|
+
const dcCount = rsBlocks[r].dataCount;
|
|
1683
|
+
const ecCount = rsBlocks[r].totalCount - dcCount;
|
|
1684
|
+
maxDcCount = Math.max(maxDcCount, dcCount);
|
|
1685
|
+
maxEcCount = Math.max(maxEcCount, ecCount);
|
|
1686
|
+
dcdata[r] = new Array(dcCount);
|
|
1687
|
+
for (let i = 0;i < dcdata[r].length; i += 1) {
|
|
1688
|
+
dcdata[r][i] = 255 & buffer.getBuffer()[i + offset];
|
|
1689
|
+
}
|
|
1690
|
+
offset += dcCount;
|
|
1691
|
+
const rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
|
|
1692
|
+
const rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);
|
|
1693
|
+
const modPoly = rawPoly.mod(rsPoly);
|
|
1694
|
+
ecdata[r] = new Array(rsPoly.getLength() - 1);
|
|
1695
|
+
for (let i = 0;i < ecdata[r].length; i += 1) {
|
|
1696
|
+
const modIndex = i + modPoly.getLength() - ecdata[r].length;
|
|
1697
|
+
ecdata[r][i] = modIndex >= 0 ? modPoly.getAt(modIndex) : 0;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
let totalCodeCount = 0;
|
|
1701
|
+
for (let i = 0;i < rsBlocks.length; i += 1) {
|
|
1702
|
+
totalCodeCount += rsBlocks[i].totalCount;
|
|
1703
|
+
}
|
|
1704
|
+
const data = new Array(totalCodeCount);
|
|
1705
|
+
let index = 0;
|
|
1706
|
+
for (let i = 0;i < maxDcCount; i += 1) {
|
|
1707
|
+
for (let r = 0;r < rsBlocks.length; r += 1) {
|
|
1708
|
+
if (i < dcdata[r].length) {
|
|
1709
|
+
data[index] = dcdata[r][i];
|
|
1710
|
+
index += 1;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
for (let i = 0;i < maxEcCount; i += 1) {
|
|
1715
|
+
for (let r = 0;r < rsBlocks.length; r += 1) {
|
|
1716
|
+
if (i < ecdata[r].length) {
|
|
1717
|
+
data[index] = ecdata[r][i];
|
|
1718
|
+
index += 1;
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
return data;
|
|
1723
|
+
};
|
|
1724
|
+
const createData = function(typeNumber2, errorCorrectionLevel2, dataList) {
|
|
1725
|
+
const rsBlocks = QRRSBlock.getRSBlocks(typeNumber2, errorCorrectionLevel2);
|
|
1726
|
+
const buffer = qrBitBuffer();
|
|
1727
|
+
for (let i = 0;i < dataList.length; i += 1) {
|
|
1728
|
+
const data = dataList[i];
|
|
1729
|
+
buffer.put(data.getMode(), 4);
|
|
1730
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber2));
|
|
1731
|
+
data.write(buffer);
|
|
1732
|
+
}
|
|
1733
|
+
let totalDataCount = 0;
|
|
1734
|
+
for (let i = 0;i < rsBlocks.length; i += 1) {
|
|
1735
|
+
totalDataCount += rsBlocks[i].dataCount;
|
|
1736
|
+
}
|
|
1737
|
+
if (buffer.getLengthInBits() > totalDataCount * 8) {
|
|
1738
|
+
throw "code length overflow. (" + buffer.getLengthInBits() + ">" + totalDataCount * 8 + ")";
|
|
1739
|
+
}
|
|
1740
|
+
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
|
|
1741
|
+
buffer.put(0, 4);
|
|
1742
|
+
}
|
|
1743
|
+
while (buffer.getLengthInBits() % 8 != 0) {
|
|
1744
|
+
buffer.putBit(false);
|
|
1745
|
+
}
|
|
1746
|
+
while (true) {
|
|
1747
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
1748
|
+
break;
|
|
1749
|
+
}
|
|
1750
|
+
buffer.put(PAD0, 8);
|
|
1751
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
|
1752
|
+
break;
|
|
1753
|
+
}
|
|
1754
|
+
buffer.put(PAD1, 8);
|
|
1755
|
+
}
|
|
1756
|
+
return createBytes(buffer, rsBlocks);
|
|
1757
|
+
};
|
|
1758
|
+
_this.addData = function(data, mode) {
|
|
1759
|
+
mode = mode || "Byte";
|
|
1760
|
+
let newData = null;
|
|
1761
|
+
switch (mode) {
|
|
1762
|
+
case "Numeric":
|
|
1763
|
+
newData = qrNumber(data);
|
|
1764
|
+
break;
|
|
1765
|
+
case "Alphanumeric":
|
|
1766
|
+
newData = qrAlphaNum(data);
|
|
1767
|
+
break;
|
|
1768
|
+
case "Byte":
|
|
1769
|
+
newData = qr8BitByte(data);
|
|
1770
|
+
break;
|
|
1771
|
+
case "Kanji":
|
|
1772
|
+
newData = qrKanji(data);
|
|
1773
|
+
break;
|
|
1774
|
+
default:
|
|
1775
|
+
throw "mode:" + mode;
|
|
1776
|
+
}
|
|
1777
|
+
_dataList.push(newData);
|
|
1778
|
+
_dataCache = null;
|
|
1779
|
+
};
|
|
1780
|
+
_this.isDark = function(row, col) {
|
|
1781
|
+
if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
|
|
1782
|
+
throw row + "," + col;
|
|
1783
|
+
}
|
|
1784
|
+
return _modules[row][col];
|
|
1785
|
+
};
|
|
1786
|
+
_this.getModuleCount = function() {
|
|
1787
|
+
return _moduleCount;
|
|
1788
|
+
};
|
|
1789
|
+
_this.make = function() {
|
|
1790
|
+
if (_typeNumber < 1) {
|
|
1791
|
+
let typeNumber2 = 1;
|
|
1792
|
+
for (;typeNumber2 < 40; typeNumber2++) {
|
|
1793
|
+
const rsBlocks = QRRSBlock.getRSBlocks(typeNumber2, _errorCorrectionLevel);
|
|
1794
|
+
const buffer = qrBitBuffer();
|
|
1795
|
+
for (let i = 0;i < _dataList.length; i++) {
|
|
1796
|
+
const data = _dataList[i];
|
|
1797
|
+
buffer.put(data.getMode(), 4);
|
|
1798
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber2));
|
|
1799
|
+
data.write(buffer);
|
|
1800
|
+
}
|
|
1801
|
+
let totalDataCount = 0;
|
|
1802
|
+
for (let i = 0;i < rsBlocks.length; i++) {
|
|
1803
|
+
totalDataCount += rsBlocks[i].dataCount;
|
|
1804
|
+
}
|
|
1805
|
+
if (buffer.getLengthInBits() <= totalDataCount * 8) {
|
|
1806
|
+
break;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
_typeNumber = typeNumber2;
|
|
1810
|
+
}
|
|
1811
|
+
makeImpl(false, getBestMaskPattern());
|
|
1812
|
+
};
|
|
1813
|
+
_this.createTableTag = function(cellSize, margin) {
|
|
1814
|
+
cellSize = cellSize || 2;
|
|
1815
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1816
|
+
let qrHtml = "";
|
|
1817
|
+
qrHtml += '<table style="';
|
|
1818
|
+
qrHtml += " border-width: 0px; border-style: none;";
|
|
1819
|
+
qrHtml += " border-collapse: collapse;";
|
|
1820
|
+
qrHtml += " padding: 0px; margin: " + margin + "px;";
|
|
1821
|
+
qrHtml += '">';
|
|
1822
|
+
qrHtml += "<tbody>";
|
|
1823
|
+
for (let r = 0;r < _this.getModuleCount(); r += 1) {
|
|
1824
|
+
qrHtml += "<tr>";
|
|
1825
|
+
for (let c = 0;c < _this.getModuleCount(); c += 1) {
|
|
1826
|
+
qrHtml += '<td style="';
|
|
1827
|
+
qrHtml += " border-width: 0px; border-style: none;";
|
|
1828
|
+
qrHtml += " border-collapse: collapse;";
|
|
1829
|
+
qrHtml += " padding: 0px; margin: 0px;";
|
|
1830
|
+
qrHtml += " width: " + cellSize + "px;";
|
|
1831
|
+
qrHtml += " height: " + cellSize + "px;";
|
|
1832
|
+
qrHtml += " background-color: ";
|
|
1833
|
+
qrHtml += _this.isDark(r, c) ? "#000000" : "#ffffff";
|
|
1834
|
+
qrHtml += ";";
|
|
1835
|
+
qrHtml += '"/>';
|
|
1836
|
+
}
|
|
1837
|
+
qrHtml += "</tr>";
|
|
1838
|
+
}
|
|
1839
|
+
qrHtml += "</tbody>";
|
|
1840
|
+
qrHtml += "</table>";
|
|
1841
|
+
return qrHtml;
|
|
1842
|
+
};
|
|
1843
|
+
_this.createSvgTag = function(cellSize, margin, alt, title) {
|
|
1844
|
+
let opts = {};
|
|
1845
|
+
if (typeof arguments[0] == "object") {
|
|
1846
|
+
opts = arguments[0];
|
|
1847
|
+
cellSize = opts.cellSize;
|
|
1848
|
+
margin = opts.margin;
|
|
1849
|
+
alt = opts.alt;
|
|
1850
|
+
title = opts.title;
|
|
1851
|
+
}
|
|
1852
|
+
cellSize = cellSize || 2;
|
|
1853
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1854
|
+
alt = typeof alt === "string" ? { text: alt } : alt || {};
|
|
1855
|
+
alt.text = alt.text || null;
|
|
1856
|
+
alt.id = alt.text ? alt.id || "qrcode-description" : null;
|
|
1857
|
+
title = typeof title === "string" ? { text: title } : title || {};
|
|
1858
|
+
title.text = title.text || null;
|
|
1859
|
+
title.id = title.text ? title.id || "qrcode-title" : null;
|
|
1860
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1861
|
+
let c, mc, r, mr, qrSvg = "", rect;
|
|
1862
|
+
rect = "l" + cellSize + ",0 0," + cellSize + " -" + cellSize + ",0 0,-" + cellSize + "z ";
|
|
1863
|
+
qrSvg += '<svg version="1.1" xmlns="http://www.w3.org/2000/svg"';
|
|
1864
|
+
qrSvg += !opts.scalable ? ' width="' + size + 'px" height="' + size + 'px"' : "";
|
|
1865
|
+
qrSvg += ' viewBox="0 0 ' + size + " " + size + '" ';
|
|
1866
|
+
qrSvg += ' preserveAspectRatio="xMinYMin meet"';
|
|
1867
|
+
qrSvg += title.text || alt.text ? ' role="img" aria-labelledby="' + escapeXml([title.id, alt.id].join(" ").trim()) + '"' : "";
|
|
1868
|
+
qrSvg += ">";
|
|
1869
|
+
qrSvg += title.text ? '<title id="' + escapeXml(title.id) + '">' + escapeXml(title.text) + "</title>" : "";
|
|
1870
|
+
qrSvg += alt.text ? '<description id="' + escapeXml(alt.id) + '">' + escapeXml(alt.text) + "</description>" : "";
|
|
1871
|
+
qrSvg += '<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>';
|
|
1872
|
+
qrSvg += '<path d="';
|
|
1873
|
+
for (r = 0;r < _this.getModuleCount(); r += 1) {
|
|
1874
|
+
mr = r * cellSize + margin;
|
|
1875
|
+
for (c = 0;c < _this.getModuleCount(); c += 1) {
|
|
1876
|
+
if (_this.isDark(r, c)) {
|
|
1877
|
+
mc = c * cellSize + margin;
|
|
1878
|
+
qrSvg += "M" + mc + "," + mr + rect;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
qrSvg += '" stroke="transparent" fill="black"/>';
|
|
1883
|
+
qrSvg += "</svg>";
|
|
1884
|
+
return qrSvg;
|
|
1885
|
+
};
|
|
1886
|
+
_this.createDataURL = function(cellSize, margin) {
|
|
1887
|
+
cellSize = cellSize || 2;
|
|
1888
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1889
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1890
|
+
const min = margin;
|
|
1891
|
+
const max = size - margin;
|
|
1892
|
+
return createDataURL(size, size, function(x, y) {
|
|
1893
|
+
if (min <= x && x < max && min <= y && y < max) {
|
|
1894
|
+
const c = Math.floor((x - min) / cellSize);
|
|
1895
|
+
const r = Math.floor((y - min) / cellSize);
|
|
1896
|
+
return _this.isDark(r, c) ? 0 : 1;
|
|
1897
|
+
} else {
|
|
1898
|
+
return 1;
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
};
|
|
1902
|
+
_this.createImgTag = function(cellSize, margin, alt) {
|
|
1903
|
+
cellSize = cellSize || 2;
|
|
1904
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1905
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1906
|
+
let img = "";
|
|
1907
|
+
img += "<img";
|
|
1908
|
+
img += ' src="';
|
|
1909
|
+
img += _this.createDataURL(cellSize, margin);
|
|
1910
|
+
img += '"';
|
|
1911
|
+
img += ' width="';
|
|
1912
|
+
img += size;
|
|
1913
|
+
img += '"';
|
|
1914
|
+
img += ' height="';
|
|
1915
|
+
img += size;
|
|
1916
|
+
img += '"';
|
|
1917
|
+
if (alt) {
|
|
1918
|
+
img += ' alt="';
|
|
1919
|
+
img += escapeXml(alt);
|
|
1920
|
+
img += '"';
|
|
1921
|
+
}
|
|
1922
|
+
img += "/>";
|
|
1923
|
+
return img;
|
|
1924
|
+
};
|
|
1925
|
+
const escapeXml = function(s) {
|
|
1926
|
+
let escaped = "";
|
|
1927
|
+
for (let i = 0;i < s.length; i += 1) {
|
|
1928
|
+
const c = s.charAt(i);
|
|
1929
|
+
switch (c) {
|
|
1930
|
+
case "<":
|
|
1931
|
+
escaped += "<";
|
|
1932
|
+
break;
|
|
1933
|
+
case ">":
|
|
1934
|
+
escaped += ">";
|
|
1935
|
+
break;
|
|
1936
|
+
case "&":
|
|
1937
|
+
escaped += "&";
|
|
1938
|
+
break;
|
|
1939
|
+
case '"':
|
|
1940
|
+
escaped += """;
|
|
1941
|
+
break;
|
|
1942
|
+
default:
|
|
1943
|
+
escaped += c;
|
|
1944
|
+
break;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
return escaped;
|
|
1948
|
+
};
|
|
1949
|
+
const _createHalfASCII = function(margin) {
|
|
1950
|
+
const cellSize = 1;
|
|
1951
|
+
margin = typeof margin == "undefined" ? cellSize * 2 : margin;
|
|
1952
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1953
|
+
const min = margin;
|
|
1954
|
+
const max = size - margin;
|
|
1955
|
+
let y, x, r1, r2, p;
|
|
1956
|
+
const blocks = {
|
|
1957
|
+
"██": "█",
|
|
1958
|
+
"█ ": "▀",
|
|
1959
|
+
" █": "▄",
|
|
1960
|
+
" ": " "
|
|
1961
|
+
};
|
|
1962
|
+
const blocksLastLineNoMargin = {
|
|
1963
|
+
"██": "▀",
|
|
1964
|
+
"█ ": "▀",
|
|
1965
|
+
" █": " ",
|
|
1966
|
+
" ": " "
|
|
1967
|
+
};
|
|
1968
|
+
let ascii = "";
|
|
1969
|
+
for (y = 0;y < size; y += 2) {
|
|
1970
|
+
r1 = Math.floor((y - min) / cellSize);
|
|
1971
|
+
r2 = Math.floor((y + 1 - min) / cellSize);
|
|
1972
|
+
for (x = 0;x < size; x += 1) {
|
|
1973
|
+
p = "█";
|
|
1974
|
+
if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x - min) / cellSize))) {
|
|
1975
|
+
p = " ";
|
|
1976
|
+
}
|
|
1977
|
+
if (min <= x && x < max && min <= y + 1 && y + 1 < max && _this.isDark(r2, Math.floor((x - min) / cellSize))) {
|
|
1978
|
+
p += " ";
|
|
1979
|
+
} else {
|
|
1980
|
+
p += "█";
|
|
1981
|
+
}
|
|
1982
|
+
ascii += margin < 1 && y + 1 >= max ? blocksLastLineNoMargin[p] : blocks[p];
|
|
1983
|
+
}
|
|
1984
|
+
ascii += `
|
|
1985
|
+
`;
|
|
1986
|
+
}
|
|
1987
|
+
if (size % 2 && margin > 0) {
|
|
1988
|
+
return ascii.substring(0, ascii.length - size - 1) + Array(size + 1).join("▀");
|
|
1989
|
+
}
|
|
1990
|
+
return ascii.substring(0, ascii.length - 1);
|
|
1991
|
+
};
|
|
1992
|
+
_this.createASCII = function(cellSize, margin) {
|
|
1993
|
+
cellSize = cellSize || 1;
|
|
1994
|
+
if (cellSize < 2) {
|
|
1995
|
+
return _createHalfASCII(margin);
|
|
1996
|
+
}
|
|
1997
|
+
cellSize -= 1;
|
|
1998
|
+
margin = typeof margin == "undefined" ? cellSize * 2 : margin;
|
|
1999
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
2000
|
+
const min = margin;
|
|
2001
|
+
const max = size - margin;
|
|
2002
|
+
let y, x, r, p;
|
|
2003
|
+
const white = Array(cellSize + 1).join("██");
|
|
2004
|
+
const black = Array(cellSize + 1).join(" ");
|
|
2005
|
+
let ascii = "";
|
|
2006
|
+
let line = "";
|
|
2007
|
+
for (y = 0;y < size; y += 1) {
|
|
2008
|
+
r = Math.floor((y - min) / cellSize);
|
|
2009
|
+
line = "";
|
|
2010
|
+
for (x = 0;x < size; x += 1) {
|
|
2011
|
+
p = 1;
|
|
2012
|
+
if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x - min) / cellSize))) {
|
|
2013
|
+
p = 0;
|
|
2014
|
+
}
|
|
2015
|
+
line += p ? white : black;
|
|
2016
|
+
}
|
|
2017
|
+
for (r = 0;r < cellSize; r += 1) {
|
|
2018
|
+
ascii += line + `
|
|
2019
|
+
`;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
return ascii.substring(0, ascii.length - 1);
|
|
2023
|
+
};
|
|
2024
|
+
_this.renderTo2dContext = function(context, cellSize) {
|
|
2025
|
+
cellSize = cellSize || 2;
|
|
2026
|
+
const length = _this.getModuleCount();
|
|
2027
|
+
for (let row = 0;row < length; row++) {
|
|
2028
|
+
for (let col = 0;col < length; col++) {
|
|
2029
|
+
context.fillStyle = _this.isDark(row, col) ? "black" : "white";
|
|
2030
|
+
context.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
};
|
|
2034
|
+
return _this;
|
|
2035
|
+
};
|
|
2036
|
+
qrcode.stringToBytes = function(s) {
|
|
2037
|
+
const bytes = [];
|
|
2038
|
+
for (let i = 0;i < s.length; i += 1) {
|
|
2039
|
+
const c = s.charCodeAt(i);
|
|
2040
|
+
bytes.push(c & 255);
|
|
2041
|
+
}
|
|
2042
|
+
return bytes;
|
|
2043
|
+
};
|
|
2044
|
+
qrcode.createStringToBytes = function(unicodeData, numChars) {
|
|
2045
|
+
const unicodeMap = function() {
|
|
2046
|
+
const bin = base64DecodeInputStream(unicodeData);
|
|
2047
|
+
const read = function() {
|
|
2048
|
+
const b = bin.read();
|
|
2049
|
+
if (b == -1)
|
|
2050
|
+
throw "eof";
|
|
2051
|
+
return b;
|
|
2052
|
+
};
|
|
2053
|
+
let count = 0;
|
|
2054
|
+
const unicodeMap2 = {};
|
|
2055
|
+
while (true) {
|
|
2056
|
+
const b0 = bin.read();
|
|
2057
|
+
if (b0 == -1)
|
|
2058
|
+
break;
|
|
2059
|
+
const b1 = read();
|
|
2060
|
+
const b2 = read();
|
|
2061
|
+
const b3 = read();
|
|
2062
|
+
const k = String.fromCharCode(b0 << 8 | b1);
|
|
2063
|
+
const v = b2 << 8 | b3;
|
|
2064
|
+
unicodeMap2[k] = v;
|
|
2065
|
+
count += 1;
|
|
2066
|
+
}
|
|
2067
|
+
if (count != numChars) {
|
|
2068
|
+
throw count + " != " + numChars;
|
|
2069
|
+
}
|
|
2070
|
+
return unicodeMap2;
|
|
2071
|
+
}();
|
|
2072
|
+
const unknownChar = 63;
|
|
2073
|
+
return function(s) {
|
|
2074
|
+
const bytes = [];
|
|
2075
|
+
for (let i = 0;i < s.length; i += 1) {
|
|
2076
|
+
const c = s.charCodeAt(i);
|
|
2077
|
+
if (c < 128) {
|
|
2078
|
+
bytes.push(c);
|
|
2079
|
+
} else {
|
|
2080
|
+
const b = unicodeMap[s.charAt(i)];
|
|
2081
|
+
if (typeof b == "number") {
|
|
2082
|
+
if ((b & 255) == b) {
|
|
2083
|
+
bytes.push(b);
|
|
2084
|
+
} else {
|
|
2085
|
+
bytes.push(b >>> 8);
|
|
2086
|
+
bytes.push(b & 255);
|
|
2087
|
+
}
|
|
2088
|
+
} else {
|
|
2089
|
+
bytes.push(unknownChar);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
return bytes;
|
|
2094
|
+
};
|
|
2095
|
+
};
|
|
2096
|
+
var QRMode = {
|
|
2097
|
+
MODE_NUMBER: 1 << 0,
|
|
2098
|
+
MODE_ALPHA_NUM: 1 << 1,
|
|
2099
|
+
MODE_8BIT_BYTE: 1 << 2,
|
|
2100
|
+
MODE_KANJI: 1 << 3
|
|
2101
|
+
};
|
|
2102
|
+
var QRErrorCorrectionLevel = {
|
|
2103
|
+
L: 1,
|
|
2104
|
+
M: 0,
|
|
2105
|
+
Q: 3,
|
|
2106
|
+
H: 2
|
|
2107
|
+
};
|
|
2108
|
+
var QRMaskPattern = {
|
|
2109
|
+
PATTERN000: 0,
|
|
2110
|
+
PATTERN001: 1,
|
|
2111
|
+
PATTERN010: 2,
|
|
2112
|
+
PATTERN011: 3,
|
|
2113
|
+
PATTERN100: 4,
|
|
2114
|
+
PATTERN101: 5,
|
|
2115
|
+
PATTERN110: 6,
|
|
2116
|
+
PATTERN111: 7
|
|
2117
|
+
};
|
|
2118
|
+
var QRUtil = function() {
|
|
2119
|
+
const PATTERN_POSITION_TABLE = [
|
|
2120
|
+
[],
|
|
2121
|
+
[6, 18],
|
|
2122
|
+
[6, 22],
|
|
2123
|
+
[6, 26],
|
|
2124
|
+
[6, 30],
|
|
2125
|
+
[6, 34],
|
|
2126
|
+
[6, 22, 38],
|
|
2127
|
+
[6, 24, 42],
|
|
2128
|
+
[6, 26, 46],
|
|
2129
|
+
[6, 28, 50],
|
|
2130
|
+
[6, 30, 54],
|
|
2131
|
+
[6, 32, 58],
|
|
2132
|
+
[6, 34, 62],
|
|
2133
|
+
[6, 26, 46, 66],
|
|
2134
|
+
[6, 26, 48, 70],
|
|
2135
|
+
[6, 26, 50, 74],
|
|
2136
|
+
[6, 30, 54, 78],
|
|
2137
|
+
[6, 30, 56, 82],
|
|
2138
|
+
[6, 30, 58, 86],
|
|
2139
|
+
[6, 34, 62, 90],
|
|
2140
|
+
[6, 28, 50, 72, 94],
|
|
2141
|
+
[6, 26, 50, 74, 98],
|
|
2142
|
+
[6, 30, 54, 78, 102],
|
|
2143
|
+
[6, 28, 54, 80, 106],
|
|
2144
|
+
[6, 32, 58, 84, 110],
|
|
2145
|
+
[6, 30, 58, 86, 114],
|
|
2146
|
+
[6, 34, 62, 90, 118],
|
|
2147
|
+
[6, 26, 50, 74, 98, 122],
|
|
2148
|
+
[6, 30, 54, 78, 102, 126],
|
|
2149
|
+
[6, 26, 52, 78, 104, 130],
|
|
2150
|
+
[6, 30, 56, 82, 108, 134],
|
|
2151
|
+
[6, 34, 60, 86, 112, 138],
|
|
2152
|
+
[6, 30, 58, 86, 114, 142],
|
|
2153
|
+
[6, 34, 62, 90, 118, 146],
|
|
2154
|
+
[6, 30, 54, 78, 102, 126, 150],
|
|
2155
|
+
[6, 24, 50, 76, 102, 128, 154],
|
|
2156
|
+
[6, 28, 54, 80, 106, 132, 158],
|
|
2157
|
+
[6, 32, 58, 84, 110, 136, 162],
|
|
2158
|
+
[6, 26, 54, 82, 110, 138, 166],
|
|
2159
|
+
[6, 30, 58, 86, 114, 142, 170]
|
|
2160
|
+
];
|
|
2161
|
+
const G15 = 1 << 10 | 1 << 8 | 1 << 5 | 1 << 4 | 1 << 2 | 1 << 1 | 1 << 0;
|
|
2162
|
+
const G18 = 1 << 12 | 1 << 11 | 1 << 10 | 1 << 9 | 1 << 8 | 1 << 5 | 1 << 2 | 1 << 0;
|
|
2163
|
+
const G15_MASK = 1 << 14 | 1 << 12 | 1 << 10 | 1 << 4 | 1 << 1;
|
|
2164
|
+
const _this = {};
|
|
2165
|
+
const getBCHDigit = function(data) {
|
|
2166
|
+
let digit = 0;
|
|
2167
|
+
while (data != 0) {
|
|
2168
|
+
digit += 1;
|
|
2169
|
+
data >>>= 1;
|
|
2170
|
+
}
|
|
2171
|
+
return digit;
|
|
2172
|
+
};
|
|
2173
|
+
_this.getBCHTypeInfo = function(data) {
|
|
2174
|
+
let d = data << 10;
|
|
2175
|
+
while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
|
|
2176
|
+
d ^= G15 << getBCHDigit(d) - getBCHDigit(G15);
|
|
2177
|
+
}
|
|
2178
|
+
return (data << 10 | d) ^ G15_MASK;
|
|
2179
|
+
};
|
|
2180
|
+
_this.getBCHTypeNumber = function(data) {
|
|
2181
|
+
let d = data << 12;
|
|
2182
|
+
while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
|
|
2183
|
+
d ^= G18 << getBCHDigit(d) - getBCHDigit(G18);
|
|
2184
|
+
}
|
|
2185
|
+
return data << 12 | d;
|
|
2186
|
+
};
|
|
2187
|
+
_this.getPatternPosition = function(typeNumber) {
|
|
2188
|
+
return PATTERN_POSITION_TABLE[typeNumber - 1];
|
|
2189
|
+
};
|
|
2190
|
+
_this.getMaskFunction = function(maskPattern) {
|
|
2191
|
+
switch (maskPattern) {
|
|
2192
|
+
case QRMaskPattern.PATTERN000:
|
|
2193
|
+
return function(i, j) {
|
|
2194
|
+
return (i + j) % 2 == 0;
|
|
2195
|
+
};
|
|
2196
|
+
case QRMaskPattern.PATTERN001:
|
|
2197
|
+
return function(i, j) {
|
|
2198
|
+
return i % 2 == 0;
|
|
2199
|
+
};
|
|
2200
|
+
case QRMaskPattern.PATTERN010:
|
|
2201
|
+
return function(i, j) {
|
|
2202
|
+
return j % 3 == 0;
|
|
2203
|
+
};
|
|
2204
|
+
case QRMaskPattern.PATTERN011:
|
|
2205
|
+
return function(i, j) {
|
|
2206
|
+
return (i + j) % 3 == 0;
|
|
2207
|
+
};
|
|
2208
|
+
case QRMaskPattern.PATTERN100:
|
|
2209
|
+
return function(i, j) {
|
|
2210
|
+
return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
|
|
2211
|
+
};
|
|
2212
|
+
case QRMaskPattern.PATTERN101:
|
|
2213
|
+
return function(i, j) {
|
|
2214
|
+
return i * j % 2 + i * j % 3 == 0;
|
|
2215
|
+
};
|
|
2216
|
+
case QRMaskPattern.PATTERN110:
|
|
2217
|
+
return function(i, j) {
|
|
2218
|
+
return (i * j % 2 + i * j % 3) % 2 == 0;
|
|
2219
|
+
};
|
|
2220
|
+
case QRMaskPattern.PATTERN111:
|
|
2221
|
+
return function(i, j) {
|
|
2222
|
+
return (i * j % 3 + (i + j) % 2) % 2 == 0;
|
|
2223
|
+
};
|
|
2224
|
+
default:
|
|
2225
|
+
throw "bad maskPattern:" + maskPattern;
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
|
|
2229
|
+
let a = qrPolynomial([1], 0);
|
|
2230
|
+
for (let i = 0;i < errorCorrectLength; i += 1) {
|
|
2231
|
+
a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0));
|
|
2232
|
+
}
|
|
2233
|
+
return a;
|
|
2234
|
+
};
|
|
2235
|
+
_this.getLengthInBits = function(mode, type) {
|
|
2236
|
+
if (1 <= type && type < 10) {
|
|
2237
|
+
switch (mode) {
|
|
2238
|
+
case QRMode.MODE_NUMBER:
|
|
2239
|
+
return 10;
|
|
2240
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
2241
|
+
return 9;
|
|
2242
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
2243
|
+
return 8;
|
|
2244
|
+
case QRMode.MODE_KANJI:
|
|
2245
|
+
return 8;
|
|
2246
|
+
default:
|
|
2247
|
+
throw "mode:" + mode;
|
|
2248
|
+
}
|
|
2249
|
+
} else if (type < 27) {
|
|
2250
|
+
switch (mode) {
|
|
2251
|
+
case QRMode.MODE_NUMBER:
|
|
2252
|
+
return 12;
|
|
2253
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
2254
|
+
return 11;
|
|
2255
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
2256
|
+
return 16;
|
|
2257
|
+
case QRMode.MODE_KANJI:
|
|
2258
|
+
return 10;
|
|
2259
|
+
default:
|
|
2260
|
+
throw "mode:" + mode;
|
|
2261
|
+
}
|
|
2262
|
+
} else if (type < 41) {
|
|
2263
|
+
switch (mode) {
|
|
2264
|
+
case QRMode.MODE_NUMBER:
|
|
2265
|
+
return 14;
|
|
2266
|
+
case QRMode.MODE_ALPHA_NUM:
|
|
2267
|
+
return 13;
|
|
2268
|
+
case QRMode.MODE_8BIT_BYTE:
|
|
2269
|
+
return 16;
|
|
2270
|
+
case QRMode.MODE_KANJI:
|
|
2271
|
+
return 12;
|
|
2272
|
+
default:
|
|
2273
|
+
throw "mode:" + mode;
|
|
2274
|
+
}
|
|
2275
|
+
} else {
|
|
2276
|
+
throw "type:" + type;
|
|
2277
|
+
}
|
|
2278
|
+
};
|
|
2279
|
+
_this.getLostPoint = function(qrcode2) {
|
|
2280
|
+
const moduleCount = qrcode2.getModuleCount();
|
|
2281
|
+
let lostPoint = 0;
|
|
2282
|
+
for (let row = 0;row < moduleCount; row += 1) {
|
|
2283
|
+
for (let col = 0;col < moduleCount; col += 1) {
|
|
2284
|
+
let sameCount = 0;
|
|
2285
|
+
const dark = qrcode2.isDark(row, col);
|
|
2286
|
+
for (let r = -1;r <= 1; r += 1) {
|
|
2287
|
+
if (row + r < 0 || moduleCount <= row + r) {
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
for (let c = -1;c <= 1; c += 1) {
|
|
2291
|
+
if (col + c < 0 || moduleCount <= col + c) {
|
|
2292
|
+
continue;
|
|
2293
|
+
}
|
|
2294
|
+
if (r == 0 && c == 0) {
|
|
2295
|
+
continue;
|
|
2296
|
+
}
|
|
2297
|
+
if (dark == qrcode2.isDark(row + r, col + c)) {
|
|
2298
|
+
sameCount += 1;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
if (sameCount > 5) {
|
|
2303
|
+
lostPoint += 3 + sameCount - 5;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
for (let row = 0;row < moduleCount - 1; row += 1) {
|
|
2308
|
+
for (let col = 0;col < moduleCount - 1; col += 1) {
|
|
2309
|
+
let count = 0;
|
|
2310
|
+
if (qrcode2.isDark(row, col))
|
|
2311
|
+
count += 1;
|
|
2312
|
+
if (qrcode2.isDark(row + 1, col))
|
|
2313
|
+
count += 1;
|
|
2314
|
+
if (qrcode2.isDark(row, col + 1))
|
|
2315
|
+
count += 1;
|
|
2316
|
+
if (qrcode2.isDark(row + 1, col + 1))
|
|
2317
|
+
count += 1;
|
|
2318
|
+
if (count == 0 || count == 4) {
|
|
2319
|
+
lostPoint += 3;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
for (let row = 0;row < moduleCount; row += 1) {
|
|
2324
|
+
for (let col = 0;col < moduleCount - 6; col += 1) {
|
|
2325
|
+
if (qrcode2.isDark(row, col) && !qrcode2.isDark(row, col + 1) && qrcode2.isDark(row, col + 2) && qrcode2.isDark(row, col + 3) && qrcode2.isDark(row, col + 4) && !qrcode2.isDark(row, col + 5) && qrcode2.isDark(row, col + 6)) {
|
|
2326
|
+
lostPoint += 40;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
for (let col = 0;col < moduleCount; col += 1) {
|
|
2331
|
+
for (let row = 0;row < moduleCount - 6; row += 1) {
|
|
2332
|
+
if (qrcode2.isDark(row, col) && !qrcode2.isDark(row + 1, col) && qrcode2.isDark(row + 2, col) && qrcode2.isDark(row + 3, col) && qrcode2.isDark(row + 4, col) && !qrcode2.isDark(row + 5, col) && qrcode2.isDark(row + 6, col)) {
|
|
2333
|
+
lostPoint += 40;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
let darkCount = 0;
|
|
2338
|
+
for (let col = 0;col < moduleCount; col += 1) {
|
|
2339
|
+
for (let row = 0;row < moduleCount; row += 1) {
|
|
2340
|
+
if (qrcode2.isDark(row, col)) {
|
|
2341
|
+
darkCount += 1;
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
const ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
|
|
2346
|
+
lostPoint += ratio * 10;
|
|
2347
|
+
return lostPoint;
|
|
2348
|
+
};
|
|
2349
|
+
return _this;
|
|
2350
|
+
}();
|
|
2351
|
+
var QRMath = function() {
|
|
2352
|
+
const EXP_TABLE = new Array(256);
|
|
2353
|
+
const LOG_TABLE = new Array(256);
|
|
2354
|
+
for (let i = 0;i < 8; i += 1) {
|
|
2355
|
+
EXP_TABLE[i] = 1 << i;
|
|
2356
|
+
}
|
|
2357
|
+
for (let i = 8;i < 256; i += 1) {
|
|
2358
|
+
EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8];
|
|
2359
|
+
}
|
|
2360
|
+
for (let i = 0;i < 255; i += 1) {
|
|
2361
|
+
LOG_TABLE[EXP_TABLE[i]] = i;
|
|
2362
|
+
}
|
|
2363
|
+
const _this = {};
|
|
2364
|
+
_this.glog = function(n) {
|
|
2365
|
+
if (n < 1) {
|
|
2366
|
+
throw "glog(" + n + ")";
|
|
2367
|
+
}
|
|
2368
|
+
return LOG_TABLE[n];
|
|
2369
|
+
};
|
|
2370
|
+
_this.gexp = function(n) {
|
|
2371
|
+
while (n < 0) {
|
|
2372
|
+
n += 255;
|
|
2373
|
+
}
|
|
2374
|
+
while (n >= 256) {
|
|
2375
|
+
n -= 255;
|
|
2376
|
+
}
|
|
2377
|
+
return EXP_TABLE[n];
|
|
2378
|
+
};
|
|
2379
|
+
return _this;
|
|
2380
|
+
}();
|
|
2381
|
+
var qrPolynomial = function(num, shift) {
|
|
2382
|
+
if (typeof num.length == "undefined") {
|
|
2383
|
+
throw num.length + "/" + shift;
|
|
2384
|
+
}
|
|
2385
|
+
const _num = function() {
|
|
2386
|
+
let offset = 0;
|
|
2387
|
+
while (offset < num.length && num[offset] == 0) {
|
|
2388
|
+
offset += 1;
|
|
2389
|
+
}
|
|
2390
|
+
const _num2 = new Array(num.length - offset + shift);
|
|
2391
|
+
for (let i = 0;i < num.length - offset; i += 1) {
|
|
2392
|
+
_num2[i] = num[i + offset];
|
|
2393
|
+
}
|
|
2394
|
+
return _num2;
|
|
2395
|
+
}();
|
|
2396
|
+
const _this = {};
|
|
2397
|
+
_this.getAt = function(index) {
|
|
2398
|
+
return _num[index];
|
|
2399
|
+
};
|
|
2400
|
+
_this.getLength = function() {
|
|
2401
|
+
return _num.length;
|
|
2402
|
+
};
|
|
2403
|
+
_this.multiply = function(e) {
|
|
2404
|
+
const num2 = new Array(_this.getLength() + e.getLength() - 1);
|
|
2405
|
+
for (let i = 0;i < _this.getLength(); i += 1) {
|
|
2406
|
+
for (let j = 0;j < e.getLength(); j += 1) {
|
|
2407
|
+
num2[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i)) + QRMath.glog(e.getAt(j)));
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
return qrPolynomial(num2, 0);
|
|
2411
|
+
};
|
|
2412
|
+
_this.mod = function(e) {
|
|
2413
|
+
if (_this.getLength() - e.getLength() < 0) {
|
|
2414
|
+
return _this;
|
|
2415
|
+
}
|
|
2416
|
+
const ratio = QRMath.glog(_this.getAt(0)) - QRMath.glog(e.getAt(0));
|
|
2417
|
+
const num2 = new Array(_this.getLength());
|
|
2418
|
+
for (let i = 0;i < _this.getLength(); i += 1) {
|
|
2419
|
+
num2[i] = _this.getAt(i);
|
|
2420
|
+
}
|
|
2421
|
+
for (let i = 0;i < e.getLength(); i += 1) {
|
|
2422
|
+
num2[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i)) + ratio);
|
|
2423
|
+
}
|
|
2424
|
+
return qrPolynomial(num2, 0).mod(e);
|
|
2425
|
+
};
|
|
2426
|
+
return _this;
|
|
2427
|
+
};
|
|
2428
|
+
var QRRSBlock = function() {
|
|
2429
|
+
const RS_BLOCK_TABLE = [
|
|
2430
|
+
[1, 26, 19],
|
|
2431
|
+
[1, 26, 16],
|
|
2432
|
+
[1, 26, 13],
|
|
2433
|
+
[1, 26, 9],
|
|
2434
|
+
[1, 44, 34],
|
|
2435
|
+
[1, 44, 28],
|
|
2436
|
+
[1, 44, 22],
|
|
2437
|
+
[1, 44, 16],
|
|
2438
|
+
[1, 70, 55],
|
|
2439
|
+
[1, 70, 44],
|
|
2440
|
+
[2, 35, 17],
|
|
2441
|
+
[2, 35, 13],
|
|
2442
|
+
[1, 100, 80],
|
|
2443
|
+
[2, 50, 32],
|
|
2444
|
+
[2, 50, 24],
|
|
2445
|
+
[4, 25, 9],
|
|
2446
|
+
[1, 134, 108],
|
|
2447
|
+
[2, 67, 43],
|
|
2448
|
+
[2, 33, 15, 2, 34, 16],
|
|
2449
|
+
[2, 33, 11, 2, 34, 12],
|
|
2450
|
+
[2, 86, 68],
|
|
2451
|
+
[4, 43, 27],
|
|
2452
|
+
[4, 43, 19],
|
|
2453
|
+
[4, 43, 15],
|
|
2454
|
+
[2, 98, 78],
|
|
2455
|
+
[4, 49, 31],
|
|
2456
|
+
[2, 32, 14, 4, 33, 15],
|
|
2457
|
+
[4, 39, 13, 1, 40, 14],
|
|
2458
|
+
[2, 121, 97],
|
|
2459
|
+
[2, 60, 38, 2, 61, 39],
|
|
2460
|
+
[4, 40, 18, 2, 41, 19],
|
|
2461
|
+
[4, 40, 14, 2, 41, 15],
|
|
2462
|
+
[2, 146, 116],
|
|
2463
|
+
[3, 58, 36, 2, 59, 37],
|
|
2464
|
+
[4, 36, 16, 4, 37, 17],
|
|
2465
|
+
[4, 36, 12, 4, 37, 13],
|
|
2466
|
+
[2, 86, 68, 2, 87, 69],
|
|
2467
|
+
[4, 69, 43, 1, 70, 44],
|
|
2468
|
+
[6, 43, 19, 2, 44, 20],
|
|
2469
|
+
[6, 43, 15, 2, 44, 16],
|
|
2470
|
+
[4, 101, 81],
|
|
2471
|
+
[1, 80, 50, 4, 81, 51],
|
|
2472
|
+
[4, 50, 22, 4, 51, 23],
|
|
2473
|
+
[3, 36, 12, 8, 37, 13],
|
|
2474
|
+
[2, 116, 92, 2, 117, 93],
|
|
2475
|
+
[6, 58, 36, 2, 59, 37],
|
|
2476
|
+
[4, 46, 20, 6, 47, 21],
|
|
2477
|
+
[7, 42, 14, 4, 43, 15],
|
|
2478
|
+
[4, 133, 107],
|
|
2479
|
+
[8, 59, 37, 1, 60, 38],
|
|
2480
|
+
[8, 44, 20, 4, 45, 21],
|
|
2481
|
+
[12, 33, 11, 4, 34, 12],
|
|
2482
|
+
[3, 145, 115, 1, 146, 116],
|
|
2483
|
+
[4, 64, 40, 5, 65, 41],
|
|
2484
|
+
[11, 36, 16, 5, 37, 17],
|
|
2485
|
+
[11, 36, 12, 5, 37, 13],
|
|
2486
|
+
[5, 109, 87, 1, 110, 88],
|
|
2487
|
+
[5, 65, 41, 5, 66, 42],
|
|
2488
|
+
[5, 54, 24, 7, 55, 25],
|
|
2489
|
+
[11, 36, 12, 7, 37, 13],
|
|
2490
|
+
[5, 122, 98, 1, 123, 99],
|
|
2491
|
+
[7, 73, 45, 3, 74, 46],
|
|
2492
|
+
[15, 43, 19, 2, 44, 20],
|
|
2493
|
+
[3, 45, 15, 13, 46, 16],
|
|
2494
|
+
[1, 135, 107, 5, 136, 108],
|
|
2495
|
+
[10, 74, 46, 1, 75, 47],
|
|
2496
|
+
[1, 50, 22, 15, 51, 23],
|
|
2497
|
+
[2, 42, 14, 17, 43, 15],
|
|
2498
|
+
[5, 150, 120, 1, 151, 121],
|
|
2499
|
+
[9, 69, 43, 4, 70, 44],
|
|
2500
|
+
[17, 50, 22, 1, 51, 23],
|
|
2501
|
+
[2, 42, 14, 19, 43, 15],
|
|
2502
|
+
[3, 141, 113, 4, 142, 114],
|
|
2503
|
+
[3, 70, 44, 11, 71, 45],
|
|
2504
|
+
[17, 47, 21, 4, 48, 22],
|
|
2505
|
+
[9, 39, 13, 16, 40, 14],
|
|
2506
|
+
[3, 135, 107, 5, 136, 108],
|
|
2507
|
+
[3, 67, 41, 13, 68, 42],
|
|
2508
|
+
[15, 54, 24, 5, 55, 25],
|
|
2509
|
+
[15, 43, 15, 10, 44, 16],
|
|
2510
|
+
[4, 144, 116, 4, 145, 117],
|
|
2511
|
+
[17, 68, 42],
|
|
2512
|
+
[17, 50, 22, 6, 51, 23],
|
|
2513
|
+
[19, 46, 16, 6, 47, 17],
|
|
2514
|
+
[2, 139, 111, 7, 140, 112],
|
|
2515
|
+
[17, 74, 46],
|
|
2516
|
+
[7, 54, 24, 16, 55, 25],
|
|
2517
|
+
[34, 37, 13],
|
|
2518
|
+
[4, 151, 121, 5, 152, 122],
|
|
2519
|
+
[4, 75, 47, 14, 76, 48],
|
|
2520
|
+
[11, 54, 24, 14, 55, 25],
|
|
2521
|
+
[16, 45, 15, 14, 46, 16],
|
|
2522
|
+
[6, 147, 117, 4, 148, 118],
|
|
2523
|
+
[6, 73, 45, 14, 74, 46],
|
|
2524
|
+
[11, 54, 24, 16, 55, 25],
|
|
2525
|
+
[30, 46, 16, 2, 47, 17],
|
|
2526
|
+
[8, 132, 106, 4, 133, 107],
|
|
2527
|
+
[8, 75, 47, 13, 76, 48],
|
|
2528
|
+
[7, 54, 24, 22, 55, 25],
|
|
2529
|
+
[22, 45, 15, 13, 46, 16],
|
|
2530
|
+
[10, 142, 114, 2, 143, 115],
|
|
2531
|
+
[19, 74, 46, 4, 75, 47],
|
|
2532
|
+
[28, 50, 22, 6, 51, 23],
|
|
2533
|
+
[33, 46, 16, 4, 47, 17],
|
|
2534
|
+
[8, 152, 122, 4, 153, 123],
|
|
2535
|
+
[22, 73, 45, 3, 74, 46],
|
|
2536
|
+
[8, 53, 23, 26, 54, 24],
|
|
2537
|
+
[12, 45, 15, 28, 46, 16],
|
|
2538
|
+
[3, 147, 117, 10, 148, 118],
|
|
2539
|
+
[3, 73, 45, 23, 74, 46],
|
|
2540
|
+
[4, 54, 24, 31, 55, 25],
|
|
2541
|
+
[11, 45, 15, 31, 46, 16],
|
|
2542
|
+
[7, 146, 116, 7, 147, 117],
|
|
2543
|
+
[21, 73, 45, 7, 74, 46],
|
|
2544
|
+
[1, 53, 23, 37, 54, 24],
|
|
2545
|
+
[19, 45, 15, 26, 46, 16],
|
|
2546
|
+
[5, 145, 115, 10, 146, 116],
|
|
2547
|
+
[19, 75, 47, 10, 76, 48],
|
|
2548
|
+
[15, 54, 24, 25, 55, 25],
|
|
2549
|
+
[23, 45, 15, 25, 46, 16],
|
|
2550
|
+
[13, 145, 115, 3, 146, 116],
|
|
2551
|
+
[2, 74, 46, 29, 75, 47],
|
|
2552
|
+
[42, 54, 24, 1, 55, 25],
|
|
2553
|
+
[23, 45, 15, 28, 46, 16],
|
|
2554
|
+
[17, 145, 115],
|
|
2555
|
+
[10, 74, 46, 23, 75, 47],
|
|
2556
|
+
[10, 54, 24, 35, 55, 25],
|
|
2557
|
+
[19, 45, 15, 35, 46, 16],
|
|
2558
|
+
[17, 145, 115, 1, 146, 116],
|
|
2559
|
+
[14, 74, 46, 21, 75, 47],
|
|
2560
|
+
[29, 54, 24, 19, 55, 25],
|
|
2561
|
+
[11, 45, 15, 46, 46, 16],
|
|
2562
|
+
[13, 145, 115, 6, 146, 116],
|
|
2563
|
+
[14, 74, 46, 23, 75, 47],
|
|
2564
|
+
[44, 54, 24, 7, 55, 25],
|
|
2565
|
+
[59, 46, 16, 1, 47, 17],
|
|
2566
|
+
[12, 151, 121, 7, 152, 122],
|
|
2567
|
+
[12, 75, 47, 26, 76, 48],
|
|
2568
|
+
[39, 54, 24, 14, 55, 25],
|
|
2569
|
+
[22, 45, 15, 41, 46, 16],
|
|
2570
|
+
[6, 151, 121, 14, 152, 122],
|
|
2571
|
+
[6, 75, 47, 34, 76, 48],
|
|
2572
|
+
[46, 54, 24, 10, 55, 25],
|
|
2573
|
+
[2, 45, 15, 64, 46, 16],
|
|
2574
|
+
[17, 152, 122, 4, 153, 123],
|
|
2575
|
+
[29, 74, 46, 14, 75, 47],
|
|
2576
|
+
[49, 54, 24, 10, 55, 25],
|
|
2577
|
+
[24, 45, 15, 46, 46, 16],
|
|
2578
|
+
[4, 152, 122, 18, 153, 123],
|
|
2579
|
+
[13, 74, 46, 32, 75, 47],
|
|
2580
|
+
[48, 54, 24, 14, 55, 25],
|
|
2581
|
+
[42, 45, 15, 32, 46, 16],
|
|
2582
|
+
[20, 147, 117, 4, 148, 118],
|
|
2583
|
+
[40, 75, 47, 7, 76, 48],
|
|
2584
|
+
[43, 54, 24, 22, 55, 25],
|
|
2585
|
+
[10, 45, 15, 67, 46, 16],
|
|
2586
|
+
[19, 148, 118, 6, 149, 119],
|
|
2587
|
+
[18, 75, 47, 31, 76, 48],
|
|
2588
|
+
[34, 54, 24, 34, 55, 25],
|
|
2589
|
+
[20, 45, 15, 61, 46, 16]
|
|
2590
|
+
];
|
|
2591
|
+
const qrRSBlock = function(totalCount, dataCount) {
|
|
2592
|
+
const _this2 = {};
|
|
2593
|
+
_this2.totalCount = totalCount;
|
|
2594
|
+
_this2.dataCount = dataCount;
|
|
2595
|
+
return _this2;
|
|
2596
|
+
};
|
|
2597
|
+
const _this = {};
|
|
2598
|
+
const getRsBlockTable = function(typeNumber, errorCorrectionLevel) {
|
|
2599
|
+
switch (errorCorrectionLevel) {
|
|
2600
|
+
case QRErrorCorrectionLevel.L:
|
|
2601
|
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
|
|
2602
|
+
case QRErrorCorrectionLevel.M:
|
|
2603
|
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
|
|
2604
|
+
case QRErrorCorrectionLevel.Q:
|
|
2605
|
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
|
|
2606
|
+
case QRErrorCorrectionLevel.H:
|
|
2607
|
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
|
|
2608
|
+
default:
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
_this.getRSBlocks = function(typeNumber, errorCorrectionLevel) {
|
|
2613
|
+
const rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel);
|
|
2614
|
+
if (typeof rsBlock == "undefined") {
|
|
2615
|
+
throw "bad rs block @ typeNumber:" + typeNumber + "/errorCorrectionLevel:" + errorCorrectionLevel;
|
|
2616
|
+
}
|
|
2617
|
+
const length = rsBlock.length / 3;
|
|
2618
|
+
const list = [];
|
|
2619
|
+
for (let i = 0;i < length; i += 1) {
|
|
2620
|
+
const count = rsBlock[i * 3 + 0];
|
|
2621
|
+
const totalCount = rsBlock[i * 3 + 1];
|
|
2622
|
+
const dataCount = rsBlock[i * 3 + 2];
|
|
2623
|
+
for (let j = 0;j < count; j += 1) {
|
|
2624
|
+
list.push(qrRSBlock(totalCount, dataCount));
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
return list;
|
|
2628
|
+
};
|
|
2629
|
+
return _this;
|
|
2630
|
+
}();
|
|
2631
|
+
var qrBitBuffer = function() {
|
|
2632
|
+
const _buffer = [];
|
|
2633
|
+
let _length = 0;
|
|
2634
|
+
const _this = {};
|
|
2635
|
+
_this.getBuffer = function() {
|
|
2636
|
+
return _buffer;
|
|
2637
|
+
};
|
|
2638
|
+
_this.getAt = function(index) {
|
|
2639
|
+
const bufIndex = Math.floor(index / 8);
|
|
2640
|
+
return (_buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
|
|
2641
|
+
};
|
|
2642
|
+
_this.put = function(num, length) {
|
|
2643
|
+
for (let i = 0;i < length; i += 1) {
|
|
2644
|
+
_this.putBit((num >>> length - i - 1 & 1) == 1);
|
|
2645
|
+
}
|
|
2646
|
+
};
|
|
2647
|
+
_this.getLengthInBits = function() {
|
|
2648
|
+
return _length;
|
|
2649
|
+
};
|
|
2650
|
+
_this.putBit = function(bit) {
|
|
2651
|
+
const bufIndex = Math.floor(_length / 8);
|
|
2652
|
+
if (_buffer.length <= bufIndex) {
|
|
2653
|
+
_buffer.push(0);
|
|
2654
|
+
}
|
|
2655
|
+
if (bit) {
|
|
2656
|
+
_buffer[bufIndex] |= 128 >>> _length % 8;
|
|
2657
|
+
}
|
|
2658
|
+
_length += 1;
|
|
2659
|
+
};
|
|
2660
|
+
return _this;
|
|
2661
|
+
};
|
|
2662
|
+
var qrNumber = function(data) {
|
|
2663
|
+
const _mode = QRMode.MODE_NUMBER;
|
|
2664
|
+
const _data = data;
|
|
2665
|
+
const _this = {};
|
|
2666
|
+
_this.getMode = function() {
|
|
2667
|
+
return _mode;
|
|
2668
|
+
};
|
|
2669
|
+
_this.getLength = function(buffer) {
|
|
2670
|
+
return _data.length;
|
|
2671
|
+
};
|
|
2672
|
+
_this.write = function(buffer) {
|
|
2673
|
+
const data2 = _data;
|
|
2674
|
+
let i = 0;
|
|
2675
|
+
while (i + 2 < data2.length) {
|
|
2676
|
+
buffer.put(strToNum(data2.substring(i, i + 3)), 10);
|
|
2677
|
+
i += 3;
|
|
2678
|
+
}
|
|
2679
|
+
if (i < data2.length) {
|
|
2680
|
+
if (data2.length - i == 1) {
|
|
2681
|
+
buffer.put(strToNum(data2.substring(i, i + 1)), 4);
|
|
2682
|
+
} else if (data2.length - i == 2) {
|
|
2683
|
+
buffer.put(strToNum(data2.substring(i, i + 2)), 7);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
};
|
|
2687
|
+
const strToNum = function(s) {
|
|
2688
|
+
let num = 0;
|
|
2689
|
+
for (let i = 0;i < s.length; i += 1) {
|
|
2690
|
+
num = num * 10 + chatToNum(s.charAt(i));
|
|
2691
|
+
}
|
|
2692
|
+
return num;
|
|
2693
|
+
};
|
|
2694
|
+
const chatToNum = function(c) {
|
|
2695
|
+
if ("0" <= c && c <= "9") {
|
|
2696
|
+
return c.charCodeAt(0) - 48;
|
|
2697
|
+
}
|
|
2698
|
+
throw "illegal char :" + c;
|
|
2699
|
+
};
|
|
2700
|
+
return _this;
|
|
2701
|
+
};
|
|
2702
|
+
var qrAlphaNum = function(data) {
|
|
2703
|
+
const _mode = QRMode.MODE_ALPHA_NUM;
|
|
2704
|
+
const _data = data;
|
|
2705
|
+
const _this = {};
|
|
2706
|
+
_this.getMode = function() {
|
|
2707
|
+
return _mode;
|
|
2708
|
+
};
|
|
2709
|
+
_this.getLength = function(buffer) {
|
|
2710
|
+
return _data.length;
|
|
2711
|
+
};
|
|
2712
|
+
_this.write = function(buffer) {
|
|
2713
|
+
const s = _data;
|
|
2714
|
+
let i = 0;
|
|
2715
|
+
while (i + 1 < s.length) {
|
|
2716
|
+
buffer.put(getCode(s.charAt(i)) * 45 + getCode(s.charAt(i + 1)), 11);
|
|
2717
|
+
i += 2;
|
|
2718
|
+
}
|
|
2719
|
+
if (i < s.length) {
|
|
2720
|
+
buffer.put(getCode(s.charAt(i)), 6);
|
|
2721
|
+
}
|
|
2722
|
+
};
|
|
2723
|
+
const getCode = function(c) {
|
|
2724
|
+
if ("0" <= c && c <= "9") {
|
|
2725
|
+
return c.charCodeAt(0) - 48;
|
|
2726
|
+
} else if ("A" <= c && c <= "Z") {
|
|
2727
|
+
return c.charCodeAt(0) - 65 + 10;
|
|
2728
|
+
} else {
|
|
2729
|
+
switch (c) {
|
|
2730
|
+
case " ":
|
|
2731
|
+
return 36;
|
|
2732
|
+
case "$":
|
|
2733
|
+
return 37;
|
|
2734
|
+
case "%":
|
|
2735
|
+
return 38;
|
|
2736
|
+
case "*":
|
|
2737
|
+
return 39;
|
|
2738
|
+
case "+":
|
|
2739
|
+
return 40;
|
|
2740
|
+
case "-":
|
|
2741
|
+
return 41;
|
|
2742
|
+
case ".":
|
|
2743
|
+
return 42;
|
|
2744
|
+
case "/":
|
|
2745
|
+
return 43;
|
|
2746
|
+
case ":":
|
|
2747
|
+
return 44;
|
|
2748
|
+
default:
|
|
2749
|
+
throw "illegal char :" + c;
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
};
|
|
2753
|
+
return _this;
|
|
2754
|
+
};
|
|
2755
|
+
var qr8BitByte = function(data) {
|
|
2756
|
+
const _mode = QRMode.MODE_8BIT_BYTE;
|
|
2757
|
+
const _data = data;
|
|
2758
|
+
const _bytes = qrcode.stringToBytes(data);
|
|
2759
|
+
const _this = {};
|
|
2760
|
+
_this.getMode = function() {
|
|
2761
|
+
return _mode;
|
|
2762
|
+
};
|
|
2763
|
+
_this.getLength = function(buffer) {
|
|
2764
|
+
return _bytes.length;
|
|
2765
|
+
};
|
|
2766
|
+
_this.write = function(buffer) {
|
|
2767
|
+
for (let i = 0;i < _bytes.length; i += 1) {
|
|
2768
|
+
buffer.put(_bytes[i], 8);
|
|
2769
|
+
}
|
|
2770
|
+
};
|
|
2771
|
+
return _this;
|
|
2772
|
+
};
|
|
2773
|
+
var qrKanji = function(data) {
|
|
2774
|
+
const _mode = QRMode.MODE_KANJI;
|
|
2775
|
+
const _data = data;
|
|
2776
|
+
const stringToBytes = qrcode.stringToBytes;
|
|
2777
|
+
(function(c, code) {
|
|
2778
|
+
const test = stringToBytes(c);
|
|
2779
|
+
if (test.length != 2 || (test[0] << 8 | test[1]) != code) {
|
|
2780
|
+
throw "sjis not supported.";
|
|
2781
|
+
}
|
|
2782
|
+
})("友", 38726);
|
|
2783
|
+
const _bytes = stringToBytes(data);
|
|
2784
|
+
const _this = {};
|
|
2785
|
+
_this.getMode = function() {
|
|
2786
|
+
return _mode;
|
|
2787
|
+
};
|
|
2788
|
+
_this.getLength = function(buffer) {
|
|
2789
|
+
return ~~(_bytes.length / 2);
|
|
2790
|
+
};
|
|
2791
|
+
_this.write = function(buffer) {
|
|
2792
|
+
const data2 = _bytes;
|
|
2793
|
+
let i = 0;
|
|
2794
|
+
while (i + 1 < data2.length) {
|
|
2795
|
+
let c = (255 & data2[i]) << 8 | 255 & data2[i + 1];
|
|
2796
|
+
if (33088 <= c && c <= 40956) {
|
|
2797
|
+
c -= 33088;
|
|
2798
|
+
} else if (57408 <= c && c <= 60351) {
|
|
2799
|
+
c -= 49472;
|
|
2800
|
+
} else {
|
|
2801
|
+
throw "illegal char at " + (i + 1) + "/" + c;
|
|
2802
|
+
}
|
|
2803
|
+
c = (c >>> 8 & 255) * 192 + (c & 255);
|
|
2804
|
+
buffer.put(c, 13);
|
|
2805
|
+
i += 2;
|
|
2806
|
+
}
|
|
2807
|
+
if (i < data2.length) {
|
|
2808
|
+
throw "illegal char at " + (i + 1);
|
|
2809
|
+
}
|
|
2810
|
+
};
|
|
2811
|
+
return _this;
|
|
2812
|
+
};
|
|
2813
|
+
var byteArrayOutputStream = function() {
|
|
2814
|
+
const _bytes = [];
|
|
2815
|
+
const _this = {};
|
|
2816
|
+
_this.writeByte = function(b) {
|
|
2817
|
+
_bytes.push(b & 255);
|
|
2818
|
+
};
|
|
2819
|
+
_this.writeShort = function(i) {
|
|
2820
|
+
_this.writeByte(i);
|
|
2821
|
+
_this.writeByte(i >>> 8);
|
|
2822
|
+
};
|
|
2823
|
+
_this.writeBytes = function(b, off, len) {
|
|
2824
|
+
off = off || 0;
|
|
2825
|
+
len = len || b.length;
|
|
2826
|
+
for (let i = 0;i < len; i += 1) {
|
|
2827
|
+
_this.writeByte(b[i + off]);
|
|
2828
|
+
}
|
|
2829
|
+
};
|
|
2830
|
+
_this.writeString = function(s) {
|
|
2831
|
+
for (let i = 0;i < s.length; i += 1) {
|
|
2832
|
+
_this.writeByte(s.charCodeAt(i));
|
|
2833
|
+
}
|
|
2834
|
+
};
|
|
2835
|
+
_this.toByteArray = function() {
|
|
2836
|
+
return _bytes;
|
|
2837
|
+
};
|
|
2838
|
+
_this.toString = function() {
|
|
2839
|
+
let s = "";
|
|
2840
|
+
s += "[";
|
|
2841
|
+
for (let i = 0;i < _bytes.length; i += 1) {
|
|
2842
|
+
if (i > 0) {
|
|
2843
|
+
s += ",";
|
|
2844
|
+
}
|
|
2845
|
+
s += _bytes[i];
|
|
2846
|
+
}
|
|
2847
|
+
s += "]";
|
|
2848
|
+
return s;
|
|
2849
|
+
};
|
|
2850
|
+
return _this;
|
|
2851
|
+
};
|
|
2852
|
+
var base64EncodeOutputStream = function() {
|
|
2853
|
+
let _buffer = 0;
|
|
2854
|
+
let _buflen = 0;
|
|
2855
|
+
let _length = 0;
|
|
2856
|
+
let _base64 = "";
|
|
2857
|
+
const _this = {};
|
|
2858
|
+
const writeEncoded = function(b) {
|
|
2859
|
+
_base64 += String.fromCharCode(encode2(b & 63));
|
|
2860
|
+
};
|
|
2861
|
+
const encode2 = function(n) {
|
|
2862
|
+
if (n < 0) {
|
|
2863
|
+
throw "n:" + n;
|
|
2864
|
+
} else if (n < 26) {
|
|
2865
|
+
return 65 + n;
|
|
2866
|
+
} else if (n < 52) {
|
|
2867
|
+
return 97 + (n - 26);
|
|
2868
|
+
} else if (n < 62) {
|
|
2869
|
+
return 48 + (n - 52);
|
|
2870
|
+
} else if (n == 62) {
|
|
2871
|
+
return 43;
|
|
2872
|
+
} else if (n == 63) {
|
|
2873
|
+
return 47;
|
|
2874
|
+
} else {
|
|
2875
|
+
throw "n:" + n;
|
|
2876
|
+
}
|
|
2877
|
+
};
|
|
2878
|
+
_this.writeByte = function(n) {
|
|
2879
|
+
_buffer = _buffer << 8 | n & 255;
|
|
2880
|
+
_buflen += 8;
|
|
2881
|
+
_length += 1;
|
|
2882
|
+
while (_buflen >= 6) {
|
|
2883
|
+
writeEncoded(_buffer >>> _buflen - 6);
|
|
2884
|
+
_buflen -= 6;
|
|
2885
|
+
}
|
|
2886
|
+
};
|
|
2887
|
+
_this.flush = function() {
|
|
2888
|
+
if (_buflen > 0) {
|
|
2889
|
+
writeEncoded(_buffer << 6 - _buflen);
|
|
2890
|
+
_buffer = 0;
|
|
2891
|
+
_buflen = 0;
|
|
2892
|
+
}
|
|
2893
|
+
if (_length % 3 != 0) {
|
|
2894
|
+
const padlen = 3 - _length % 3;
|
|
2895
|
+
for (let i = 0;i < padlen; i += 1) {
|
|
2896
|
+
_base64 += "=";
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
};
|
|
2900
|
+
_this.toString = function() {
|
|
2901
|
+
return _base64;
|
|
2902
|
+
};
|
|
2903
|
+
return _this;
|
|
2904
|
+
};
|
|
2905
|
+
var base64DecodeInputStream = function(str) {
|
|
2906
|
+
const _str = str;
|
|
2907
|
+
let _pos = 0;
|
|
2908
|
+
let _buffer = 0;
|
|
2909
|
+
let _buflen = 0;
|
|
2910
|
+
const _this = {};
|
|
2911
|
+
_this.read = function() {
|
|
2912
|
+
while (_buflen < 8) {
|
|
2913
|
+
if (_pos >= _str.length) {
|
|
2914
|
+
if (_buflen == 0) {
|
|
2915
|
+
return -1;
|
|
2916
|
+
}
|
|
2917
|
+
throw "unexpected end of file./" + _buflen;
|
|
2918
|
+
}
|
|
2919
|
+
const c = _str.charAt(_pos);
|
|
2920
|
+
_pos += 1;
|
|
2921
|
+
if (c == "=") {
|
|
2922
|
+
_buflen = 0;
|
|
2923
|
+
return -1;
|
|
2924
|
+
} else if (c.match(/^\s$/)) {
|
|
2925
|
+
continue;
|
|
2926
|
+
}
|
|
2927
|
+
_buffer = _buffer << 6 | decode2(c.charCodeAt(0));
|
|
2928
|
+
_buflen += 6;
|
|
2929
|
+
}
|
|
2930
|
+
const n = _buffer >>> _buflen - 8 & 255;
|
|
2931
|
+
_buflen -= 8;
|
|
2932
|
+
return n;
|
|
2933
|
+
};
|
|
2934
|
+
const decode2 = function(c) {
|
|
2935
|
+
if (65 <= c && c <= 90) {
|
|
2936
|
+
return c - 65;
|
|
2937
|
+
} else if (97 <= c && c <= 122) {
|
|
2938
|
+
return c - 97 + 26;
|
|
2939
|
+
} else if (48 <= c && c <= 57) {
|
|
2940
|
+
return c - 48 + 52;
|
|
2941
|
+
} else if (c == 43) {
|
|
2942
|
+
return 62;
|
|
2943
|
+
} else if (c == 47) {
|
|
2944
|
+
return 63;
|
|
2945
|
+
} else {
|
|
2946
|
+
throw "c:" + c;
|
|
2947
|
+
}
|
|
2948
|
+
};
|
|
2949
|
+
return _this;
|
|
2950
|
+
};
|
|
2951
|
+
var gifImage = function(width, height) {
|
|
2952
|
+
const _width = width;
|
|
2953
|
+
const _height = height;
|
|
2954
|
+
const _data = new Array(width * height);
|
|
2955
|
+
const _this = {};
|
|
2956
|
+
_this.setPixel = function(x, y, pixel) {
|
|
2957
|
+
_data[y * _width + x] = pixel;
|
|
2958
|
+
};
|
|
2959
|
+
_this.write = function(out) {
|
|
2960
|
+
out.writeString("GIF87a");
|
|
2961
|
+
out.writeShort(_width);
|
|
2962
|
+
out.writeShort(_height);
|
|
2963
|
+
out.writeByte(128);
|
|
2964
|
+
out.writeByte(0);
|
|
2965
|
+
out.writeByte(0);
|
|
2966
|
+
out.writeByte(0);
|
|
2967
|
+
out.writeByte(0);
|
|
2968
|
+
out.writeByte(0);
|
|
2969
|
+
out.writeByte(255);
|
|
2970
|
+
out.writeByte(255);
|
|
2971
|
+
out.writeByte(255);
|
|
2972
|
+
out.writeString(",");
|
|
2973
|
+
out.writeShort(0);
|
|
2974
|
+
out.writeShort(0);
|
|
2975
|
+
out.writeShort(_width);
|
|
2976
|
+
out.writeShort(_height);
|
|
2977
|
+
out.writeByte(0);
|
|
2978
|
+
const lzwMinCodeSize = 2;
|
|
2979
|
+
const raster = getLZWRaster(lzwMinCodeSize);
|
|
2980
|
+
out.writeByte(lzwMinCodeSize);
|
|
2981
|
+
let offset = 0;
|
|
2982
|
+
while (raster.length - offset > 255) {
|
|
2983
|
+
out.writeByte(255);
|
|
2984
|
+
out.writeBytes(raster, offset, 255);
|
|
2985
|
+
offset += 255;
|
|
2986
|
+
}
|
|
2987
|
+
out.writeByte(raster.length - offset);
|
|
2988
|
+
out.writeBytes(raster, offset, raster.length - offset);
|
|
2989
|
+
out.writeByte(0);
|
|
2990
|
+
out.writeString(";");
|
|
2991
|
+
};
|
|
2992
|
+
const bitOutputStream = function(out) {
|
|
2993
|
+
const _out = out;
|
|
2994
|
+
let _bitLength = 0;
|
|
2995
|
+
let _bitBuffer = 0;
|
|
2996
|
+
const _this2 = {};
|
|
2997
|
+
_this2.write = function(data, length) {
|
|
2998
|
+
if (data >>> length != 0) {
|
|
2999
|
+
throw "length over";
|
|
3000
|
+
}
|
|
3001
|
+
while (_bitLength + length >= 8) {
|
|
3002
|
+
_out.writeByte(255 & (data << _bitLength | _bitBuffer));
|
|
3003
|
+
length -= 8 - _bitLength;
|
|
3004
|
+
data >>>= 8 - _bitLength;
|
|
3005
|
+
_bitBuffer = 0;
|
|
3006
|
+
_bitLength = 0;
|
|
3007
|
+
}
|
|
3008
|
+
_bitBuffer = data << _bitLength | _bitBuffer;
|
|
3009
|
+
_bitLength = _bitLength + length;
|
|
3010
|
+
};
|
|
3011
|
+
_this2.flush = function() {
|
|
3012
|
+
if (_bitLength > 0) {
|
|
3013
|
+
_out.writeByte(_bitBuffer);
|
|
3014
|
+
}
|
|
3015
|
+
};
|
|
3016
|
+
return _this2;
|
|
3017
|
+
};
|
|
3018
|
+
const getLZWRaster = function(lzwMinCodeSize) {
|
|
3019
|
+
const clearCode = 1 << lzwMinCodeSize;
|
|
3020
|
+
const endCode = (1 << lzwMinCodeSize) + 1;
|
|
3021
|
+
let bitLength = lzwMinCodeSize + 1;
|
|
3022
|
+
const table = lzwTable();
|
|
3023
|
+
for (let i = 0;i < clearCode; i += 1) {
|
|
3024
|
+
table.add(String.fromCharCode(i));
|
|
3025
|
+
}
|
|
3026
|
+
table.add(String.fromCharCode(clearCode));
|
|
3027
|
+
table.add(String.fromCharCode(endCode));
|
|
3028
|
+
const byteOut = byteArrayOutputStream();
|
|
3029
|
+
const bitOut = bitOutputStream(byteOut);
|
|
3030
|
+
bitOut.write(clearCode, bitLength);
|
|
3031
|
+
let dataIndex = 0;
|
|
3032
|
+
let s = String.fromCharCode(_data[dataIndex]);
|
|
3033
|
+
dataIndex += 1;
|
|
3034
|
+
while (dataIndex < _data.length) {
|
|
3035
|
+
const c = String.fromCharCode(_data[dataIndex]);
|
|
3036
|
+
dataIndex += 1;
|
|
3037
|
+
if (table.contains(s + c)) {
|
|
3038
|
+
s = s + c;
|
|
3039
|
+
} else {
|
|
3040
|
+
bitOut.write(table.indexOf(s), bitLength);
|
|
3041
|
+
if (table.size() < 4095) {
|
|
3042
|
+
if (table.size() == 1 << bitLength) {
|
|
3043
|
+
bitLength += 1;
|
|
3044
|
+
}
|
|
3045
|
+
table.add(s + c);
|
|
3046
|
+
}
|
|
3047
|
+
s = c;
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
bitOut.write(table.indexOf(s), bitLength);
|
|
3051
|
+
bitOut.write(endCode, bitLength);
|
|
3052
|
+
bitOut.flush();
|
|
3053
|
+
return byteOut.toByteArray();
|
|
3054
|
+
};
|
|
3055
|
+
const lzwTable = function() {
|
|
3056
|
+
const _map = {};
|
|
3057
|
+
let _size = 0;
|
|
3058
|
+
const _this2 = {};
|
|
3059
|
+
_this2.add = function(key) {
|
|
3060
|
+
if (_this2.contains(key)) {
|
|
3061
|
+
throw "dup key:" + key;
|
|
3062
|
+
}
|
|
3063
|
+
_map[key] = _size;
|
|
3064
|
+
_size += 1;
|
|
3065
|
+
};
|
|
3066
|
+
_this2.size = function() {
|
|
3067
|
+
return _size;
|
|
3068
|
+
};
|
|
3069
|
+
_this2.indexOf = function(key) {
|
|
3070
|
+
return _map[key];
|
|
3071
|
+
};
|
|
3072
|
+
_this2.contains = function(key) {
|
|
3073
|
+
return typeof _map[key] != "undefined";
|
|
3074
|
+
};
|
|
3075
|
+
return _this2;
|
|
3076
|
+
};
|
|
3077
|
+
return _this;
|
|
3078
|
+
};
|
|
3079
|
+
var createDataURL = function(width, height, getPixel) {
|
|
3080
|
+
const gif = gifImage(width, height);
|
|
3081
|
+
for (let y = 0;y < height; y += 1) {
|
|
3082
|
+
for (let x = 0;x < width; x += 1) {
|
|
3083
|
+
gif.setPixel(x, y, getPixel(x, y));
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
const b = byteArrayOutputStream();
|
|
3087
|
+
gif.write(b);
|
|
3088
|
+
const base64 = base64EncodeOutputStream();
|
|
3089
|
+
const bytes = b.toByteArray();
|
|
3090
|
+
for (let i = 0;i < bytes.length; i += 1) {
|
|
3091
|
+
base64.writeByte(bytes[i]);
|
|
3092
|
+
}
|
|
3093
|
+
base64.flush();
|
|
3094
|
+
return "data:image/gif;base64," + base64;
|
|
3095
|
+
};
|
|
3096
|
+
var qrcode_default = qrcode;
|
|
3097
|
+
var stringToBytes = qrcode.stringToBytes;
|
|
3098
|
+
|
|
3099
|
+
// src/server/qr.ts
|
|
3100
|
+
function printQr(text) {
|
|
3101
|
+
let qr = qrcode_default(0, "L");
|
|
3102
|
+
qr.addData(text);
|
|
3103
|
+
qr.make();
|
|
3104
|
+
let modCount = qr.getModuleCount();
|
|
3105
|
+
const QR_INK = "\x1B[30;107m";
|
|
3106
|
+
const QR_TILE_FG = "\x1B[97m";
|
|
3107
|
+
const QR_RESET = "\x1B[0m";
|
|
3108
|
+
const QUIET_ZONE = 2;
|
|
3109
|
+
let qrWidth = modCount + 2 * QUIET_ZONE;
|
|
3110
|
+
let dark = (y, x) => y >= 0 && y < modCount && x >= 0 && x < modCount && qr.isDark(y, x);
|
|
3111
|
+
for (let y = -QUIET_ZONE;y < modCount + QUIET_ZONE - 1; y += 2) {
|
|
3112
|
+
let row = " " + QR_INK;
|
|
3113
|
+
for (let x = -QUIET_ZONE;x < modCount + QUIET_ZONE; x++) {
|
|
3114
|
+
let top = dark(y, x);
|
|
3115
|
+
let bot = dark(y + 1, x);
|
|
3116
|
+
row += top && bot ? "█" : top ? "▀" : bot ? "▄" : " ";
|
|
3117
|
+
}
|
|
3118
|
+
console.log(row + QR_RESET);
|
|
3119
|
+
}
|
|
3120
|
+
console.log(" " + QR_TILE_FG + "▀".repeat(qrWidth) + QR_RESET);
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
// src/server/tunnel.ts
|
|
3124
|
+
import { Endpoint } from "flux:p2p";
|
|
3125
|
+
import { file as file5 } from "flux:fs";
|
|
3126
|
+
import { join as join4 } from "flux:path";
|
|
3127
|
+
var TUNNEL_PROTOCOL = "solidrt-dev/0";
|
|
3128
|
+
var KEY_FILE = "tunnel.key";
|
|
3129
|
+
async function createTunnelEndpoint(port, keyDir) {
|
|
3130
|
+
let keyPath = join4(keyDir, KEY_FILE);
|
|
3131
|
+
let secretKey;
|
|
3132
|
+
let keyFile = file5(keyPath);
|
|
3133
|
+
if (await keyFile.exists()) {
|
|
3134
|
+
let saved = (await keyFile.text()).trim();
|
|
3135
|
+
if (saved.length === 64)
|
|
3136
|
+
secretKey = saved;
|
|
3137
|
+
}
|
|
3138
|
+
let endpoint = await Endpoint.create({ local: true, protocols: [TUNNEL_PROTOCOL], ...port !== null ? { port } : {}, secretKey });
|
|
3139
|
+
if (!secretKey)
|
|
3140
|
+
await keyFile.write(endpoint.secretKey);
|
|
3141
|
+
let ticket = await endpoint.ticket();
|
|
3142
|
+
console.log("");
|
|
3143
|
+
printQr(ticket);
|
|
3144
|
+
console.log("");
|
|
3145
|
+
console.log(`[cli] Tunnel ticket: ${ticket}`);
|
|
3146
|
+
return endpoint;
|
|
3147
|
+
}
|
|
3148
|
+
|
|
3149
|
+
// src/server/repl.ts
|
|
3150
|
+
import { isTTY, on as on2, write as write2 } from "flux:tty";
|
|
3151
|
+
import { dir as dir4 } from "flux:fs";
|
|
3152
|
+
|
|
3153
|
+
// src/server/line-editor.ts
|
|
3154
|
+
import { on, setRawMode, write } from "flux:tty";
|
|
3155
|
+
var CLEAR_LINE = "\r\x1B[K";
|
|
3156
|
+
function commonPrefix(items) {
|
|
3157
|
+
let prefix = items[0] ?? "";
|
|
3158
|
+
for (let item of items) {
|
|
3159
|
+
let i = 0;
|
|
3160
|
+
while (i < prefix.length && i < item.length && prefix[i] === item[i])
|
|
3161
|
+
i++;
|
|
3162
|
+
prefix = prefix.slice(0, i);
|
|
3163
|
+
}
|
|
3164
|
+
return prefix;
|
|
3165
|
+
}
|
|
3166
|
+
function startLineEditor(opts) {
|
|
3167
|
+
let buffer = "";
|
|
3168
|
+
let cursor = 0;
|
|
3169
|
+
let history = [];
|
|
3170
|
+
let historyAt = -1;
|
|
3171
|
+
let draft = "";
|
|
3172
|
+
let busy = false;
|
|
3173
|
+
let stopped = false;
|
|
3174
|
+
let redraw = () => {
|
|
3175
|
+
if (stopped || busy)
|
|
3176
|
+
return;
|
|
3177
|
+
let back = buffer.length - cursor;
|
|
3178
|
+
write(CLEAR_LINE + opts.prompt + buffer + (back > 0 ? `\x1B[${back}D` : ""));
|
|
3179
|
+
};
|
|
3180
|
+
let submit = () => {
|
|
3181
|
+
let line = buffer;
|
|
3182
|
+
write(`\r
|
|
3183
|
+
`);
|
|
3184
|
+
if (line.trim() && history[history.length - 1] !== line)
|
|
3185
|
+
history.push(line);
|
|
3186
|
+
historyAt = -1;
|
|
3187
|
+
buffer = "";
|
|
3188
|
+
cursor = 0;
|
|
3189
|
+
busy = true;
|
|
3190
|
+
Promise.resolve().then(() => opts.onLine(line)).catch((e) => console.error(`[cli] ${String(e)}`)).then(() => {
|
|
3191
|
+
busy = false;
|
|
3192
|
+
redraw();
|
|
3193
|
+
});
|
|
3194
|
+
};
|
|
3195
|
+
let recall = (at) => {
|
|
3196
|
+
if (historyAt === -1)
|
|
3197
|
+
draft = buffer;
|
|
3198
|
+
historyAt = at;
|
|
3199
|
+
buffer = at === -1 ? draft : history[at];
|
|
3200
|
+
cursor = buffer.length;
|
|
3201
|
+
};
|
|
3202
|
+
let complete = async () => {
|
|
3203
|
+
if (!opts.complete)
|
|
3204
|
+
return;
|
|
3205
|
+
let head = buffer.slice(0, cursor);
|
|
3206
|
+
let asked = buffer;
|
|
3207
|
+
let { matches, replace } = await opts.complete(head);
|
|
3208
|
+
if (buffer !== asked || cursor !== head.length)
|
|
3209
|
+
return;
|
|
3210
|
+
if (matches.length === 0)
|
|
3211
|
+
return;
|
|
3212
|
+
let insert = matches.length === 1 ? matches[0] : commonPrefix(matches);
|
|
3213
|
+
if (insert.length > replace.length) {
|
|
3214
|
+
buffer = head.slice(0, head.length - replace.length) + insert + buffer.slice(cursor);
|
|
3215
|
+
cursor += insert.length - replace.length;
|
|
3216
|
+
} else if (matches.length > 1) {
|
|
3217
|
+
write(`\r
|
|
3218
|
+
` + matches.join(" ") + `\r
|
|
3219
|
+
`);
|
|
3220
|
+
}
|
|
3221
|
+
redraw();
|
|
3222
|
+
};
|
|
3223
|
+
let onKey = (key) => {
|
|
3224
|
+
if (key.ctrl) {
|
|
3225
|
+
switch (key.name) {
|
|
3226
|
+
case "c":
|
|
3227
|
+
opts.onQuit();
|
|
3228
|
+
return;
|
|
3229
|
+
case "d":
|
|
3230
|
+
if (buffer.length === 0)
|
|
3231
|
+
opts.onQuit();
|
|
3232
|
+
else if (cursor < buffer.length)
|
|
3233
|
+
buffer = buffer.slice(0, cursor) + buffer.slice(cursor + 1);
|
|
3234
|
+
break;
|
|
3235
|
+
case "a":
|
|
3236
|
+
cursor = 0;
|
|
3237
|
+
break;
|
|
3238
|
+
case "e":
|
|
3239
|
+
cursor = buffer.length;
|
|
3240
|
+
break;
|
|
3241
|
+
case "u":
|
|
3242
|
+
buffer = buffer.slice(cursor);
|
|
3243
|
+
cursor = 0;
|
|
3244
|
+
break;
|
|
3245
|
+
default:
|
|
3246
|
+
return;
|
|
3247
|
+
}
|
|
3248
|
+
redraw();
|
|
3249
|
+
return;
|
|
3250
|
+
}
|
|
3251
|
+
if (key.meta)
|
|
3252
|
+
return;
|
|
3253
|
+
switch (key.name) {
|
|
3254
|
+
case "return":
|
|
3255
|
+
submit();
|
|
3256
|
+
return;
|
|
3257
|
+
case "backspace":
|
|
3258
|
+
if (cursor > 0) {
|
|
3259
|
+
buffer = buffer.slice(0, cursor - 1) + buffer.slice(cursor);
|
|
3260
|
+
cursor--;
|
|
3261
|
+
}
|
|
3262
|
+
break;
|
|
3263
|
+
case "delete":
|
|
3264
|
+
if (cursor < buffer.length)
|
|
3265
|
+
buffer = buffer.slice(0, cursor) + buffer.slice(cursor + 1);
|
|
3266
|
+
break;
|
|
3267
|
+
case "left":
|
|
3268
|
+
if (cursor > 0)
|
|
3269
|
+
cursor--;
|
|
3270
|
+
break;
|
|
3271
|
+
case "right":
|
|
3272
|
+
if (cursor < buffer.length)
|
|
3273
|
+
cursor++;
|
|
3274
|
+
break;
|
|
3275
|
+
case "home":
|
|
3276
|
+
cursor = 0;
|
|
3277
|
+
break;
|
|
3278
|
+
case "end":
|
|
3279
|
+
cursor = buffer.length;
|
|
3280
|
+
break;
|
|
3281
|
+
case "up":
|
|
3282
|
+
if (history.length && historyAt !== 0)
|
|
3283
|
+
recall(historyAt === -1 ? history.length - 1 : historyAt - 1);
|
|
3284
|
+
break;
|
|
3285
|
+
case "down":
|
|
3286
|
+
if (historyAt !== -1)
|
|
3287
|
+
recall(historyAt === history.length - 1 ? -1 : historyAt + 1);
|
|
3288
|
+
break;
|
|
3289
|
+
case "tab":
|
|
3290
|
+
complete();
|
|
3291
|
+
return;
|
|
3292
|
+
default:
|
|
3293
|
+
if (key.char === undefined)
|
|
3294
|
+
return;
|
|
3295
|
+
buffer = buffer.slice(0, cursor) + key.char + buffer.slice(cursor);
|
|
3296
|
+
cursor += key.char.length;
|
|
3297
|
+
}
|
|
3298
|
+
redraw();
|
|
3299
|
+
};
|
|
3300
|
+
setRawMode(true);
|
|
3301
|
+
let offKey = on("key", onKey);
|
|
3302
|
+
let offClose = on("close", opts.onQuit);
|
|
3303
|
+
let original = { log: console.log, warn: console.warn, error: console.error, debug: console.debug };
|
|
3304
|
+
let wrap = (print) => (...args) => {
|
|
3305
|
+
if (!busy)
|
|
3306
|
+
write(CLEAR_LINE);
|
|
3307
|
+
print(...args);
|
|
3308
|
+
redraw();
|
|
3309
|
+
};
|
|
3310
|
+
console.log = wrap(original.log);
|
|
3311
|
+
console.warn = wrap(original.warn);
|
|
3312
|
+
console.error = wrap(original.error);
|
|
3313
|
+
console.debug = wrap(original.debug);
|
|
3314
|
+
redraw();
|
|
3315
|
+
return () => {
|
|
3316
|
+
if (stopped)
|
|
3317
|
+
return;
|
|
3318
|
+
stopped = true;
|
|
3319
|
+
Object.assign(console, original);
|
|
3320
|
+
offKey();
|
|
3321
|
+
offClose();
|
|
3322
|
+
write(`\r
|
|
3323
|
+
`);
|
|
3324
|
+
setRawMode(false);
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3328
|
+
// src/server/repl.ts
|
|
3329
|
+
var PROMPT = "srt> ";
|
|
3330
|
+
var HELP = "Commands: load <file>, reload [id...], stop [id...], list, stats [on|off], watch on|off, mute on|off, quit, help";
|
|
3331
|
+
var COMMANDS = ["load ", "reload", "stop", "list", "stats", "watch ", "mute ", "quit", "exit", "help"];
|
|
3332
|
+
function targets(args) {
|
|
3333
|
+
let entries = [...state.clients.entries()];
|
|
3334
|
+
if (!args)
|
|
3335
|
+
return entries.map(([ws]) => ws);
|
|
3336
|
+
let found2 = [];
|
|
3337
|
+
for (let token of args.split(/\s+/)) {
|
|
3338
|
+
let id = parseInt(token, 10);
|
|
3339
|
+
let entry = entries.find(([, info]) => info.id === id);
|
|
3340
|
+
if (!entry) {
|
|
3341
|
+
console.log(`No client with id ${token}`);
|
|
3342
|
+
continue;
|
|
3343
|
+
}
|
|
3344
|
+
found2.push(entry[0]);
|
|
3345
|
+
}
|
|
3346
|
+
return found2;
|
|
3347
|
+
}
|
|
3348
|
+
function describe(args, count) {
|
|
3349
|
+
return args ? `client(s) ${args.split(/\s+/).join(", ")}` : `all ${count} client(s)`;
|
|
3350
|
+
}
|
|
3351
|
+
async function cmdReload(args) {
|
|
3352
|
+
let error = await rebuildAndBroadcast();
|
|
3353
|
+
if (error) {
|
|
3354
|
+
console.error(error);
|
|
3355
|
+
showBuildFailure();
|
|
3356
|
+
return;
|
|
3357
|
+
}
|
|
3358
|
+
if (args) {
|
|
3359
|
+
for (let ws of targets(args))
|
|
3360
|
+
if (state.currentReload)
|
|
3361
|
+
ws.send(state.currentReload);
|
|
3362
|
+
}
|
|
3363
|
+
console.log(`[cli] Sent reload to ${describe(args, state.clients.size)}`);
|
|
3364
|
+
}
|
|
3365
|
+
function cmdStop(args) {
|
|
3366
|
+
let text = JSON.stringify({ type: "stop" });
|
|
3367
|
+
let list = targets(args);
|
|
3368
|
+
for (let ws of list)
|
|
3369
|
+
ws.send(text);
|
|
3370
|
+
if (list.length)
|
|
3371
|
+
console.log(`[cli] Sent stop to ${describe(args, list.length)}`);
|
|
3372
|
+
}
|
|
3373
|
+
async function cmdLoad(file6) {
|
|
3374
|
+
if (!file6) {
|
|
3375
|
+
console.log("Usage: load <file>");
|
|
3376
|
+
return;
|
|
3377
|
+
}
|
|
3378
|
+
let result = await loadEntry(file6);
|
|
3379
|
+
if ("error" in result) {
|
|
3380
|
+
console.error(result.error);
|
|
3381
|
+
if (result.status === 502)
|
|
3382
|
+
showBuildFailure();
|
|
3383
|
+
return;
|
|
3384
|
+
}
|
|
3385
|
+
console.log(`[cli] Loaded ${result.entry}`);
|
|
3386
|
+
}
|
|
3387
|
+
function cmdList() {
|
|
3388
|
+
let clients = clientList(true);
|
|
3389
|
+
if (clients.length === 0) {
|
|
3390
|
+
console.log("No connected clients");
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
console.log(`${clients.length} connected client(s):`);
|
|
3394
|
+
for (let c of clients) {
|
|
3395
|
+
console.log(` ${c.id}: ${c.address ?? "unknown"} [${c.platform}, ${c.version}]`);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
function toggle(args, current, usage) {
|
|
3399
|
+
if (args === "on")
|
|
3400
|
+
return true;
|
|
3401
|
+
if (args === "off")
|
|
3402
|
+
return false;
|
|
3403
|
+
if (!args)
|
|
3404
|
+
return !current;
|
|
3405
|
+
console.log(usage);
|
|
3406
|
+
return;
|
|
3407
|
+
}
|
|
3408
|
+
function cmdStats(args) {
|
|
3409
|
+
let on3 = toggle(args, state.stats, "Usage: stats [on|off]");
|
|
3410
|
+
if (on3 === undefined)
|
|
3411
|
+
return;
|
|
3412
|
+
setStats(on3);
|
|
3413
|
+
console.log(`[cli] Stats overlay ${on3 ? "on" : "off"}`);
|
|
3414
|
+
}
|
|
3415
|
+
function cmdWatch(args) {
|
|
3416
|
+
let on3 = toggle(args, !state.watchPaused, "Usage: watch on|off");
|
|
3417
|
+
if (on3 === undefined)
|
|
3418
|
+
return;
|
|
3419
|
+
setWatchActive(on3);
|
|
3420
|
+
}
|
|
3421
|
+
function cmdMute(args) {
|
|
3422
|
+
let on3 = toggle(args, state.userInputMuted, "Usage: mute on|off");
|
|
3423
|
+
if (on3 === undefined)
|
|
3424
|
+
return;
|
|
3425
|
+
setUserInputMuted(on3);
|
|
3426
|
+
}
|
|
3427
|
+
function split(line) {
|
|
3428
|
+
let cmd = line.trim();
|
|
3429
|
+
let i = cmd.indexOf(" ");
|
|
3430
|
+
return i < 0 ? [cmd, ""] : [cmd.slice(0, i), cmd.slice(i + 1).trim()];
|
|
3431
|
+
}
|
|
3432
|
+
async function dispatch(line, quit) {
|
|
3433
|
+
let [cmd, args] = split(line);
|
|
3434
|
+
switch (cmd) {
|
|
3435
|
+
case "":
|
|
3436
|
+
return;
|
|
3437
|
+
case "reload":
|
|
3438
|
+
return cmdReload(args);
|
|
3439
|
+
case "stop":
|
|
3440
|
+
return cmdStop(args);
|
|
3441
|
+
case "load":
|
|
3442
|
+
return cmdLoad(args);
|
|
3443
|
+
case "list":
|
|
3444
|
+
return cmdList();
|
|
3445
|
+
case "stats":
|
|
3446
|
+
return cmdStats(args);
|
|
3447
|
+
case "watch":
|
|
3448
|
+
return cmdWatch(args);
|
|
3449
|
+
case "mute":
|
|
3450
|
+
return cmdMute(args);
|
|
3451
|
+
case "quit":
|
|
3452
|
+
case "exit":
|
|
3453
|
+
return quit();
|
|
3454
|
+
case "help":
|
|
3455
|
+
console.log(HELP);
|
|
3456
|
+
return;
|
|
3457
|
+
default:
|
|
3458
|
+
console.log(`Unknown command: ${cmd}`);
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
async function complete(line) {
|
|
3462
|
+
if (!line.startsWith("load ")) {
|
|
3463
|
+
return { matches: COMMANDS.filter((c) => c.startsWith(line)), replace: line };
|
|
3464
|
+
}
|
|
3465
|
+
let partial = line.slice(5);
|
|
3466
|
+
let slash = partial.lastIndexOf("/");
|
|
3467
|
+
let dirPart = slash < 0 ? "" : partial.slice(0, slash + 1);
|
|
3468
|
+
let prefix = partial.slice(dirPart.length);
|
|
3469
|
+
let base = state.config.projectDir ?? state.config.sourceDir;
|
|
3470
|
+
let matches = [];
|
|
3471
|
+
try {
|
|
3472
|
+
for (let entry of await dir4(absolute(dirPart || ".", base)).entries()) {
|
|
3473
|
+
if (!entry.name.startsWith(prefix))
|
|
3474
|
+
continue;
|
|
3475
|
+
if (entry.type === "directory")
|
|
3476
|
+
matches.push(entry.name + "/");
|
|
3477
|
+
else if (ENTRY_EXTENSIONS.some((ext) => entry.name.endsWith(ext)))
|
|
3478
|
+
matches.push(entry.name);
|
|
3479
|
+
}
|
|
3480
|
+
} catch {}
|
|
3481
|
+
return { matches, replace: prefix };
|
|
3482
|
+
}
|
|
3483
|
+
function startRepl(quit) {
|
|
3484
|
+
if (!isTTY) {
|
|
3485
|
+
console.log("[cli] No terminal on stdin, running without the repl");
|
|
3486
|
+
return () => {};
|
|
3487
|
+
}
|
|
3488
|
+
try {
|
|
3489
|
+
return startLineEditor({ prompt: PROMPT, onLine: (line) => dispatch(line, quit), onQuit: quit, complete });
|
|
3490
|
+
} catch (e) {
|
|
3491
|
+
console.log(`[cli] ${String(e)}; using plain line input`);
|
|
3492
|
+
return startLineInput(quit);
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
function startLineInput(quit) {
|
|
3496
|
+
let attached = true;
|
|
3497
|
+
let prompt = () => {
|
|
3498
|
+
if (attached)
|
|
3499
|
+
write2(PROMPT);
|
|
3500
|
+
};
|
|
3501
|
+
let chain = Promise.resolve();
|
|
3502
|
+
let offLine = on2("line", (line) => {
|
|
3503
|
+
chain = chain.then(() => dispatch(line, quit)).then(prompt, (e) => {
|
|
3504
|
+
console.error(`[cli] ${String(e)}`);
|
|
3505
|
+
prompt();
|
|
3506
|
+
});
|
|
3507
|
+
});
|
|
3508
|
+
let offClose = on2("close", quit);
|
|
3509
|
+
prompt();
|
|
3510
|
+
return () => {
|
|
3511
|
+
attached = false;
|
|
3512
|
+
offLine();
|
|
3513
|
+
offClose();
|
|
3514
|
+
};
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
// src/server/registry.ts
|
|
3518
|
+
import { dir as dir5, file as file6, realpath as realpath3 } from "flux:fs";
|
|
3519
|
+
import { join as join5 } from "flux:path";
|
|
3520
|
+
import { alive, homedir, pid } from "flux:process";
|
|
3521
|
+
var RECORD_FILE = "live.json";
|
|
3522
|
+
var PORT_FILE = "port";
|
|
3523
|
+
function devDir(...parts) {
|
|
3524
|
+
let home = homedir();
|
|
3525
|
+
if (!home)
|
|
3526
|
+
fail("No home directory: the dev server keeps its state under ~/.solidrt");
|
|
3527
|
+
return join5(home, ".solidrt", ...parts);
|
|
3528
|
+
}
|
|
3529
|
+
async function serverDirFor(key) {
|
|
3530
|
+
let digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(key));
|
|
3531
|
+
let hex = Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
3532
|
+
return devDir("servers", hex.slice(0, 16));
|
|
3533
|
+
}
|
|
3534
|
+
async function records() {
|
|
3535
|
+
let root = devDir("servers");
|
|
3536
|
+
let entries = await dir5(root).entries().catch(() => []);
|
|
3537
|
+
let found2 = [];
|
|
3538
|
+
for (let entry of entries) {
|
|
3539
|
+
if (entry.type !== "directory")
|
|
3540
|
+
continue;
|
|
3541
|
+
let path = join5(root, entry.name, RECORD_FILE);
|
|
3542
|
+
let record = await file6(path).json().catch(() => null);
|
|
3543
|
+
if (typeof record?.pid === "number" && typeof record?.port === "number" && typeof record?.key === "string") {
|
|
3544
|
+
found2.push({ path, record });
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
return found2;
|
|
3548
|
+
}
|
|
3549
|
+
async function pruneDeadRecords() {
|
|
3550
|
+
for (let { path, record } of await records()) {
|
|
3551
|
+
if (!alive(record.pid))
|
|
3552
|
+
await file6(path).remove().catch(() => {});
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
async function serves(record) {
|
|
3556
|
+
let control = new AbortController;
|
|
3557
|
+
let timer2 = setTimeout(() => control.abort(), 1000);
|
|
3558
|
+
try {
|
|
3559
|
+
let resp = await fetch(`http://127.0.0.1:${record.port}/__control__/clients`, { signal: control.signal });
|
|
3560
|
+
return resp.headers.get("x-solidrt-project") === record.key;
|
|
3561
|
+
} catch {
|
|
3562
|
+
return false;
|
|
3563
|
+
} finally {
|
|
3564
|
+
clearTimeout(timer2);
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
async function runningFor(key) {
|
|
3568
|
+
for (let { path, record } of await records()) {
|
|
3569
|
+
if (!alive(record.pid))
|
|
3570
|
+
continue;
|
|
3571
|
+
if (record.key === key || await realpath3(record.key).catch(() => null) === key) {
|
|
3572
|
+
if (await serves(record))
|
|
3573
|
+
return record;
|
|
3574
|
+
await file6(path).remove().catch(() => {});
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
return;
|
|
3578
|
+
}
|
|
3579
|
+
async function rememberedPort(serverDir) {
|
|
3580
|
+
let text = await file6(join5(serverDir, PORT_FILE)).text().catch(() => "");
|
|
3581
|
+
let port = Number(text.trim());
|
|
3582
|
+
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : null;
|
|
3583
|
+
}
|
|
3584
|
+
async function writeRecord(config, port, address) {
|
|
3585
|
+
await dir5(config.serverDir).create();
|
|
3586
|
+
let record = {
|
|
3587
|
+
pid,
|
|
3588
|
+
port,
|
|
3589
|
+
address,
|
|
3590
|
+
key: config.key,
|
|
3591
|
+
mode: config.mode,
|
|
3592
|
+
entry: config.entry,
|
|
3593
|
+
projectDir: config.projectDir,
|
|
3594
|
+
started: new Date().toISOString()
|
|
3595
|
+
};
|
|
3596
|
+
await file6(join5(config.serverDir, RECORD_FILE)).write(JSON.stringify(record));
|
|
3597
|
+
await file6(join5(config.serverDir, PORT_FILE)).write(String(port));
|
|
3598
|
+
}
|
|
3599
|
+
async function removeRecord(serverDir) {
|
|
3600
|
+
await file6(join5(serverDir, RECORD_FILE)).remove().catch(() => {});
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3603
|
+
// src/server/main.ts
|
|
3604
|
+
var args = parseArgs();
|
|
3605
|
+
var mode = await resolveMode(args);
|
|
3606
|
+
await pruneDeadRecords();
|
|
3607
|
+
var running = await runningFor(mode.key);
|
|
3608
|
+
if (running) {
|
|
3609
|
+
fail(`A dev server already serves ${mode.key} on port ${running.port} (pid ${running.pid}). Stop it first, or attach a client with srt client.`);
|
|
3610
|
+
}
|
|
3611
|
+
var srt = srtCommand();
|
|
3612
|
+
var runner = args.client !== null ? await requireBinary("solidrt-go") : null;
|
|
3613
|
+
var serverDir = await serverDirFor(mode.key);
|
|
3614
|
+
var lanAddress = args.lan ? lanAddressOf(interfaces()) : undefined;
|
|
3615
|
+
function lanAddressOf(ifaces) {
|
|
3616
|
+
let v4 = (list) => list.flatMap((i) => i.addrs).find((a) => a.family === "v4")?.ip;
|
|
3617
|
+
let reachable = ifaces.filter((i) => i.up && !i.loopback);
|
|
3618
|
+
return v4(reachable.filter((i) => i.default)) ?? v4(reachable);
|
|
3619
|
+
}
|
|
3620
|
+
var clientArgs = [];
|
|
3621
|
+
if (args.client !== null) {
|
|
3622
|
+
clientArgs.push("--data-root", args.dataRoot ? absolute(args.dataRoot, await realpath4(".")) : devDir("clients"));
|
|
3623
|
+
clientArgs.push("--client", String(args.client));
|
|
3624
|
+
if (args.size)
|
|
3625
|
+
clientArgs.push("--size", args.size);
|
|
3626
|
+
}
|
|
3627
|
+
var config = {
|
|
3628
|
+
mode: mode.mode,
|
|
3629
|
+
key: mode.key,
|
|
3630
|
+
serverDir,
|
|
3631
|
+
entry: mode.entry,
|
|
3632
|
+
sourceDir: sourceDirOf(mode),
|
|
3633
|
+
projectDir: mode.projectDir,
|
|
3634
|
+
cwd: mode.projectDir ?? sourceDirOf(mode),
|
|
3635
|
+
entryArgs: [mode.entry, mode.mode === "project" ? "--project" : "--file"],
|
|
3636
|
+
srt,
|
|
3637
|
+
port: args.port,
|
|
3638
|
+
lan: args.lan,
|
|
3639
|
+
address: lanAddress ?? "127.0.0.1",
|
|
3640
|
+
proxyHttp: args.proxyHttp,
|
|
3641
|
+
args: args.appArgs,
|
|
3642
|
+
minify: args.minify,
|
|
3643
|
+
cache: args.proxyHttp,
|
|
3644
|
+
cacheDir: mode.projectDir ? join6(mode.projectDir, ".srt-data") : join6(serverDir, "data"),
|
|
3645
|
+
capture: args.capture,
|
|
3646
|
+
stats: args.stats,
|
|
3647
|
+
tunnel: args.tunnel,
|
|
3648
|
+
client: runner ? { cmd: runner, args: clientArgs } : null
|
|
3649
|
+
};
|
|
3650
|
+
state.config = config;
|
|
3651
|
+
state.stats = config.stats;
|
|
3652
|
+
if (config.cache) {
|
|
3653
|
+
await initCache({ dir: config.cacheDir });
|
|
3654
|
+
console.log("[cli] HTTP cache enabled");
|
|
3655
|
+
}
|
|
3656
|
+
if (config.capture) {
|
|
3657
|
+
await file7(config.capture).write("");
|
|
3658
|
+
state.captureStartMs = Date.now();
|
|
3659
|
+
}
|
|
3660
|
+
function splitQuery(url) {
|
|
3661
|
+
let i = url.indexOf("?");
|
|
3662
|
+
let path = i < 0 ? url : url.slice(0, i);
|
|
3663
|
+
let query = new Map;
|
|
3664
|
+
if (i >= 0) {
|
|
3665
|
+
for (let pair of url.slice(i + 1).split("&")) {
|
|
3666
|
+
if (!pair)
|
|
3667
|
+
continue;
|
|
3668
|
+
let j = pair.indexOf("=");
|
|
3669
|
+
let k = j < 0 ? pair : pair.slice(0, j);
|
|
3670
|
+
let v = j < 0 ? "" : pair.slice(j + 1);
|
|
3671
|
+
query.set(decodeURIComponent(k), decodeURIComponent(v.replace(/\+/g, " ")));
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
return { path: decodeURIComponent(path), query };
|
|
3675
|
+
}
|
|
3676
|
+
async function handleFiles(req, path, root) {
|
|
3677
|
+
let filePath = resolveWithin(root, "." + path);
|
|
3678
|
+
if (!filePath) {
|
|
3679
|
+
return new Response("Forbidden", { status: 403 });
|
|
3680
|
+
}
|
|
3681
|
+
console.log("[cli] get " + path);
|
|
3682
|
+
let stat;
|
|
3683
|
+
try {
|
|
3684
|
+
stat = await file7(filePath).stat();
|
|
3685
|
+
} catch {
|
|
3686
|
+
console.log(`[cli] file not found ${path}`);
|
|
3687
|
+
return new Response("Not found", { status: 404 });
|
|
3688
|
+
}
|
|
3689
|
+
if (stat.type === "directory") {
|
|
3690
|
+
return new Response("Not found", { status: 404 });
|
|
3691
|
+
}
|
|
3692
|
+
let baseHeaders = { "Accept-Ranges": "bytes" };
|
|
3693
|
+
let range = req.headers.get("range");
|
|
3694
|
+
let match = range ? /^bytes=(\d*)-(\d*)$/.exec(range.trim()) : null;
|
|
3695
|
+
if (match) {
|
|
3696
|
+
let size = stat.size;
|
|
3697
|
+
let start;
|
|
3698
|
+
let end;
|
|
3699
|
+
if (match[1] === "") {
|
|
3700
|
+
let n = parseInt(match[2], 10);
|
|
3701
|
+
start = isNaN(n) ? 0 : Math.max(0, size - n);
|
|
3702
|
+
end = size - 1;
|
|
3703
|
+
} else {
|
|
3704
|
+
start = parseInt(match[1], 10);
|
|
3705
|
+
end = match[2] === "" ? size - 1 : Math.min(parseInt(match[2], 10), size - 1);
|
|
3706
|
+
}
|
|
3707
|
+
if (start > end || start >= size) {
|
|
3708
|
+
return new Response("Range not satisfiable", {
|
|
3709
|
+
status: 416,
|
|
3710
|
+
headers: { ...baseHeaders, "Content-Range": `bytes */${size}` }
|
|
3711
|
+
});
|
|
3712
|
+
}
|
|
3713
|
+
return new Response(await file7(filePath).read(start, end - start + 1), {
|
|
3714
|
+
status: 206,
|
|
3715
|
+
headers: {
|
|
3716
|
+
...baseHeaders,
|
|
3717
|
+
"Content-Range": `bytes ${start}-${end}/${size}`,
|
|
3718
|
+
"Content-Length": String(end - start + 1)
|
|
3719
|
+
}
|
|
3720
|
+
});
|
|
3721
|
+
}
|
|
3722
|
+
return new Response(await file7(filePath).bytes(), { headers: baseHeaders });
|
|
3723
|
+
}
|
|
3724
|
+
async function handleRequest(req, server) {
|
|
3725
|
+
if (server.upgrade(req))
|
|
3726
|
+
return;
|
|
3727
|
+
let { path, query } = splitQuery(req.url);
|
|
3728
|
+
if (path === "/__proxy__") {
|
|
3729
|
+
return handleProxy(req);
|
|
3730
|
+
}
|
|
3731
|
+
if (path.startsWith("/__control__/")) {
|
|
3732
|
+
let resp = await handleControl(req, path, query);
|
|
3733
|
+
resp.headers.set("x-solidrt-project", config.key);
|
|
3734
|
+
resp.headers.set("x-solidrt-generation", String(state.generation));
|
|
3735
|
+
return resp;
|
|
3736
|
+
}
|
|
3737
|
+
if (path === "/assets" || path.startsWith("/assets/")) {
|
|
3738
|
+
if (!config.projectDir)
|
|
3739
|
+
return new Response("Not found", { status: 404 });
|
|
3740
|
+
return handleFiles(req, path, config.projectDir);
|
|
3741
|
+
}
|
|
3742
|
+
if (path.startsWith("/isolates/")) {
|
|
3743
|
+
return handleFiles(req, path, config.cacheDir);
|
|
3744
|
+
}
|
|
3745
|
+
return handleFiles(req, path, config.sourceDir);
|
|
3746
|
+
}
|
|
3747
|
+
function text(value) {
|
|
3748
|
+
return typeof value === "string" ? value : null;
|
|
3749
|
+
}
|
|
3750
|
+
function onOpen(ws) {
|
|
3751
|
+
let id = state.nextClientId++;
|
|
3752
|
+
state.clients.set(ws, {
|
|
3753
|
+
platform: "unknown",
|
|
3754
|
+
version: "unknown",
|
|
3755
|
+
profile: "unknown",
|
|
3756
|
+
id,
|
|
3757
|
+
capabilities: [],
|
|
3758
|
+
queries: [],
|
|
3759
|
+
stats: state.stats,
|
|
3760
|
+
timeScale: 1,
|
|
3761
|
+
clientDir: null,
|
|
3762
|
+
pid: null,
|
|
3763
|
+
execPath: null,
|
|
3764
|
+
host: null,
|
|
3765
|
+
os: null,
|
|
3766
|
+
kernel: null,
|
|
3767
|
+
videoDriver: null,
|
|
3768
|
+
gpu: null
|
|
3769
|
+
});
|
|
3770
|
+
console.log(`[cli] Client connected ${ws.remoteAddr ?? "unknown"}`);
|
|
3771
|
+
ws.send(JSON.stringify({ type: "welcome", address: state.serverUrl, stats: state.stats, capture: !!config.capture, mute: state.userInputMuted }));
|
|
3772
|
+
if (state.currentReload) {
|
|
3773
|
+
ws.send(state.currentReload);
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
function onClose(ws) {
|
|
3777
|
+
let info = state.clients.get(ws);
|
|
3778
|
+
state.clients.delete(ws);
|
|
3779
|
+
console.log(`[cli] Client disconnected: ${info?.platform ?? "unknown"}`);
|
|
3780
|
+
if (config.client && localClientExited && state.clients.size === 0)
|
|
3781
|
+
shutdown();
|
|
3782
|
+
}
|
|
3783
|
+
function onMessage(ws, msg) {
|
|
3784
|
+
try {
|
|
3785
|
+
let data = JSON.parse(typeof msg === "string" ? msg : new TextDecoder().decode(msg));
|
|
3786
|
+
if (data.type === "info") {
|
|
3787
|
+
let existing = state.clients.get(ws);
|
|
3788
|
+
state.clients.set(ws, {
|
|
3789
|
+
platform: data.platform ?? "unknown",
|
|
3790
|
+
version: data.version ?? "unknown",
|
|
3791
|
+
profile: data.profile ?? "unknown",
|
|
3792
|
+
id: existing?.id ?? state.nextClientId++,
|
|
3793
|
+
capabilities: Array.isArray(data.capabilities) ? data.capabilities.map(String) : [],
|
|
3794
|
+
queries: Array.isArray(data.queries) ? data.queries.map(String) : [],
|
|
3795
|
+
stats: existing?.stats ?? state.stats,
|
|
3796
|
+
timeScale: existing?.timeScale ?? 1,
|
|
3797
|
+
clientDir: text(data.clientDir),
|
|
3798
|
+
pid: typeof data.pid === "number" ? data.pid : null,
|
|
3799
|
+
execPath: text(data.execPath),
|
|
3800
|
+
host: text(data.host),
|
|
3801
|
+
os: text(data.os),
|
|
3802
|
+
kernel: text(data.kernel),
|
|
3803
|
+
videoDriver: text(data.videoDriver),
|
|
3804
|
+
gpu: data.gpu && typeof data.gpu === "object" ? { vendor: text(data.gpu.vendor) ?? "", renderer: text(data.gpu.renderer) ?? "", version: text(data.gpu.version) ?? "" } : null
|
|
3805
|
+
});
|
|
3806
|
+
console.log(`[cli] Client info ${ws.remoteAddr ?? "unknown"} ${data.platform} (${data.version})`);
|
|
3807
|
+
} else if (data.type === "log") {
|
|
3808
|
+
let device = state.clients.get(ws)?.id ?? -1;
|
|
3809
|
+
appendLog(device, String(data.level ?? "log"), String(data.text ?? ""));
|
|
3810
|
+
} else if (data.type === "result") {
|
|
3811
|
+
resolveQuery(data);
|
|
3812
|
+
} else if (data.type === "capture" && config.capture) {
|
|
3813
|
+
let device = state.clients.get(ws)?.id ?? -1;
|
|
3814
|
+
let at = Date.now() - state.captureStartMs;
|
|
3815
|
+
let after = at - state.captureLastAt;
|
|
3816
|
+
state.captureLastAt = at;
|
|
3817
|
+
let line = JSON.stringify({ after, type: data.kind, key: data.key, device }) + `
|
|
3818
|
+
`;
|
|
3819
|
+
state.captureChain = state.captureChain.then(() => file7(config.capture).append(line));
|
|
3820
|
+
}
|
|
3821
|
+
} catch {}
|
|
3822
|
+
}
|
|
3823
|
+
var DEFAULT_PORT = 34884;
|
|
3824
|
+
var PORT_TRIES = 100;
|
|
3825
|
+
var host = config.lan ? "0.0.0.0" : "127.0.0.1";
|
|
3826
|
+
var remembered = config.port ?? await rememberedPort(config.serverDir);
|
|
3827
|
+
var tunnel = config.tunnel ? await createTunnelEndpoint(remembered, config.serverDir) : null;
|
|
3828
|
+
function bind(port) {
|
|
3829
|
+
return serve({
|
|
3830
|
+
host,
|
|
3831
|
+
port,
|
|
3832
|
+
p2p: tunnel ? { endpoint: tunnel, protocol: TUNNEL_PROTOCOL } : undefined,
|
|
3833
|
+
fetch: handleRequest,
|
|
3834
|
+
websocket: { open: onOpen, close: onClose, message: onMessage }
|
|
3835
|
+
});
|
|
3836
|
+
}
|
|
3837
|
+
async function bindFirstFree() {
|
|
3838
|
+
if (config.port !== undefined)
|
|
3839
|
+
return bind(config.port);
|
|
3840
|
+
let candidates = remembered !== null ? [remembered] : [];
|
|
3841
|
+
for (let p = DEFAULT_PORT;p < DEFAULT_PORT + PORT_TRIES; p++)
|
|
3842
|
+
candidates.push(p);
|
|
3843
|
+
let last = null;
|
|
3844
|
+
for (let port of candidates) {
|
|
3845
|
+
if (await probe("127.0.0.1", port, { timeoutMs: 200 }) !== "closed")
|
|
3846
|
+
continue;
|
|
3847
|
+
try {
|
|
3848
|
+
return bind(port);
|
|
3849
|
+
} catch (e) {
|
|
3850
|
+
last = e;
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
throw last ?? new Error(`No free port between ${DEFAULT_PORT} and ${DEFAULT_PORT + PORT_TRIES - 1}`);
|
|
3854
|
+
}
|
|
3855
|
+
var server = await bindFirstFree();
|
|
3856
|
+
var address = config.lan ? config.address : "127.0.0.1";
|
|
3857
|
+
state.serverUrl = `${address}:${server.port}`;
|
|
3858
|
+
await writeRecord(config, server.port, address);
|
|
3859
|
+
if (config.lan && !config.tunnel) {
|
|
3860
|
+
console.log("");
|
|
3861
|
+
printQr(state.serverUrl);
|
|
3862
|
+
console.log("");
|
|
3863
|
+
}
|
|
3864
|
+
console.log(`[cli] Dev server on http://${state.serverUrl} serving ${config.mode} ${config.key}`);
|
|
3865
|
+
var keepalive = setInterval(() => {
|
|
3866
|
+
for (let ws of state.clients.keys()) {
|
|
3867
|
+
ws.ping();
|
|
3868
|
+
}
|
|
3869
|
+
}, 5000);
|
|
3870
|
+
var shuttingDown = false;
|
|
3871
|
+
var stopRepl = () => {};
|
|
3872
|
+
var localClient = null;
|
|
3873
|
+
var localClientExited = false;
|
|
3874
|
+
var signalOffs = ["SIGINT", "SIGTERM"].map((signal) => onSignal(signal, () => {
|
|
3875
|
+
shutdown();
|
|
3876
|
+
}));
|
|
3877
|
+
async function shutdown() {
|
|
3878
|
+
if (shuttingDown)
|
|
3879
|
+
return;
|
|
3880
|
+
shuttingDown = true;
|
|
3881
|
+
clearInterval(keepalive);
|
|
3882
|
+
for (let off of signalOffs)
|
|
3883
|
+
off();
|
|
3884
|
+
stopRepl();
|
|
3885
|
+
stopWatcher();
|
|
3886
|
+
await removeRecord(config.serverDir);
|
|
3887
|
+
if (localClient)
|
|
3888
|
+
localClient.kill();
|
|
3889
|
+
server.close();
|
|
3890
|
+
if (tunnel)
|
|
3891
|
+
await tunnel.close();
|
|
3892
|
+
}
|
|
3893
|
+
async function pump(stream, print) {
|
|
3894
|
+
let decoder = new TextDecoder;
|
|
3895
|
+
let rest = "";
|
|
3896
|
+
for await (let chunk of stream) {
|
|
3897
|
+
rest += decoder.decode(chunk, { stream: true });
|
|
3898
|
+
let lines = rest.split(`
|
|
3899
|
+
`);
|
|
3900
|
+
rest = lines.pop() ?? "";
|
|
3901
|
+
for (let line of lines)
|
|
3902
|
+
print(line);
|
|
3903
|
+
}
|
|
3904
|
+
if (rest)
|
|
3905
|
+
print(rest);
|
|
3906
|
+
}
|
|
3907
|
+
console.log("[cli] Bundling (development)");
|
|
3908
|
+
var buildError = await rebuildAndBroadcast();
|
|
3909
|
+
if (buildError) {
|
|
3910
|
+
console.error(buildError);
|
|
3911
|
+
showBuildFailure();
|
|
3912
|
+
}
|
|
3913
|
+
console.log("[cli] Reload on save is on (pause it with the MCP pause_watch tool)");
|
|
3914
|
+
stopRepl = startRepl(shutdown);
|
|
3915
|
+
if (!config.entry.endsWith(".srt.js")) {
|
|
3916
|
+
let check = command2(config.srt[0], [...config.srt.slice(1), "check", config.entry], { cwd: config.cwd }).spawn();
|
|
3917
|
+
pump(check.stdout, (line) => console.log(line));
|
|
3918
|
+
pump(check.stderr, (line) => console.error(line));
|
|
3919
|
+
}
|
|
3920
|
+
if (config.client) {
|
|
3921
|
+
let child = command2(config.client.cmd, [...config.client.args, "--dev-server", `127.0.0.1:${server.port}`]).spawn();
|
|
3922
|
+
localClient = child;
|
|
3923
|
+
pump(child.stdout, (line) => console.log(line));
|
|
3924
|
+
pump(child.stderr, (line) => console.error(line));
|
|
3925
|
+
child.status().then(() => {
|
|
3926
|
+
localClient = null;
|
|
3927
|
+
localClientExited = true;
|
|
3928
|
+
if (shuttingDown)
|
|
3929
|
+
return;
|
|
3930
|
+
if (state.clients.size === 0) {
|
|
3931
|
+
shutdown();
|
|
3932
|
+
} else {
|
|
3933
|
+
console.log(`[cli] Local client exited, ${state.clients.size} remote client(s) still connected`);
|
|
3934
|
+
}
|
|
3935
|
+
});
|
|
3936
|
+
}
|