wasm-quickjs-dom 0.0.0-stage → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/limits.js ADDED
@@ -0,0 +1,114 @@
1
+ const MAX_REDIRECTS = 10;
2
+ /** True for localhost and private, loopback and link-local addresses (`host` as `URL.hostname` gives it). */
3
+ export function isPrivateHost(host) {
4
+ const h = host.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
5
+ if (h === "localhost" || h.endsWith(".localhost") || h.endsWith(".local") || h.endsWith(".internal"))
6
+ return true;
7
+ const v4 = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(h);
8
+ if (v4) {
9
+ const a = Number(v4[1]);
10
+ const b = Number(v4[2]);
11
+ return a === 0 || a === 10 || a === 127 || (a === 169 && b === 254) || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 100 && b >= 64 && b <= 127) || a >= 224;
12
+ }
13
+ if (h.includes(":")) {
14
+ if (h === "::" || h === "::1" || /^f[cd]/.test(h) || /^fe[89ab]/.test(h))
15
+ return true;
16
+ const dotted = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(h);
17
+ if (dotted)
18
+ return isPrivateHost(dotted[1]);
19
+ // `new URL` writes an IPv4-mapped address in hex: ::ffff:7f00:1
20
+ const hex = /^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/.exec(h);
21
+ if (hex) {
22
+ const n = (parseInt(hex[1], 16) << 16) | parseInt(hex[2], 16);
23
+ return isPrivateHost([n >>> 24, (n >>> 16) & 255, (n >>> 8) & 255, n & 255].join("."));
24
+ }
25
+ }
26
+ return false;
27
+ }
28
+ /** `fetch` and body reading that obey {@link FetchLimits}. With no limits they are plain `fetch` and `arrayBuffer`. */
29
+ export function createGuard(limits = {}) {
30
+ const restricted = !!(limits.allowedProtocols || limits.allowedHosts || limits.blockedHosts || limits.blockPrivateHosts);
31
+ let requests = 0;
32
+ let total = 0;
33
+ const check = (url) => {
34
+ const deny = (why) => {
35
+ throw new Error(`Blocked by the fetch limits (${why}): ${url.href}`);
36
+ };
37
+ if (limits.allowedProtocols && !limits.allowedProtocols.includes(url.protocol))
38
+ deny(`protocol ${url.protocol}`);
39
+ const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
40
+ if (limits.allowedHosts && !limits.allowedHosts.includes(host))
41
+ deny("host not allowed");
42
+ if (limits.blockedHosts && limits.blockedHosts.includes(host))
43
+ deny("host blocked");
44
+ if (limits.blockPrivateHosts && isPrivateHost(host))
45
+ deny("private host");
46
+ };
47
+ return {
48
+ /** Fetch `input`; with host or protocol limits every redirect is checked before it is followed. */
49
+ async fetch(input, init = {}) {
50
+ if (limits.maxResourceCount !== undefined && ++requests > limits.maxResourceCount) {
51
+ throw new Error(`Blocked by the fetch limits (more than ${limits.maxResourceCount} requests): ${String(input)}`);
52
+ }
53
+ let url = new URL(input);
54
+ let method = init.method;
55
+ for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
56
+ check(url);
57
+ if (!restricted)
58
+ return fetch(url, init);
59
+ const res = await fetch(url, { ...init, method, redirect: "manual" });
60
+ if (res.type === "opaqueredirect")
61
+ throw new Error(`Blocked by the fetch limits (this runtime hides the redirect target, so it cannot be checked): ${url.href}`);
62
+ if (res.status >= 300 && res.status < 400 && res.headers.has("location")) {
63
+ await res.body?.cancel();
64
+ url = new URL(res.headers.get("location"), url);
65
+ if (res.status === 303 || ((res.status === 301 || res.status === 302) && method === "POST"))
66
+ method = "GET";
67
+ continue;
68
+ }
69
+ return res;
70
+ }
71
+ throw new Error(`Blocked by the fetch limits (more than ${MAX_REDIRECTS} redirects): ${String(input)}`);
72
+ },
73
+ /** The body of `res`, refused when it is over the size limits. */
74
+ async bytes(res) {
75
+ const max = limits.maxResourceBytes;
76
+ const tooBig = () => new Error(`Blocked by the fetch limits (a response is over ${max} bytes): ${res.url}`);
77
+ if (max !== undefined && Number(res.headers.get("content-length")) > max) {
78
+ await res.body?.cancel();
79
+ throw tooBig();
80
+ }
81
+ let bytes;
82
+ if (max !== undefined && res.body) {
83
+ const reader = res.body.getReader();
84
+ const chunks = [];
85
+ let size = 0;
86
+ for (;;) {
87
+ const { done, value } = await reader.read();
88
+ if (done)
89
+ break;
90
+ size += value.length;
91
+ if (size > max) {
92
+ await reader.cancel();
93
+ throw tooBig();
94
+ }
95
+ chunks.push(value);
96
+ }
97
+ bytes = new Uint8Array(size);
98
+ let at = 0;
99
+ for (const c of chunks) {
100
+ bytes.set(c, at);
101
+ at += c.length;
102
+ }
103
+ }
104
+ else {
105
+ bytes = new Uint8Array(await res.arrayBuffer());
106
+ }
107
+ total += bytes.length;
108
+ if (limits.maxTotalResourceBytes !== undefined && total > limits.maxTotalResourceBytes) {
109
+ throw new Error(`Blocked by the fetch limits (responses together are over ${limits.maxTotalResourceBytes} bytes): ${res.url}`);
110
+ }
111
+ return bytes;
112
+ },
113
+ };
114
+ }
@@ -0,0 +1 @@
1
+ export default function defaultQuickJSWasm(): Promise<WebAssembly.Module | undefined>;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `#quickjs-wasm` on Vercel Edge and the Next.js Edge runtime (the `edge-light` condition of `imports` in package.json):
3
+ * their bundlers turn `import x from "….wasm?module"` into a pre-compiled `WebAssembly.Module`, since the runtime
4
+ * cannot compile WebAssembly from bytes it fetches.
5
+ */
6
+ import quickjsWasm from "../dist/quickjs.wasm?module";
7
+ export default function defaultQuickJSWasm() {
8
+ return Promise.resolve(quickjsWasm);
9
+ }
@@ -0,0 +1 @@
1
+ export default function defaultQuickJSWasm(): Promise<WebAssembly.Module | undefined>;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `#quickjs-wasm` on Node.js and Bun (the `node` condition of `imports` in package.json): the package's own
3
+ * `quickjs.wasm`, read from beside this file and compiled once. `undefined` when it cannot be read (the library
4
+ * then loads its own copy on every run).
5
+ */
6
+ let compiled;
7
+ export default function defaultQuickJSWasm() {
8
+ compiled ??= (async () => {
9
+ try {
10
+ const fs = "node:fs/promises", url = "node:url"; // not literals: a bundler must not try to resolve them
11
+ const file = (await import(/* @vite-ignore */ url)).fileURLToPath(new URL("./quickjs.wasm", import.meta.url));
12
+ const bytes = await (await import(/* @vite-ignore */ fs)).readFile(file);
13
+ return await WebAssembly.compile(bytes);
14
+ }
15
+ catch {
16
+ return undefined;
17
+ }
18
+ })();
19
+ return compiled;
20
+ }
@@ -0,0 +1,2 @@
1
+ declare const quickjsWasm: WebAssembly.Module;
2
+ export default quickjsWasm;
@@ -0,0 +1 @@
1
+ export default function defaultQuickJSWasm(): Promise<WebAssembly.Module | undefined>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `#quickjs-wasm` for Deno, browsers, Vercel Edge and anything else (the default of `imports` in package.json):
3
+ * `quickjs.wasm` is fetched from beside this file and compiled once. `undefined` when that fails (the library then
4
+ * loads its own copy on every run).
5
+ */
6
+ let compiled;
7
+ export default function defaultQuickJSWasm() {
8
+ compiled ??= (async () => {
9
+ try {
10
+ const url = new URL("./quickjs.wasm", import.meta.url);
11
+ if (url.protocol === "file:") {
12
+ const read = globalThis.Deno?.readFile;
13
+ if (read)
14
+ return await WebAssembly.compile((await read(url)));
15
+ }
16
+ return await WebAssembly.compile(await (await fetch(url)).arrayBuffer());
17
+ }
18
+ catch {
19
+ return undefined;
20
+ }
21
+ })();
22
+ return compiled;
23
+ }
@@ -0,0 +1 @@
1
+ export default function defaultQuickJSWasm(): Promise<WebAssembly.Module | undefined>;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `#quickjs-wasm` on Cloudflare Workers (the `workerd` condition of `imports` in package.json): the wasm is imported
3
+ * as a pre-compiled `WebAssembly.Module`, because workerd cannot compile WebAssembly at run time.
4
+ */
5
+ import quickjsWasm from "../dist/quickjs.wasm";
6
+ export default function defaultQuickJSWasm() {
7
+ return Promise.resolve(quickjsWasm);
8
+ }
Binary file
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The host side of csrc/qjsdom.c: one QuickJS runtime in one instance of `quickjs.wasm`.
3
+ *
4
+ * Plain WebAssembly (`new WebAssembly.Instance(module, imports)`), no Emscripten glue, so it works wherever a compiled
5
+ * module can be instantiated: Cloudflare Workers, Vercel Edge, Deno, Bun, Node.js, browsers. Every page gets an
6
+ * instance of its own; dropping the instance frees everything.
7
+ */
8
+ /** What the page's shims call out to (`__host` in the guest). */
9
+ export interface QuickJSHost {
10
+ log(level: string, message: string): void;
11
+ timer(id: number, ms: number): void;
12
+ /** A promise was rejected and nothing handled it. */
13
+ rejection(message: string): void;
14
+ /** The page called `__host.fetch(target, method)`; answer later with {@link QuickJSInstance.settle}. */
15
+ fetch(id: number, target: string, method: string): void;
16
+ /**
17
+ * Module requests. kind 0: the specifier of `name` as imported from `base`. kind 1: the source of module `name`
18
+ * (`base` is the name). `undefined` when there is none.
19
+ */
20
+ module(kind: 0 | 1, base: string, name: string): string | undefined;
21
+ }
22
+ /** What an evaluation produced: its type decides how `value` reads. */
23
+ export type EvalResult = {
24
+ type: "undefined";
25
+ } | {
26
+ type: "string";
27
+ value: string;
28
+ } | {
29
+ type: "json";
30
+ value: string;
31
+ } | {
32
+ type: "error";
33
+ value: string;
34
+ };
35
+ export declare class QuickJSInstance {
36
+ private readonly x;
37
+ private constructor();
38
+ /**
39
+ * Instantiate `wasm` and start a runtime in it. `memoryLimit` is what QuickJS may allocate, `stackLimit` what a script
40
+ * may recurse into (bytes).
41
+ *
42
+ * With `warm` (the same function every time for a module), the instance is started by it once; its memory is then
43
+ * kept, and every later instance is that memory copied into a fresh instance. `warm` must leave nothing of a page
44
+ * behind: no host callbacks pending, nothing that depends on when it ran.
45
+ */
46
+ static create(wasm: WebAssembly.Module, host: QuickJSHost, memoryLimit: number, stackLimit: number, warm?: (instance: QuickJSInstance) => void): QuickJSInstance;
47
+ /** Put a string in wasm memory, NUL-terminated; `use` gets its pointer and byte length. */
48
+ private withString;
49
+ private result;
50
+ /** Script running past this wall-clock time (ms since the epoch) is interrupted; 0 = no limit. */
51
+ setDeadline(ms: number): void;
52
+ /** Evaluate code (as a module when asked); a script error comes back as `{ type: "error" }`. */
53
+ eval(code: string, name: string, module?: boolean): EvalResult;
54
+ /** Run bytecode that {@link QuickJSInstance.compile} made with this very build of QuickJS. */
55
+ evalBytecode(bytes: Uint8Array): EvalResult;
56
+ /** Compile code to bytecode (build time). */
57
+ compile(code: string, name: string): Uint8Array;
58
+ /** Run the queued promise jobs. */
59
+ jobs(): number;
60
+ /** Resolve (or reject) the promise of fetch `id` with a string. */
61
+ settle(id: number, ok: boolean, text: string): void;
62
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * The host side of csrc/qjsdom.c: one QuickJS runtime in one instance of `quickjs.wasm`.
3
+ *
4
+ * Plain WebAssembly (`new WebAssembly.Instance(module, imports)`), no Emscripten glue, so it works wherever a compiled
5
+ * module can be instantiated: Cloudflare Workers, Vercel Edge, Deno, Bun, Node.js, browsers. Every page gets an
6
+ * instance of its own; dropping the instance frees everything.
7
+ */
8
+ const encoder = new TextEncoder();
9
+ const decoder = new TextDecoder();
10
+ // What a started instance looks like in memory, per compiled module, so the next one is a copy instead of a start
11
+ // (see QuickJSInstance.create): starting the guest program costs more than copying a few megabytes.
12
+ const snapshots = new WeakMap();
13
+ function takeSnapshot(memory) {
14
+ const words = new Uint32Array(memory.buffer);
15
+ let end = words.length;
16
+ while (end > 0 && words[end - 1] === 0)
17
+ end--; // the rest is still zero in a new instance
18
+ return new Uint8Array(memory.buffer.slice(0, end * 4));
19
+ }
20
+ const RESULT_TYPES = ["undefined", "string", "json", "error"];
21
+ export class QuickJSInstance {
22
+ x;
23
+ constructor(x) {
24
+ this.x = x;
25
+ }
26
+ /**
27
+ * Instantiate `wasm` and start a runtime in it. `memoryLimit` is what QuickJS may allocate, `stackLimit` what a script
28
+ * may recurse into (bytes).
29
+ *
30
+ * With `warm` (the same function every time for a module), the instance is started by it once; its memory is then
31
+ * kept, and every later instance is that memory copied into a fresh instance. `warm` must leave nothing of a page
32
+ * behind: no host callbacks pending, nothing that depends on when it ran.
33
+ */
34
+ static create(wasm, host, memoryLimit, stackLimit, warm) {
35
+ let x;
36
+ const u8 = () => new Uint8Array(x.memory.buffer);
37
+ const text = (ptr, len) => decoder.decode(u8().subarray(ptr, ptr + len));
38
+ const imports = {
39
+ host: {
40
+ call(op, n1, n2, a, alen, b, blen) {
41
+ if (op === 0)
42
+ host.log(text(a, alen), text(b, blen));
43
+ else if (op === 1)
44
+ host.timer(n1, n2);
45
+ else if (op === 2)
46
+ host.fetch(n1, text(a, alen), text(b, blen));
47
+ else if (op === 3)
48
+ host.rejection(text(a, alen));
49
+ },
50
+ module(kind, a, alen, b, blen) {
51
+ const out = host.module(kind, text(a, alen), text(b, blen));
52
+ if (out === undefined)
53
+ return -1;
54
+ const bytes = encoder.encode(out);
55
+ const ptr = x.qd_scratch(bytes.length);
56
+ u8().set(bytes, ptr);
57
+ return bytes.length;
58
+ },
59
+ },
60
+ wasi_snapshot_preview1: {
61
+ // the C library asks for a clock, an empty environment and a way to write that goes nowhere
62
+ clock_time_get(_id, _precision, out) {
63
+ new DataView(x.memory.buffer).setBigUint64(out, BigInt(Date.now()) * 1000000n, true);
64
+ return 0;
65
+ },
66
+ environ_sizes_get(count, size) {
67
+ const view = new DataView(x.memory.buffer);
68
+ view.setUint32(count, 0, true);
69
+ view.setUint32(size, 0, true);
70
+ return 0;
71
+ },
72
+ environ_get: () => 0,
73
+ fd_close: () => 0,
74
+ fd_write(_fd, iovs, iovsLen, written) {
75
+ const view = new DataView(x.memory.buffer);
76
+ let total = 0;
77
+ for (let i = 0; i < iovsLen; i++)
78
+ total += view.getUint32(iovs + i * 8 + 4, true);
79
+ view.setUint32(written, total, true);
80
+ return 0;
81
+ },
82
+ proc_exit(code) {
83
+ throw new Error(`QuickJS exited (${code})`);
84
+ },
85
+ },
86
+ env: { emscripten_notify_memory_growth: () => { } },
87
+ };
88
+ // another Emscripten version may ask for more of the C library's system calls than these: say "not implemented"
89
+ for (const i of WebAssembly.Module.imports(wasm)) {
90
+ if (i.module === "wasi_snapshot_preview1" && !(i.name in imports.wasi_snapshot_preview1))
91
+ imports.wasi_snapshot_preview1[i.name] = () => 52;
92
+ }
93
+ const wanted = WebAssembly.Module.imports(wasm).filter((i) => !(i.name in (imports[i.module] ?? {})));
94
+ if (wanted.length)
95
+ throw new Error(`quickjs.wasm needs imports this runtime does not provide: ${wanted.map((i) => `${i.module}.${i.name}`).join(", ")}`);
96
+ x = new WebAssembly.Instance(wasm, imports).exports;
97
+ const key = `${memoryLimit}:${stackLimit}`;
98
+ const saved = warm ? snapshots.get(wasm) : undefined;
99
+ if (saved && saved.key === key) {
100
+ const pages = Math.ceil(saved.bytes.length / 65536);
101
+ const have = x.memory.buffer.byteLength / 65536;
102
+ if (pages > have)
103
+ x.memory.grow(pages - have);
104
+ new Uint8Array(x.memory.buffer).set(saved.bytes);
105
+ return new QuickJSInstance(x);
106
+ }
107
+ x._initialize?.();
108
+ if (x.qd_init(memoryLimit, stackLimit) !== 0)
109
+ throw new Error("The QuickJS runtime could not be created.");
110
+ const instance = new QuickJSInstance(x);
111
+ if (warm) {
112
+ warm(instance);
113
+ snapshots.set(wasm, { key, bytes: takeSnapshot(x.memory) });
114
+ }
115
+ return instance;
116
+ }
117
+ /** Put a string in wasm memory, NUL-terminated; `use` gets its pointer and byte length. */
118
+ withString(s, use) {
119
+ const bytes = encoder.encode(s);
120
+ const ptr = this.x.qd_alloc(bytes.length + 1);
121
+ const view = new Uint8Array(this.x.memory.buffer);
122
+ view.set(bytes, ptr);
123
+ view[ptr + bytes.length] = 0;
124
+ try {
125
+ return use(ptr, bytes.length);
126
+ }
127
+ finally {
128
+ this.x.qd_free(ptr);
129
+ }
130
+ }
131
+ result() {
132
+ const type = RESULT_TYPES[this.x.qd_result_type()] ?? "undefined";
133
+ if (type === "undefined")
134
+ return { type };
135
+ const ptr = this.x.qd_result_ptr();
136
+ return { type, value: decoder.decode(new Uint8Array(this.x.memory.buffer, ptr, this.x.qd_result_len())) };
137
+ }
138
+ /** Script running past this wall-clock time (ms since the epoch) is interrupted; 0 = no limit. */
139
+ setDeadline(ms) {
140
+ this.x.qd_set_deadline(ms);
141
+ }
142
+ /** Evaluate code (as a module when asked); a script error comes back as `{ type: "error" }`. */
143
+ eval(code, name, module = false) {
144
+ return this.withString(code, (c, len) => this.withString(name, (n) => (this.x.qd_eval(c, len, n, module ? 1 : 0), this.result())));
145
+ }
146
+ /** Run bytecode that {@link QuickJSInstance.compile} made with this very build of QuickJS. */
147
+ evalBytecode(bytes) {
148
+ const ptr = this.x.qd_alloc(bytes.length);
149
+ new Uint8Array(this.x.memory.buffer).set(bytes, ptr);
150
+ try {
151
+ const failed = this.x.qd_eval_bytecode(ptr, bytes.length);
152
+ return failed ? this.result() : { type: "undefined" };
153
+ }
154
+ finally {
155
+ this.x.qd_free(ptr);
156
+ }
157
+ }
158
+ /** Compile code to bytecode (build time). */
159
+ compile(code, name) {
160
+ return this.withString(code, (c, len) => this.withString(name, (n) => {
161
+ if (this.x.qd_compile(c, len, n))
162
+ throw new Error(`QuickJS could not compile ${name}: ${this.result().value}`);
163
+ return new Uint8Array(this.x.memory.buffer, this.x.qd_result_ptr(), this.x.qd_result_len()).slice();
164
+ }));
165
+ }
166
+ /** Run the queued promise jobs. */
167
+ jobs() {
168
+ return this.x.qd_jobs();
169
+ }
170
+ /** Resolve (or reject) the promise of fetch `id` with a string. */
171
+ settle(id, ok, text) {
172
+ this.withString(text, (ptr, len) => this.x.qd_settle(id, ok ? 1 : 0, ptr, len));
173
+ }
174
+ }
package/package.json CHANGED
@@ -1,6 +1,74 @@
1
1
  {
2
2
  "name": "wasm-quickjs-dom",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.3",
4
+ "description": "A small JSDOM for runtimes without Node or eval: QuickJS-ng (WebAssembly) on a linkedom DOM, for Cloudflare Workers, Vercel Edge, Deno, Bun and browsers",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "wasm-quickjs-dom": "./dist/bin.js"
10
+ },
11
+ "author": "SoraKumo <info@croud.jp>",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/SoraKumo001/wasm-html-to-image.git",
15
+ "directory": "packages/quickjs-dom"
16
+ },
17
+ "homepage": "https://sorakumo001.github.io/wasm-html-to-image/master/docs/",
18
+ "bugs": {
19
+ "url": "https://github.com/SoraKumo001/wasm-html-to-image/issues"
20
+ },
21
+ "keywords": [
22
+ "jsdom",
23
+ "dom",
24
+ "quickjs",
25
+ "linkedom",
26
+ "wasm",
27
+ "webassembly",
28
+ "cloudflare-workers",
29
+ "edge-runtime"
30
+ ],
31
+ "engines": {
32
+ "node": ">=20.0.0"
33
+ },
34
+ "imports": {
35
+ "#quickjs-wasm": {
36
+ "workerd": "./dist/quickjs-workerd.js",
37
+ "edge-light": "./dist/quickjs-edge-light.js",
38
+ "deno": "./dist/quickjs-web.js",
39
+ "node": "./dist/quickjs-node.js",
40
+ "bun": "./dist/quickjs-node.js",
41
+ "default": "./dist/quickjs-web.js"
42
+ }
43
+ },
44
+ "exports": {
45
+ ".": {
46
+ "types": "./dist/index.d.ts",
47
+ "import": "./dist/index.js",
48
+ "default": "./dist/index.js"
49
+ },
50
+ "./quickjs.wasm": {
51
+ "types": "./dist/quickjs-wasm.d.ts",
52
+ "default": "./dist/quickjs.wasm"
53
+ }
54
+ },
55
+ "files": [
56
+ "dist",
57
+ "vendor/quickjs/LICENSE",
58
+ "THIRD-PARTY-NOTICES.md",
59
+ "README.md",
60
+ "LICENSE"
61
+ ],
62
+ "license": "MIT",
63
+ "devDependencies": {
64
+ "@types/node": "26.6.5",
65
+ "linkedom": "0.18.13",
66
+ "typescript": "^7.0.2",
67
+ "vitest": "^5.0.5",
68
+ "web-streams-polyfill": "4.3.0"
69
+ },
70
+ "scripts": {
71
+ "build": "node scripts/build-quickjs.mjs && tsc -b && node scripts/build-edge-js.mjs",
72
+ "test": "vitest run"
73
+ }
6
74
  }
@@ -0,0 +1,24 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017-2026 Fabrice Bellard
4
+ Copyright (c) 2017-2024 Charlie Gordon
5
+ Copyright (c) 2023-2026 Ben Noordhuis
6
+ Copyright (c) 2023-2026 Saúl Ibarra Corretgé
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in
16
+ all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ THE SOFTWARE.