@fairfox/polly 0.71.0 → 0.72.0
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/src/client/index.js +2 -2
- package/dist/src/client/index.js.map +2 -2
- package/dist/src/polly-ui/ActionInput.d.ts +10 -1
- package/dist/src/polly-ui/ActionSelect.d.ts +35 -0
- package/dist/src/polly-ui/Cluster.d.ts +35 -0
- package/dist/src/polly-ui/Code.d.ts +17 -0
- package/dist/src/polly-ui/Text.d.ts +31 -0
- package/dist/src/polly-ui/index.css +278 -185
- package/dist/src/polly-ui/index.d.ts +5 -1
- package/dist/src/polly-ui/index.js +480 -284
- package/dist/src/polly-ui/index.js.map +11 -6
- package/dist/src/polly-ui/internal/dispatch-action.d.ts +13 -0
- package/dist/src/polly-ui/markdown.js +3 -3
- package/dist/src/polly-ui/markdown.js.map +2 -2
- package/dist/src/polly-ui/styles.css +288 -185
- package/dist/tools/quality/src/cli.js +6 -2
- package/dist/tools/quality/src/cli.js.map +3 -3
- package/dist/tools/quality/src/index.js +6 -2
- package/dist/tools/quality/src/index.js.map +3 -3
- package/dist/tools/test/src/browser/run.js +75 -44
- package/dist/tools/test/src/browser/run.js.map +3 -3
- package/dist/tools/test/src/visual/index.js +24 -24
- package/dist/tools/test/src/visual/index.js.map +2 -2
- package/dist/tools/verify/src/cli.js +82 -51
- package/dist/tools/verify/src/cli.js.map +7 -6
- package/package.json +1 -1
|
@@ -178,14 +178,18 @@ var signalingApp = new Elysia2().use(signalingServer({ path: "/polly/signaling"
|
|
|
178
178
|
console.log(`[browser-runner] signaling server on ws://127.0.0.1:${signalingPort}/polly/signaling`);
|
|
179
179
|
var browser = await puppeteer.launch({
|
|
180
180
|
headless,
|
|
181
|
-
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
181
|
+
args: ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
182
|
+
protocolTimeout: 30000
|
|
182
183
|
});
|
|
183
184
|
var totalPassed = 0;
|
|
184
185
|
var totalFailed = 0;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
186
|
+
function isProtocolError(err) {
|
|
187
|
+
return err instanceof Error && err.name === "ProtocolError";
|
|
188
|
+
}
|
|
189
|
+
function errMessage(err) {
|
|
190
|
+
return err instanceof Error ? err.message : String(err);
|
|
191
|
+
}
|
|
192
|
+
async function runFile(testFile) {
|
|
189
193
|
const buildResult = await Bun.build({
|
|
190
194
|
entrypoints: [testFile],
|
|
191
195
|
target: "browser",
|
|
@@ -202,14 +206,12 @@ for (const testFile of testFiles) {
|
|
|
202
206
|
for (const log of buildResult.logs) {
|
|
203
207
|
console.log(` ${log}`);
|
|
204
208
|
}
|
|
205
|
-
|
|
206
|
-
continue;
|
|
209
|
+
return { passed: 0, failed: 1 };
|
|
207
210
|
}
|
|
208
211
|
const jsText = await buildResult.outputs[0]?.text();
|
|
209
212
|
if (!jsText) {
|
|
210
213
|
console.log(" ❌ build produced no output");
|
|
211
|
-
|
|
212
|
-
continue;
|
|
214
|
+
return { passed: 0, failed: 1 };
|
|
213
215
|
}
|
|
214
216
|
const html = `<!DOCTYPE html>
|
|
215
217
|
<html><head><meta charset="utf-8"></head>
|
|
@@ -222,46 +224,75 @@ for (const testFile of testFiles) {
|
|
|
222
224
|
return new Response(html, { headers: { "Content-Type": "text/html" } });
|
|
223
225
|
}
|
|
224
226
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
let page;
|
|
228
|
+
try {
|
|
229
|
+
page = await browser.newPage();
|
|
230
|
+
page.on("console", (msg) => {
|
|
231
|
+
const text = msg.text();
|
|
232
|
+
if (text.includes("[test]")) {
|
|
233
|
+
console.log(` ${text}`);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
page.on("pageerror", (err) => {
|
|
237
|
+
console.log(` ❌ page error: ${errMessage(err)}`);
|
|
238
|
+
});
|
|
239
|
+
await page.goto(`http://127.0.0.1:${server.port}/`, { waitUntil: "domcontentloaded" });
|
|
240
|
+
const timeout = 15000;
|
|
241
|
+
const deadline = Date.now() + timeout;
|
|
242
|
+
let finished = false;
|
|
243
|
+
while (Date.now() < deadline) {
|
|
244
|
+
finished = await page.evaluate(() => window["__done"] === true);
|
|
245
|
+
if (finished)
|
|
246
|
+
break;
|
|
247
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
248
|
+
}
|
|
249
|
+
if (!finished) {
|
|
250
|
+
console.log(` ❌ timed out after ${timeout}ms`);
|
|
251
|
+
return { passed: 0, failed: 1 };
|
|
252
|
+
}
|
|
253
|
+
const results = await page.evaluate(() => window["__testResults"]);
|
|
254
|
+
let passed = 0;
|
|
255
|
+
let failed = 0;
|
|
256
|
+
for (const r of results ?? []) {
|
|
257
|
+
if (r.passed) {
|
|
258
|
+
console.log(` ✅ ${r.name}`);
|
|
259
|
+
passed += 1;
|
|
260
|
+
} else {
|
|
261
|
+
console.log(` ❌ ${r.name}: ${r.error}`);
|
|
262
|
+
failed += 1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return { passed, failed };
|
|
266
|
+
} finally {
|
|
267
|
+
if (page) {
|
|
268
|
+
await page.close().catch(() => {});
|
|
230
269
|
}
|
|
231
|
-
});
|
|
232
|
-
page.on("pageerror", (err) => {
|
|
233
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
234
|
-
console.log(` ❌ page error: ${msg}`);
|
|
235
|
-
});
|
|
236
|
-
await page.goto(`http://127.0.0.1:${server.port}/`, { waitUntil: "domcontentloaded" });
|
|
237
|
-
const timeout = 15000;
|
|
238
|
-
const deadline = Date.now() + timeout;
|
|
239
|
-
let finished = false;
|
|
240
|
-
while (Date.now() < deadline) {
|
|
241
|
-
finished = await page.evaluate(() => window["__done"] === true);
|
|
242
|
-
if (finished)
|
|
243
|
-
break;
|
|
244
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
245
|
-
}
|
|
246
|
-
if (!finished) {
|
|
247
|
-
console.log(` ❌ timed out after ${timeout}ms`);
|
|
248
|
-
totalFailed += 1;
|
|
249
|
-
await page.close();
|
|
250
270
|
server.stop();
|
|
251
|
-
continue;
|
|
252
271
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
272
|
+
}
|
|
273
|
+
for (const testFile of testFiles) {
|
|
274
|
+
const shortName = testFile.replace(`${testDir}/`, "");
|
|
275
|
+
console.log(`
|
|
276
|
+
[browser-runner] running ${shortName}`);
|
|
277
|
+
let result;
|
|
278
|
+
try {
|
|
279
|
+
result = await runFile(testFile);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
if (isProtocolError(err)) {
|
|
282
|
+
console.log(` ⚠️ protocol error (${errMessage(err)}) — retrying once on a fresh page`);
|
|
283
|
+
try {
|
|
284
|
+
result = await runFile(testFile);
|
|
285
|
+
} catch (retryErr) {
|
|
286
|
+
console.log(` ❌ retry failed: ${errMessage(retryErr)}`);
|
|
287
|
+
result = { passed: 0, failed: 1 };
|
|
288
|
+
}
|
|
258
289
|
} else {
|
|
259
|
-
console.log(` ❌ ${
|
|
260
|
-
|
|
290
|
+
console.log(` ❌ ${errMessage(err)}`);
|
|
291
|
+
result = { passed: 0, failed: 1 };
|
|
261
292
|
}
|
|
262
293
|
}
|
|
263
|
-
|
|
264
|
-
|
|
294
|
+
totalPassed += result.passed;
|
|
295
|
+
totalFailed += result.failed;
|
|
265
296
|
}
|
|
266
297
|
await browser.close();
|
|
267
298
|
signalingApp.server?.stop?.(true);
|
|
@@ -269,4 +300,4 @@ console.log(`
|
|
|
269
300
|
[browser-runner] ${totalPassed} passed, ${totalFailed} failed`);
|
|
270
301
|
process.exit(totalFailed > 0 ? 1 : 0);
|
|
271
302
|
|
|
272
|
-
//# debugId=
|
|
303
|
+
//# debugId=995635FD6B287CDC64756E2164756E21
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../tools/test/src/browser/run.ts", "../src/elysia/signaling-server-plugin.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"#!/usr/bin/env bun\n\n/**\n * Browser test runner for Polly applications.\n *\n * Finds all *.browser.ts files in a given directory, bundles each with\n * Bun.build for the browser target (with an internal Automerge WASM fix),\n * serves the bundle on an ephemeral port, opens a Puppeteer page, and\n * polls window.__done for results. Prints pass/fail per test and exits\n * non-zero if any test failed.\n *\n * A signalling server for WebRTC tests starts automatically on a random\n * port. The URL is injected into the bundle via process.env.SIGNALING_URL.\n *\n * Usage (from project root):\n *\n * bun tools/test/src/browser/run.ts [testDir] [filter]\n *\n * Examples:\n *\n * bun tools/test/src/browser/run.ts tests/browser\n * bun tools/test/src/browser/run.ts tests/browser mesh-webrtc\n * HEADLESS=false bun tools/test/src/browser/run.ts tests/browser\n *\n * When invoked without a testDir, defaults to tests/browser relative to cwd.\n */\n\nimport { resolve } from \"node:path\";\nimport { type BunPlugin, Glob } from \"bun\";\nimport { Elysia } from \"elysia\";\nimport puppeteer from \"puppeteer\";\nimport { signalingServer } from \"../../../../src/elysia/signaling-server-plugin\";\n\n// Automerge WASM fix\n// Bun.build's target: \"browser\" picks Automerge's fullfat_bundler.js which\n// does a static .wasm import that Bun can't wire up. Redirect to the\n// base64 variant which embeds the WASM as a string and self-initialises.\n\nconst automergeBase64Path = resolve(\n process.cwd(),\n \"node_modules/@automerge/automerge/dist/mjs/entrypoints/fullfat_base64.js\"\n);\n\nconst automergeBase64Plugin: BunPlugin = {\n name: \"automerge-base64\",\n setup(build) {\n build.onResolve({ filter: /^@automerge\\/automerge(\\/slim)?$/ }, () => {\n return { path: automergeBase64Path };\n });\n },\n};\n\n// Argument parsing\n\nconst testDir = resolve(process.cwd(), process.argv[2] ?? \"tests/browser\");\nconst filter = process.argv[3] ?? \"\";\nconst headless = process.env[\"HEADLESS\"] !== \"false\";\n\nconst glob = new Glob(\"**/*.browser.{ts,tsx}\");\nconst testFiles: string[] = [];\nfor await (const file of glob.scan({ cwd: testDir, absolute: true })) {\n if (file.includes(\"harness\")) continue;\n if (filter && !file.includes(filter)) continue;\n testFiles.push(file);\n}\n\nif (testFiles.length === 0) {\n console.log(`[browser-runner] no test files found${filter ? ` matching \"${filter}\"` : \"\"}`);\n process.exit(0);\n}\n\nconsole.log(`[browser-runner] found ${testFiles.length} test file(s)`);\n\n// Start server-side infrastructure\n\nconst signalingPort = 39000 + Math.floor(Math.random() * 1000);\nconst signalingApp = new Elysia()\n .use(signalingServer({ path: \"/polly/signaling\" }))\n .listen(signalingPort);\nconsole.log(`[browser-runner] signaling server on ws://127.0.0.1:${signalingPort}/polly/signaling`);\n\n// Launch browser\n\nconst browser = await puppeteer.launch({\n headless,\n args: [\"--no-sandbox\", \"--disable-setuid-sandbox\"],\n});\n\nlet totalPassed = 0;\nlet totalFailed = 0;\n\
|
|
5
|
+
"#!/usr/bin/env bun\n\n/**\n * Browser test runner for Polly applications.\n *\n * Finds all *.browser.ts files in a given directory, bundles each with\n * Bun.build for the browser target (with an internal Automerge WASM fix),\n * serves the bundle on an ephemeral port, opens a Puppeteer page, and\n * polls window.__done for results. Prints pass/fail per test and exits\n * non-zero if any test failed.\n *\n * A signalling server for WebRTC tests starts automatically on a random\n * port. The URL is injected into the bundle via process.env.SIGNALING_URL.\n *\n * Usage (from project root):\n *\n * bun tools/test/src/browser/run.ts [testDir] [filter]\n *\n * Examples:\n *\n * bun tools/test/src/browser/run.ts tests/browser\n * bun tools/test/src/browser/run.ts tests/browser mesh-webrtc\n * HEADLESS=false bun tools/test/src/browser/run.ts tests/browser\n *\n * When invoked without a testDir, defaults to tests/browser relative to cwd.\n */\n\nimport { resolve } from \"node:path\";\nimport { type BunPlugin, Glob } from \"bun\";\nimport { Elysia } from \"elysia\";\nimport puppeteer, { type Page } from \"puppeteer\";\nimport { signalingServer } from \"../../../../src/elysia/signaling-server-plugin\";\n\n// Automerge WASM fix\n// Bun.build's target: \"browser\" picks Automerge's fullfat_bundler.js which\n// does a static .wasm import that Bun can't wire up. Redirect to the\n// base64 variant which embeds the WASM as a string and self-initialises.\n\nconst automergeBase64Path = resolve(\n process.cwd(),\n \"node_modules/@automerge/automerge/dist/mjs/entrypoints/fullfat_base64.js\"\n);\n\nconst automergeBase64Plugin: BunPlugin = {\n name: \"automerge-base64\",\n setup(build) {\n build.onResolve({ filter: /^@automerge\\/automerge(\\/slim)?$/ }, () => {\n return { path: automergeBase64Path };\n });\n },\n};\n\n// Argument parsing\n\nconst testDir = resolve(process.cwd(), process.argv[2] ?? \"tests/browser\");\nconst filter = process.argv[3] ?? \"\";\nconst headless = process.env[\"HEADLESS\"] !== \"false\";\n\nconst glob = new Glob(\"**/*.browser.{ts,tsx}\");\nconst testFiles: string[] = [];\nfor await (const file of glob.scan({ cwd: testDir, absolute: true })) {\n if (file.includes(\"harness\")) continue;\n if (filter && !file.includes(filter)) continue;\n testFiles.push(file);\n}\n\nif (testFiles.length === 0) {\n console.log(`[browser-runner] no test files found${filter ? ` matching \"${filter}\"` : \"\"}`);\n process.exit(0);\n}\n\nconsole.log(`[browser-runner] found ${testFiles.length} test file(s)`);\n\n// Start server-side infrastructure\n\nconst signalingPort = 39000 + Math.floor(Math.random() * 1000);\nconst signalingApp = new Elysia()\n .use(signalingServer({ path: \"/polly/signaling\" }))\n .listen(signalingPort);\nconsole.log(`[browser-runner] signaling server on ws://127.0.0.1:${signalingPort}/polly/signaling`);\n\n// Launch browser\n//\n// protocolTimeout caps how long any single CDP call (e.g. page.evaluate)\n// waits before throwing. Puppeteer's default is 180s, so a stalled\n// renderer hangs the runner for three minutes. 30s fails fast while\n// still tolerating a slow-but-healthy initial sync.\n\nconst browser = await puppeteer.launch({\n headless,\n args: [\"--no-sandbox\", \"--disable-setuid-sandbox\"],\n protocolTimeout: 30_000,\n});\n\nlet totalPassed = 0;\nlet totalFailed = 0;\n\n/** A transient CDP timeout (renderer stall) — retryable, unlike a red test. */\nfunction isProtocolError(err: unknown): boolean {\n return err instanceof Error && err.name === \"ProtocolError\";\n}\n\nfunction errMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Build, serve, and run one test file on a fresh page. Returns its\n * pass/fail tally. Build failures and test timeouts are reported here\n * (not thrown). A thrown error (e.g. a ProtocolError from a stalled\n * renderer) propagates to the caller so it can retry the file; the\n * page and server are always cleaned up first.\n */\nasync function runFile(testFile: string): Promise<{ passed: number; failed: number }> {\n const buildResult = await Bun.build({\n entrypoints: [testFile],\n target: \"browser\",\n format: \"esm\",\n minify: false,\n sourcemap: \"inline\",\n plugins: [automergeBase64Plugin],\n define: {\n \"process.env.SIGNALING_URL\": JSON.stringify(\n `ws://127.0.0.1:${signalingPort}/polly/signaling`\n ),\n },\n });\n\n if (!buildResult.success) {\n console.log(\" ❌ build failed:\");\n for (const log of buildResult.logs) {\n console.log(` ${log}`);\n }\n return { passed: 0, failed: 1 };\n }\n\n const jsText = await buildResult.outputs[0]?.text();\n if (!jsText) {\n console.log(\" ❌ build produced no output\");\n return { passed: 0, failed: 1 };\n }\n\n const html = `<!DOCTYPE html>\n<html><head><meta charset=\"utf-8\"></head>\n<body>\n<script type=\"module\">${jsText}</script>\n</body></html>`;\n\n const server = Bun.serve({\n port: 0,\n fetch() {\n return new Response(html, { headers: { \"Content-Type\": \"text/html\" } });\n },\n });\n\n let page: Page | undefined;\n try {\n page = await browser.newPage();\n page.on(\"console\", (msg) => {\n const text = msg.text();\n if (text.includes(\"[test]\")) {\n console.log(` ${text}`);\n }\n });\n page.on(\"pageerror\", (err: unknown) => {\n console.log(` ❌ page error: ${errMessage(err)}`);\n });\n\n await page.goto(`http://127.0.0.1:${server.port}/`, { waitUntil: \"domcontentloaded\" });\n\n const timeout = 15_000;\n const deadline = Date.now() + timeout;\n let finished = false;\n while (Date.now() < deadline) {\n finished = await page.evaluate(\n () => (window as unknown as Record<string, unknown>)[\"__done\"] === true\n );\n if (finished) break;\n await new Promise((r) => setTimeout(r, 100));\n }\n\n if (!finished) {\n console.log(` ❌ timed out after ${timeout}ms`);\n return { passed: 0, failed: 1 };\n }\n\n const results = await page.evaluate(\n () =>\n (window as unknown as Record<string, unknown>)[\"__testResults\"] as unknown as Array<{\n name: string;\n passed: boolean;\n error?: string;\n }>\n );\n\n let passed = 0;\n let failed = 0;\n for (const r of results ?? []) {\n if (r.passed) {\n console.log(` ✅ ${r.name}`);\n passed += 1;\n } else {\n console.log(` ❌ ${r.name}: ${r.error}`);\n failed += 1;\n }\n }\n return { passed, failed };\n } finally {\n // Always run, even on the error path. close() can itself throw a\n // ProtocolError under a stall — swallow it so cleanup completes.\n if (page) {\n await page.close().catch(() => {\n // ignore — page may already be gone after a stall\n });\n }\n server.stop();\n }\n}\n\nfor (const testFile of testFiles) {\n const shortName = testFile.replace(`${testDir}/`, \"\");\n console.log(`\\n[browser-runner] running ${shortName}`);\n\n let result: { passed: number; failed: number };\n try {\n result = await runFile(testFile);\n } catch (err) {\n if (isProtocolError(err)) {\n console.log(` ⚠️ protocol error (${errMessage(err)}) — retrying once on a fresh page`);\n try {\n result = await runFile(testFile);\n } catch (retryErr) {\n console.log(` ❌ retry failed: ${errMessage(retryErr)}`);\n result = { passed: 0, failed: 1 };\n }\n } else {\n // A non-protocol error: record the file as failed and keep going,\n // never abort the whole suite.\n console.log(` ❌ ${errMessage(err)}`);\n result = { passed: 0, failed: 1 };\n }\n }\n\n totalPassed += result.passed;\n totalFailed += result.failed;\n}\n\nawait browser.close();\n(signalingApp as unknown as { server?: { stop?: (f?: boolean) => void } }).server?.stop?.(true);\n\nconsole.log(`\\n[browser-runner] ${totalPassed} passed, ${totalFailed} failed`);\nprocess.exit(totalFailed > 0 ? 1 : 0);\n",
|
|
6
6
|
"// @ts-nocheck - Optional peer dependencies (elysia, @elysiajs/eden)\n/**\n * signalingServer — Phase 2 Elysia plugin that exposes a stateless\n * WebSocket route for SDP/ICE relay between $meshState peers.\n *\n * The mesh transport is a star-of-data-channels: peers establish direct\n * WebRTC connections to each other and exchange document operations\n * peer-to-peer once those channels are open. WebRTC connection setup\n * needs an out-of-band channel for SDP offer/answer and ICE candidate\n * exchange, and that channel is what this plugin provides. The plugin\n * does not own any document state, does not hold any encryption keys,\n * and never inspects the contents of the messages it relays. It is a\n * pure pub-sub by peer id.\n *\n * Once two peers have completed the SDP exchange and opened a direct\n * data channel, the signalling server is no longer on the critical\n * path — the peers talk directly. The signalling server's role is\n * therefore intermittent: peers connect to it only during the brief\n * windows when they are establishing or re-establishing connections.\n *\n * Wire protocol:\n *\n * Client → server (join):\n * { type: \"join\", peerId: \"peer-alice\" }\n *\n * Client → server (signal to another peer):\n * { type: \"signal\", peerId: \"peer-alice\", targetPeerId: \"peer-bob\",\n * payload: { ... } }\n *\n * Server → client (delivered signal):\n * { type: \"signal\", peerId: \"peer-alice\", targetPeerId: \"peer-bob\",\n * payload: { ... } }\n *\n * Server → client (notification of unknown target):\n * { type: \"error\", reason: \"unknown-target\", targetPeerId: \"...\" }\n *\n * The `payload` is opaque to the signalling server — typically it\n * carries an SDP offer, SDP answer, or ICE candidate. Applications can\n * also use the relay for any other peer-to-peer message that needs an\n * intermediary, such as the initial handshake of a pairing flow.\n *\n * @example\n * ```ts\n * import { Elysia } from \"elysia\";\n * import { signalingServer } from \"@fairfox/polly/elysia\";\n *\n * const app = new Elysia()\n * .use(signalingServer({ path: \"/polly/signaling\" }))\n * .listen(8080);\n * ```\n */\n\nimport { Elysia } from \"elysia\";\n\n/** A signalling message. The `type` discriminates between client-to-server\n * requests (join, signal), the error envelope, and the server-to-client\n * discovery frames (peers-present, peer-joined, peer-left) that let\n * peers learn about each other without polling. */\nexport type SignalingMessage =\n | {\n type: \"join\";\n /** The peer registering itself with the signalling server. */\n peerId: string;\n }\n | {\n type: \"signal\";\n /** The peer sending the signal. */\n peerId: string;\n /** The peer the signal is being relayed to. */\n targetPeerId: string;\n /** Opaque payload, typically SDP or ICE. */\n payload: unknown;\n }\n | {\n type: \"error\";\n reason: \"unknown-target\" | \"not-joined\" | \"malformed\";\n targetPeerId?: string;\n }\n | {\n /** Sent to a newcomer immediately after it joins, listing every\n * peer that was already joined at that moment. Empty for a lone\n * newcomer. */\n type: \"peers-present\";\n peerIds: string[];\n }\n | {\n /** Broadcast to every incumbent when a new peer joins. */\n type: \"peer-joined\";\n peerId: string;\n }\n | {\n /** Broadcast to every remaining incumbent when a joined peer\n * closes its socket. Never emitted for a connection that never\n * sent a join frame. */\n type: \"peer-left\";\n peerId: string;\n };\n\n/** A frame whose `type` is outside the built-in signalling vocabulary.\n * Consumers who pass an {@link SignalingServerOptions.onCustomFrame}\n * handler receive these on the server side; everything else — including\n * routing them to a specific peer, storing a session, or rejecting the\n * frame — is the consumer's call. Polly does not touch the body. */\nexport interface CustomSignalingFrame {\n type: string;\n [key: string]: unknown;\n}\n\n/** Minimal surface the custom-frame handler receives in place of the\n * Elysia-specific `ws` object so the plugin stays portable. Exposes the\n * `data` bag (used to stash the authenticated peerId under the existing\n * join handshake) and a `send` method. */\nexport interface CustomFrameSocket {\n data: Record<string, unknown>;\n send: (msg: unknown) => void;\n}\n\nexport interface SignalingServerOptions {\n /** WebSocket route path. Defaults to \"/polly/signaling\". */\n path?: string;\n /** Optional hook for frames whose `type` is outside the built-in\n * vocabulary. The plugin invokes it in place of returning a\n * `malformed` error, so consumers can layer their own application\n * protocol (pairing return tokens, presence pings, etc.) on the\n * existing socket. The `peerId` argument is the sender's\n * authenticated peer id from their prior `join` frame, or\n * `undefined` if they haven't joined yet. */\n onCustomFrame?: (\n socket: CustomFrameSocket,\n frame: CustomSignalingFrame,\n peerId: string | undefined\n ) => void;\n}\n\n/**\n * Construct the signalling-server Elysia plugin. The plugin keeps a\n * per-instance map of peer id → WebSocket connection so that incoming\n * \"signal\" messages can be routed to the right target socket. The map\n * is local to the plugin instance, not shared across processes; for\n * multi-instance deployments behind a load balancer, applications need\n * sticky connection routing or a shared backplane (Redis pub-sub or\n * similar). That is a follow-up.\n */\nexport function signalingServer(options: SignalingServerOptions = {}) {\n const path = options.path ?? \"/polly/signaling\";\n const onCustomFrame = options.onCustomFrame;\n // Per-peer-id map of joined sockets. The inverse mapping is stored\n // directly on ws.data (a mutable property bag that Elysia preserves\n // across message callbacks for a given connection); the webrtc-p2p-chat\n // example in examples/ confirms this pattern is stable under Bun.\n const peerSockets = new Map<string, { send: (msg: unknown) => void }>();\n\n // Intentionally unnamed — Elysia deduplicates plugins by name, and each\n // signalingServer() call needs its own closure-captured peer map.\n const parseMessage = (raw: unknown): SignalingMessage | undefined => {\n try {\n return typeof raw === \"string\" ? JSON.parse(raw) : (raw as unknown as SignalingMessage);\n } catch {\n return undefined;\n }\n };\n\n const handleJoin = (ws: unknown, peerId: string): void => {\n const newcomer = ws as unknown as { send: (m: unknown) => void };\n // Collect the peers that were already joined so we can (a) tell the\n // newcomer who is present and (b) tell each of them about the\n // newcomer. A rejoin with the same peerId replaces the prior entry\n // but is otherwise treated as a fresh arrival.\n const incumbents: Array<{ peerId: string; socket: { send: (m: unknown) => void } }> = [];\n for (const [existingPeerId, existingSocket] of peerSockets) {\n if (existingPeerId === peerId) continue;\n incumbents.push({ peerId: existingPeerId, socket: existingSocket });\n }\n peerSockets.set(peerId, newcomer);\n const wsWithData = ws as unknown as { data: Record<string, unknown> };\n wsWithData.data.peerId = peerId;\n\n newcomer.send({\n type: \"peers-present\",\n peerIds: incumbents.map((i) => i.peerId),\n } as unknown as SignalingMessage);\n\n for (const incumbent of incumbents) {\n try {\n incumbent.socket.send({ type: \"peer-joined\", peerId } as unknown as SignalingMessage);\n } catch {\n // If a send fails we leave the stale socket to its own close\n // handler to evict. Dropping here would open a window where\n // the next signal to this peer still thinks it's alive.\n }\n }\n };\n\n const sendUnknownTarget = (ws: unknown, targetPeerId: string): void => {\n (ws as unknown as { send: (m: unknown) => void }).send({\n type: \"error\",\n reason: \"unknown-target\",\n targetPeerId,\n } as unknown as SignalingMessage);\n };\n\n /** Look up a target socket and confirm it is still open. */\n const findOpenTarget = (targetPeerId: string): { send: (msg: unknown) => void } | undefined => {\n const target = peerSockets.get(targetPeerId);\n if (!target) return undefined;\n const readyState = (target as unknown as { readyState?: number }).readyState;\n const OPEN = 1;\n if (readyState !== undefined && readyState !== OPEN) {\n peerSockets.delete(targetPeerId);\n return undefined;\n }\n return target;\n };\n\n const handleSignal = (ws: unknown, msg: Extract<SignalingMessage, { type: \"signal\" }>): void => {\n const wsWithData = ws as unknown as {\n data: Record<string, unknown>;\n send: (m: unknown) => void;\n };\n const senderId = wsWithData.data.peerId as unknown as string | undefined;\n if (!senderId) {\n wsWithData.send({ type: \"error\", reason: \"not-joined\" } as unknown as SignalingMessage);\n return;\n }\n const target = findOpenTarget(msg.targetPeerId);\n if (!target) {\n sendUnknownTarget(ws, msg.targetPeerId);\n return;\n }\n const relayed: SignalingMessage = {\n type: \"signal\",\n peerId: senderId,\n targetPeerId: msg.targetPeerId,\n payload: msg.payload,\n };\n try {\n target.send(relayed);\n } catch {\n peerSockets.delete(msg.targetPeerId);\n sendUnknownTarget(ws, msg.targetPeerId);\n }\n };\n\n return new Elysia().ws(path, {\n message(ws, raw) {\n const msg = parseMessage(raw);\n if (!msg) {\n ws.send({ type: \"error\", reason: \"malformed\" } as unknown as SignalingMessage);\n return;\n }\n if (msg.type === \"join\") {\n handleJoin(ws, msg.peerId);\n return;\n }\n if (msg.type === \"signal\") {\n handleSignal(ws, msg);\n return;\n }\n // Unknown types route to the consumer's custom-frame hook when\n // one is configured. Without a hook they still fall through to\n // the `malformed` error — same behaviour as before this branch\n // existed.\n if (onCustomFrame !== undefined) {\n const wsWithData = ws as unknown as CustomFrameSocket;\n const senderId = wsWithData.data[\"peerId\"];\n const peerId = typeof senderId === \"string\" ? senderId : undefined;\n onCustomFrame(wsWithData, msg as unknown as CustomSignalingFrame, peerId);\n return;\n }\n ws.send({ type: \"error\", reason: \"malformed\" } as unknown as SignalingMessage);\n },\n\n close(ws) {\n const peerId = (ws.data as unknown as Record<string, unknown>).peerId as unknown as\n | string\n | undefined;\n if (!peerId) {\n // Connection that never sent a join — nothing to broadcast and\n // nothing to evict. A bystander coming and going leaves no trace.\n return;\n }\n // Only evict if the map still points at *this* socket. A stale\n // close after the same peerId rejoined on a new socket should not\n // take the fresh entry with it. The comparison uses the `data` bag\n // Elysia attaches to each connection because it is preserved across\n // message and close callbacks, unlike the `ws` wrapper object which\n // Elysia may or may not reuse.\n const mapped = peerSockets.get(peerId);\n const wsData = (ws as unknown as { data: Record<string, unknown> }).data;\n const mappedData = (mapped as unknown as { data?: Record<string, unknown> } | undefined)\n ?.data;\n if (mapped === undefined || mappedData !== wsData) {\n return;\n }\n peerSockets.delete(peerId);\n for (const [_incumbentId, incumbentSocket] of peerSockets) {\n try {\n incumbentSocket.send({ type: \"peer-left\", peerId } as unknown as SignalingMessage);\n } catch {\n // Incumbent socket is gone; its own close handler will tidy.\n }\n }\n },\n });\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "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
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AA2BA;AACA;AACA,mBAAS;AACT;;;ACsBA;AA2FO,SAAS,eAAe,CAAC,UAAkC,CAAC,GAAG;AAAA,EACpE,MAAM,OAAO,QAAQ,QAAQ;AAAA,EAC7B,MAAM,gBAAgB,QAAQ;AAAA,EAK9B,MAAM,cAAc,IAAI;AAAA,EAIxB,MAAM,eAAe,CAAC,QAA+C;AAAA,IACnE,IAAI;AAAA,MACF,OAAO,OAAO,QAAQ,WAAW,KAAK,MAAM,GAAG,IAAK;AAAA,MACpD,MAAM;AAAA,MACN;AAAA;AAAA;AAAA,EAIJ,MAAM,aAAa,CAAC,IAAa,WAAyB;AAAA,IACxD,MAAM,WAAW;AAAA,IAKjB,MAAM,aAAgF,CAAC;AAAA,IACvF,YAAY,gBAAgB,mBAAmB,aAAa;AAAA,MAC1D,IAAI,mBAAmB;AAAA,QAAQ;AAAA,MAC/B,WAAW,KAAK,EAAE,QAAQ,gBAAgB,QAAQ,eAAe,CAAC;AAAA,IACpE;AAAA,IACA,YAAY,IAAI,QAAQ,QAAQ;AAAA,IAChC,MAAM,aAAa;AAAA,IACnB,WAAW,KAAK,SAAS;AAAA,IAEzB,SAAS,KAAK;AAAA,MACZ,MAAM;AAAA,MACN,SAAS,WAAW,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,IACzC,CAAgC;AAAA,IAEhC,WAAW,aAAa,YAAY;AAAA,MAClC,IAAI;AAAA,QACF,UAAU,OAAO,KAAK,EAAE,MAAM,eAAe,OAAO,CAAgC;AAAA,QACpF,MAAM;AAAA,IAKV;AAAA;AAAA,EAGF,MAAM,oBAAoB,CAAC,IAAa,iBAA+B;AAAA,IACpE,GAAiD,KAAK;AAAA,MACrD,MAAM;AAAA,MACN,QAAQ;AAAA,MACR;AAAA,IACF,CAAgC;AAAA;AAAA,EAIlC,MAAM,iBAAiB,CAAC,iBAAuE;AAAA,IAC7F,MAAM,SAAS,YAAY,IAAI,YAAY;AAAA,IAC3C,IAAI,CAAC;AAAA,MAAQ;AAAA,IACb,MAAM,aAAc,OAA8C;AAAA,IAClE,MAAM,OAAO;AAAA,IACb,IAAI,eAAe,aAAa,eAAe,MAAM;AAAA,MACnD,YAAY,OAAO,YAAY;AAAA,MAC/B;AAAA,IACF;AAAA,IACA,OAAO;AAAA;AAAA,EAGT,MAAM,eAAe,CAAC,IAAa,QAA6D;AAAA,IAC9F,MAAM,aAAa;AAAA,IAInB,MAAM,WAAW,WAAW,KAAK;AAAA,IACjC,IAAI,CAAC,UAAU;AAAA,MACb,WAAW,KAAK,EAAE,MAAM,SAAS,QAAQ,aAAa,CAAgC;AAAA,MACtF;AAAA,IACF;AAAA,IACA,MAAM,SAAS,eAAe,IAAI,YAAY;AAAA,IAC9C,IAAI,CAAC,QAAQ;AAAA,MACX,kBAAkB,IAAI,IAAI,YAAY;AAAA,MACtC;AAAA,IACF;AAAA,IACA,MAAM,UAA4B;AAAA,MAChC,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,cAAc,IAAI;AAAA,MAClB,SAAS,IAAI;AAAA,IACf;AAAA,IACA,IAAI;AAAA,MACF,OAAO,KAAK,OAAO;AAAA,MACnB,MAAM;AAAA,MACN,YAAY,OAAO,IAAI,YAAY;AAAA,MACnC,kBAAkB,IAAI,IAAI,YAAY;AAAA;AAAA;AAAA,EAI1C,OAAO,IAAI,OAAO,EAAE,GAAG,MAAM;AAAA,IAC3B,OAAO,CAAC,IAAI,KAAK;AAAA,MACf,MAAM,MAAM,aAAa,GAAG;AAAA,MAC5B,IAAI,CAAC,KAAK;AAAA,QACR,GAAG,KAAK,EAAE,MAAM,SAAS,QAAQ,YAAY,CAAgC;AAAA,QAC7E;AAAA,MACF;AAAA,MACA,IAAI,IAAI,SAAS,QAAQ;AAAA,QACvB,WAAW,IAAI,IAAI,MAAM;AAAA,QACzB;AAAA,MACF;AAAA,MACA,IAAI,IAAI,SAAS,UAAU;AAAA,QACzB,aAAa,IAAI,GAAG;AAAA,QACpB;AAAA,MACF;AAAA,MAKA,IAAI,kBAAkB,WAAW;AAAA,QAC/B,MAAM,aAAa;AAAA,QACnB,MAAM,WAAW,WAAW,KAAK;AAAA,QACjC,MAAM,SAAS,OAAO,aAAa,WAAW,WAAW;AAAA,QACzD,cAAc,YAAY,KAAwC,MAAM;AAAA,QACxE;AAAA,MACF;AAAA,MACA,GAAG,KAAK,EAAE,MAAM,SAAS,QAAQ,YAAY,CAAgC;AAAA;AAAA,IAG/E,KAAK,CAAC,IAAI;AAAA,MACR,MAAM,SAAU,GAAG,KAA4C;AAAA,MAG/D,IAAI,CAAC,QAAQ;AAAA,QAGX;AAAA,MACF;AAAA,MAOA,MAAM,SAAS,YAAY,IAAI,MAAM;AAAA,MACrC,MAAM,SAAU,GAAoD;AAAA,MACpE,MAAM,aAAc,QAChB;AAAA,MACJ,IAAI,WAAW,aAAa,eAAe,QAAQ;AAAA,QACjD;AAAA,MACF;AAAA,MACA,YAAY,OAAO,MAAM;AAAA,MACzB,YAAY,cAAc,oBAAoB,aAAa;AAAA,QACzD,IAAI;AAAA,UACF,gBAAgB,KAAK,EAAE,MAAM,aAAa,OAAO,CAAgC;AAAA,UACjF,MAAM;AAAA,MAGV;AAAA;AAAA,EAEJ,CAAC;AAAA;;;ADzQH,IAAM,sBAAsB,QAC1B,QAAQ,IAAI,GACZ,0EACF;AAEA,IAAM,wBAAmC;AAAA,EACvC,MAAM;AAAA,EACN,KAAK,CAAC,OAAO;AAAA,IACX,MAAM,UAAU,EAAE,QAAQ,mCAAmC,GAAG,MAAM;AAAA,MACpE,OAAO,EAAE,MAAM,oBAAoB;AAAA,KACpC;AAAA;AAEL;AAIA,IAAM,UAAU,QAAQ,QAAQ,IAAI,GAAG,QAAQ,KAAK,MAAM,eAAe;AACzE,IAAM,SAAS,QAAQ,KAAK,MAAM;AAClC,IAAM,WAAW,QAAQ,IAAI,gBAAgB;AAE7C,IAAM,OAAO,IAAI,KAAK,uBAAuB;AAC7C,IAAM,YAAsB,CAAC;AAC7B,iBAAiB,QAAQ,KAAK,KAAK,EAAE,KAAK,SAAS,UAAU,KAAK,CAAC,GAAG;AAAA,EACpE,IAAI,KAAK,SAAS,SAAS;AAAA,IAAG;AAAA,EAC9B,IAAI,UAAU,CAAC,KAAK,SAAS,MAAM;AAAA,IAAG;AAAA,EACtC,UAAU,KAAK,IAAI;AACrB;AAEA,IAAI,UAAU,WAAW,GAAG;AAAA,EAC1B,QAAQ,IAAI,uCAAuC,SAAS,cAAc,YAAY,IAAI;AAAA,EAC1F,QAAQ,KAAK,CAAC;AAChB;AAEA,QAAQ,IAAI,0BAA0B,UAAU,qBAAqB;AAIrE,IAAM,gBAAgB,QAAQ,KAAK,MAAM,KAAK,OAAO,IAAI,IAAI;AAC7D,IAAM,eAAe,IAAI,QAAO,EAC7B,IAAI,gBAAgB,EAAE,MAAM,mBAAmB,CAAC,CAAC,EACjD,OAAO,aAAa;AACvB,QAAQ,IAAI,uDAAuD,+BAA+B;AASlG,IAAM,UAAU,MAAM,UAAU,OAAO;AAAA,EACrC;AAAA,EACA,MAAM,CAAC,gBAAgB,0BAA0B;AAAA,EACjD,iBAAiB;AACnB,CAAC;AAED,IAAI,cAAc;AAClB,IAAI,cAAc;AAGlB,SAAS,eAAe,CAAC,KAAuB;AAAA,EAC9C,OAAO,eAAe,SAAS,IAAI,SAAS;AAAA;AAG9C,SAAS,UAAU,CAAC,KAAsB;AAAA,EACxC,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA;AAUxD,eAAe,OAAO,CAAC,UAA+D;AAAA,EACpF,MAAM,cAAc,MAAM,IAAI,MAAM;AAAA,IAClC,aAAa,CAAC,QAAQ;AAAA,IACtB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS,CAAC,qBAAqB;AAAA,IAC/B,QAAQ;AAAA,MACN,6BAA6B,KAAK,UAChC,kBAAkB,+BACpB;AAAA,IACF;AAAA,EACF,CAAC;AAAA,EAED,IAAI,CAAC,YAAY,SAAS;AAAA,IACxB,QAAQ,IAAI,mBAAkB;AAAA,IAC9B,WAAW,OAAO,YAAY,MAAM;AAAA,MAClC,QAAQ,IAAI,QAAQ,KAAK;AAAA,IAC3B;AAAA,IACA,OAAO,EAAE,QAAQ,GAAG,QAAQ,EAAE;AAAA,EAChC;AAAA,EAEA,MAAM,SAAS,MAAM,YAAY,QAAQ,IAAI,KAAK;AAAA,EAClD,IAAI,CAAC,QAAQ;AAAA,IACX,QAAQ,IAAI,8BAA6B;AAAA,IACzC,OAAO,EAAE,QAAQ,GAAG,QAAQ,EAAE;AAAA,EAChC;AAAA,EAEA,MAAM,OAAO;AAAA;AAAA;AAAA,wBAGS;AAAA;AAAA,EAGtB,MAAM,SAAS,IAAI,MAAM;AAAA,IACvB,MAAM;AAAA,IACN,KAAK,GAAG;AAAA,MACN,OAAO,IAAI,SAAS,MAAM,EAAE,SAAS,EAAE,gBAAgB,YAAY,EAAE,CAAC;AAAA;AAAA,EAE1E,CAAC;AAAA,EAED,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,OAAO,MAAM,QAAQ,QAAQ;AAAA,IAC7B,KAAK,GAAG,WAAW,CAAC,QAAQ;AAAA,MAC1B,MAAM,OAAO,IAAI,KAAK;AAAA,MACtB,IAAI,KAAK,SAAS,QAAQ,GAAG;AAAA,QAC3B,QAAQ,IAAI,KAAK,MAAM;AAAA,MACzB;AAAA,KACD;AAAA,IACD,KAAK,GAAG,aAAa,CAAC,QAAiB;AAAA,MACrC,QAAQ,IAAI,mBAAkB,WAAW,GAAG,GAAG;AAAA,KAChD;AAAA,IAED,MAAM,KAAK,KAAK,oBAAoB,OAAO,SAAS,EAAE,WAAW,mBAAmB,CAAC;AAAA,IAErF,MAAM,UAAU;AAAA,IAChB,MAAM,WAAW,KAAK,IAAI,IAAI;AAAA,IAC9B,IAAI,WAAW;AAAA,IACf,OAAO,KAAK,IAAI,IAAI,UAAU;AAAA,MAC5B,WAAW,MAAM,KAAK,SACpB,MAAO,OAA8C,cAAc,IACrE;AAAA,MACA,IAAI;AAAA,QAAU;AAAA,MACd,MAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAG,CAAC;AAAA,IAC7C;AAAA,IAEA,IAAI,CAAC,UAAU;AAAA,MACb,QAAQ,IAAI,uBAAsB,WAAW;AAAA,MAC7C,OAAO,EAAE,QAAQ,GAAG,QAAQ,EAAE;AAAA,IAChC;AAAA,IAEA,MAAM,UAAU,MAAM,KAAK,SACzB,MACG,OAA8C,gBAKnD;AAAA,IAEA,IAAI,SAAS;AAAA,IACb,IAAI,SAAS;AAAA,IACb,WAAW,KAAK,WAAW,CAAC,GAAG;AAAA,MAC7B,IAAI,EAAE,QAAQ;AAAA,QACZ,QAAQ,IAAI,OAAM,EAAE,MAAM;AAAA,QAC1B,UAAU;AAAA,MACZ,EAAO;AAAA,QACL,QAAQ,IAAI,OAAM,EAAE,SAAS,EAAE,OAAO;AAAA,QACtC,UAAU;AAAA;AAAA,IAEd;AAAA,IACA,OAAO,EAAE,QAAQ,OAAO;AAAA,YACxB;AAAA,IAGA,IAAI,MAAM;AAAA,MACR,MAAM,KAAK,MAAM,EAAE,MAAM,MAAM,EAE9B;AAAA,IACH;AAAA,IACA,OAAO,KAAK;AAAA;AAAA;AAIhB,WAAW,YAAY,WAAW;AAAA,EAChC,MAAM,YAAY,SAAS,QAAQ,GAAG,YAAY,EAAE;AAAA,EACpD,QAAQ,IAAI;AAAA,2BAA8B,WAAW;AAAA,EAErD,IAAI;AAAA,EACJ,IAAI;AAAA,IACF,SAAS,MAAM,QAAQ,QAAQ;AAAA,IAC/B,OAAO,KAAK;AAAA,IACZ,IAAI,gBAAgB,GAAG,GAAG;AAAA,MACxB,QAAQ,IAAI,yBAAwB,WAAW,GAAG,oCAAoC;AAAA,MACtF,IAAI;AAAA,QACF,SAAS,MAAM,QAAQ,QAAQ;AAAA,QAC/B,OAAO,UAAU;AAAA,QACjB,QAAQ,IAAI,qBAAoB,WAAW,QAAQ,GAAG;AAAA,QACtD,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAAE;AAAA;AAAA,IAEpC,EAAO;AAAA,MAGL,QAAQ,IAAI,OAAM,WAAW,GAAG,GAAG;AAAA,MACnC,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAAE;AAAA;AAAA;AAAA,EAIpC,eAAe,OAAO;AAAA,EACtB,eAAe,OAAO;AACxB;AAEA,MAAM,QAAQ,MAAM;AACnB,aAA0E,QAAQ,OAAO,IAAI;AAE9F,QAAQ,IAAI;AAAA,mBAAsB,uBAAuB,oBAAoB;AAC7E,QAAQ,KAAK,cAAc,IAAI,IAAI,CAAC;",
|
|
9
|
+
"debugId": "995635FD6B287CDC64756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -11487,7 +11487,7 @@ var init_zlib = __esm(() => {
|
|
|
11487
11487
|
export_default = import_browserify_zlib.default;
|
|
11488
11488
|
});
|
|
11489
11489
|
|
|
11490
|
-
//
|
|
11490
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/chunkstream.js
|
|
11491
11491
|
var require_chunkstream = __commonJS((exports, module) => {
|
|
11492
11492
|
var util = (init_util(), __toCommonJS(exports_util));
|
|
11493
11493
|
var Stream = require_stream();
|
|
@@ -11619,7 +11619,7 @@ var require_chunkstream = __commonJS((exports, module) => {
|
|
|
11619
11619
|
};
|
|
11620
11620
|
});
|
|
11621
11621
|
|
|
11622
|
-
//
|
|
11622
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/interlace.js
|
|
11623
11623
|
var require_interlace = __commonJS((exports) => {
|
|
11624
11624
|
var imagePasses = [
|
|
11625
11625
|
{
|
|
@@ -11692,7 +11692,7 @@ var require_interlace = __commonJS((exports) => {
|
|
|
11692
11692
|
};
|
|
11693
11693
|
});
|
|
11694
11694
|
|
|
11695
|
-
//
|
|
11695
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/paeth-predictor.js
|
|
11696
11696
|
var require_paeth_predictor = __commonJS((exports, module) => {
|
|
11697
11697
|
module.exports = function paethPredictor(left, above, upLeft) {
|
|
11698
11698
|
let paeth = left + above - upLeft;
|
|
@@ -11709,7 +11709,7 @@ var require_paeth_predictor = __commonJS((exports, module) => {
|
|
|
11709
11709
|
};
|
|
11710
11710
|
});
|
|
11711
11711
|
|
|
11712
|
-
//
|
|
11712
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/filter-parse.js
|
|
11713
11713
|
var require_filter_parse = __commonJS((exports, module) => {
|
|
11714
11714
|
var interlaceUtils = require_interlace();
|
|
11715
11715
|
var paethPredictor = require_paeth_predictor();
|
|
@@ -11844,7 +11844,7 @@ var require_filter_parse = __commonJS((exports, module) => {
|
|
|
11844
11844
|
};
|
|
11845
11845
|
});
|
|
11846
11846
|
|
|
11847
|
-
//
|
|
11847
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/filter-parse-async.js
|
|
11848
11848
|
var require_filter_parse_async = __commonJS((exports, module) => {
|
|
11849
11849
|
var util = (init_util(), __toCommonJS(exports_util));
|
|
11850
11850
|
var ChunkStream = require_chunkstream();
|
|
@@ -11867,7 +11867,7 @@ var require_filter_parse_async = __commonJS((exports, module) => {
|
|
|
11867
11867
|
util.inherits(FilterAsync, ChunkStream);
|
|
11868
11868
|
});
|
|
11869
11869
|
|
|
11870
|
-
//
|
|
11870
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/constants.js
|
|
11871
11871
|
var require_constants2 = __commonJS((exports, module) => {
|
|
11872
11872
|
module.exports = {
|
|
11873
11873
|
PNG_SIGNATURE: [137, 80, 78, 71, 13, 10, 26, 10],
|
|
@@ -11894,7 +11894,7 @@ var require_constants2 = __commonJS((exports, module) => {
|
|
|
11894
11894
|
};
|
|
11895
11895
|
});
|
|
11896
11896
|
|
|
11897
|
-
//
|
|
11897
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/crc.js
|
|
11898
11898
|
var require_crc = __commonJS((exports, module) => {
|
|
11899
11899
|
var crcTable = [];
|
|
11900
11900
|
(function() {
|
|
@@ -11931,7 +11931,7 @@ var require_crc = __commonJS((exports, module) => {
|
|
|
11931
11931
|
};
|
|
11932
11932
|
});
|
|
11933
11933
|
|
|
11934
|
-
//
|
|
11934
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/parser.js
|
|
11935
11935
|
var require_parser = __commonJS((exports, module) => {
|
|
11936
11936
|
var constants2 = require_constants2();
|
|
11937
11937
|
var CrcCalculator = require_crc();
|
|
@@ -12148,7 +12148,7 @@ var require_parser = __commonJS((exports, module) => {
|
|
|
12148
12148
|
};
|
|
12149
12149
|
});
|
|
12150
12150
|
|
|
12151
|
-
//
|
|
12151
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/bitmapper.js
|
|
12152
12152
|
var require_bitmapper = __commonJS((exports) => {
|
|
12153
12153
|
var interlaceUtils = require_interlace();
|
|
12154
12154
|
var pixelBppMapper = [
|
|
@@ -12359,7 +12359,7 @@ var require_bitmapper = __commonJS((exports) => {
|
|
|
12359
12359
|
};
|
|
12360
12360
|
});
|
|
12361
12361
|
|
|
12362
|
-
//
|
|
12362
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/format-normaliser.js
|
|
12363
12363
|
var require_format_normaliser = __commonJS((exports, module) => {
|
|
12364
12364
|
function dePalette(indata, outdata, width, height, palette) {
|
|
12365
12365
|
let pxPos = 0;
|
|
@@ -12435,7 +12435,7 @@ var require_format_normaliser = __commonJS((exports, module) => {
|
|
|
12435
12435
|
};
|
|
12436
12436
|
});
|
|
12437
12437
|
|
|
12438
|
-
//
|
|
12438
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/parser-async.js
|
|
12439
12439
|
var require_parser_async = __commonJS((exports, module) => {
|
|
12440
12440
|
var util = (init_util(), __toCommonJS(exports_util));
|
|
12441
12441
|
var zlib = (init_zlib(), __toCommonJS(exports_zlib));
|
|
@@ -12557,7 +12557,7 @@ var require_parser_async = __commonJS((exports, module) => {
|
|
|
12557
12557
|
};
|
|
12558
12558
|
});
|
|
12559
12559
|
|
|
12560
|
-
//
|
|
12560
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/bitpacker.js
|
|
12561
12561
|
var require_bitpacker = __commonJS((exports, module) => {
|
|
12562
12562
|
var constants2 = require_constants2();
|
|
12563
12563
|
module.exports = function(dataIn, width, height, options) {
|
|
@@ -12686,7 +12686,7 @@ var require_bitpacker = __commonJS((exports, module) => {
|
|
|
12686
12686
|
};
|
|
12687
12687
|
});
|
|
12688
12688
|
|
|
12689
|
-
//
|
|
12689
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/filter-pack.js
|
|
12690
12690
|
var require_filter_pack = __commonJS((exports, module) => {
|
|
12691
12691
|
var paethPredictor = require_paeth_predictor();
|
|
12692
12692
|
function filterNone(pxData, pxPos, byteWidth, rawData, rawPos) {
|
|
@@ -12825,7 +12825,7 @@ var require_filter_pack = __commonJS((exports, module) => {
|
|
|
12825
12825
|
};
|
|
12826
12826
|
});
|
|
12827
12827
|
|
|
12828
|
-
//
|
|
12828
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/packer.js
|
|
12829
12829
|
var require_packer = __commonJS((exports, module) => {
|
|
12830
12830
|
var constants2 = require_constants2();
|
|
12831
12831
|
var CrcStream = require_crc();
|
|
@@ -12913,7 +12913,7 @@ var require_packer = __commonJS((exports, module) => {
|
|
|
12913
12913
|
};
|
|
12914
12914
|
});
|
|
12915
12915
|
|
|
12916
|
-
//
|
|
12916
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/packer-async.js
|
|
12917
12917
|
var require_packer_async = __commonJS((exports, module) => {
|
|
12918
12918
|
var util = (init_util(), __toCommonJS(exports_util));
|
|
12919
12919
|
var Stream = require_stream();
|
|
@@ -12946,7 +12946,7 @@ var require_packer_async = __commonJS((exports, module) => {
|
|
|
12946
12946
|
};
|
|
12947
12947
|
});
|
|
12948
12948
|
|
|
12949
|
-
//
|
|
12949
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/sync-inflate.js
|
|
12950
12950
|
var require_sync_inflate = __commonJS((exports, module) => {
|
|
12951
12951
|
var assert2 = (init_assert(), __toCommonJS(exports_assert)).ok;
|
|
12952
12952
|
var zlib = (init_zlib(), __toCommonJS(exports_zlib));
|
|
@@ -13065,7 +13065,7 @@ var require_sync_inflate = __commonJS((exports, module) => {
|
|
|
13065
13065
|
exports.inflateSync = inflateSync;
|
|
13066
13066
|
});
|
|
13067
13067
|
|
|
13068
|
-
//
|
|
13068
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/sync-reader.js
|
|
13069
13069
|
var require_sync_reader = __commonJS((exports, module) => {
|
|
13070
13070
|
var SyncReader = module.exports = function(buffer) {
|
|
13071
13071
|
this._buffer = buffer;
|
|
@@ -13099,7 +13099,7 @@ var require_sync_reader = __commonJS((exports, module) => {
|
|
|
13099
13099
|
};
|
|
13100
13100
|
});
|
|
13101
13101
|
|
|
13102
|
-
//
|
|
13102
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/filter-parse-sync.js
|
|
13103
13103
|
var require_filter_parse_sync = __commonJS((exports) => {
|
|
13104
13104
|
var SyncReader = require_sync_reader();
|
|
13105
13105
|
var Filter = require_filter_parse();
|
|
@@ -13119,7 +13119,7 @@ var require_filter_parse_sync = __commonJS((exports) => {
|
|
|
13119
13119
|
};
|
|
13120
13120
|
});
|
|
13121
13121
|
|
|
13122
|
-
//
|
|
13122
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/parser-sync.js
|
|
13123
13123
|
var require_parser_sync = __commonJS((exports, module) => {
|
|
13124
13124
|
var hasSyncZlib = true;
|
|
13125
13125
|
var zlib = (init_zlib(), __toCommonJS(exports_zlib));
|
|
@@ -13205,7 +13205,7 @@ var require_parser_sync = __commonJS((exports, module) => {
|
|
|
13205
13205
|
};
|
|
13206
13206
|
});
|
|
13207
13207
|
|
|
13208
|
-
//
|
|
13208
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/packer-sync.js
|
|
13209
13209
|
var require_packer_sync = __commonJS((exports, module) => {
|
|
13210
13210
|
var hasSyncZlib = true;
|
|
13211
13211
|
var zlib = (init_zlib(), __toCommonJS(exports_zlib));
|
|
@@ -13238,7 +13238,7 @@ var require_packer_sync = __commonJS((exports, module) => {
|
|
|
13238
13238
|
};
|
|
13239
13239
|
});
|
|
13240
13240
|
|
|
13241
|
-
//
|
|
13241
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/png-sync.js
|
|
13242
13242
|
var require_png_sync = __commonJS((exports) => {
|
|
13243
13243
|
var parse = require_parser_sync();
|
|
13244
13244
|
var pack = require_packer_sync();
|
|
@@ -13250,7 +13250,7 @@ var require_png_sync = __commonJS((exports) => {
|
|
|
13250
13250
|
};
|
|
13251
13251
|
});
|
|
13252
13252
|
|
|
13253
|
-
//
|
|
13253
|
+
// node_modules/.bun/pngjs@7.0.0/node_modules/pngjs/lib/png.js
|
|
13254
13254
|
var require_png = __commonJS((exports) => {
|
|
13255
13255
|
var util = (init_util(), __toCommonJS(exports_util));
|
|
13256
13256
|
var Stream = require_stream();
|
|
@@ -13377,7 +13377,7 @@ var require_png = __commonJS((exports) => {
|
|
|
13377
13377
|
// tools/test/src/visual/compare.ts
|
|
13378
13378
|
var {readFileSync, writeFileSync} = (() => ({}));
|
|
13379
13379
|
|
|
13380
|
-
//
|
|
13380
|
+
// node_modules/.bun/pixelmatch@7.2.0/node_modules/pixelmatch/index.js
|
|
13381
13381
|
function pixelmatch(img1, img2, output, width, height, options = {}) {
|
|
13382
13382
|
const {
|
|
13383
13383
|
threshold = 0.1,
|
|
@@ -14017,4 +14017,4 @@ export {
|
|
|
14017
14017
|
assertSafeUpdateMode
|
|
14018
14018
|
};
|
|
14019
14019
|
|
|
14020
|
-
//# debugId=
|
|
14020
|
+
//# debugId=51DC7226BD2C4BE464756E2164756E21
|