Humuhumu33 commited on
Commit
c5d2a4f
Β·
verified Β·
1 Parent(s): b05742c

kappa mirror: player music browser holo-games hub (235 objects, blake3-addressed)

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .gitattributes +25 -0
  2. b/00ba75b00204d793d1f53a8ea8117ce9f85f8d972601b38b90610d6aa7260c42 +0 -0
  3. b/00fca18e5e8b84d77cb8b15277f435facadf1d4a2e253c06606dbceaf32b3dd1 +107 -0
  4. b/01e35b81160fecabd3e3c4273aef5c16354978c2cfa459691c17393c71073af8 +3 -0
  5. b/024ba97c7a82deeb6e1bb6d5d2bc50482998a5c7b40b9709593ad13c02b0880b +2 -0
  6. b/02d1839e4ea70a15f2c74a223ccaae8c25d6e9dfe59eb4a5245fc7ba611fe11b +36 -0
  7. b/042296e5aa778bea5ab7738ddf2348fa24624594b635969de2dade303003c0e4 +3 -0
  8. b/0735f1235411301a0b029916eebf0afd12d6172218d88ccf704338bb0fe175db +90 -0
  9. b/08337e2c815a5857529850f731098df53f08cea9f3f10f4abfbbcd698ae7f8e6 +7 -0
  10. b/0979e7bbda9f6c6826edd5985e07df9a9c1ff46fe9a9237f96a38964c45a25f8 +95 -0
  11. b/0c2ce71ed4c90185264533d39fbc20256360d33ba67339004f659abea234e114 +3 -0
  12. b/0e6532482fd07af7080999d69b71e39021ad01e51db76d7fa4dd21ad55230ae4 +1 -0
  13. b/0efc17c59789fb4e0a5fdbcd5453a1cd048f46ecfdfbf7ea25adc10a2a726de5 +289 -0
  14. b/0f81d7cefcce467b1096895773003117f5b519c5eef3e58e73f72bb83a60873f +141 -0
  15. b/147d5e8e69d4267a89c077bbc57abcf0d090290141e2840ec47b6b569f62c216 +36 -0
  16. b/17106929ff73f05d27a651cc1d2f2a6ce8219e6f0178cbd4358d0bdd8f806d46 +80 -0
  17. b/184ff87b24d7fd1527420d2e895f8d6a829e58536018d6518c3697a38d9bc11d +42 -0
  18. b/1866a03ba1df11dac02458b4a919fb8ad3577872c016ba7f528e0159dd151703 +71 -0
  19. b/19f2d0028cf70070bfa1922c1bcc981972a9f527517d8d5b8f8ab36c8ce20039 +25 -0
  20. b/1ac6077adc6fa547c8937fbeaf064fbe060f8e7272602e341f758148a2d43a4f +1 -0
  21. b/1ae88505e750e8cd74448f6c46f20cc6a5d5a8e7167e6fac2ca9726946b652eb +2 -0
  22. b/1c96e9fb329de4c45f295222442767b3dea9e8ec16f51bae5edb1a081f3f65f1 +21 -0
  23. b/1ca52bd58fce88351972bad776549b81c983d43ccc86858d6ffc37a727d8ee4e +179 -0
  24. b/1cfa7aa1528b10e89b4dc0b051482de2c2ff3af821b1d13d9bcb2184ad55ff99 +146 -0
  25. b/1f0258122adee3b1a865da516c2ab2a91f22939cba63f999cc159118b753370b +49 -0
  26. b/1f1976bb4609e4bf91e616016ac1af4f81ad9d5d8b8c07360763e97db0b6029b +100 -0
  27. b/20317750d4063d3c9b8e4350458d28f0a021d419c46a4cb422a02089ff039005 +88 -0
  28. b/214e51f0f754eec5fb6bde71dc5011a5ec69a42420bc52156babaf7b6e27da26 +0 -0
  29. b/24338bb3184e12efd6b3e94683130a283c7a584f2f04e6ef85efec036371f230 +23 -0
  30. b/248172b3ccb76e102c66fec90bd358f3bb6ae35165e9016d3a18b4e0a8aa6f48 +2 -0
  31. b/2796065b82a04e8d3d3e5045070f564cbdddc3b3a7f6716d1ea1c241e4f69daa +44 -0
  32. b/28b4f28cbaa7bce7335085d8d5d44b04e0abd42a41fe8141f90cbf47aa06d41b +71 -0
  33. b/295b87c6e792570b60ebbe5053f59b66418e28d5afefb263fbffb5a2b0d66900 +3 -0
  34. b/29656a5df86aef555c69787686c3ded84c3187dc1d3f50cd0d1ffd514daade6c +21 -0
  35. b/2cd5d13e57edc5d4658bdb65efb329cb677f758f5c87d77cb9e95de87ba28821 +0 -0
  36. b/300bc8c5fe7cf6dfcfc61a0601b4e16082990da558f93b2408c74ab451228d59 +28 -0
  37. b/308bb1c176791ad8b0390e37036819cae063f6ee656e06839c1db6eca0b35430 +0 -0
  38. b/3409f443106e4dafbe11d69d59436026892e04e0a23b9533cf444c8ab13d837e +155 -0
  39. b/34a4996e7fc708c03acccb3efd9a39b995b81ba23fdfe440c81a4e598453803d +3 -0
  40. b/359adc26b17b4a3c1e8927a61fb169b4d6c757c7fc45d00669621aee4d2e93c2 +3 -0
  41. b/362148d3ecaabd71460e10f06bb75bb6b13535f17924fe4ea7ef8d73f9fe7c54 +42 -0
  42. b/364687deaf3342b8e0f9126100aade8f739a1650e6939d334b4eda67ba8e0058 +77 -0
  43. b/368419920dea19a3102a3c57db6b17a38001850aed2e4b73149ce44380701d07 +3 -0
  44. b/379cdc501c2fe6824601fa6ccfd64d32d7f23a9a19a5edad4ae4e25e4f93cdda +0 -0
  45. b/37a53f9635d37c43c9cff03a30e51d91c058ab0ac6f86d82fe82fb379093bc60 +1 -0
  46. b/37af9d7eda050c8fd1658e20475de9958012e8d128273512e0136c9a446f1535 +25 -0
  47. b/38c718bfb8248e8f2a7ea814eb19d0e06895b691dc2ba01453a3c864caafcd1a +3 -0
  48. b/391b5487b1ea161f001c35bfe921d71a1f4f21ca95f795600cdaa63ff1f3af11 +41 -0
  49. b/3933995a0bb0fe7dcbed795b3d0ef19b070833c1743a70c3c61939cf3c2eaf40 +38 -0
  50. b/3ad6f7a995b02b7307ee62d7867693bcfbd60388aac1964c8002452407e80dbe +110 -0
.gitattributes CHANGED
@@ -65,3 +65,28 @@ b/ed23baff6c0015f9a92afba85a8e1043afe1ca57733c08ad8ff79273270ef861 filter=lfs di
65
  b/535ed9bd32c3c964ae2cd9dbf69406fc007db53501a6bdbefe33c70d949e4b90 filter=lfs diff=lfs merge=lfs -text
66
  b/00b5326a4545594a940e078d74bec9064a36162a04eb3fd2a90fe1a4af14d841 filter=lfs diff=lfs merge=lfs -text
67
  b/86444b599981098edbc377ed17cf81236efd56321edb86119dcf516f80fc2632 filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  b/535ed9bd32c3c964ae2cd9dbf69406fc007db53501a6bdbefe33c70d949e4b90 filter=lfs diff=lfs merge=lfs -text
66
  b/00b5326a4545594a940e078d74bec9064a36162a04eb3fd2a90fe1a4af14d841 filter=lfs diff=lfs merge=lfs -text
67
  b/86444b599981098edbc377ed17cf81236efd56321edb86119dcf516f80fc2632 filter=lfs diff=lfs merge=lfs -text
68
+ b/01e35b81160fecabd3e3c4273aef5c16354978c2cfa459691c17393c71073af8 filter=lfs diff=lfs merge=lfs -text
69
+ b/042296e5aa778bea5ab7738ddf2348fa24624594b635969de2dade303003c0e4 filter=lfs diff=lfs merge=lfs -text
70
+ b/0c2ce71ed4c90185264533d39fbc20256360d33ba67339004f659abea234e114 filter=lfs diff=lfs merge=lfs -text
71
+ b/34a4996e7fc708c03acccb3efd9a39b995b81ba23fdfe440c81a4e598453803d filter=lfs diff=lfs merge=lfs -text
72
+ b/359adc26b17b4a3c1e8927a61fb169b4d6c757c7fc45d00669621aee4d2e93c2 filter=lfs diff=lfs merge=lfs -text
73
+ b/368419920dea19a3102a3c57db6b17a38001850aed2e4b73149ce44380701d07 filter=lfs diff=lfs merge=lfs -text
74
+ b/38c718bfb8248e8f2a7ea814eb19d0e06895b691dc2ba01453a3c864caafcd1a filter=lfs diff=lfs merge=lfs -text
75
+ b/61a8751a618d8a3a855472eddc0fffc64a0b44ea273987e18e0ca67a01eeda5c filter=lfs diff=lfs merge=lfs -text
76
+ b/69742e8de18bbb3ac727e14375e8ce462a41b02bd92510e2a3fb634b10c7d679 filter=lfs diff=lfs merge=lfs -text
77
+ b/6ca7817c05a3d0c0a17edf33151e8e1457e56058678ea0dcc791f8f87c83e1c3 filter=lfs diff=lfs merge=lfs -text
78
+ b/709a462105d2ef7e496a8249d00bb77db1d454cf5251dc3414e0e3f8bc3d218e filter=lfs diff=lfs merge=lfs -text
79
+ b/73cc153dce464dd873d7b0b6d36d07a8cf52fdcf937dd828b8b03729e20cb472 filter=lfs diff=lfs merge=lfs -text
80
+ b/779f33a5c4de9630428105a8fd4b43b3903901e5708384d32884e223773662c2 filter=lfs diff=lfs merge=lfs -text
81
+ b/950cfadaaad844f144b153ada64452ce4b58ea5710a5e3a0ab0ad36282737389 filter=lfs diff=lfs merge=lfs -text
82
+ b/bdd88349176eb46434e9b3d48ab2680a4f874312fc8c83954957ef3fea480f35 filter=lfs diff=lfs merge=lfs -text
83
+ b/cc9222220df89dceb1c32b506f7359e9e57556ca2ba6c7c9c5e8b8d26e2165d5 filter=lfs diff=lfs merge=lfs -text
84
+ b/ccaeb2253d7de91d5cd3776bb00cc20dcbe778c06023ca2281a1b8c7a4ae7be1 filter=lfs diff=lfs merge=lfs -text
85
+ b/cd60f6eca09e5b1dadfb89d5312f542ac5795b9198b44b0edc11449ce74e9b28 filter=lfs diff=lfs merge=lfs -text
86
+ b/d5189dda27226a2205ae1f5be29f46dce70e942ba7090e85e36777780d8a0b6d filter=lfs diff=lfs merge=lfs -text
87
+ b/d86c7acd179e9d6f7f1985a858ed2ef6382d038772938e9f5e131a48d5920f98 filter=lfs diff=lfs merge=lfs -text
88
+ b/da90eb74379c15cf062b5bcbcc1fcbf678cf411ec4049494e0428be4bc7bcc78 filter=lfs diff=lfs merge=lfs -text
89
+ b/daee0f757cced9dcf7c84ec013917a92b9daa71507600744403f2439cf30ad63 filter=lfs diff=lfs merge=lfs -text
90
+ b/dc1803fede15ca1fde6a3fb74180d4e1f23c3607395613cb68d8f2594886a5c1 filter=lfs diff=lfs merge=lfs -text
91
+ b/eda0598d0d05fd4c1a6275ae8620eec2845018e71326e74b781d60a0d2a0c6d2 filter=lfs diff=lfs merge=lfs -text
92
+ b/f21f24c8b61d9e142292626c92fbef6afa11e47e03c550054be2c3d527c07ba1 filter=lfs diff=lfs merge=lfs -text
b/00ba75b00204d793d1f53a8ea8117ce9f85f8d972601b38b90610d6aa7260c42 ADDED
Binary file (24.6 kB). View file
 
b/00fca18e5e8b84d77cb8b15277f435facadf1d4a2e253c06606dbceaf32b3dd1 ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // holo-kappa-anchor-witness.mjs β€” the ΞΊ-ANCHORING INVARIANT, proven across the REAL modules: every byte the
3
+ // unified streaming experience shows or plays is a content-addressed ΞΊ-object (or carries X-Holo-Source
4
+ // provenance) β€” never a raw, un-addressed network byte on a warm path. Composes the actual caches, source
5
+ // hub, and every resolver with fake fetches (no network), and asserts the whole pipeline is ΞΊ-anchored +
6
+ // Law-L5 tamper-refusing.
7
+ //
8
+ // Checks:
9
+ // 1 metadataKappa β€” a metadata response is content-addressed (sha256 ΞΊ) and re-served from ΞΊ (hit).
10
+ // 2 artKappa+L5 β€” an image is content-addressed; bytes that don't re-derive to the ΞΊ are REFUSED.
11
+ // 3 streamProvenance β€” every resolver's stream candidate carries provenance {resolver,kind,label}.
12
+ // 4 sourceAnchored β€” every catalogue item is tagged (_sourceId + provenance) by the hub.
13
+ // 5 metaTamperRefusedβ€” a forged metadata ΞΊ entry is refused (Law L5).
14
+ // 6 noRawWarmPath β€” the anchor predicate accepts ΞΊ/blob/data + provenanced, REJECTS a raw https URL.
15
+ //
16
+ // node holo-kappa-anchor-witness.mjs (from holo-apps/apps/player/)
17
+
18
+ import { writeFileSync } from "node:fs";
19
+ import { fileURLToPath } from "node:url";
20
+ import { dirname, join } from "node:path";
21
+ import { createMediaCache, memKV as memKVm, address } from "./holo-media-cache.mjs";
22
+ import { createArtCache, memKV as memKVa } from "./holo-art-cache.mjs";
23
+ import { createSourceHub } from "./holo-source.mjs";
24
+ import { createIA } from "./holo-source-ia.mjs";
25
+ import { createRealDebrid, resolveStream } from "./holo-realdebrid.mjs";
26
+ import { createJellyfinProvider } from "./holo-source-jellyfin.mjs";
27
+
28
+ const here = dirname(fileURLToPath(import.meta.url));
29
+ const checks = {}; const fail = [];
30
+ const ok = (n, c, d = "") => { checks[n] = !!c; if (!c) fail.push(n + (d ? ` β€” ${d}` : "")); return !!c; };
31
+
32
+ // the anchor predicate the player must honour on every warm-path source.
33
+ const isAnchored = (src, provenance) => /^sha256:|^blob:|^data:/.test(String(src || "")) || !!(provenance && provenance.label);
34
+
35
+ // 1 β€” metadata is content-addressed + re-served from ΞΊ
36
+ {
37
+ const cache = createMediaCache({ kv: memKVm() });
38
+ let calls = 0;
39
+ const fetcher = async () => { calls++; return { title: "Inception", year: 2010 }; };
40
+ const a = await cache.through("meta|inception", fetcher);
41
+ const b = await cache.through("meta|inception", fetcher);
42
+ ok("metadataKappa", /^sha256:/.test(a.kappa) && b.fromCache === true && calls === 1, JSON.stringify({ kappa: a.kappa.slice(0, 14), calls }));
43
+ }
44
+ // 2 β€” art is content-addressed + Law-L5 verified (tampered bytes refused)
45
+ {
46
+ const kv = memKVa();
47
+ const bytes = new TextEncoder().encode("\x89PNG-poster-bytes");
48
+ const art = createArtCache({ kv, fetch: async () => ({ ok: true, arrayBuffer: async () => bytes.buffer.slice(0) }) });
49
+ const r = await art.resolve("https://img/p.jpg");
50
+ const good = (await art.bytesFor(r.kappa)) != null;
51
+ // forge: overwrite the blob under its ΞΊ with different bytes β†’ must refuse
52
+ const kv2 = memKVa(); const forged = createArtCache({ kv: kv2 });
53
+ kv2.set("b:" + r.kappa, Buffer.from(new TextEncoder().encode("tampered")).toString("base64"));
54
+ const refused = (await forged.bytesFor(r.kappa)) === null && forged.stats().refused >= 1;
55
+ ok("artKappa+L5", /^sha256:/.test(r.kappa) && good && refused, JSON.stringify({ kappa: r.kappa.slice(0, 14), refused }));
56
+ }
57
+ // 3 β€” every resolver's stream candidate carries provenance
58
+ {
59
+ const iaFetch = async (u) => ({ ok: true, json: async () => (/advancedsearch/.test(u) ? { response: { docs: [{ identifier: "x", title: "X" }] } } : { files: [{ name: "x.mp4", format: "h.264" }] }) });
60
+ const ia = createIA({ fetch: iaFetch });
61
+ const iaStream = (await ia.resolve({ _iaId: "x" }))[0];
62
+ const rd = createRealDebrid({ token: "t", fetch: async () => ({ ok: true, json: async () => ({ download: "https://x.rdeb.io/f.mp4", filename: "f.mp4" }) }) });
63
+ const rdStream = await resolveStream(rd, { url: "https://hoster/x" });
64
+ const jf = createJellyfinProvider({ base: "https://j", token: "T", userId: "U", name: "Home", fetch: async () => ({ ok: true, json: async () => ({ Items: [] }) }) });
65
+ const jfStream = (await jf.resolve({ _jfId: "m1" }))[0];
66
+ const provd = [iaStream, rdStream, jfStream].every((s) => s && s.provenance && s.provenance.label && s.provenance.kind);
67
+ ok("streamProvenance", provd, JSON.stringify([iaStream, rdStream, jfStream].map((s) => s && s.provenance && s.provenance.label)));
68
+ }
69
+ // 4 β€” every catalogue item is tagged by the hub (_sourceId + provenance)
70
+ {
71
+ const hub = createSourceHub();
72
+ hub.register({ id: "P", name: "P", kind: "open", async catalogs() { return [{ id: "c", type: "movie", name: "C" }]; }, async browse() { return [{ id: "i1", name: "Title", source: "tmdb" }]; }, async resolve() { return []; } });
73
+ const shelves = await hub.discover();
74
+ const it = shelves[0].items[0];
75
+ ok("sourceAnchored", it._sourceId === "P" && it.provenance && it.provenance.label === "P", JSON.stringify(it.provenance));
76
+ }
77
+ // 5 β€” forged metadata ΞΊ refused (Law L5)
78
+ {
79
+ const kv = memKVm();
80
+ const realKappa = await address({ a: 1 });
81
+ kv.set("req:k", realKappa); kv.set("obj:" + realKappa, JSON.stringify({ id: realKappa, body: { a: 999 } }));
82
+ const cache = createMediaCache({ kv });
83
+ ok("metaTamperRefused", (await cache.get("k")) === null && cache.stats().refused >= 1, JSON.stringify(cache.stats()));
84
+ }
85
+ // 6 β€” the anchor predicate rejects a raw warm-path URL
86
+ {
87
+ ok("noRawWarmPath",
88
+ isAnchored("sha256:abc") && isAnchored("blob:http://x/1") && isAnchored("data:image/png;base64,AA") &&
89
+ isAnchored("https://addon/strm.mp4", { label: "Real-Debrid", kind: "rd" }) &&
90
+ isAnchored("https://x/raw.mp4") === false,
91
+ "predicate");
92
+ }
93
+
94
+ const witnessed = Object.values(checks).every(Boolean);
95
+ const result = {
96
+ "@type": "earl:TestResult",
97
+ spec: "holo-kappa-anchor β€” the ΞΊ-anchoring invariant across the real streaming modules: metadata + art are content-addressed ΞΊ (Law-L5 verified, tamper-refused), every stream candidate + catalogue item carries provenance, and the anchor predicate rejects a raw un-addressed warm-path URL. Proof that the unified experience is 100% anchored in the ΞΊ-addressable substrate.",
98
+ authority: "rests on #holo-kappa-anchor over #holo-media-cache + #holo-art-cache + #holo-source + #holo-resolvers β€” Step 0 of the unified-streaming action plan",
99
+ witnessed,
100
+ covers: witnessed ? ["metadata-kappa", "art-kappa-l5", "stream-provenance", "source-anchored", "meta-tamper-refused", "no-raw-warm-path"] : [],
101
+ checks, failed: fail,
102
+ };
103
+ writeFileSync(join(here, "holo-kappa-anchor-witness.result.json"), JSON.stringify(result, null, 2) + "\n");
104
+ console.log("holo-kappa-anchor witness β€” 100% ΞΊ-anchored: every byte content-addressed or provenanced\n");
105
+ for (const [k, val] of Object.entries(checks)) console.log(` ${val ? "βœ“" : "βœ—"} ${k}`);
106
+ console.log(`\n ${witnessed ? "WITNESSED βœ“ the unified experience is anchored in the ΞΊ-substrate, end to end" : "NOT witnessed β€” " + fail.join("; ")}`);
107
+ process.exit(witnessed ? 0 : 1);
b/01e35b81160fecabd3e3c4273aef5c16354978c2cfa459691c17393c71073af8 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:73c0b6b62c440decc5e0f05bd5a27a04bc36ed737908b35c0c3141e7a3073756
3
+ size 262144
b/024ba97c7a82deeb6e1bb6d5d2bc50482998a5c7b40b9709593ad13c02b0880b ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ async function Snes9xCore(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var programArgs=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}programArgs=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var EXITSTATUS;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["r"]();FS.ignorePermissions=false}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("snes9x.wasm")}return new URL("snes9x.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>(nodeCrypto.randomFillSync(view),0)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[start]!=="")break}var end=arr.length-1;for(;end>=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i<length;i++){if(fromParts[i]!==toParts[i]){samePartsLength=i;break}}var outputParts=[];for(var i=samePartsLength;i<fromParts.length;i++){outputParts.push("..")}outputParts=outputParts.concat(toParts.slice(samePartsLength));return outputParts.join("/")}};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=stream.tty.ops.get_char(stream.tty)}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.atime=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.put_char){throw new FS.ErrnoError(60)}try{for(var i=0;i<length;i++){stream.tty.ops.put_char(stream.tty,buffer[offset+i])}}catch(e){throw new FS.ErrnoError(29)}if(length){stream.node.mtime=stream.node.ctime=Date.now()}return i}},default_tty_ops:{get_char(tty){return FS_stdin_getChar()},put_char(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=MEMFS.emptyFileContents??=new Uint8Array(0)}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){return node.contents.subarray(0,node.usedBytes)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents.length;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity<CAPACITY_DOUBLING_MAX?2:1.125)>>>0);if(prevCapacity)newCapacity=Math.max(newCapacity,256);var oldContents=MEMFS.getFileDataAsTypedArray(node);node.contents=new Uint8Array(newCapacity);node.contents.set(oldContents)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;var oldContents=node.contents;node.contents=new Uint8Array(newSize);node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));node.usedBytes=newSize},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack="<generic error, no stack>"}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);buffer.set(contents.subarray(position,position+size),offset);return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.mtime=node.ctime=Date.now();if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length}else{MEMFS.expandFileStorage(node,position+length);node.contents.set(buffer.subarray(offset,offset+length),position);node.usedBytes=Math.max(node.usedBytes,position+length)}return length},llseek(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===HEAP8.buffer){allocated=false;ptr=contents.byteOffset}else{allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}if(contents){if(position>0||position+length<contents.length){if(contents.subarray){contents=contents.subarray(position,position+length)}else{contents=Array.prototype.slice.call(contents,position,position+length)}}HEAP8.set(contents,ptr)}}return{ptr,allocated}},msync(stream,buffer,offset,length,mmapFlags){MEMFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS_modeStringToFlags=str=>{if(typeof str!="string")return str;var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_fileDataToTypedArray=data=>{if(typeof data=="string"){data=intArrayFromString(data,true)}if(!data.subarray){data=new Uint8Array(data)}return data};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var runDependencies=0;var dependenciesFulfilled=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}};var addRunDependency=id=>{runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i<parts.length;i++){var islast=i===parts.length-1;if(islast&&opts.parent){break}if(parts[i]==="."){continue}if(parts[i]===".."){current_path=PATH.dirname(current_path);if(FS.isRoot(current)){path=current_path+"/"+parts.slice(i+1).join("/");nlinks--;continue linkloop}else{current=current.parent}continue}current_path=PATH.join2(current_path,parts[i]);try{current=FS.lookupNode(current,parts[i])}catch(e){if(e?.errno===44&&islast&&opts.noent_okay){return{path:current_path}}throw e}if(FS.isMountpoint(current)&&(!islast||opts.follow_mount)){current=current.mounted.root}if(FS.isLink(current.mode)&&(!islast||opts.follow)){if(!current.node_ops.readlink){throw new FS.ErrnoError(52)}var link=current.node_ops.readlink(current);if(!PATH.isAbs(link)){link=PATH.dirname(current_path)+"/"+link}path=link+"/"+parts.slice(i+1).join("/");continue linkloop}}return{path:current_path,node:current}}throw new FS.ErrnoError(32)},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i<name.length;i++){hash=(hash<<5)-hash+name.charCodeAt(i)|0}return(parentid+hash>>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}if(perms.includes("w")&&!(node.mode&146)){return 2}if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else if(FS.isDir(node.mode)){return 31}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}var mode=FS.flagsToPermissionString(flags);if(FS.isDir(node.mode)){if(mode!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,mode)},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);try{setattr(arg,attr)}catch(e){if(e instanceof RangeError){throw new FS.ErrnoError(22)}throw e}},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=FS_modeStringToFlags(flags);if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags??0;opts.encoding=opts.encoding??"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags??577;var stream=FS.open(path,opts.flags,opts.mode);data=FS_fileDataToTypedArray(data);FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn);FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){data=FS_fileDataToTypedArray(data);FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);FS.createDevice.major??=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output?.buffer?.length){output(10)}},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=input()}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.atime=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){for(var i=0;i<length;i++){try{output(buffer[offset+i])}catch(e){throw new FS.ErrnoError(29)}}if(length){stream.node.mtime=stream.node.ctime=Date.now()}return i}});return FS.mkdev(path,mode,dev)},forceLoadFile(obj){if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(globalThis.XMLHttpRequest){abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else{try{obj.contents=readBinary(obj.url)}catch(e){throw new FS.ErrnoError(29)}}},createLazyFile(parent,name,url,canRead,canWrite){class LazyUint8Array{lengthKnown=false;chunks=[];get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort(`invalid range (${from}, ${to}) or no bytes requested!`);if(to>datalength-1)abort(`only ${datalength} bytes available! programmer error!`);var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText??"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i<size;i++){buffer[offset+i]=contents[position+i]}}else{for(var i=0;i<size;i++){buffer[offset+i]=contents.get(position+i)}}return size}stream_ops.read=(stream,buffer,offset,length,position)=>{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var SYSCALLS={currentUmask:18,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>2]=stat.dev;HEAPU32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAPU32[buf+12>>2]=stat.uid;HEAPU32[buf+16>>2]=stat.gid;HEAPU32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>2]=stats.bsize;HEAPU32[buf+60>>2]=stats.bsize;HEAP64[buf+8>>3]=BigInt(stats.blocks);HEAP64[buf+16>>3]=BigInt(stats.bfree);HEAP64[buf+24>>3]=BigInt(stats.bavail);HEAP64[buf+32>>3]=BigInt(stats.files);HEAP64[buf+40>>3]=BigInt(stats.ffree);HEAPU32[buf+48>>2]=stats.fsid;HEAPU32[buf+64>>2]=stats.flags;HEAPU32[buf+56>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.subarray(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();var mask=289792;stream.flags=stream.flags&~mask|arg&mask;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);function ___syscall_ftruncate64(fd,length){length=bigintToI53Checked(length);try{if(isNaN(length))return-22;FS.ftruncate(fd,length);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;if(flags&64){mode&=~SYSCALLS.currentUmask}return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>abort("");var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};function __localtime_js(time,tmPtr){time=bigintToI53Checked(time);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffset<winterOffset){stringToUTF8(winterName,std_name,17);stringToUTF8(summerName,dst_name,17)}else{stringToUTF8(winterName,dst_name,17);stringToUTF8(summerName,std_name,17)}};var _emscripten_date_now=()=>Date.now();var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram;var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(globalThis.navigator?.language??"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var _environ_get=(__environ,environ_buf)=>{var bufSize=0;var envp=0;for(var string of getEnvStrings()){var ptr=environ_buf+bufSize;HEAPU32[__environ+envp>>2]=ptr;bufSize+=stringToUTF8(string,ptr,Infinity)+1;envp+=4}return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;for(var string of strings){bufSize+=lengthBytesUTF8(string)+1}HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr<len)break;if(typeof offset!="undefined"){offset+=curr}}return ret};function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 22;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr<len){break}if(typeof offset!="undefined"){offset+=curr}}return ret};function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var updateTableMap=(offset,count)=>{if(functionsInTableMap){for(var i=offset;i<offset+count;i++){var item=getWasmTableEntry(i);if(item){functionsInTableMap.set(item,i)}}}};var functionsInTableMap;var getFunctionAddress=func=>{if(!functionsInTableMap){functionsInTableMap=new WeakMap;updateTableMap(0,wasmTable.length)}return functionsInTableMap.get(func)||0};var freeTableIndexes=[];var getEmptyTableSlot=()=>{if(freeTableIndexes.length){return freeTableIndexes.pop()}return wasmTable["grow"](1)};var setWasmTableEntry=(idx,func)=>{wasmTable.set(idx,func);wasmTableMirror[idx]=wasmTable.get(idx)};var uleb128EncodeWithLen=arr=>{const n=arr.length;return[n%128|128,n>>7,...arr]};var wasmTypeCodes={i:127,p:127,j:126,f:125,d:124,e:111};var generateTypePack=types=>uleb128EncodeWithLen(Array.from(types,type=>{var code=wasmTypeCodes[type];return code}));var convertJsFunctionToWasm=(func,sig)=>{var bytes=Uint8Array.of(0,97,115,109,1,0,0,0,1,...uleb128EncodeWithLen([1,96,...generateTypePack(sig.slice(1)),...generateTypePack(sig[0]==="v"?"":sig[0])]),2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);var module=new WebAssembly.Module(bytes);var instance=new WebAssembly.Instance(module,{e:{f:func}});var wrappedFunc=instance.exports["f"];return wrappedFunc};var addFunction=(func,sig)=>{var rtn=getFunctionAddress(func);if(rtn){return rtn}var ret=getEmptyTableSlot();try{setWasmTableEntry(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);setWasmTableEntry(ret,wrapped)}functionsInTableMap.set(func,ret);return ret};var removeFunction=index=>{functionsInTableMap.delete(getWasmTableEntry(index));setWasmTableEntry(index,null);freeTableIndexes.push(index)};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])programArgs=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["removeFunction"]=removeFunction;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["UTF8ToString"]=UTF8ToString;var _retro_set_video_refresh,_retro_set_audio_sample,_retro_set_audio_sample_batch,_retro_set_input_poll,_retro_set_input_state,_retro_set_environment,_retro_get_system_av_info,_retro_get_system_info,_retro_api_version,_retro_reset,_retro_load_game,_retro_unload_game,_retro_init,_retro_run,_retro_deinit,_retro_serialize_size,_retro_serialize,_retro_unserialize,_malloc,_free,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_retro_set_video_refresh=Module["_retro_set_video_refresh"]=wasmExports["s"];_retro_set_audio_sample=Module["_retro_set_audio_sample"]=wasmExports["t"];_retro_set_audio_sample_batch=Module["_retro_set_audio_sample_batch"]=wasmExports["u"];_retro_set_input_poll=Module["_retro_set_input_poll"]=wasmExports["v"];_retro_set_input_state=Module["_retro_set_input_state"]=wasmExports["w"];_retro_set_environment=Module["_retro_set_environment"]=wasmExports["x"];_retro_get_system_av_info=Module["_retro_get_system_av_info"]=wasmExports["y"];_retro_get_system_info=Module["_retro_get_system_info"]=wasmExports["z"];_retro_api_version=Module["_retro_api_version"]=wasmExports["A"];_retro_reset=Module["_retro_reset"]=wasmExports["B"];_retro_load_game=Module["_retro_load_game"]=wasmExports["C"];_retro_unload_game=Module["_retro_unload_game"]=wasmExports["D"];_retro_init=Module["_retro_init"]=wasmExports["E"];_retro_run=Module["_retro_run"]=wasmExports["F"];_retro_deinit=Module["_retro_deinit"]=wasmExports["G"];_retro_serialize_size=Module["_retro_serialize_size"]=wasmExports["H"];_retro_serialize=Module["_retro_serialize"]=wasmExports["I"];_retro_unserialize=Module["_retro_unserialize"]=wasmExports["J"];_malloc=Module["_malloc"]=wasmExports["K"];_free=Module["_free"]=wasmExports["L"];__emscripten_stack_restore=wasmExports["M"];__emscripten_stack_alloc=wasmExports["N"];_emscripten_stack_get_current=wasmExports["O"];memory=wasmMemory=wasmExports["q"];__indirect_function_table=wasmTable=wasmExports["P"]}var wasmImports={a:___syscall_fcntl64,k:___syscall_ftruncate64,e:___syscall_ioctl,f:___syscall_openat,o:__abort_js,l:__localtime_js,g:__tzset_js,m:_emscripten_date_now,p:_emscripten_resize_heap,h:_environ_get,i:_environ_sizes_get,j:_exit,b:_fd_close,n:_fd_read,d:_fd_seek,c:_fd_write};async function run(){preRun();if(runDependencies>0){await new Promise(resolve=>dependenciesFulfilled=resolve)}var setStatus=Module["setStatus"];if(setStatus){setStatus("Running...");await new Promise(resolve=>setTimeout(resolve,1));setTimeout(setStatus,1,"")}if(ABORT)return;initRuntime();Module["onRuntimeInitialized"]?.();postRun()}var wasmExports;wasmExports=await createWasm();await run();
2
+ ;return Module}export default Snes9xCore;
b/02d1839e4ea70a15f2c74a223ccaae8c25d6e9dfe59eb4a5245fc7ba611fe11b ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-source-kodi.mjs β€” a Kodi media center as a SourceProvider via its JSON-RPC HTTP API.
2
+ // VideoLibrary.GetMovies / .GetTVShows β†’ items; the item's `file` plays via Kodi's vfs URL.
3
+ // auth: optional Basic (user:password). injected fetch β†’ Node-witnessable.
4
+
5
+ export function createKodiProvider({ base, user, password, name, fetch: f, cache } = {}) {
6
+ base = String(base || "").replace(/\/+$/, "");
7
+ const doFetch = f || (typeof fetch !== "undefined" ? fetch.bind(globalThis) : null);
8
+ if (!doFetch) throw new Error("holo-source-kodi: fetch required");
9
+ const label = name || base.replace(/^https?:\/\//, "");
10
+ const authHdr = user ? { Authorization: "Basic " + (typeof btoa !== "undefined" ? btoa(`${user}:${password || ""}`) : Buffer.from(`${user}:${password || ""}`).toString("base64")) } : {};
11
+
12
+ async function rpc(method, params) {
13
+ const url = base + "/jsonrpc";
14
+ const go = async () => { const r = await doFetch(url, { method: "POST", headers: { "Content-Type": "application/json", ...authHdr }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }) }); if (!r.ok) throw new Error("kodi " + r.status); return r.json(); };
15
+ if (!cache) return (await go()).result;
16
+ const { body } = await cache.through("kodi|" + method + "|" + JSON.stringify(params || {}), go);
17
+ return body.result;
18
+ }
19
+ const vfs = (file) => `${base}/vfs/${encodeURIComponent(file)}`;
20
+ const normMovie = (m) => ({ id: "kodi:m:" + m.movieid, _file: m.file, kind: "movie", name: m.label || m.title || "Untitled", year: m.year || null, overview: m.plot || "", blurb: m.plot || "", posterUrl: m.thumbnail ? `${base}/image/${encodeURIComponent(m.thumbnail)}` : null, backdrop: m.fanart ? `${base}/image/${encodeURIComponent(m.fanart)}` : null, runtimeSec: m.runtime || 0, rating: typeof m.rating === "number" ? m.rating : null, genres: m.genre || [], topics: (m.genre || []).map((g) => String(g).toLowerCase()), channel: label, quality: 0.75, license: "", source: "tmdb", provider: "kodi", kappa: "", holoKappa: "kodi:m:" + m.movieid, availability: { playable: false, source: null, kappa: "", playSrc: "", type: "" } });
21
+ const normShow = (s) => ({ ...normMovie({ ...s, movieid: "tv" + s.tvshowid }), id: "kodi:s:" + s.tvshowid, kind: "series", _file: null });
22
+ const provider = {
23
+ id: "kodi:" + base, name: label, kind: "kodi", enabled: true, trust: 2,
24
+ async catalogs() { return [{ id: "movies", type: "movie", name: "Movies Β· " + label }, { id: "shows", type: "series", name: "Shows Β· " + label }]; },
25
+ async browse(catalogId) {
26
+ if (catalogId === "shows") { const r = await rpc("VideoLibrary.GetTVShows", { properties: ["title", "year", "plot", "thumbnail", "fanart", "genre", "rating"], limits: { end: 48 } }); return ((r && r.tvshows) || []).map(normShow); }
27
+ const r = await rpc("VideoLibrary.GetMovies", { properties: ["title", "year", "plot", "thumbnail", "fanart", "genre", "rating", "runtime", "file"], limits: { end: 48 } });
28
+ return ((r && r.movies) || []).map(normMovie);
29
+ },
30
+ async search(q) { const items = await provider.browse("movies"); return items.filter((x) => x.name.toLowerCase().includes(String(q).toLowerCase())); },
31
+ async resolve(item) { if (!item._file) return []; return [{ playSrc: vfs(item._file), type: "video/mp4", kind: "kodi", httpDirect: true, quality: 1080, provenance: { resolver: label, kind: "kodi", label: "Kodi Β· " + label } }]; },
32
+ };
33
+ return provider;
34
+ }
35
+ export default { createKodiProvider };
36
+ if (typeof window !== "undefined") window.HoloSourceKodi = { createKodiProvider };
b/042296e5aa778bea5ab7738ddf2348fa24624594b635969de2dade303003c0e4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9422ee2ca7b7ea1d46b58b2a429fff3f354dfd3e732dee1e7ae6220f148ce6e0
3
+ size 131072
b/0735f1235411301a0b029916eebf0afd12d6172218d88ccf704338bb0fe175db ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-retro-audio.js β€” the ONE new seam: a lock-free SPSC float32 audio ring.
2
+ //
3
+ // QEMU/Alpine never needed tight audio; a game console does β€” it is the difference
4
+ // between "feels like the hardware" and "feels like an emulator." This is the audio
5
+ // twin of holo-input-ring.js: a single-producer / single-consumer ring over
6
+ // SharedArrayBuffer + Atomics, so the emulation worker (producer) hands the core's
7
+ // `audio_sample_batch` samples to the AudioWorklet (consumer) WITHOUT either thread
8
+ // ever locking or blocking.
9
+ // β€’ producer = emulation worker: pushes interleaved stereo float32 from retro_run.
10
+ // β€’ consumer = AudioWorklet render thread: pops exactly the frames it needs each
11
+ // 128-sample quantum.
12
+ // β€’ bounded β†’ tight A/V sync; a full ring drops (never stalls the core), an empty
13
+ // ring underruns (counted, outputs silence) β€” both observable, never a
14
+ // glitch that wedges emulation.
15
+ //
16
+ // Layout over one SAB: i32[0]=readIdx, i32[1]=writeIdx (monotonic FRAME indices, where
17
+ // one "frame" = one stereo sample pair). The sample payload is a Float32Array view that
18
+ // starts after the 2-int header (8 bytes, 4-aligned β†’ valid for Float32).
19
+
20
+ const HEADER_I32 = 2; // i32[0]=read, i32[1]=write (in stereo frames)
21
+ const CH = 2; // interleaved stereo
22
+
23
+ export class AudioRing {
24
+ constructor(sab) {
25
+ this.i32 = new Int32Array(sab, 0, HEADER_I32);
26
+ this.f32 = new Float32Array(sab, HEADER_I32 * 4); // payload after the header
27
+ this.capFrames = (this.f32.length / CH) | 0; // ring capacity in stereo frames
28
+ this.stats = { pushed: 0, dropped: 0, popped: 0, underruns: 0 };
29
+ }
30
+ // capacityFrames β‰ˆ how many stereo sample-pairs the ring holds (latency budget).
31
+ static create(capacityFrames = 8192) {
32
+ return new SharedArrayBuffer(HEADER_I32 * 4 + capacityFrames * CH * 4);
33
+ }
34
+
35
+ // producer (emulation worker): push `frames` interleaved-stereo float32 samples.
36
+ // Drops the whole batch if it won't fit (back-pressure) rather than stall the core.
37
+ push(interleaved) {
38
+ const frames = (interleaved.length / CH) | 0;
39
+ const w = Atomics.load(this.i32, 1), r = Atomics.load(this.i32, 0);
40
+ if (w - r + frames > this.capFrames) { this.stats.dropped += frames; return false; }
41
+ for (let i = 0; i < frames; i++) {
42
+ const slot = ((w + i) % this.capFrames) * CH;
43
+ this.f32[slot] = interleaved[i * CH];
44
+ this.f32[slot + 1] = interleaved[i * CH + 1];
45
+ }
46
+ Atomics.store(this.i32, 1, w + frames); // publish (release)
47
+ this.stats.pushed += frames;
48
+ return true;
49
+ }
50
+
51
+ // consumer (AudioWorklet): pop up to `frames` into outL/outR (planar). Returns the
52
+ // number of frames actually delivered; the rest is silence (an underrun, counted).
53
+ pop(outL, outR, frames) {
54
+ const r = Atomics.load(this.i32, 0), w = Atomics.load(this.i32, 1);
55
+ const have = Math.min(frames, w - r);
56
+ for (let i = 0; i < have; i++) {
57
+ const slot = ((r + i) % this.capFrames) * CH;
58
+ outL[i] = this.f32[slot]; outR[i] = this.f32[slot + 1];
59
+ }
60
+ for (let i = have; i < frames; i++) { outL[i] = 0; outR[i] = 0; }
61
+ if (have < frames) this.stats.underruns += (frames - have);
62
+ Atomics.store(this.i32, 0, r + have); // free the consumed frames
63
+ this.stats.popped += have;
64
+ return have;
65
+ }
66
+
67
+ get pending() { return Atomics.load(this.i32, 1) - Atomics.load(this.i32, 0); } // frames buffered
68
+ }
69
+
70
+ // Linear resampler: the core runs at its native rate (e.g. 32000/44100 Hz); the device
71
+ // AudioContext is typically 48000. Resample interleaved-stereo float32 src→dst rate.
72
+ // Linear is the floor (cheap, no ringing); a windowed-sinc / hologram-backend kernel is
73
+ // the analytic upgrade slot, exactly as the video path has Catmull-Rom→learned.
74
+ export function resampleLinear(interleaved, srcRate, dstRate, channels = CH) {
75
+ if (srcRate === dstRate) return interleaved.slice();
76
+ const inFrames = (interleaved.length / channels) | 0;
77
+ const outFrames = Math.max(1, Math.round(inFrames * dstRate / srcRate));
78
+ const out = new Float32Array(outFrames * channels);
79
+ const ratio = (inFrames - 1) / Math.max(1, outFrames - 1);
80
+ for (let o = 0; o < outFrames; o++) {
81
+ const pos = o * ratio;
82
+ const i0 = Math.floor(pos), i1 = Math.min(inFrames - 1, i0 + 1);
83
+ const t = pos - i0;
84
+ for (let c = 0; c < channels; c++) {
85
+ const a = interleaved[i0 * channels + c], b = interleaved[i1 * channels + c];
86
+ out[o * channels + c] = a + (b - a) * t;
87
+ }
88
+ }
89
+ return out;
90
+ }
b/08337e2c815a5857529850f731098df53f08cea9f3f10f4abfbbcd698ae7f8e6 ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "library.json": "sha256:05775236425a2c869163db9e33cb052ca753570c957a89798c3fdb5a92021778",
3
+ "posters/7919ce70b9b799a413ccb07c47d6911e04d121616b116aad5d64552e35b6a264.jpg": "sha256:7919ce70b9b799a413ccb07c47d6911e04d121616b116aad5d64552e35b6a264",
4
+ "posters/5fe3d58399cf0c1e1d444546935dc33010bd6ad86805e1ee1f7adb547d7da15e.jpg": "sha256:5fe3d58399cf0c1e1d444546935dc33010bd6ad86805e1ee1f7adb547d7da15e",
5
+ "posters/bdf7cbdaa7862c4af9b8ccfb0b621ccf10b2461979d91e39010d566da62e948b.jpg": "sha256:bdf7cbdaa7862c4af9b8ccfb0b621ccf10b2461979d91e39010d566da62e948b",
6
+ "posters/b944d37c6c9450d98ce0412604be28484074aa1fe79fce045e53af6f8cb85421.jpg": "sha256:b944d37c6c9450d98ce0412604be28484074aa1fe79fce045e53af6f8cb85421"
7
+ }
b/0979e7bbda9f6c6826edd5985e07df9a9c1ff46fe9a9237f96a38964c45a25f8 ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // holo-source-adapters-witness.mjs β€” proves the EXHAUSTIVE sibling-adapter set: every common existing
3
+ // library/source implements the same SourceProvider interface (catalogs/browse→one shape/resolve→playable),
4
+ // each verified with a fake backend (no network). Together with Jellyfin+Plex+Stremio+IA+RD, this covers
5
+ // the realistic universe of "all existing movie/show/video catalogs."
6
+ //
7
+ // Checks (one per adapter): subsonic Β· kodi Β· peertube Β· m3u Β· youtube Β· local Β· webdav Β· emby-alias
8
+
9
+ import { writeFileSync } from "node:fs";
10
+ import { fileURLToPath } from "node:url";
11
+ import { dirname, join } from "node:path";
12
+ import { createSubsonicProvider } from "./holo-source-subsonic.mjs";
13
+ import { createKodiProvider } from "./holo-source-kodi.mjs";
14
+ import { createPeertubeProvider } from "./holo-source-peertube.mjs";
15
+ import { createM3UProvider, parseM3U } from "./holo-source-m3u.mjs";
16
+ import { createYouTubeProvider, parseYouTubeFeed } from "./holo-source-youtube.mjs";
17
+ import { createLocalProvider } from "./holo-source-local.mjs";
18
+ import { createWebDAVProvider } from "./holo-source-webdav.mjs";
19
+ import { createEmbyProvider } from "./holo-source-jellyfin.mjs";
20
+
21
+ const here = dirname(fileURLToPath(import.meta.url));
22
+ const checks = {}; const fail = [];
23
+ const ok = (n, c, d = "") => { checks[n] = !!c; if (!c) fail.push(n + (d ? ` β€” ${d}` : "")); return !!c; };
24
+ const json = (b) => async () => ({ ok: true, status: 200, json: async () => b });
25
+ const text = (s) => async () => ({ ok: true, status: 200, text: async () => s });
26
+
27
+ // subsonic
28
+ {
29
+ const sub = createSubsonicProvider({ base: "https://sub", user: "u", password: "p", name: "Navidrome", fetch: json({ "subsonic-response": { videos: { video: [{ id: "v1", title: "Clip", year: 2020, coverArt: "c1" }] } } }) });
30
+ const it = (await sub.browse())[0]; const r = await sub.resolve(it);
31
+ ok("subsonic", it.id === "sub:v1" && it.name === "Clip" && r[0].httpDirect && /\/rest\/stream\.view\?.*id=v1/.test(r[0].playSrc) && /Subsonic/.test(r[0].provenance.label), JSON.stringify(r[0] && r[0].playSrc));
32
+ }
33
+ // kodi
34
+ {
35
+ const kodi = createKodiProvider({ base: "https://kodi", name: "Kodi", fetch: json({ result: { movies: [{ movieid: 7, label: "Heat", year: 1995, file: "/movies/heat.mkv", thumbnail: "image://t", genre: ["Crime"] }] } }) });
36
+ const it = (await kodi.browse("movies"))[0]; const r = await kodi.resolve(it);
37
+ ok("kodi", it.id === "kodi:m:7" && it.kind === "movie" && r[0].httpDirect && /\/vfs\/.*heat\.mkv/.test(r[0].playSrc), JSON.stringify(r[0] && r[0].playSrc));
38
+ }
39
+ // peertube
40
+ {
41
+ const pt = createPeertubeProvider({ base: "https://tube", name: "PeerTube", fetch: json({ data: [{ uuid: "abc", name: "Doc", publishedAt: "2021-05-01", thumbnailPath: "/t.jpg", duration: 600, streamingPlaylists: [{ playlistUrl: "https://tube/hls/abc.m3u8" }] }] }) });
42
+ const it = (await pt.browse("recent"))[0]; const r = await pt.resolve(it);
43
+ ok("peertube", it.id === "pt:abc" && /thumbnailPath|t\.jpg/.test(it.posterUrl) && r[0].type === "application/x-mpegURL" && /abc\.m3u8/.test(r[0].playSrc), JSON.stringify(r[0] && r[0].playSrc));
44
+ }
45
+ // m3u
46
+ {
47
+ const pl = "#EXTM3U\n#EXTINF:-1 tvg-logo=\"l.png\" group-title=\"News\",CNN\nhttps://cdn/cnn.m3u8\n#EXTINF:-1,Movie\nhttps://cdn/movie.mp4\n";
48
+ const m = createM3UProvider({ url: "https://x/list.m3u", name: "IPTV", fetch: text(pl) });
49
+ const cats = await m.catalogs(); const items = await m.browse("News"); const r = await m.resolve((await m.browse("all")).find((x) => /Movie/.test(x.name)) || items[0]);
50
+ ok("m3u", parseM3U(pl).length === 2 && items[0].name === "CNN" && cats.some((c) => /News/.test(c.name)) && r[0].httpDirect, JSON.stringify({ cats: cats.map((c) => c.name), first: items[0] && items[0].name }));
51
+ }
52
+ // youtube (RSS, keyless)
53
+ {
54
+ const xml = `<feed><entry><yt:videoId>VID123</yt:videoId><title>Trailer</title><published>2023-01-02T00:00:00Z</published><media:thumbnail url="https://i.ytimg.com/vi/VID123/hq.jpg"/></entry></feed>`;
55
+ const yt = createYouTubeProvider({ channelId: "UCxyz", name: "Channel", fetch: text(xml) });
56
+ const it = (await yt.browse())[0]; const r = await yt.resolve(it);
57
+ ok("youtube", parseYouTubeFeed(xml).length === 1 && it.id === "yt:VID123" && it.name === "Trailer" && /sc\/vstream/.test(r[0].playSrc) && /youtube-nocookie\.com\/embed\/VID123/.test(r[0].embedFallback), JSON.stringify({ playSrc: r[0] && r[0].playSrc, fb: r[0] && r[0].embedFallback }));
58
+ }
59
+ // local / OPFS (fake directory handle)
60
+ {
61
+ const fakeDir = { async *entries() { yield ["Movie.mp4", { kind: "file", getFile: async () => ({ name: "Movie.mp4", type: "video/mp4" }) }]; yield ["notes.txt", { kind: "file", getFile: async () => ({}) }]; } };
62
+ const loc = createLocalProvider({ getDir: async () => fakeDir, name: "On this device" });
63
+ const items = await loc.browse();
64
+ try { Object.defineProperty(globalThis.URL, "createObjectURL", { value: () => "blob:fake/Movie.mp4", configurable: true }); } catch { globalThis.URL = { createObjectURL: () => "blob:fake/Movie.mp4" }; }
65
+ const r = await loc.resolve(items[0]);
66
+ ok("local", items.length === 1 && items[0].name === "Movie" && r[0].httpDirect && /^blob:/.test(r[0].playSrc), JSON.stringify({ n: items.length, src: r[0] && r[0].playSrc }));
67
+ }
68
+ // webdav (PROPFIND xml)
69
+ {
70
+ const xml = `<d:multistatus><d:response><d:href>/dav/Film.mp4</d:href><d:propstat><d:prop><d:displayname>Film.mp4</d:displayname></d:prop></d:propstat></d:response><d:response><d:href>/dav/readme.txt</d:href></d:response></d:multistatus>`;
71
+ const dav = createWebDAVProvider({ base: "https://cloud/dav", name: "Nextcloud", fetch: text(xml) });
72
+ const items = await dav.browse(); const r = await dav.resolve(items[0]);
73
+ ok("webdav", items.length === 1 && items[0].name === "Film" && r[0].httpDirect && /Film\.mp4/.test(r[0].playSrc), JSON.stringify({ n: items.length, src: r[0] && r[0].playSrc }));
74
+ }
75
+ // emby alias (Jellyfin-compatible)
76
+ {
77
+ const emby = createEmbyProvider({ base: "https://emby", token: "T", userId: "U", name: "Emby", fetch: json({ Items: [{ Id: "e1", Type: "Movie", Name: "Dune", ProductionYear: 2021, ImageTags: { Primary: "t" } }] }) });
78
+ const cats = await emby.catalogs(); const it = (await emby.browse("Movie"))[0]; const r = await emby.resolve({ _jfId: "e1" });
79
+ ok("emby", emby.kind === "emby" && it.id === "jf:e1" && /Videos\/e1\/master\.m3u8/.test(r[0].playSrc), JSON.stringify({ kind: emby.kind, src: r[0] && r[0].playSrc }));
80
+ }
81
+
82
+ const witnessed = Object.values(checks).every(Boolean);
83
+ const result = {
84
+ "@type": "earl:TestResult",
85
+ spec: "holo-source-adapters — the exhaustive sibling-adapter set (Subsonic/Navidrome · Kodi · PeerTube · M3U/IPTV · YouTube · local/OPFS · WebDAV/Nextcloud · Emby) each implementing the ONE SourceProvider interface: catalogs→rails, browse→the one item shape, resolve→a playable stream with provenance. With Jellyfin/Plex/Stremio/IA/RD this covers the realistic universe of existing movie/show/video catalogs.",
86
+ authority: "rests on #holo-source-{subsonic,kodi,peertube,m3u,youtube,local,webdav} + emby alias β€” full-library-interop",
87
+ witnessed,
88
+ covers: witnessed ? ["subsonic", "kodi", "peertube", "m3u", "youtube", "local", "webdav", "emby"] : [],
89
+ checks, failed: fail,
90
+ };
91
+ writeFileSync(join(here, "holo-source-adapters-witness.result.json"), JSON.stringify(result, null, 2) + "\n");
92
+ console.log("holo-source-adapters witness β€” every existing library, one interface\n");
93
+ for (const [k, val] of Object.entries(checks)) console.log(` ${val ? "βœ“" : "βœ—"} ${k}`);
94
+ console.log(`\n ${witnessed ? "WITNESSED βœ“ exhaustive library interop β€” SubsonicΒ·KodiΒ·PeerTubeΒ·M3UΒ·YouTubeΒ·localΒ·WebDAVΒ·Emby" : "NOT witnessed β€” " + fail.join("; ")}`);
95
+ process.exit(witnessed ? 0 : 1);
b/0c2ce71ed4c90185264533d39fbc20256360d33ba67339004f659abea234e114 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:546e96e238291c87da8ea049c1f27c7b5cd41963e413b59db2dabc18ba063607
3
+ size 262144
b/0e6532482fd07af7080999d69b71e39021ad01e51db76d7fa4dd21ad55230ae4 ADDED
@@ -0,0 +1 @@
 
 
1
+ <svg fill="#ffffff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Netflix</title><path d="m5.398 0 8.348 23.602c2.346.059 4.856.398 4.856.398L10.113 0H5.398zm8.489 0v9.172l4.715 13.33V0h-4.715zM5.398 1.5V24c1.873-.225 2.81-.312 4.715-.398V14.83L5.398 1.5z"/></svg>
b/0efc17c59789fb4e0a5fdbcd5453a1cd048f46ecfdfbf7ea25adc10a2a726de5 ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-app-worker.mjs β€” holds the resumed machine and serves the app's HTTP requests from the LIVE guest.
2
+ // The machine ticks continuously; each request from the service worker (relayed by the page) is dialed to
3
+ // the guest over the loopback bridge and its real response returned β€” so navigations, assets, and XHR all
4
+ // hit the running app, making it fully functional in the tab.
5
+ import init, { X64Workspace, kappa_manifest_pages } from "./pkg/holospaces_web.js";
6
+
7
+ const enc = new TextEncoder(), dec = new TextDecoder();
8
+ const bytesOf = async (p) => new Uint8Array(await (await fetch(p)).arrayBuffer());
9
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
10
+ const append = (a, b) => { const c = new Uint8Array(a.length + b.length); c.set(a); c.set(b, a.length); return c; };
11
+ function indexOfSeq(buf, seq) { outer: for (let i = 0; i <= buf.length - seq.length; i++) { for (let j = 0; j < seq.length; j++) if (buf[i + j] !== seq[j]) continue outer; return i; } return -1; }
12
+
13
+ let ws = null, port = 80, running = false;
14
+ let diskStats = null; // lazy mode: {faults, bytes} β€” disk ΞΊ-objects Range-streamed on fault
15
+
16
+ // ── OPFS object cache: one append-only local pack shared by every app on this origin. Objects are
17
+ // content-named (ΞΊ), so cross-app reuse is free β€” httpd finds the alpine base nginx already pulled,
18
+ // and a repeat open needs no network for objects at all. Exclusive sync-handle lock: if another tab
19
+ // holds it we silently run network-only.
20
+ const opfs = { ready: false, pack: null, idxH: null, idx: new Map(), size: 0, dir: null, dirty: [] };
21
+ async function opfsOpen() {
22
+ try {
23
+ const root = await navigator.storage.getDirectory();
24
+ opfs.dir = await root.getDirectoryHandle("holo-fabric", { create: true });
25
+ const pf = await opfs.dir.getFileHandle("objects.pack", { create: true });
26
+ opfs.pack = await pf.createSyncAccessHandle();
27
+ opfs.size = opfs.pack.getSize();
28
+ const xf = await opfs.dir.getFileHandle("objects.idx", { create: true });
29
+ opfs.idxH = await xf.createSyncAccessHandle();
30
+ const buf = new Uint8Array(opfs.idxH.getSize());
31
+ if (buf.length) opfs.idxH.read(buf, { at: 0 });
32
+ for (const line of dec.decode(buf).split("\n")) {
33
+ const [n, off, len] = line.split(" ");
34
+ if (n && len) opfs.idx.set(n, [Number(off), Number(len)]);
35
+ }
36
+ opfs.ready = true;
37
+ } catch (_) { opfs.ready = false; }
38
+ }
39
+ function opfsGet(name) {
40
+ if (!opfs.ready) return null;
41
+ const e = opfs.idx.get(name);
42
+ if (!e) return null;
43
+ const b = new Uint8Array(e[1]);
44
+ opfs.pack.read(b, { at: e[0] });
45
+ return b;
46
+ }
47
+ function opfsPut(name, bytes) {
48
+ if (!opfs.ready || opfs.idx.has(name)) return;
49
+ opfs.pack.write(bytes, { at: opfs.size });
50
+ opfs.idx.set(name, [opfs.size, bytes.length]);
51
+ opfs.dirty.push(`${name} ${opfs.size} ${bytes.length}\n`);
52
+ opfs.size += bytes.length;
53
+ }
54
+ function opfsFlush() {
55
+ if (!opfs.ready || !opfs.dirty.length) return;
56
+ const chunk = new TextEncoder().encode(opfs.dirty.join(""));
57
+ opfs.idxH.write(chunk, { at: opfs.idxH.getSize() });
58
+ opfs.dirty = [];
59
+ opfs.pack.flush(); opfs.idxH.flush();
60
+ }
61
+ function opfsPutBatch(entries) {
62
+ // One concatenated pack append instead of thousands of small sync writes.
63
+ if (!opfs.ready) return;
64
+ const fresh = entries.filter(([n]) => !opfs.idx.has(n));
65
+ if (!fresh.length) return;
66
+ let total = 0;
67
+ for (const [, b] of fresh) total += b.length;
68
+ const blob = new Uint8Array(total);
69
+ let at = 0;
70
+ for (const [n, b] of fresh) {
71
+ blob.set(b, at);
72
+ opfs.idx.set(n, [opfs.size + at, b.length]);
73
+ opfs.dirty.push(`${n} ${opfs.size + at} ${b.length}\n`);
74
+ at += b.length;
75
+ }
76
+ opfs.pack.write(blob, { at: opfs.size });
77
+ opfs.size += total;
78
+ opfsFlush();
79
+ }
80
+ // Small async side-files (manifest/idx twins) so a repeat open works fully offline.
81
+ async function opfsFileRead(name) {
82
+ try { const fh = await opfs.dir.getFileHandle(name); return new Uint8Array(await (await fh.getFile()).arrayBuffer()); } catch { return null; }
83
+ }
84
+ async function opfsFileWrite(name, bytes) {
85
+ try { const fh = await opfs.dir.getFileHandle(name, { create: true }); const w = await fh.createWritable(); await w.write(bytes); await w.close(); } catch {}
86
+ }
87
+
88
+ async function open(msg) {
89
+ const T = { t0: performance.now() };
90
+ await init(); T.init = performance.now();
91
+ port = msg.port || 80;
92
+ let bytesFetched = 0;
93
+ if (msg.pack && msg.lazy) {
94
+ // ── LAZY PACK path: fetch the manifest + ONLY the RAM working set (coalesced HTTP Range over
95
+ // the shared packfile); disk ΞΊ-objects stream ON FAULT via sync XHR Range. The pack index is
96
+ // itself lazy: it locates objects we DON'T have, so when OPFS covers the working set it is
97
+ // never fetched β€” a repeat open costs zero network and works fully offline.
98
+ const inflate = async (u8) => new Uint8Array(await new Response(
99
+ new Blob([u8]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).arrayBuffer());
100
+ const parseIdx = (text) => {
101
+ // objstore.idx: `<64-hex ΞΊ name> <off> <len>` per line.
102
+ const m = new Map();
103
+ for (const line of text.split("\n")) {
104
+ if (line.charCodeAt(64) !== 32) continue;
105
+ const sp = line.indexOf(" ", 65);
106
+ m.set(line.slice(0, 64), [Number(line.slice(65, sp)), Number(line.slice(sp + 1))]);
107
+ }
108
+ return m;
109
+ };
110
+ await opfsOpen();
111
+ // Manifest is content-keyed (immutable) β†’ OPFS-first; only a miss touches the network.
112
+ const mKey = "m-" + msg.manifestZ.split("/").pop();
113
+ let netBytes = 0;
114
+ let mzB = opfs.ready ? await opfsFileRead(mKey) : null;
115
+ if (!mzB) {
116
+ mzB = await bytesOf(msg.manifestZ);
117
+ netBytes += mzB.length;
118
+ if (opfs.ready) opfsFileWrite(mKey, mzB);
119
+ }
120
+ const manifest = await inflate(mzB);
121
+ // Index grows with the fabric β†’ network-first when needed, OPFS fallback for offline.
122
+ let idx = null;
123
+ const loadIdx = async () => {
124
+ try {
125
+ const b = msg.idxZ ? await bytesOf(msg.idxZ) : await fetch(msg.idx).then((r) => r.text());
126
+ netBytes += b.length;
127
+ if (opfs.ready && msg.idxZ) opfsFileWrite("objstore.idx.z", b);
128
+ return parseIdx(typeof b === "string" ? b : dec.decode(await inflate(b)));
129
+ } catch (e) {
130
+ const c = opfs.ready ? await opfsFileRead("objstore.idx.z") : null;
131
+ if (c) return parseIdx(dec.decode(await inflate(c)));
132
+ throw e;
133
+ }
134
+ };
135
+ // NOTHING is prefetched (CC-82 inc-3): RAM pages are FAULT-LAZY inside wasm and stream through
136
+ // the same ΞΊ-fetch as disk sectors. The only planning here is an OPFS coverage check β€” the pack
137
+ // idx is fetched only if some ΞΊ-object will have to come over the network.
138
+ const names = kappa_manifest_pages(manifest).map((s) => s.slice(7));
139
+ let covered = 0;
140
+ for (const n of names) if (opfs.ready && opfs.idx.has(n)) covered++;
141
+ if (covered < names.length) idx = await loadIdx();
142
+ bytesFetched = netBytes;
143
+ T.opfs = { covered, of: names.length, on: opfs.ready, idxSkipped: !idx };
144
+ T.fetch = performance.now();
145
+ // Disk objects: OPFS first, else sync XHR Range on fault (allowed in a worker). If a fault
146
+ // misses OPFS on an open that skipped the idx, sync-fetch the RAW idx once (rare escape hatch;
147
+ // deflate can't be inflated synchronously).
148
+ const idxSync = () => {
149
+ if (idx) return idx;
150
+ const xhr = new XMLHttpRequest();
151
+ xhr.open("GET", msg.idx, false);
152
+ xhr.send();
153
+ idx = xhr.status === 200 ? parseIdx(xhr.responseText) : new Map();
154
+ return idx;
155
+ };
156
+ const stats = (diskStats = { faults: 0, bytes: 0 });
157
+ const diskCache = new Map();
158
+ const rangeSync = (off, len) => {
159
+ const xhr = new XMLHttpRequest();
160
+ xhr.open("GET", msg.pack, false);
161
+ xhr.setRequestHeader("Range", `bytes=${off}-${off + len - 1}`);
162
+ try {
163
+ xhr.responseType = "arraybuffer";
164
+ xhr.send();
165
+ if (xhr.status !== 206 && xhr.status !== 200) return null;
166
+ return new Uint8Array(xhr.response);
167
+ } catch (_) {
168
+ // sync-XHR arraybuffer refused in this engine β†’ binary-string fallback
169
+ const x2 = new XMLHttpRequest();
170
+ x2.open("GET", msg.pack, false);
171
+ x2.overrideMimeType("text/plain; charset=x-user-defined");
172
+ x2.setRequestHeader("Range", `bytes=${off}-${off + len - 1}`);
173
+ x2.send();
174
+ if (x2.status !== 206 && x2.status !== 200) return null;
175
+ const s = x2.responseText, b = new Uint8Array(s.length);
176
+ for (let i = 0; i < s.length; i++) b[i] = s.charCodeAt(i) & 0xff;
177
+ return b;
178
+ }
179
+ };
180
+ // The ONE ΞΊ-fetch backing both RAM faults and disk faults: OPFS first; on a network fault,
181
+ // READ-AHEAD β€” pull a ~256 KiB window of neighboring ΞΊ-objects in one sync Range (objects that
182
+ // fault together sit together in the pack, which is written in manifest order) and persist the
183
+ // whole window, so a fault storm costs dozens of round-trips, not thousands.
184
+ let idxArr = null;
185
+ const idxArrOf = (m) => {
186
+ if (!idxArr) idxArr = [...m.entries()].map(([n, [o, l]]) => [o, l, n]).sort((a, b) => a[0] - b[0]);
187
+ return idxArr;
188
+ };
189
+ // Read-ahead size: the pack is DENSE (~560 B/object), so a big window is pure over-fetch β€”
190
+ // 256 KiB windows measured 32 MiB streamed for a ~3 MiB working set. 32 KiB keeps the
191
+ // sequential-fault clustering (manifest order = RAM page order) at bounded waste.
192
+ const WINDOW = 32768;
193
+ const kappaFetch = (name) => {
194
+ const c = diskCache.get(name);
195
+ if (c) return c;
196
+ const hit = opfsGet(name);
197
+ if (hit) { stats.faults++; diskCache.set(name, hit); return hit; }
198
+ const m = idxSync();
199
+ const e = m.get(name);
200
+ if (!e) return null;
201
+ const arr = idxArrOf(m);
202
+ let lo = 0, hi = arr.length;
203
+ while (lo < hi) { const mid = (lo + hi) >> 1; if (arr[mid][0] < e[0]) lo = mid + 1; else hi = mid; }
204
+ const w = [];
205
+ let end = e[0] + e[1];
206
+ for (let i = lo; i < arr.length && arr[i][0] + arr[i][1] - e[0] <= WINDOW; i++) { w.push(arr[i]); end = arr[i][0] + arr[i][1]; }
207
+ if (!w.length) w.push([e[0], e[1], name]);
208
+ const buf = rangeSync(e[0], end - e[0]);
209
+ if (!buf) return null;
210
+ stats.faults++; stats.bytes += buf.length;
211
+ const fresh = [];
212
+ for (const [o, l, n2] of w) {
213
+ const b = buf.subarray(o - e[0], o - e[0] + l);
214
+ if (!diskCache.has(n2)) { diskCache.set(n2, b); fresh.push([n2, b]); }
215
+ }
216
+ opfsPutBatch(fresh);
217
+ return diskCache.get(name) || null;
218
+ };
219
+ ws = X64Workspace.resume_kappa_pack_lazy_ram(manifest, kappaFetch);
220
+ T.resume = performance.now();
221
+ } else if (msg.pack) {
222
+ // ── PACK path (CC-81 M4 in the tab): fetch the deflate manifest + shared packfile + index β€”
223
+ // ~3x fewer bytes than the flat .holo, and the pack is SHARED across images (a 2nd alpine app
224
+ // re-fetches almost nothing once HTTP-cached). All inflate + L5 verify happens inside wasm;
225
+ // disk sectors stay LAZY (inflated from the resident pack on first guest read).
226
+ const [mz, idxText, pack] = await Promise.all([
227
+ bytesOf(msg.manifestZ),
228
+ fetch(msg.idx).then((r) => r.text()),
229
+ bytesOf(msg.pack),
230
+ ]);
231
+ bytesFetched = mz.length + idxText.length + pack.length;
232
+ T.fetch = performance.now();
233
+ ws = X64Workspace.resume_kappa_pack(mz, idxText, pack); T.resume = performance.now();
234
+ } else {
235
+ const blob = await bytesOf(msg.src); T.fetch = performance.now(); // fetch the whole .holo over HTTP
236
+ bytesFetched = blob.length;
237
+ ws = X64Workspace.resume_kappa(blob); T.resume = performance.now(); // rebuild the RUNNING machine β€” no boot
238
+ }
239
+ ws.enable_loopback();
240
+ running = true;
241
+ const tick = () => { if (!running) return; ws.run(2_000_000); setTimeout(tick, 0); };
242
+ tick();
243
+ self.postMessage({ type: "ready", timing: {
244
+ wasmInitMs: Math.round(T.init - T.t0),
245
+ blobFetchMs: Math.round(T.fetch - T.init),
246
+ resumeMs: Math.round(T.resume - T.fetch),
247
+ blobMiB: Math.round(bytesFetched / 1048576),
248
+ pack: !!msg.pack,
249
+ lazy: !!(msg.pack && msg.lazy),
250
+ opfs: T.opfs || null,
251
+ } });
252
+ }
253
+
254
+ // Serve one request from the guest over the loopback bridge.
255
+ async function serve(id, method, pth) {
256
+ if (!ws) return self.postMessage({ type: "served", id, status: 503, headers: {}, body: enc.encode("not ready") });
257
+ const conn = ws.dial_guest(port);
258
+ if (conn == null) return self.postMessage({ type: "served", id, status: 502, headers: {}, body: enc.encode("no guest") });
259
+ ws.guest_send(conn, enc.encode(`${method} ${pth} HTTP/1.0\r\nHost: app\r\nConnection: close\r\n\r\n`));
260
+
261
+ let raw = new Uint8Array(0), idle = 0, headEnd = -1, need = -1;
262
+ for (let i = 0; i < 4000; i++) {
263
+ const c = ws.guest_recv(conn);
264
+ if (c && c.length) { raw = append(raw, c); idle = 0; } else { idle++; }
265
+ if (headEnd < 0) {
266
+ headEnd = indexOfSeq(raw, enc.encode("\r\n\r\n"));
267
+ if (headEnd >= 0) { const m = /content-length:\s*(\d+)/i.exec(dec.decode(raw.slice(0, headEnd))); if (m) need = headEnd + 4 + Number(m[1]); }
268
+ }
269
+ if (headEnd >= 0 && need >= 0 && raw.length >= need) break; // full body received
270
+ if (headEnd >= 0 && need < 0 && idle > 160) break; // no content-length: idle after data = done
271
+ if (idle > 400) break; // give up if the guest never answers
272
+ await sleep(2);
273
+ }
274
+
275
+ let status = 200, headers = {}, body = raw;
276
+ if (headEnd >= 0) {
277
+ const lines = dec.decode(raw.slice(0, headEnd)).split("\r\n");
278
+ const ms = /HTTP\/\d\.\d (\d+)/.exec(lines[0] || ""); if (ms) status = Number(ms[1]);
279
+ for (const l of lines.slice(1)) { const i = l.indexOf(":"); if (i > 0) headers[l.slice(0, i).trim().toLowerCase()] = l.slice(i + 1).trim(); }
280
+ body = raw.slice(headEnd + 4);
281
+ }
282
+ self.postMessage({ type: "served", id, status, headers, body, disk: diskStats && { ...diskStats } }, [body.buffer]);
283
+ }
284
+
285
+ self.onmessage = (e) => {
286
+ const d = e.data;
287
+ if (d.type === "open") open(d).catch((err) => self.postMessage({ type: "error", error: String((err && err.stack) || err) }));
288
+ else if (d.type === "serve") serve(d.id, d.method, d.path);
289
+ };
b/0f81d7cefcce467b1096895773003117f5b519c5eef3e58e73f72bb83a60873f ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-install.js β€” make Hologram a ONE-TAP, Chrome-less install on any device, and claim
2
+ // web+hologram:// links so a single link opens the installed app. The OS becomes its own window:
3
+ // the manifest (display:fullscreen) drops all browser chrome and Hologram's own chrome takes over.
4
+ //
5
+ // Drop-in: <script src="./holo-install.js" defer></script> (exposes window.HoloInstall)
6
+ //
7
+ // Effortless by design: a subtle glass pill appears once when the device CAN install (the browser's
8
+ // beforeinstallprompt) β€” one tap fires the native install dialog; after that it lives in the dock /
9
+ // home screen and every web+hologram://ΞΊ link opens it directly. Pure DOM + Web APIs, no deps (L4).
10
+
11
+ (function () {
12
+ "use strict";
13
+ var W = window; if (W.HoloInstall) return;
14
+ if (typeof document === "undefined") return;
15
+ try { if (W.top !== W.self) return; } catch (e) { return; } // top-level only
16
+
17
+ var DOC = document, root = DOC.documentElement;
18
+ var SELF = (DOC.currentScript && DOC.currentScript.src) || (DOC.querySelector('script[src*="holo-install.js"]') || {}).src || new URL("./holo-install.js", location.href).href;
19
+ var base = SELF.replace(/holo-install\.js.*$/, "");
20
+ var ROOT = new URL("../", base).href;
21
+ var LOGO = new URL("usr/share/icons/hologram-light.svg", ROOT).href;
22
+
23
+ function isInstalled() {
24
+ try {
25
+ if (W.navigator && W.navigator.standalone === true) return true;
26
+ if (W.matchMedia) for (var m of ["standalone", "fullscreen", "window-controls-overlay"]) if (W.matchMedia("(display-mode: " + m + ")").matches) return true;
27
+ } catch (e) {}
28
+ return false;
29
+ }
30
+ // iOS / iPadOS Safari is the ONE platform that can install a PWA but never fires beforeinstallprompt
31
+ // β€” the only route is Share β†’ "Add to Home Screen", so we surface that explicitly. (iPadOS 13+
32
+ // masquerades as MacIntel, caught by the touch-point check.)
33
+ function isIOS() {
34
+ try {
35
+ var ua = navigator.userAgent || "";
36
+ return /iPad|iPhone|iPod/.test(ua) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
37
+ } catch (e) { return false; }
38
+ }
39
+ function dismissed() { try { return localStorage.getItem("holo.install.dismissed") === "1"; } catch (e) { return false; } }
40
+ function setDismissed() { try { localStorage.setItem("holo.install.dismissed", "1"); } catch (e) {} }
41
+
42
+ // claim web+hologram:// + web+holo:// links (best-effort; the manifest also declares them on install).
43
+ try {
44
+ if (navigator.registerProtocolHandler) {
45
+ var h = ROOT + "shell.html?open=%s"; // the canonical shell resolves holo:// Β· did Β· web from its omnibar
46
+ navigator.registerProtocolHandler("web+hologram", h);
47
+ navigator.registerProtocolHandler("web+holo", h);
48
+ }
49
+ } catch (e) {}
50
+
51
+ function css() {
52
+ if (DOC.getElementById("holo-install-css")) return;
53
+ var s = DOC.createElement("style"); s.id = "holo-install-css";
54
+ s.textContent =
55
+ "#holo-install{position:fixed;left:50%;bottom:calc(1.1rem + var(--holo-dock-h,0px));transform:translateX(-50%) translateY(1.2rem);z-index:2147483000;" +
56
+ "display:flex;align-items:center;gap:.7rem;padding:.55rem .65rem .55rem .85rem;border-radius:999px;opacity:0;pointer-events:none;" +
57
+ "background:var(--holo-glass-acrylic-bg,color-mix(in srgb,rgba(22,26,34,.82) 86%,var(--holo-accent,#5b8cff) 14%));" +
58
+ "-webkit-backdrop-filter:var(--holo-glass-acrylic-fx,blur(22px) saturate(1.8) brightness(1.06));backdrop-filter:var(--holo-glass-acrylic-fx,blur(22px) saturate(1.8) brightness(1.06));" +
59
+ "border:1px solid var(--holo-glass-border,rgba(255,255,255,.5));box-shadow:0 .6rem 2.4rem rgba(0,0,0,.5),inset 0 1px 0 0 rgba(255,255,255,.28);" +
60
+ "color:var(--holo-ink,#e9eef7);font:var(--holo-text-sm,1rem)/1.2 var(--holo-font-sans,system-ui,-apple-system,'Segoe UI',sans-serif);transition:opacity .28s ease,transform .28s cubic-bezier(.2,.8,.2,1)}" +
61
+ "#holo-install.in{opacity:1;transform:translateX(-50%) translateY(0);pointer-events:auto}" +
62
+ "#holo-install .hi-logo{width:1.6rem;height:1.6rem;flex:0 0 auto;filter:drop-shadow(0 1px 3px rgba(0,0,0,.5))}" +
63
+ "#holo-install .hi-txt{display:flex;flex-direction:column;line-height:1.15;white-space:nowrap}" +
64
+ "#holo-install .hi-txt b{font-weight:600}#holo-install .hi-txt span{font-size:.82em;opacity:.7}" +
65
+ "#holo-install .hi-go{appearance:none;border:0;cursor:pointer;border-radius:999px;padding:.42rem .95rem;margin-left:.3rem;font:inherit;font-weight:600;" +
66
+ "color:#fff;background:linear-gradient(180deg,color-mix(in srgb,var(--holo-accent,#5b8cff) 78%,#fff),var(--holo-accent,#5b8cff));box-shadow:0 2px 8px color-mix(in srgb,var(--holo-accent,#5b8cff) 50%,transparent)}" +
67
+ "#holo-install .hi-go:hover{filter:brightness(1.08)}#holo-install .hi-go:active{transform:scale(.96)}" +
68
+ "#holo-install .hi-x{appearance:none;border:0;background:transparent;color:var(--holo-ink-dim,#9aa6b6);cursor:pointer;width:1.6rem;height:1.6rem;border-radius:999px;display:grid;place-items:center;font-size:1.1em;line-height:1}" +
69
+ "#holo-install .hi-x:hover{background:rgba(255,255,255,.12);color:var(--holo-ink,#e9eef7)}" +
70
+ "@media (prefers-reduced-motion:reduce){#holo-install{transition:opacity .2s ease}}" +
71
+ "#holo-install-toast{position:fixed;left:50%;bottom:calc(1.1rem + var(--holo-dock-h,0px));transform:translateX(-50%);z-index:2147483001;max-width:90vw;" +
72
+ "background:var(--holo-surface,#14161b);color:var(--holo-ink,#e9eef7);border:1px solid var(--holo-border,#2b3440);border-radius:999px;padding:.6rem 1.1rem;" +
73
+ "font:var(--holo-text-sm,1rem) var(--holo-font-sans,system-ui,sans-serif);box-shadow:0 .6rem 2rem rgba(0,0,0,.5);opacity:0;transition:opacity .2s;pointer-events:none}" +
74
+ "#holo-install-toast.in{opacity:1}";
75
+ (DOC.head || root).appendChild(s);
76
+ }
77
+
78
+ var pill = null, deferred = null;
79
+ function showPill(mode) {
80
+ if (isInstalled() || dismissed() || pill) return;
81
+ if (mode === "ios" && !isIOS()) return;
82
+ css();
83
+ pill = DOC.createElement("div"); pill.id = "holo-install"; pill.setAttribute("role", "dialog"); pill.setAttribute("aria-label", "Install Hologram");
84
+ var img = DOC.createElement("img"); img.className = "hi-logo"; img.src = LOGO; img.alt = ""; img.draggable = false;
85
+ var txt = DOC.createElement("div"); txt.className = "hi-txt";
86
+ pill.appendChild(img); pill.appendChild(txt);
87
+ if (mode === "ios") {
88
+ // iOS can't auto-prompt β€” guide the one gesture that installs it, then let them dismiss.
89
+ txt.innerHTML = "<b>Add to Home Screen</b><span>Tap Share, then β€œAdd to Home Screen”</span>";
90
+ var ok = DOC.createElement("button"); ok.className = "hi-go"; ok.type = "button"; ok.textContent = "Got it";
91
+ ok.addEventListener("click", function () { setDismissed(); hidePill(); });
92
+ pill.appendChild(ok);
93
+ } else {
94
+ txt.innerHTML = "<b>Install Hologram</b><span>one tap β€” opens like an app</span>";
95
+ var go = DOC.createElement("button"); go.className = "hi-go"; go.type = "button"; go.textContent = "Install";
96
+ go.addEventListener("click", doInstall);
97
+ pill.appendChild(go);
98
+ }
99
+ var x = DOC.createElement("button"); x.className = "hi-x"; x.type = "button"; x.setAttribute("aria-label", "Not now"); x.textContent = "βœ•";
100
+ x.addEventListener("click", function () { setDismissed(); hidePill(); });
101
+ pill.appendChild(x);
102
+ DOC.body.appendChild(pill);
103
+ requestAnimationFrame(function () { if (pill) pill.classList.add("in"); });
104
+ }
105
+ function hidePill() { if (!pill) return; pill.classList.remove("in"); var p = pill; pill = null; setTimeout(function () { if (p && p.parentNode) p.remove(); }, 320); }
106
+ function toast(msg) {
107
+ css(); var t = DOC.getElementById("holo-install-toast"); if (!t) { t = DOC.createElement("div"); t.id = "holo-install-toast"; DOC.body.appendChild(t); }
108
+ t.textContent = msg; t.classList.add("in"); clearTimeout(t._h); t._h = setTimeout(function () { t.classList.remove("in"); }, 4200);
109
+ }
110
+ function doInstall() {
111
+ if (!deferred) { toast("Use your browser menu β†’ β€œInstall Hologram / Add to Home Screen”."); return; }
112
+ deferred.prompt();
113
+ Promise.resolve(deferred.userChoice).then(function (c) { if (c && c.outcome === "accepted") hidePill(); deferred = null; }).catch(function () {});
114
+ }
115
+
116
+ W.addEventListener("beforeinstallprompt", function (e) { e.preventDefault(); deferred = e; showPill(); });
117
+ W.addEventListener("appinstalled", function () { deferred = null; hidePill(); toast("Hologram installed β€” it lives in your dock now. Open it anytime, no browser needed."); });
118
+
119
+ // iOS Safari emits no event, so offer the Home-Screen path proactively (the only install route there).
120
+ function init() { if (isIOS() && !isInstalled() && !dismissed()) setTimeout(function () { showPill("ios"); }, 1200); }
121
+ if (DOC.readyState === "loading") DOC.addEventListener("DOMContentLoaded", init); else init();
122
+
123
+ // public API (scriptable + agent-usable). offer() = the universal "install the app" entry: fire the
124
+ // native dialog if the device staged one, else surface the right affordance / instruction.
125
+ function offer() {
126
+ if (isInstalled()) { toast("Hologram is already installed."); return; }
127
+ try { localStorage.removeItem("holo.install.dismissed"); } catch (e) {}
128
+ if (deferred) return doInstall();
129
+ if (isIOS()) return showPill("ios");
130
+ showPill();
131
+ if (!deferred) toast("Install from your browser menu β†’ β€œInstall Hologram / Add to Home Screen”.");
132
+ }
133
+ W.HoloInstall = {
134
+ version: "0.2",
135
+ prompt: doInstall,
136
+ offer: offer,
137
+ available: function () { return !!deferred; },
138
+ installed: isInstalled,
139
+ show: showPill, hide: hidePill,
140
+ };
141
+ })();
b/147d5e8e69d4267a89c077bbc57abcf0d090290141e2840ec47b6b569f62c216 ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$comment": "Seed catalog so Holo Player is ALREADY FULL on first open β€” no TMDb key, offline. Real popular titles with accurate TMDb ids + metadata (genres/year/rating/overview). Each carries a verified imdb_id so the normalizer fills real, beautiful poster/backdrop art KEY-FREE via the Stremio metahub CDN (the same source Cinemeta uses); a configured TMDb key still live-upgrades to TMDb's own art/trailers. Each item is TMDb-shaped (media_type=movie|tv, genre_ids) so holo-media-item normalizers consume it exactly like a live TMDb row.",
3
+ "generatedAt": "2026-06-27",
4
+ "items": [
5
+ { "id": 27205, "media_type": "movie", "title": "Inception", "imdb_id": "tt1375666", "release_date": "2010-07-16", "genre_ids": [28, 878, 12], "vote_average": 8.4, "runtime": 148, "poster_path": null, "backdrop_path": null, "overview": "A thief who steals corporate secrets through dream-sharing technology is given the inverse task of planting an idea into a CEO's mind." },
6
+ { "id": 157336, "media_type": "movie", "title": "Interstellar", "imdb_id": "tt0816692", "release_date": "2014-11-05", "genre_ids": [12, 18, 878], "vote_average": 8.4, "runtime": 169, "poster_path": null, "backdrop_path": null, "overview": "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival." },
7
+ { "id": 603, "media_type": "movie", "title": "The Matrix", "imdb_id": "tt0133093", "release_date": "1999-03-30", "genre_ids": [28, 878], "vote_average": 8.2, "runtime": 136, "poster_path": null, "backdrop_path": null, "overview": "A computer hacker learns the true nature of his reality and his role in the war against its controllers." },
8
+ { "id": 155, "media_type": "movie", "title": "The Dark Knight", "imdb_id": "tt0468569", "release_date": "2008-07-16", "genre_ids": [18, 28, 80, 53], "vote_average": 8.5, "runtime": 152, "poster_path": null, "backdrop_path": null, "overview": "Batman raises the stakes in his war on crime as he faces the Joker, who plunges Gotham into anarchy." },
9
+ { "id": 680, "media_type": "movie", "title": "Pulp Fiction", "imdb_id": "tt0110912", "release_date": "1994-09-10", "genre_ids": [53, 80], "vote_average": 8.5, "runtime": 154, "poster_path": null, "backdrop_path": null, "overview": "The lives of two mob hitmen, a boxer, and others intertwine in four tales of violence and redemption." },
10
+ { "id": 13, "media_type": "movie", "title": "Forrest Gump", "imdb_id": "tt0109830", "release_date": "1994-06-23", "genre_ids": [35, 18, 10749], "vote_average": 8.5, "runtime": 142, "poster_path": null, "backdrop_path": null, "overview": "A man with a low IQ recounts the extraordinary events of his life as they shape American history." },
11
+ { "id": 24428, "media_type": "movie", "title": "The Avengers", "imdb_id": "tt0848228", "release_date": "2012-04-25", "genre_ids": [878, 28, 12], "vote_average": 7.7, "runtime": 143, "poster_path": null, "backdrop_path": null, "overview": "Earth's mightiest heroes must come together to stop Loki and his alien army from enslaving humanity." },
12
+ { "id": 19995, "media_type": "movie", "title": "Avatar", "imdb_id": "tt0499549", "release_date": "2009-12-15", "genre_ids": [28, 12, 14, 878], "vote_average": 7.6, "runtime": 162, "poster_path": null, "backdrop_path": null, "overview": "A paraplegic Marine on the moon Pandora is torn between following orders and protecting an alien world." },
13
+ { "id": 122, "media_type": "movie", "title": "The Lord of the Rings: The Return of the King", "imdb_id": "tt0167260", "release_date": "2003-12-01", "genre_ids": [12, 14, 28], "vote_average": 8.5, "runtime": 201, "poster_path": null, "backdrop_path": null, "overview": "Gandalf and Aragorn lead the World of Men against Sauron's army to draw his gaze from Frodo and Sam." },
14
+ { "id": 597, "media_type": "movie", "title": "Titanic", "imdb_id": "tt0120338", "release_date": "1997-11-18", "genre_ids": [18, 10749], "vote_average": 7.9, "runtime": 194, "poster_path": null, "backdrop_path": null, "overview": "A seventeen-year-old aristocrat falls in love with a poor artist aboard the ill-fated R.M.S. Titanic." },
15
+ { "id": 496243, "media_type": "movie", "title": "Parasite", "imdb_id": "tt6751668", "release_date": "2019-05-30", "genre_ids": [35, 53, 18], "vote_average": 8.5, "runtime": 133, "poster_path": null, "backdrop_path": null, "overview": "A poor family schemes to become employed by a wealthy household by infiltrating it one by one." },
16
+ { "id": 372058, "media_type": "movie", "title": "Your Name.", "imdb_id": "tt5311514", "release_date": "2016-08-26", "genre_ids": [16, 18, 10749], "vote_average": 8.5, "runtime": 106, "poster_path": null, "backdrop_path": null, "overview": "Two strangers find themselves linked in a bizarre way: when they sleep, they swap bodies." },
17
+ { "id": 769, "media_type": "movie", "title": "GoodFellas", "imdb_id": "tt0099685", "release_date": "1990-09-12", "genre_ids": [18, 80], "vote_average": 8.5, "runtime": 145, "poster_path": null, "backdrop_path": null, "overview": "The story of Henry Hill and his life in the mob, covering his relationship with his family and partners." },
18
+ { "id": 438631, "media_type": "movie", "title": "Dune", "imdb_id": "tt1160419", "release_date": "2021-09-15", "genre_ids": [878, 12], "vote_average": 7.8, "runtime": 155, "poster_path": null, "backdrop_path": null, "overview": "Paul Atreides arrives on the dangerous desert planet Arrakis to ensure the future of his family and people." },
19
+ { "id": 1396, "media_type": "tv", "name": "Breaking Bad", "imdb_id": "tt0903747", "first_air_date": "2008-01-20", "genre_ids": [18, 80], "vote_average": 8.9, "number_of_seasons": 5, "number_of_episodes": 62, "poster_path": null, "backdrop_path": null, "overview": "A high school chemistry teacher diagnosed with cancer turns to manufacturing meth to secure his family's future." },
20
+ { "id": 1399, "media_type": "tv", "name": "Game of Thrones", "imdb_id": "tt0944947", "first_air_date": "2011-04-17", "genre_ids": [10765, 18, 10759], "vote_average": 8.4, "number_of_seasons": 8, "number_of_episodes": 73, "poster_path": null, "backdrop_path": null, "overview": "Noble families vie for control of the Iron Throne as an ancient enemy returns after millennia." },
21
+ { "id": 66732, "media_type": "tv", "name": "Stranger Things", "imdb_id": "tt4574334", "first_air_date": "2016-07-15", "genre_ids": [18, 10765, 9648], "vote_average": 8.6, "number_of_seasons": 4, "number_of_episodes": 34, "poster_path": null, "backdrop_path": null, "overview": "When a boy vanishes, a town uncovers a mystery of secret experiments, supernatural forces and a strange girl." },
22
+ { "id": 1398, "media_type": "tv", "name": "The Sopranos", "imdb_id": "tt0141842", "first_air_date": "1999-01-10", "genre_ids": [18, 80], "vote_average": 8.7, "number_of_seasons": 6, "number_of_episodes": 86, "poster_path": null, "backdrop_path": null, "overview": "A New Jersey mob boss balances the demands of his crime family with those of his real one." },
23
+ { "id": 60625, "media_type": "tv", "name": "Rick and Morty", "imdb_id": "tt2861424", "first_air_date": "2013-12-02", "genre_ids": [16, 35, 10765], "vote_average": 8.7, "number_of_seasons": 7, "number_of_episodes": 71, "poster_path": null, "backdrop_path": null, "overview": "An alcoholic scientist drags his grandson on absurd, dangerous adventures across the multiverse." },
24
+ { "id": 94605, "media_type": "tv", "name": "Arcane", "imdb_id": "tt11126994", "first_air_date": "2021-11-06", "genre_ids": [16, 10765, 18], "vote_average": 8.7, "number_of_seasons": 2, "number_of_episodes": 18, "poster_path": null, "backdrop_path": null, "overview": "Amid the unrest of two twin cities, two sisters fight on rival sides of a war between magic and technology." },
25
+ { "id": 1668, "media_type": "tv", "name": "Friends", "imdb_id": "tt0108778", "first_air_date": "1994-09-22", "genre_ids": [35], "vote_average": 8.5, "number_of_seasons": 10, "number_of_episodes": 236, "poster_path": null, "backdrop_path": null, "overview": "Six twenty-something friends navigate life and love in New York City." },
26
+ { "id": 1429, "media_type": "tv", "name": "Attack on Titan", "imdb_id": "tt2560140", "first_air_date": "2013-04-07", "genre_ids": [16, 10765, 10759], "vote_average": 8.8, "number_of_seasons": 4, "number_of_episodes": 88, "poster_path": null, "backdrop_path": null, "overview": "After his hometown is destroyed, a young man vows to wipe out the giant Titans that devour humanity." },
27
+ { "id": 82856, "media_type": "tv", "name": "The Mandalorian", "imdb_id": "tt8111088", "first_air_date": "2019-11-12", "genre_ids": [10765, 10759, 37], "vote_average": 8.4, "number_of_seasons": 3, "number_of_episodes": 24, "poster_path": null, "backdrop_path": null, "overview": "A lone bounty hunter travels the outer reaches of the galaxy, far from the authority of the New Republic." },
28
+ { "id": 87739, "media_type": "tv", "name": "The Queen's Gambit", "imdb_id": "tt10048342", "first_air_date": "2020-10-23", "genre_ids": [18], "vote_average": 8.4, "number_of_seasons": 1, "number_of_episodes": 7, "poster_path": null, "backdrop_path": null, "overview": "Orphaned at nine, a chess prodigy rises to the top of the game while battling addiction." },
29
+
30
+ { "$comment": "These four exist in the open/CC catalog (feed/catalog.json) — the availability resolver binds them to that source so they become truly PLAYABLE through Holo Video, demonstrating discovery→play. Their TMDb ids enrich them live when a key is set; art comes from the bound catalog source.",
31
+ "id": 45745, "media_type": "movie", "title": "Sintel", "release_date": "2010-09-30", "genre_ids": [14, 16, 12], "vote_average": 7.5, "poster_path": null, "backdrop_path": null, "overview": "A lone warrior crosses a harsh world to find the dragon she once raised β€” a Blender open movie." },
32
+ { "id": 133701, "media_type": "movie", "title": "Tears of Steel", "release_date": "2012-09-26", "genre_ids": [878, 28, 16], "vote_average": 6.7, "poster_path": null, "backdrop_path": null, "overview": "A group fights to save the world from destructive robots in a ruined near-future Amsterdam." },
33
+ { "id": 9761, "media_type": "movie", "title": "Elephants Dream", "release_date": "2006-03-24", "genre_ids": [878, 16, 14], "vote_average": 7.1, "poster_path": null, "backdrop_path": null, "overview": "Two men wander a vast, shifting machine β€” the first Blender open movie." },
34
+ { "id": 10378, "media_type": "movie", "title": "Big Buck Bunny", "release_date": "2008-05-20", "genre_ids": [16, 35, 10751], "vote_average": 7.2, "poster_path": null, "backdrop_path": null, "overview": "A gentle giant rabbit turns the tables on three bullying rodents." }
35
+ ]
36
+ }
b/17106929ff73f05d27a651cc1d2f2a6ce8219e6f0178cbd4358d0bdd8f806d46 ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-zip.js β€” a minimal, dependency-free ZIP reader/writer.
2
+ //
3
+ // OOXML (.docx/.xlsx/.pptx, ISO/IEC 29500) and ODF (.odt/.ods/.odp, ISO/IEC 26300)
4
+ // are both ZIP containers of XML. This is the lean, hologram-native way to read and
5
+ // write them ENTIRELY IN THE BROWSER with no vendored zlib and no CDN: DEFLATE is
6
+ // the platform's own Compression Streams API ('deflate-raw'), so the "engine" is
7
+ // the runtime itself. Isomorphic β€” the same module backs the live editor and the
8
+ // Node conformance witness (Compression Streams, Blob, Response are global in
9
+ // browsers and Node β‰₯18).
10
+
11
+ // ── CRC-32 (ZIP/PKZIP polynomial) ───────────────────────────────────────────────
12
+ const CRC_TABLE = (() => { const t = new Uint32Array(256);
13
+ for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; t[n] = c >>> 0; } return t; })();
14
+ function crc32(u8) { let c = 0xffffffff; for (let i = 0; i < u8.length; i++) c = CRC_TABLE[(c ^ u8[i]) & 0xff] ^ (c >>> 8); return (c ^ 0xffffffff) >>> 0; }
15
+
16
+ // ── DEFLATE via the platform Compression Streams API ────────────────────────────
17
+ async function streamBytes(u8, transform) {
18
+ const stream = new Blob([u8]).stream().pipeThrough(transform);
19
+ return new Uint8Array(await new Response(stream).arrayBuffer());
20
+ }
21
+ export const deflateRaw = (u8) => streamBytes(u8, new CompressionStream("deflate-raw"));
22
+ export const inflateRaw = (u8) => streamBytes(u8, new DecompressionStream("deflate-raw"));
23
+
24
+ const te = new TextEncoder();
25
+ const td = new TextDecoder();
26
+ export const utf8 = (s) => te.encode(s);
27
+ export const fromUtf8 = (u8) => td.decode(u8);
28
+
29
+ // ── writer ───────────────────────────────────────────────────────────────────────
30
+ // files: [{ name, data: string|Uint8Array, store?: bool }]. `store` (no compression)
31
+ // is required for the ODF `mimetype` entry, which must be the first, uncompressed.
32
+ export async function zip(files) {
33
+ const enc = files.map((f) => ({ name: f.name, data: typeof f.data === "string" ? te.encode(f.data) : f.data, store: !!f.store }));
34
+ const parts = []; const central = []; let offset = 0;
35
+ const u16 = (n) => new Uint8Array([n & 0xff, (n >>> 8) & 0xff]);
36
+ const u32 = (n) => new Uint8Array([n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff]);
37
+ for (const f of enc) {
38
+ const nameB = te.encode(f.name); const crc = crc32(f.data);
39
+ const comp = f.store ? f.data : await deflateRaw(f.data);
40
+ const method = f.store ? 0 : 8;
41
+ const local = concat([u32(0x04034b50), u16(20), u16(0), u16(method), u16(0), u16(0), u32(crc), u32(comp.length), u32(f.data.length), u16(nameB.length), u16(0), nameB, comp]);
42
+ central.push(concat([u32(0x02014b50), u16(20), u16(20), u16(0), u16(method), u16(0), u16(0), u32(crc), u32(comp.length), u32(f.data.length), u16(nameB.length), u16(0), u16(0), u16(0), u16(0), u32(0), u32(offset), nameB]));
43
+ parts.push(local); offset += local.length;
44
+ }
45
+ const cd = concat(central); const cdOffset = offset;
46
+ const eocd = concat([u32(0x06054b50), u16(0), u16(0), u16(enc.length), u16(enc.length), u32(cd.length), u32(cdOffset), u16(0)]);
47
+ return concat([...parts, cd, eocd]);
48
+ }
49
+
50
+ // ── reader ───────────────────────────────────────────────────────────────────────
51
+ // Returns Map(name β†’ Uint8Array). Robust: drives off the central directory (correct
52
+ // sizes/CRC even when entries used a streaming data descriptor).
53
+ export async function unzip(bytes) {
54
+ const u8 = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
55
+ const dv = new DataView(u8.buffer, u8.byteOffset, u8.byteLength);
56
+ let eocd = -1;
57
+ for (let i = u8.length - 22; i >= 0; i--) { if (dv.getUint32(i, true) === 0x06054b50) { eocd = i; break; } }
58
+ if (eocd < 0) throw new Error("not a zip (no EOCD)");
59
+ const count = dv.getUint16(eocd + 10, true); let p = dv.getUint32(eocd + 16, true);
60
+ const out = new Map();
61
+ for (let n = 0; n < count; n++) {
62
+ if (dv.getUint32(p, true) !== 0x02014b50) break;
63
+ const method = dv.getUint16(p + 10, true);
64
+ const compSize = dv.getUint32(p + 20, true);
65
+ const nameLen = dv.getUint16(p + 28, true), extraLen = dv.getUint16(p + 30, true), commentLen = dv.getUint16(p + 32, true);
66
+ const localOff = dv.getUint32(p + 42, true);
67
+ const name = td.decode(u8.subarray(p + 46, p + 46 + nameLen));
68
+ // local header: data starts after its own (possibly different) name+extra lengths
69
+ const lNameLen = dv.getUint16(localOff + 26, true), lExtraLen = dv.getUint16(localOff + 28, true);
70
+ const dataStart = localOff + 30 + lNameLen + lExtraLen;
71
+ const comp = u8.subarray(dataStart, dataStart + compSize);
72
+ out.set(name, method === 0 ? comp.slice() : await inflateRaw(comp));
73
+ p += 46 + nameLen + extraLen + commentLen;
74
+ }
75
+ return out;
76
+ }
77
+
78
+ function concat(arrs) { let n = 0; for (const a of arrs) n += a.length; const o = new Uint8Array(n); let p = 0; for (const a of arrs) { o.set(a, p); p += a.length; } return o; }
79
+
80
+ export default { zip, unzip, deflateRaw, inflateRaw, crc32, utf8, fromUtf8 };
b/184ff87b24d7fd1527420d2e895f8d6a829e58536018d6518c3697a38d9bc11d ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Witness for verify-before-trust on game saves β€” the security property the live front door enforces:
2
+ // a save whose ΞΊ-claim fails is refused on EVERY hand-off (resume, load-slot, roam, share/import),
3
+ // so a poisoned roam peer or share link cannot inject arbitrary emulator state. Tests the ACTUAL
4
+ // predicate the app imports.
5
+ // Run: node holo-save-trust.test.mjs
6
+ import { createHash } from "node:crypto";
7
+ import { saveVerifies, trustedStateOf } from "./holo-save-trust.mjs";
8
+
9
+ const hash = async (b) => createHash("sha256").update(Buffer.from(b.buffer, b.byteOffset, b.byteLength)).digest("hex");
10
+ let pass = 0, fail = 0;
11
+ const ok = (c, m) => (c ? (pass++, console.log(" βœ“", m)) : (fail++, console.error(" βœ—", m)));
12
+
13
+ const state = new Uint8Array(64).map((_, i) => (i * 7 + 3) & 0xff);
14
+ const saveKappa = await hash(state);
15
+ const clean = { state, saveKappa };
16
+ const tampered = { state: Uint8Array.from(state, (v, i) => (i === 0 ? v ^ 0xff : v)), saveKappa }; // bytes changed, ΞΊ-claim unchanged
17
+ const legacy = { state }; // pre-ΞΊ save: no claim
18
+ const stateless = { saveKappa }; // a claim with no bytes
19
+
20
+ // ── the predicate ──
21
+ ok((await saveVerifies(clean, hash)) === true, "a save whose bytes re-hash to its ΞΊ VERIFIES");
22
+ ok((await saveVerifies(tampered, hash)) === false, "a tampered save (ΞΊ-claim fails) is REFUSED");
23
+ ok((await saveVerifies(legacy, hash)) === true, "a legacy save (no ΞΊ-claim) loads as unverified (old data still works)");
24
+ ok((await saveVerifies(stateless, hash)) === false, "a record with no state to load is refused");
25
+
26
+ // ── the load sites (resume / load-slot) ──
27
+ ok((await trustedStateOf(clean, hash)) === clean.state, "resume/load returns the state for a verified save");
28
+ ok((await trustedStateOf(tampered, hash)) === false, "resume/load returns false (β†’ \"refused\") for a tampered save");
29
+ ok((await trustedStateOf(null, hash)) === null, "resume/load returns null when there is no save");
30
+
31
+ // ── roam: a poisoned save from another device is dropped before it ever reaches the store ──
32
+ const roamPoisoned = { type: "save", key: "k#auto", rec: tampered };
33
+ ok(!(await saveVerifies(roamPoisoned.rec, hash)), "a poisoned ROAM save is dropped (verify-before-trust)");
34
+ ok(await saveVerifies({ type: "save", rec: clean }.rec, hash), "a clean roam save is accepted");
35
+
36
+ // ── share/import: a tampered embedded playthrough is refused; the clean one imports ──
37
+ const shareTampered = { state: tampered.state, saveKappa };
38
+ ok(!(await saveVerifies(shareTampered, hash)), "a tampered SHARED playthrough is refused on import");
39
+ ok(await saveVerifies({ state, saveKappa }, hash), "a clean shared playthrough imports");
40
+
41
+ console.log(`\n${fail === 0 ? "PASS" : "FAIL"} β€” verify-before-trust on saves (resume/slot/roam/share) (${pass}/${pass + fail})`);
42
+ process.exit(fail === 0 ? 0 : 1);
b/1866a03ba1df11dac02458b4a919fb8ad3577872c016ba7f528e0159dd151703 ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Witness: holo-ladder β€” the serverless paywall bypass. Detection, in-place unlock (JSON-LD reader,
2
+ // overlay/scroll-lock neutralize, metering-script drop), per-host rules, archive sources, rung-3
3
+ // header spoof. Pure/offline β€” no wire. Run: node ladder.witness.mjs
4
+ import { detectPaywall, unlock, ruleFor, archiveSources, ladderHeaders } from "./_shared/holo-ladder.mjs";
5
+
6
+ const results = []; const ok = (n, c) => { results.push([n, c]); console.log((c ? "βœ“ " : "βœ— ") + n); };
7
+ const URL_NYT = "https://www.nytimes.com/2024/01/01/world/story.html";
8
+ const URL_PLAIN = "https://example.com/blog/free-post";
9
+
10
+ const BODY = ("First paragraph with plenty of substance to clear the length gate and prove the reader reconstructs the body. " +
11
+ "It runs well past four hundred characters so the substantial-body test is unambiguous.\n\n" +
12
+ "Second paragraph continues the reporting with more detail so the recovered text is unmistakably the real article, " +
13
+ "carrying the phrase clear the length gate that the witness looks for, plus enough words to be a genuine article body.");
14
+ const mkDoc = (headline) => `<!doctype html><html><head>
15
+ <script type="application/ld+json">${JSON.stringify({ "@type": "NewsArticle", headline, author: { name: "Jane Reporter" }, datePublished: "2024-01-01", isAccessibleForFree: false, articleBody: BODY })}</script>
16
+ </head><body><div class="paywall-overlay">Subscribe to continue</div><article class="article-body--truncated">First paragraph…</article>
17
+ <script src="https://cdn.tinypass.com/api/tinypass.min.js"></script></body></html>`;
18
+ const LOCKED_LDJSON = mkDoc("The Big Story");
19
+
20
+ const FREE = `<!doctype html><html><head></head><body><article><p>Free content, no wall here at all.</p></article></body></html>`;
21
+
22
+ // ── detection ──
23
+ { const d = detectPaywall(LOCKED_LDJSON, URL_NYT); ok("detect: locked doc flagged", d.locked && d.why.length > 0); }
24
+ { const d = detectPaywall(FREE, URL_PLAIN); ok("detect: free doc NOT flagged", !d.locked); }
25
+ { const d = detectPaywall(`<meta property="article:content_tier" content="locked">`, URL_PLAIN); ok("detect: content_tier=locked", d.locked); }
26
+ // REGRESSION (real, from the live scorecard): a free article that merely LOADS Piano/tinypass for a
27
+ // newsletter/consent (BBC) must NOT be flagged β€” a vendor meter present β‰  this page is walled.
28
+ { const bbc = `<!doctype html><html><head><script src="https://cdn.tinypass.com/api/tinypass.min.js"></script></head><body><div class="tp-modal" style="display:none"></div><article><p>Free BBC news article, fully readable, no wall.</p></article></body></html>`;
29
+ const d = detectPaywall(bbc, "https://www.bbc.com/news/x"); ok("detect: vendor-meter-only NOT flagged (no false defacing)", !d.locked);
30
+ ok("unlock: free-with-vendor-meter untouched", unlock(bbc, "https://www.bbc.com/news/x").html === bbc); }
31
+
32
+ // ── rung 1: in-place unlock ──
33
+ { const u = unlock(LOCKED_LDJSON, URL_NYT);
34
+ ok("unlock: recovers body from JSON-LD", u.recovered && u.html.includes("holo-ladder-reader"));
35
+ ok("unlock: reader carries real article text", u.html.includes("clear the length gate"));
36
+ ok("unlock: headline + byline rendered", u.html.includes("The Big Story") && u.html.includes("Jane Reporter"));
37
+ ok("unlock: neutralize CSS injected", u.html.includes("holo-ladder-css"));
38
+ ok("unlock: metering script dropped", !u.html.includes("tinypass.min.js"));
39
+ ok("unlock: labeled applied steps", u.applied.includes("reader:ld+json") && u.applied.includes("strip-scripts")); }
40
+
41
+ // idempotent β€” a second pass over already-unlocked HTML must NOT inject a second reader/CSS
42
+ { const once = unlock(LOCKED_LDJSON, URL_NYT).html; const twice = unlock(once, URL_NYT).html;
43
+ ok("unlock: idempotent (no double reader)", (twice.match(/id="holo-ladder-reader"/g) || []).length === 1 && (twice.match(/id="holo-ladder-css"/g) || []).length === 1); }
44
+
45
+ // free doc in AUTO passes through untouched; forced unlock still neutralizes
46
+ { const u = unlock(FREE, URL_PLAIN); ok("unlock: free doc untouched in auto", !u.locked && u.html === FREE); }
47
+ { const u = unlock(FREE, URL_PLAIN, { force: true }); ok("unlock: force injects neutralize even when free", u.html.includes("holo-ladder-css")); }
48
+
49
+ // escaping β€” a hostile headline must be escaped when WE inject it into the reader panel
50
+ { const u = unlock(mkDoc("Evil <img src=x onerror=alert(1)> Headline"), URL_NYT);
51
+ const at = u.html.indexOf('id="holo-ladder-reader"'); const panel = u.html.slice(at, at + 2000);
52
+ ok("unlock: reader-injected headline HTML-escaped", panel.includes("Evil &lt;img") && !/<img src=x onerror/.test(panel)); }
53
+
54
+ // ── per-host rules ──
55
+ { ok("rule: nytimes matched", !!ruleFor(URL_NYT) && ruleFor(URL_NYT).host === "nytimes.com"); }
56
+ { ok("rule: unknown host β†’ none", ruleFor(URL_PLAIN) === null); }
57
+ { const u = unlock(LOCKED_LDJSON, "https://www.wsj.com/x"); ok("rule: wsj kill-css applied", u.html.includes("holo-ladder-rule")); }
58
+
59
+ // ── rung 2: archive sources ──
60
+ { const a = archiveSources(URL_NYT);
61
+ ok("archive: wayback first", a[0].via === "wayback" && a[0].api.includes("archive.org/wayback/available"));
62
+ ok("archive: includes archive.today + reader", a.some((s) => s.via === "archive.today") && a.some((s) => s.via === "reader")); }
63
+
64
+ // ── rung 3: header spoof shape ──
65
+ { const h = ladderHeaders(URL_NYT);
66
+ ok("headers: googlebot UA", /Googlebot/i.test(h["x-holo-ua"]));
67
+ ok("headers: google referer + xff", h["x-holo-referer"].includes("google.com") && h["x-holo-xff"] === "66.249.66.1"); }
68
+
69
+ const pass = results.filter((r) => r[1]).length;
70
+ console.log("\n" + pass + "/" + results.length + (pass === results.length ? " ALL GREEN" : " β€” FAIL"));
71
+ process.exit(pass === results.length ? 0 : 1);
b/19f2d0028cf70070bfa1922c1bcc981972a9f527517d8d5b8f8ab36c8ce20039 ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "@type": "earl:TestResult",
3
+ "spec": "holo-resolvers β€” sources as an open, federated plugin point (Stremio-addon shape, ΞΊ-native): resolvers register, the registry fuses their candidates with trust-weighted RRF (owned ΞΊ > ΞΊ-store > server > open > addon), the best source wins, an unresolved title is a virtual item, provenance is stamped, and addons plug in disabled-by-default + always provenanced (no infringing indexers bundled).",
4
+ "authority": "rests on #holo-resolvers (+ #holo-availability) β€” Phase 4 of the streaming/metadata plan",
5
+ "witnessed": true,
6
+ "covers": [
7
+ "register-and-list",
8
+ "federates",
9
+ "trust-wins",
10
+ "virtual-item",
11
+ "provenance",
12
+ "addon-pluggable",
13
+ "jellyfin-resolver"
14
+ ],
15
+ "checks": {
16
+ "registerAndList": true,
17
+ "federates": true,
18
+ "trustWins": true,
19
+ "virtualItem": true,
20
+ "provenance": true,
21
+ "addonPluggable": true,
22
+ "jellyfinResolver": true
23
+ },
24
+ "failed": []
25
+ }
b/1ac6077adc6fa547c8937fbeaf064fbe060f8e7272602e341f758148a2d43a4f ADDED
@@ -0,0 +1 @@
 
 
1
+ <svg fill="#FF0000" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>YouTube</title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
b/1ae88505e750e8cd74448f6c46f20cc6a5d5a8e7167e6fac2ca9726946b652eb ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ async function GenesisCore(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var programArgs=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}programArgs=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();TTY.init();wasmExports["n"]();FS.ignorePermissions=false}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("genesis.wasm")}return new URL("genesis.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var syscallGetVarargI=()=>{var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret};var syscallGetVarargP=syscallGetVarargI;var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.slice(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.slice(0,-1)}return root+dir},basename:path=>path&&path.match(/([^\/]+|\/)\/*$/)[1],join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(ENVIRONMENT_IS_NODE){var nodeCrypto=require("node:crypto");return view=>(nodeCrypto.randomFillSync(view),0)}return view=>(crypto.getRandomValues(view),0)};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).slice(1);to=PATH_FS.resolve(to).slice(1);function trim(arr){var start=0;for(;start<arr.length;start++){if(arr[start]!=="")break}var end=arr.length-1;for(;end>=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i<length;i++){if(fromParts[i]!==toParts[i]){samePartsLength=i;break}}var outputParts=[];for(var i=samePartsLength;i<fromParts.length;i++){outputParts.push("..")}outputParts=outputParts.concat(toParts.slice(samePartsLength));return outputParts.join("/")}};var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var intArrayFromString=(stringy,dontAddNull,length)=>{var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array};var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(globalThis.window?.prompt){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=stream.tty.ops.get_char(stream.tty)}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.atime=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.put_char){throw new FS.ErrnoError(60)}try{for(var i=0;i<length;i++){stream.tty.ops.put_char(stream.tty,buffer[offset+i])}}catch(e){throw new FS.ErrnoError(29)}if(length){stream.node.mtime=stream.node.ctime=Date.now()}return i}},default_tty_ops:{get_char(tty){return FS_stdin_getChar()},put_char(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output?.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16895,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=MEMFS.emptyFileContents??=new Uint8Array(0)}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.atime=node.mtime=node.ctime=Date.now();if(parent){parent.contents[name]=node;parent.atime=parent.mtime=parent.ctime=node.atime}return node},getFileDataAsTypedArray(node){return node.contents.subarray(0,node.usedBytes)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents.length;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity<CAPACITY_DOUBLING_MAX?2:1.125)>>>0);if(prevCapacity)newCapacity=Math.max(newCapacity,256);var oldContents=MEMFS.getFileDataAsTypedArray(node);node.contents=new Uint8Array(newCapacity);node.contents.set(oldContents)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;var oldContents=node.contents;node.contents=new Uint8Array(newSize);node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)));node.usedBytes=newSize},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.atime);attr.mtime=new Date(node.mtime);attr.ctime=new Date(node.ctime);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){for(const key of["mode","atime","mtime","ctime"]){if(attr[key]!=null){node[key]=attr[key]}}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){if(!MEMFS.doesNotExistError){MEMFS.doesNotExistError=new FS.ErrnoError(44);MEMFS.doesNotExistError.stack="<generic error, no stack>"}throw MEMFS.doesNotExistError},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){if(FS.isDir(old_node.mode)){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}FS.hashRemoveNode(new_node)}delete old_node.parent.contents[old_node.name];new_dir.contents[new_name]=old_node;old_node.name=new_name;new_dir.ctime=new_dir.mtime=old_node.parent.ctime=old_node.parent.mtime=Date.now()},unlink(parent,name){delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.ctime=parent.mtime=Date.now()},readdir(node){return[".","..",...Object.keys(node.contents)]},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);buffer.set(contents.subarray(position,position+size),offset);return size},write(stream,buffer,offset,length,position,canOwn){if(buffer.buffer===HEAP8.buffer){canOwn=false}if(!length)return 0;var node=stream.node;node.mtime=node.ctime=Date.now();if(canOwn){node.contents=buffer.subarray(offset,offset+length);node.usedBytes=length}else if(node.usedBytes===0&&position===0){node.contents=buffer.slice(offset,offset+length);node.usedBytes=length}else{MEMFS.expandFileStorage(node,position+length);node.contents.set(buffer.subarray(offset,offset+length),position);node.usedBytes=Math.max(node.usedBytes,position+length)}return length},llseek(stream,offset,whence){var position=offset;if(whence===1){position+=stream.position}else if(whence===2){if(FS.isFile(stream.node.mode)){position+=stream.node.usedBytes}}if(position<0){throw new FS.ErrnoError(28)}return position},mmap(stream,length,position,prot,flags){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}var ptr;var allocated;var contents=stream.node.contents;if(!(flags&2)&&contents.buffer===HEAP8.buffer){allocated=false;ptr=contents.byteOffset}else{allocated=true;ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}if(contents){if(position>0||position+length<contents.length){if(contents.subarray){contents=contents.subarray(position,position+length)}else{contents=Array.prototype.slice.call(contents,position,position+length)}}HEAP8.set(contents,ptr)}}return{ptr,allocated}},msync(stream,buffer,offset,length,mmapFlags){MEMFS.stream_ops.write(stream,buffer,0,length,offset,false);return 0}}};var FS_modeStringToFlags=str=>{if(typeof str!="string")return str;var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_fileDataToTypedArray=data=>{if(typeof data=="string"){data=intArrayFromString(data,true)}if(!data.subarray){data=new Uint8Array(data)}return data};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var asyncLoad=async url=>{var arrayBuffer=await readAsync(url);return new Uint8Array(arrayBuffer)};var FS_createDataFile=(...args)=>FS.createDataFile(...args);var getUniqueRunDependency=id=>id;var runDependencies=0;var dependenciesFulfilled=null;var removeRunDependency=id=>{runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}};var addRunDependency=id=>{runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)};var preloadPlugins=[];var FS_handledByPreloadPlugin=async(byteArray,fullname)=>{if(typeof Browser!="undefined")Browser.init();for(var plugin of preloadPlugins){if(plugin["canHandle"](fullname)){return plugin["handle"](byteArray,fullname)}}return byteArray};var FS_preloadFile=async(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);addRunDependency(dep);try{var byteArray=url;if(typeof url=="string"){byteArray=await asyncLoad(url)}byteArray=await FS_handledByPreloadPlugin(byteArray,fullname);preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}}finally{removeRunDependency(dep)}};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{FS_preloadFile(parent,name,url,canRead,canWrite,dontCreateFile,canOwn,preFinish).then(onload).catch(onerror)};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,filesystems:null,syncFSRequests:0,ErrnoError:class{name="ErrnoError";constructor(errno){this.errno=errno}},FSStream:class{shared={};get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{node_ops={};stream_ops={};readMode=292|73;writeMode=146;mounted=null;constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.rdev=rdev;this.atime=this.mtime=this.ctime=Date.now()}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){if(!path){throw new FS.ErrnoError(44)}opts.follow_mount??=true;if(!PATH.isAbs(path)){path=FS.cwd()+"/"+path}linkloop:for(var nlinks=0;nlinks<40;nlinks++){var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i<parts.length;i++){var islast=i===parts.length-1;if(islast&&opts.parent){break}if(parts[i]==="."){continue}if(parts[i]===".."){current_path=PATH.dirname(current_path);if(FS.isRoot(current)){path=current_path+"/"+parts.slice(i+1).join("/");nlinks--;continue linkloop}else{current=current.parent}continue}current_path=PATH.join2(current_path,parts[i]);try{current=FS.lookupNode(current,parts[i])}catch(e){if(e?.errno===44&&islast&&opts.noent_okay){return{path:current_path}}throw e}if(FS.isMountpoint(current)&&(!islast||opts.follow_mount)){current=current.mounted.root}if(FS.isLink(current.mode)&&(!islast||opts.follow)){if(!current.node_ops.readlink){throw new FS.ErrnoError(52)}var link=current.node_ops.readlink(current);if(!PATH.isAbs(link)){link=PATH.dirname(current_path)+"/"+link}path=link+"/"+parts.slice(i+1).join("/");continue linkloop}}return{path:current_path,node:current}}throw new FS.ErrnoError(32)},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i<name.length;i++){hash=(hash<<5)-hash+name.charCodeAt(i)|0}return(parentid+hash>>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}if(perms.includes("w")&&!(node.mode&146)){return 2}if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){if(!FS.isDir(dir.mode)){return 54}try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else if(FS.isDir(node.mode)){return 31}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}var mode=FS.flagsToPermissionString(flags);if(FS.isDir(node.mode)){if(mode!=="r"||flags&(512|64)){return 31}}return FS.nodePermissions(node,mode)},checkOpExists(op,err){if(!op){throw new FS.ErrnoError(err)}return op},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},doSetAttr(stream,node,attr){var setattr=stream?.stream_ops.setattr;var arg=setattr?stream:node;setattr??=node.node_ops.setattr;FS.checkOpExists(setattr,63);try{setattr(arg,attr)}catch(e){if(e instanceof RangeError){throw new FS.ErrnoError(22)}throw e}},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}for(var mount of mounts){if(mount.type.syncfs){mount.type.syncfs(mount,populate,done)}else{done(null)}}},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);for(var[hash,current]of Object.entries(FS.nameTable)){while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}}node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name){throw new FS.ErrnoError(28)}if(name==="."||name===".."){throw new FS.ErrnoError(20)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},statfs(path){return FS.statfsNode(FS.lookupPath(path,{follow:true}).node)},statfsStream(stream){return FS.statfsNode(stream.node)},statfsNode(node){var rtn={bsize:4096,frsize:4096,blocks:1e6,bfree:5e5,bavail:5e5,files:FS.nextInode,ffree:FS.nextInode-1,fsid:42,flags:2,namelen:255};if(node.node_ops.statfs){Object.assign(rtn,node.node_ops.statfs(node.mount.opts.root))}return rtn},create(path,mode=438){mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode=511){mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var dir of dirs){if(!dir)continue;if(d||PATH.isAbs(path))d+="/";d+=dir;try{FS.mkdir(d,mode)}catch(e){if(e.errno!=20)throw e}}},mkdev(path,mode,dev){if(typeof dev=="undefined"){dev=mode;mode=438}mode|=8192;return FS.mknod(path,mode,dev)},symlink(oldpath,newpath){if(!PATH_FS.resolve(oldpath)){throw new FS.ErrnoError(44)}var lookup=FS.lookupPath(newpath,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var newname=PATH.basename(newpath);var errCode=FS.mayCreate(parent,newname);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.symlink){throw new FS.ErrnoError(63)}return parent.node_ops.symlink(parent,newname,oldpath)},rename(old_path,new_path){var old_dirname=PATH.dirname(old_path);var new_dirname=PATH.dirname(new_path);var old_name=PATH.basename(old_path);var new_name=PATH.basename(new_path);var lookup,old_dir,new_dir;lookup=FS.lookupPath(old_path,{parent:true});old_dir=lookup.node;lookup=FS.lookupPath(new_path,{parent:true});new_dir=lookup.node;if(!old_dir||!new_dir)throw new FS.ErrnoError(44);if(old_dir.mount!==new_dir.mount){throw new FS.ErrnoError(75)}var old_node=FS.lookupNode(old_dir,old_name);var relative=PATH_FS.relative(old_path,new_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(28)}relative=PATH_FS.relative(new_path,old_dirname);if(relative.charAt(0)!=="."){throw new FS.ErrnoError(55)}var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(old_node===new_node){return}var isdir=FS.isDir(old_node.mode);var errCode=FS.mayDelete(old_dir,old_name,isdir);if(errCode){throw new FS.ErrnoError(errCode)}errCode=new_node?FS.mayDelete(new_dir,new_name,isdir):FS.mayCreate(new_dir,new_name);if(errCode){throw new FS.ErrnoError(errCode)}if(!old_dir.node_ops.rename){throw new FS.ErrnoError(63)}if(FS.isMountpoint(old_node)||new_node&&FS.isMountpoint(new_node)){throw new FS.ErrnoError(10)}if(new_dir!==old_dir){errCode=FS.nodePermissions(old_dir,"w");if(errCode){throw new FS.ErrnoError(errCode)}}FS.hashRemoveNode(old_node);try{old_dir.node_ops.rename(old_node,new_dir,new_name);old_node.parent=new_dir}catch(e){throw e}finally{FS.hashAddNode(old_node)}},rmdir(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,true);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.rmdir){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.rmdir(parent,name);FS.destroyNode(node)},readdir(path){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var readdir=FS.checkOpExists(node.node_ops.readdir,54);return readdir(node)},unlink(path){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;if(!parent){throw new FS.ErrnoError(44)}var name=PATH.basename(path);var node=FS.lookupNode(parent,name);var errCode=FS.mayDelete(parent,name,false);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.unlink){throw new FS.ErrnoError(63)}if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}parent.node_ops.unlink(parent,name);FS.destroyNode(node)},readlink(path){var lookup=FS.lookupPath(path);var link=lookup.node;if(!link){throw new FS.ErrnoError(44)}if(!link.node_ops.readlink){throw new FS.ErrnoError(28)}return link.node_ops.readlink(link)},stat(path,dontFollow){var lookup=FS.lookupPath(path,{follow:!dontFollow});var node=lookup.node;var getattr=FS.checkOpExists(node.node_ops.getattr,63);return getattr(node)},fstat(fd){var stream=FS.getStreamChecked(fd);var node=stream.node;var getattr=stream.stream_ops.getattr;var arg=getattr?stream:node;getattr??=node.node_ops.getattr;FS.checkOpExists(getattr,63);return getattr(arg)},lstat(path){return FS.stat(path,true)},doChmod(stream,node,mode,dontFollow){FS.doSetAttr(stream,node,{mode:mode&4095|node.mode&~4095,ctime:Date.now(),dontFollow})},chmod(path,mode,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChmod(null,node,mode,dontFollow)},lchmod(path,mode){FS.chmod(path,mode,true)},fchmod(fd,mode){var stream=FS.getStreamChecked(fd);FS.doChmod(stream,stream.node,mode,false)},doChown(stream,node,dontFollow){FS.doSetAttr(stream,node,{timestamp:Date.now(),dontFollow})},chown(path,uid,gid,dontFollow){var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:!dontFollow});node=lookup.node}else{node=path}FS.doChown(null,node,dontFollow)},lchown(path,uid,gid){FS.chown(path,uid,gid,true)},fchown(fd,uid,gid){var stream=FS.getStreamChecked(fd);FS.doChown(stream,stream.node,false)},doTruncate(stream,node,len){if(FS.isDir(node.mode)){throw new FS.ErrnoError(31)}if(!FS.isFile(node.mode)){throw new FS.ErrnoError(28)}var errCode=FS.nodePermissions(node,"w");if(errCode){throw new FS.ErrnoError(errCode)}FS.doSetAttr(stream,node,{size:len,timestamp:Date.now()})},truncate(path,len){if(len<0){throw new FS.ErrnoError(28)}var node;if(typeof path=="string"){var lookup=FS.lookupPath(path,{follow:true});node=lookup.node}else{node=path}FS.doTruncate(null,node,len)},ftruncate(fd,len){var stream=FS.getStreamChecked(fd);if(len<0||(stream.flags&2097155)===0){throw new FS.ErrnoError(28)}FS.doTruncate(stream,stream.node,len)},utime(path,atime,mtime){var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;var setattr=FS.checkOpExists(node.node_ops.setattr,63);setattr(node,{atime,mtime})},open(path,flags,mode=438){if(path===""){throw new FS.ErrnoError(44)}flags=FS_modeStringToFlags(flags);if(flags&64){mode=mode&4095|32768}else{mode=0}var node;var isDirPath;if(typeof path=="object"){node=path}else{isDirPath=path.endsWith("/");var lookup=FS.lookupPath(path,{follow:!(flags&131072),noent_okay:true});node=lookup.node;path=lookup.path}var created=false;if(flags&64){if(node){if(flags&128){throw new FS.ErrnoError(20)}}else if(isDirPath){throw new FS.ErrnoError(31)}else{node=FS.mknod(path,mode|511,0);created=true}}if(!node){throw new FS.ErrnoError(44)}if(FS.isChrdev(node.mode)){flags&=~512}if(flags&65536&&!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}if(!created){var errCode=FS.mayOpen(node,flags);if(errCode){throw new FS.ErrnoError(errCode)}}if(flags&512&&!created){FS.truncate(node,0)}flags&=~(128|512|131072);var stream=FS.createStream({node,path:FS.getPath(node),flags,seekable:true,position:0,stream_ops:node.stream_ops,ungotten:[],error:false});if(stream.stream_ops.open){stream.stream_ops.open(stream)}if(created){FS.chmod(node,mode&511)}return stream},close(stream){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(stream.getdents)stream.getdents=null;try{if(stream.stream_ops.close){stream.stream_ops.close(stream)}}catch(e){throw e}finally{FS.closeStream(stream.fd)}stream.fd=null},isClosed(stream){return stream.fd===null},llseek(stream,offset,whence){if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if(!stream.seekable||!stream.stream_ops.llseek){throw new FS.ErrnoError(70)}if(whence!=0&&whence!=1&&whence!=2){throw new FS.ErrnoError(28)}stream.position=stream.stream_ops.llseek(stream,offset,whence);stream.ungotten=[];return stream.position},read(stream,buffer,offset,length,position){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.read){throw new FS.ErrnoError(28)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesRead=stream.stream_ops.read(stream,buffer,offset,length,position);if(!seeking)stream.position+=bytesRead;return bytesRead},write(stream,buffer,offset,length,position,canOwn){if(length<0||position<0){throw new FS.ErrnoError(28)}if(FS.isClosed(stream)){throw new FS.ErrnoError(8)}if((stream.flags&2097155)===0){throw new FS.ErrnoError(8)}if(FS.isDir(stream.node.mode)){throw new FS.ErrnoError(31)}if(!stream.stream_ops.write){throw new FS.ErrnoError(28)}if(stream.seekable&&stream.flags&1024){FS.llseek(stream,0,2)}var seeking=typeof position!="undefined";if(!seeking){position=stream.position}else if(!stream.seekable){throw new FS.ErrnoError(70)}var bytesWritten=stream.stream_ops.write(stream,buffer,offset,length,position,canOwn);if(!seeking)stream.position+=bytesWritten;return bytesWritten},mmap(stream,length,position,prot,flags){if((prot&2)!==0&&(flags&2)===0&&(stream.flags&2097155)!==2){throw new FS.ErrnoError(2)}if((stream.flags&2097155)===1){throw new FS.ErrnoError(2)}if(!stream.stream_ops.mmap){throw new FS.ErrnoError(43)}if(!length){throw new FS.ErrnoError(28)}return stream.stream_ops.mmap(stream,length,position,prot,flags)},msync(stream,buffer,offset,length,mmapFlags){if(!stream.stream_ops.msync){return 0}return stream.stream_ops.msync(stream,buffer,offset,length,mmapFlags)},ioctl(stream,cmd,arg){if(!stream.stream_ops.ioctl){throw new FS.ErrnoError(59)}return stream.stream_ops.ioctl(stream,cmd,arg)},readFile(path,opts={}){opts.flags=opts.flags??0;opts.encoding=opts.encoding??"binary";if(opts.encoding!=="utf8"&&opts.encoding!=="binary"){abort(`Invalid encoding type "${opts.encoding}"`)}var stream=FS.open(path,opts.flags);var stat=FS.stat(path);var length=stat.size;var buf=new Uint8Array(length);FS.read(stream,buf,0,length,0);if(opts.encoding==="utf8"){buf=UTF8ArrayToString(buf)}FS.close(stream);return buf},writeFile(path,data,opts={}){opts.flags=opts.flags??577;var stream=FS.open(path,opts.flags,opts.mode);data=FS_fileDataToTypedArray(data);FS.write(stream,data,0,data.byteLength,undefined,opts.canOwn);FS.close(stream)},cwd:()=>FS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length,llseek:()=>0});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomFill(randomBuffer);randomLeft=randomBuffer.byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16895,73);node.stream_ops={llseek:MEMFS.stream_ops.llseek};node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path},id:fd+1};ret.parent=ret;return ret},readdir(){return Array.from(FS.streams.entries()).filter(([k,v])=>v).map(([k,v])=>k.toString())}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var stream of FS.streams){if(stream){FS.close(stream)}}},findObject(path,dontResolveLastLink){var ret=FS.analyzePath(path,dontResolveLastLink);if(!ret.exists){return null}return ret.object},analyzePath(path,dontResolveLastLink){try{var lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});path=lookup.path}catch(e){}var ret={isRoot:false,exists:false,error:0,name:null,path:null,object:null,parentExists:false,parentPath:null,parentObject:null};try{var lookup=FS.lookupPath(path,{parent:true});ret.parentExists=true;ret.parentPath=lookup.path;ret.parentObject=lookup.node;ret.name=PATH.basename(path);lookup=FS.lookupPath(path,{follow:!dontResolveLastLink});ret.exists=true;ret.path=lookup.path;ret.object=lookup.node;ret.name=lookup.node.name;ret.isRoot=lookup.path==="/"}catch(e){ret.error=e.errno}return ret},createPath(parent,path,canRead,canWrite){parent=typeof parent=="string"?parent:FS.getPath(parent);var parts=path.split("/").reverse();while(parts.length){var part=parts.pop();if(!part)continue;var current=PATH.join2(parent,part);try{FS.mkdir(current)}catch(e){if(e.errno!=20)throw e}parent=current}return current},createFile(parent,name,properties,canRead,canWrite){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(canRead,canWrite);return FS.create(path,mode)},createDataFile(parent,name,data,canRead,canWrite,canOwn){var path=name;if(parent){parent=typeof parent=="string"?parent:FS.getPath(parent);path=name?PATH.join2(parent,name):parent}var mode=FS_getMode(canRead,canWrite);var node=FS.create(path,mode);if(data){data=FS_fileDataToTypedArray(data);FS.chmod(node,mode|146);var stream=FS.open(node,577);FS.write(stream,data,0,data.length,0,canOwn);FS.close(stream);FS.chmod(node,mode)}},createDevice(parent,name,input,output){var path=PATH.join2(typeof parent=="string"?parent:FS.getPath(parent),name);var mode=FS_getMode(!!input,!!output);FS.createDevice.major??=64;var dev=FS.makedev(FS.createDevice.major++,0);FS.registerDevice(dev,{open(stream){stream.seekable=false},close(stream){if(output?.buffer?.length){output(10)}},read(stream,buffer,offset,length,pos){var bytesRead=0;for(var i=0;i<length;i++){var result;try{result=input()}catch(e){throw new FS.ErrnoError(29)}if(result===undefined&&bytesRead===0){throw new FS.ErrnoError(6)}if(result===null||result===undefined)break;bytesRead++;buffer[offset+i]=result}if(bytesRead){stream.node.atime=Date.now()}return bytesRead},write(stream,buffer,offset,length,pos){for(var i=0;i<length;i++){try{output(buffer[offset+i])}catch(e){throw new FS.ErrnoError(29)}}if(length){stream.node.mtime=stream.node.ctime=Date.now()}return i}});return FS.mkdev(path,mode,dev)},forceLoadFile(obj){if(obj.isDevice||obj.isFolder||obj.link||obj.contents)return true;if(globalThis.XMLHttpRequest){abort("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.")}else{try{obj.contents=readBinary(obj.url)}catch(e){throw new FS.ErrnoError(29)}}},createLazyFile(parent,name,url,canRead,canWrite){class LazyUint8Array{lengthKnown=false;chunks=[];get(idx){if(idx>this.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)abort(`invalid range (${from}, ${to}) or no bytes requested!`);if(to>datalength-1)abort(`only ${datalength} bytes available! programmer error!`);var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))abort("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText??"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")abort("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(globalThis.XMLHttpRequest){if(!ENVIRONMENT_IS_WORKER)abort("Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc");var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};for(const[key,fn]of Object.entries(node.stream_ops)){stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}}function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i<size;i++){buffer[offset+i]=contents[position+i]}}else{for(var i=0;i<size;i++){buffer[offset+i]=contents.get(position+i)}}return size}stream_ops.read=(stream,buffer,offset,length,position)=>{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var SYSCALLS={currentUmask:18,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return dir+"/"+path},writeStat(buf,stat){HEAPU32[buf>>2]=stat.dev;HEAPU32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAPU32[buf+12>>2]=stat.uid;HEAPU32[buf+16>>2]=stat.gid;HEAPU32[buf+20>>2]=stat.rdev;HEAP64[buf+24>>3]=BigInt(stat.size);HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();HEAP64[buf+40>>3]=BigInt(Math.floor(atime/1e3));HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;HEAP64[buf+56>>3]=BigInt(Math.floor(mtime/1e3));HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;HEAP64[buf+72>>3]=BigInt(Math.floor(ctime/1e3));HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;HEAP64[buf+88>>3]=BigInt(stat.ino);return 0},writeStatFs(buf,stats){HEAPU32[buf+4>>2]=stats.bsize;HEAPU32[buf+60>>2]=stats.bsize;HEAP64[buf+8>>3]=BigInt(stats.blocks);HEAP64[buf+16>>3]=BigInt(stats.bfree);HEAP64[buf+24>>3]=BigInt(stats.bavail);HEAP64[buf+32>>3]=BigInt(stats.files);HEAP64[buf+40>>3]=BigInt(stats.ffree);HEAPU32[buf+48>>2]=stats.fsid;HEAPU32[buf+64>>2]=stats.flags;HEAPU32[buf+56>>2]=stats.namelen},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.subarray(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();var mask=289792;stream.flags=stream.flags&~mask|arg&mask;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21537:case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;if(flags&64){mode&=~SYSCALLS.currentUmask}return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __emscripten_throw_longjmp=()=>{throw new EmscriptenSjLj};var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr<len)break;if(typeof offset!="undefined"){offset+=curr}}return ret};function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doReadv(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);try{if(isNaN(offset))return 22;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);HEAP64[newOffset>>3]=BigInt(stream.position);if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr<len){break}if(typeof offset!="undefined"){offset+=curr}}return ret};function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=doWritev(stream,iov,iovcnt);HEAPU32[pnum>>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var updateTableMap=(offset,count)=>{if(functionsInTableMap){for(var i=offset;i<offset+count;i++){var item=getWasmTableEntry(i);if(item){functionsInTableMap.set(item,i)}}}};var functionsInTableMap;var getFunctionAddress=func=>{if(!functionsInTableMap){functionsInTableMap=new WeakMap;updateTableMap(0,wasmTable.length)}return functionsInTableMap.get(func)||0};var freeTableIndexes=[];var getEmptyTableSlot=()=>{if(freeTableIndexes.length){return freeTableIndexes.pop()}return wasmTable["grow"](1)};var setWasmTableEntry=(idx,func)=>{wasmTable.set(idx,func);wasmTableMirror[idx]=wasmTable.get(idx)};var uleb128EncodeWithLen=arr=>{const n=arr.length;return[n%128|128,n>>7,...arr]};var wasmTypeCodes={i:127,p:127,j:126,f:125,d:124,e:111};var generateTypePack=types=>uleb128EncodeWithLen(Array.from(types,type=>{var code=wasmTypeCodes[type];return code}));var convertJsFunctionToWasm=(func,sig)=>{var bytes=Uint8Array.of(0,97,115,109,1,0,0,0,1,...uleb128EncodeWithLen([1,96,...generateTypePack(sig.slice(1)),...generateTypePack(sig[0]==="v"?"":sig[0])]),2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);var module=new WebAssembly.Module(bytes);var instance=new WebAssembly.Instance(module,{e:{f:func}});var wrappedFunc=instance.exports["f"];return wrappedFunc};var addFunction=(func,sig)=>{var rtn=getFunctionAddress(func);if(rtn){return rtn}var ret=getEmptyTableSlot();try{setWasmTableEntry(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);setWasmTableEntry(ret,wrapped)}functionsInTableMap.set(func,ret);return ret};var removeFunction=index=>{functionsInTableMap.delete(getWasmTableEntry(index));setWasmTableEntry(index,null);freeTableIndexes.push(index)};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var FS_createPath=(...args)=>FS.createPath(...args);var FS_unlink=(...args)=>FS.unlink(...args);var FS_createLazyFile=(...args)=>FS.createLazyFile(...args);var FS_createDevice=(...args)=>FS.createDevice(...args);FS.createPreloadedFile=FS_createPreloadedFile;FS.preloadFile=FS_preloadFile;FS.staticInit();{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["preloadPlugins"])preloadPlugins=Module["preloadPlugins"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])programArgs=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["removeFunction"]=removeFunction;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["UTF8ToString"]=UTF8ToString;Module["FS_preloadFile"]=FS_preloadFile;Module["FS_unlink"]=FS_unlink;Module["FS_createPath"]=FS_createPath;Module["FS_createDevice"]=FS_createDevice;Module["FS"]=FS;Module["FS_createDataFile"]=FS_createDataFile;Module["FS_createLazyFile"]=FS_createLazyFile;var _retro_api_version,_retro_set_environment,_retro_set_video_refresh,_retro_set_audio_sample,_retro_set_audio_sample_batch,_retro_set_input_poll,_retro_set_input_state,_retro_get_system_info,_retro_get_system_av_info,_retro_serialize_size,_retro_serialize,_retro_unserialize,_retro_load_game,_retro_unload_game,_retro_init,_retro_deinit,_retro_reset,_retro_run,_malloc,_free,_setThrew,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_retro_api_version=Module["_retro_api_version"]=wasmExports["o"];_retro_set_environment=Module["_retro_set_environment"]=wasmExports["p"];_retro_set_video_refresh=Module["_retro_set_video_refresh"]=wasmExports["q"];_retro_set_audio_sample=Module["_retro_set_audio_sample"]=wasmExports["r"];_retro_set_audio_sample_batch=Module["_retro_set_audio_sample_batch"]=wasmExports["s"];_retro_set_input_poll=Module["_retro_set_input_poll"]=wasmExports["t"];_retro_set_input_state=Module["_retro_set_input_state"]=wasmExports["u"];_retro_get_system_info=Module["_retro_get_system_info"]=wasmExports["v"];_retro_get_system_av_info=Module["_retro_get_system_av_info"]=wasmExports["w"];_retro_serialize_size=Module["_retro_serialize_size"]=wasmExports["x"];_retro_serialize=Module["_retro_serialize"]=wasmExports["y"];_retro_unserialize=Module["_retro_unserialize"]=wasmExports["z"];_retro_load_game=Module["_retro_load_game"]=wasmExports["A"];_retro_unload_game=Module["_retro_unload_game"]=wasmExports["B"];_retro_init=Module["_retro_init"]=wasmExports["C"];_retro_deinit=Module["_retro_deinit"]=wasmExports["D"];_retro_reset=Module["_retro_reset"]=wasmExports["E"];_retro_run=Module["_retro_run"]=wasmExports["F"];_malloc=Module["_malloc"]=wasmExports["G"];_free=Module["_free"]=wasmExports["I"];_setThrew=wasmExports["J"];__emscripten_stack_restore=wasmExports["K"];__emscripten_stack_alloc=wasmExports["L"];_emscripten_stack_get_current=wasmExports["M"];memory=wasmMemory=wasmExports["m"];__indirect_function_table=wasmTable=wasmExports["H"]}var wasmImports={f:___syscall_fcntl64,k:___syscall_ioctl,l:___syscall_openat,h:__emscripten_throw_longjmp,i:_emscripten_resize_heap,c:_fd_close,e:_fd_read,j:_fd_seek,b:_fd_write,g:invoke_ii,d:invoke_v,a:invoke_vii};function invoke_v(index){var sp=stackSave();try{getWasmTableEntry(index)()}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{getWasmTableEntry(index)(a1,a2)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}function invoke_ii(index,a1){var sp=stackSave();try{return getWasmTableEntry(index)(a1)}catch(e){stackRestore(sp);if(!(e instanceof EmscriptenEH))throw e;_setThrew(1,0)}}async function run(){preRun();if(runDependencies>0){await new Promise(resolve=>dependenciesFulfilled=resolve)}var setStatus=Module["setStatus"];if(setStatus){setStatus("Running...");await new Promise(resolve=>setTimeout(resolve,1));setTimeout(setStatus,1,"")}if(ABORT)return;initRuntime();Module["onRuntimeInitialized"]?.();postRun()}var wasmExports;wasmExports=await createWasm();await run();
2
+ ;return Module}export default GenesisCore;
b/1c96e9fb329de4c45f295222442767b3dea9e8ec16f51bae5edb1a081f3f65f1 ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "@type": "earl:TestResult",
3
+ "spec": "holo-source-plex — a Plex Media Server as a full SourceProvider: library sections→rails, Metadata→the one shape (token-stamped thumbs), search via /search, resolve→direct-play URL with X-Plex-Token. Federated + deduped with Jellyfin/Stremio/RD/IA under one interface — full library interoperability.",
4
+ "authority": "rests on #holo-source-plex β€” full-library-interop (sibling of #holo-source-jellyfin)",
5
+ "witnessed": true,
6
+ "covers": [
7
+ "sections",
8
+ "browse-shape",
9
+ "search-query",
10
+ "resolve-play",
11
+ "show-kind"
12
+ ],
13
+ "checks": {
14
+ "sections": true,
15
+ "browseShape": true,
16
+ "searchQuery": true,
17
+ "resolvePlay": true,
18
+ "showKind": true
19
+ },
20
+ "failed": []
21
+ }
b/1ca52bd58fce88351972bad776549b81c983d43ccc86858d6ffc37a727d8ee4e ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-sovereign.mjs β€” THE SOVEREIGN BROKER: one presence (identity + Holo Pass) projected onto every
2
+ // page through ONE message protocol, behind ONE biometric gate. This is the door the live-page seam
3
+ // (pageGuard) and the self-contained demo both call. It never lets a page reach the store directly.
4
+ //
5
+ // LAWS: SEC-5 a secret reaches ONLY the exact origin that earned it (a look-alike gets null) Β· SEC-2 read
6
+ // β‰  fill β‰  save, each a distinct capability Β· SEC-4 the right to reveal is the operator's biometric, not a
7
+ // page-supplied token Β· fail-closed: no operator β†’ every op is answered but reveals NOTHING.
8
+ //
9
+ // The store is pluggable: a REAL signed-in operator uses the TEE-encrypted holo-vault; a guest/demo uses a
10
+ // clearly-labeled local store. Either way the ORIGIN-BINDING (the anti-phishing core) is identical β€” that
11
+ // is the property this proves, and it is orthogonal to where the bytes are encrypted.
12
+
13
+ // registrable-origin equality: exact scheme + host. No suffix match, no normalization β€” a look-alike
14
+ // (IDN homograph, extra subdomain, scheme downgrade) is a DIFFERENT origin and earns nothing (SEC-5).
15
+ export function sameOrigin(a, b) {
16
+ try { const x = new URL(a), y = new URL(b); return x.protocol === y.protocol && x.host === y.host; }
17
+ catch { return String(a) === String(b); }
18
+ }
19
+ export function originOf(u) { try { return new URL(u).origin; } catch { return String(u || ""); } }
20
+
21
+ // A store is { get(origin)->cred|null, put(origin,cred), list()->[{origin,username}], has()->bool }.
22
+ // The demo/guest store is localStorage, labeled so nobody mistakes it for the TEE vault.
23
+ export function makeLocalStore(ns = "holo.pass.demo.v1") {
24
+ const load = () => { try { return JSON.parse(localStorage.getItem(ns) || "{}"); } catch { return {}; } };
25
+ const save = (o) => { try { localStorage.setItem(ns, JSON.stringify(o)); } catch {} };
26
+ return {
27
+ kind: "local",
28
+ async get(origin) { const o = load(); const e = o[originOf(origin)]; return e ? { username: e.username, secret: e.secret, totp: e.totp || null } : null; },
29
+ async put(origin, cred) { const o = load(); o[originOf(origin)] = { username: cred.username || "", secret: cred.secret || "", totp: cred.totp || null, ts: Date.now() }; save(o); },
30
+ async list() { const o = load(); return Object.keys(o).map((k) => ({ origin: k, username: o[k].username })); },
31
+ async has() { return Object.keys(load()).length > 0; },
32
+ };
33
+ }
34
+
35
+ // Wrap a real holo-vault projection as a store (used when an operator is signed in). The vault is opened
36
+ // elsewhere (behind the biometric); here we just read/write its origin→credential view.
37
+ export function makeVaultStore(vault) {
38
+ return {
39
+ kind: "vault",
40
+ async get(origin) { const c = vault.find ? vault.find(originOf(origin)) : null; return c ? { username: c.username, secret: c.secret, totp: c.totp || null } : null; },
41
+ async put(origin, cred) { if (vault.save) await vault.save({ origin: originOf(origin), username: cred.username, secret: cred.secret, totp: cred.totp || null }); },
42
+ async list() { return vault.list ? vault.list() : []; },
43
+ async has() { return vault.list ? vault.list().length > 0 : false; },
44
+ };
45
+ }
46
+
47
+ // The broker. `stepUp(action)` is the biometric gate: returns true to REVEAL, false/throw to refuse. For a
48
+ // real operator it wraps holo-stepup-gate.enforce; for the demo it can be a confirm() or an auto-approve
49
+ // with an on-screen note. `operator` null β†’ guest: queries work (to show "sign in to use Pass") but no
50
+ // reveal ever happens. presentFor(origin) tells the seam WHAT to surface (a saved login? a place to save?).
51
+ export function makeBroker({ store, operator = null, stepUp = null, hostOrigin = () => location.href } = {}) {
52
+ const trusted = (declared) => {
53
+ // SEC-5: trust ONLY the host-verified page origin, never a page-supplied string. In the live seam the
54
+ // SW stamps <meta holo-source>; the caller passes that. If a declared origin disagrees, refuse.
55
+ const real = hostOrigin();
56
+ if (declared && !sameOrigin(declared, real)) return null;
57
+ return real;
58
+ };
59
+ async function gate(action) {
60
+ if (!operator) return false; // guest: never reveal (fail-closed)
61
+ if (typeof stepUp !== "function") return true;
62
+ try { return await stepUp({ ...action, operator }); } catch { return false; }
63
+ }
64
+ return {
65
+ kind: store.kind, operator,
66
+ // What should the seam show at this origin? (no reveal β€” just presence)
67
+ async presentFor(declaredOrigin) {
68
+ const origin = trusted(declaredOrigin); if (!origin) return { present: false };
69
+ const cred = await store.get(origin);
70
+ return { present: true, operator: !!operator, hasLogin: !!cred, canSave: !cred, origin: originOf(origin), storeKind: store.kind };
71
+ },
72
+ // Fill: reveal the saved secret for THIS EXACT origin, behind the biometric. Look-alike β†’ null.
73
+ async fill(declaredOrigin) {
74
+ const origin = trusted(declaredOrigin);
75
+ if (!origin) return { ok: false, refused: "origin-mismatch" }; // the anti-phishing refusal
76
+ const cred = await store.get(origin);
77
+ if (!cred) return { ok: false, refused: "no-login-for-origin" };
78
+ if (!(await gate({ kind: "pass.fill", origin: originOf(origin) }))) return { ok: false, refused: operator ? "step-up-denied" : "no-identity" };
79
+ return { ok: true, credential: { username: cred.username, secret: cred.secret } };
80
+ },
81
+ // Save a login captured on submit (a write capability, distinct from fill β€” SEC-2).
82
+ async save(declaredOrigin, cred) {
83
+ const origin = trusted(declaredOrigin);
84
+ if (!origin) return { ok: false, refused: "origin-mismatch" };
85
+ if (!operator && store.kind !== "local") return { ok: false, refused: "no-identity" };
86
+ await store.put(origin, cred);
87
+ return { ok: true };
88
+ },
89
+ async totp(declaredOrigin) {
90
+ const origin = trusted(declaredOrigin); if (!origin) return { ok: false, refused: "origin-mismatch" };
91
+ const cred = await store.get(origin); if (!cred || !cred.totp) return { ok: false, refused: "no-totp" };
92
+ if (!(await gate({ kind: "pass.totp", origin: originOf(origin) }))) return { ok: false, refused: "step-up-denied" };
93
+ return { ok: true, code: cred.totp }; // (a real TOTP derives the live code; the demo stores a fixed one)
94
+ },
95
+
96
+ // ── WALLET β€” the SECOND face of the SAME presence. SEC-2 attenuation: connect β‰  sign β‰  send. The dApp
97
+ // gets your ADDRESS, never a key. connect is low-friction (like MetaMask); sign needs the biometric;
98
+ // send needs a FRESH biometric BOUND to the transaction (a connected site can't drain you silently). ──
99
+ async walletConnect(declaredOrigin) {
100
+ const origin = trusted(declaredOrigin); if (!origin) return { ok: false, refused: "origin-mismatch" };
101
+ if (!operator) return { ok: false, refused: "no-identity" };
102
+ return { ok: true, address: addressFor(operator), chainId: "0x1" }; // real operator β†’ holo-wdk derives it
103
+ },
104
+ async walletSign(declaredOrigin, message) {
105
+ const origin = trusted(declaredOrigin); if (!origin) return { ok: false, refused: "origin-mismatch" };
106
+ if (!operator) return { ok: false, refused: "no-identity" };
107
+ if (!(await gate({ kind: "wallet.sign", origin: originOf(origin), message }))) return { ok: false, refused: "step-up-denied" };
108
+ return { ok: true, signature: demoSignature(operator, "sign", message), address: addressFor(operator) };
109
+ },
110
+ async walletSend(declaredOrigin, tx) {
111
+ const origin = trusted(declaredOrigin); if (!origin) return { ok: false, refused: "origin-mismatch" };
112
+ if (!operator) return { ok: false, refused: "no-identity" };
113
+ // a SEND is the strongest capability β€” a FRESH biometric bound to THIS tx, never suppressible by a
114
+ // prior connect/sign window (the caller passes force:true to enforce.enforce β†’ a real re-prompt).
115
+ if (!(await gate({ kind: "wallet.send", force: true, origin: originOf(origin), to: tx && tx.to, value: tx && tx.value }))) return { ok: false, refused: "step-up-denied" };
116
+ return { ok: true, txHash: demoSignature(operator, "send", JSON.stringify(tx || {})) };
117
+ },
118
+ };
119
+ }
120
+
121
+ // a display address derived deterministically from the operator (NO key material). A real signed-in
122
+ // operator's address comes from holo-wdk; here it's a stable stand-in so the demo shows a consistent "you".
123
+ export function addressFor(operator) {
124
+ let h = 2166136261 >>> 0; const s = "holo-addr:" + String(operator);
125
+ for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619) >>> 0; }
126
+ let hex = ""; let x = h; for (let i = 0; i < 40; i++) { hex += (x & 0xf).toString(16); x = (Math.imul(x, 1103515245) + 12345) >>> 0; }
127
+ return "0x" + hex;
128
+ }
129
+ function demoSignature(operator, kind, payload) {
130
+ let h = 5381 >>> 0; const s = kind + "|" + operator + "|" + payload;
131
+ for (let i = 0; i < s.length; i++) h = (Math.imul(h, 33) ^ s.charCodeAt(i)) >>> 0;
132
+ let hex = ""; let x = h; for (let i = 0; i < 64; i++) { hex += (x & 0xf).toString(16); x = (Math.imul(x, 1103515245) + 12345) >>> 0; }
133
+ return "0x" + hex; // demo stand-in; a real operator signs with holo-wdk (secp256k1), key never leaving the device
134
+ }
135
+
136
+ // ── EIP-1193 provider backed by the broker, announced via EIP-6963 β€” how a live dApp discovers "Hologram".
137
+ // The dApp calls provider.request({method, params}); every privileged method routes through the broker's
138
+ // gate. window.ethereum-freezing dApps still find it via the 6963 announce event (SEC-2, modern path). ──
139
+ export function makeProvider(broker, origin) {
140
+ const listeners = {};
141
+ const provider = {
142
+ isHologram: true,
143
+ async request({ method, params }) {
144
+ params = params || [];
145
+ switch (method) {
146
+ case "eth_chainId": return "0x1";
147
+ case "eth_accounts": case "eth_requestAccounts": {
148
+ const r = await broker.walletConnect(origin);
149
+ if (!r.ok) throw Object.assign(new Error(r.refused), { code: 4001 });
150
+ emit("accountsChanged", [r.address]); return [r.address];
151
+ }
152
+ case "personal_sign": case "eth_sign": {
153
+ const r = await broker.walletSign(origin, params[0]);
154
+ if (!r.ok) throw Object.assign(new Error(r.refused), { code: 4001 });
155
+ return r.signature;
156
+ }
157
+ case "eth_sendTransaction": {
158
+ const r = await broker.walletSend(origin, params[0]);
159
+ if (!r.ok) throw Object.assign(new Error(r.refused), { code: 4001 });
160
+ return r.txHash;
161
+ }
162
+ default: throw Object.assign(new Error("unsupported method: " + method), { code: 4200 });
163
+ }
164
+ },
165
+ on(ev, cb) { (listeners[ev] || (listeners[ev] = [])).push(cb); },
166
+ removeListener(ev, cb) { listeners[ev] = (listeners[ev] || []).filter((f) => f !== cb); },
167
+ };
168
+ function emit(ev, data) { (listeners[ev] || []).forEach((cb) => { try { cb(data); } catch {} }); }
169
+ return provider;
170
+ }
171
+ // announce the provider the EIP-6963 way (works even when a dApp froze window.ethereum).
172
+ export function announceProvider(provider, target = (typeof window !== "undefined" ? window : null)) {
173
+ if (!target) return;
174
+ const info = { uuid: "holo-" + (target.crypto && target.crypto.randomUUID ? target.crypto.randomUUID() : "0000"), name: "Hologram", icon: "data:image/svg+xml,%3Csvg/%3E", rdns: "id.holo.wallet" };
175
+ const detail = Object.freeze({ info, provider });
176
+ const fire = () => target.dispatchEvent(new CustomEvent("eip6963:announceProvider", { detail }));
177
+ target.addEventListener("eip6963:requestProvider", fire);
178
+ fire();
179
+ }
b/1cfa7aa1528b10e89b4dc0b051482de2c2ff3af821b1d13d9bcb2184ad55ff99 ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
2
+ <title>Holo Search</title>
3
+ <!-- HOLO SEARCH β€” the default engine of the Hologram browser. A plain query is answered from YOUR OWN
4
+ ΞΊ-graph (visit history) + the Hologram app universe, ranked ON-DEVICE (holo-rank), instant, zero-egress.
5
+ A bare query never needs a third party to paint. The long tail escapes to the web in one tap. (SEC-7:
6
+ the personal index + rank signals never leave the device.) Loaded same-origin by the browser; result
7
+ clicks post navigation intent back to the browser page (which owns the loading seam). -->
8
+ <style>
9
+ :root{color-scheme:dark;--bg:#0b141a;--fg:#e8eef5;--dim:#93a4b8;--card:#121a24;--line:#1e2a38;--accent:#7b68ee;--accent2:#3b6ee0}
10
+ *{box-sizing:border-box}
11
+ html,body{margin:0;height:100%;background:var(--bg);color:var(--fg);font:15px/1.55 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}
12
+ .wrap{max-width:720px;margin:0 auto;padding:2.4rem 1.2rem 4rem}
13
+ .brand{display:flex;align-items:center;gap:.6rem;justify-content:center;margin-bottom:1.4rem;opacity:.95}
14
+ .brand svg{width:30px;height:30px}
15
+ .brand b{font-size:1.15rem;font-weight:600;letter-spacing:.2px}
16
+ form{display:flex;gap:.5rem;margin-bottom:.4rem}
17
+ input[type=search]{flex:1;padding:.85rem 1.1rem;border-radius:12px;border:1px solid var(--line);background:var(--card);color:var(--fg);font-size:1rem;outline:none}
18
+ input[type=search]:focus{border-color:var(--accent)}
19
+ button.go{padding:.85rem 1.2rem;border-radius:12px;border:none;background:linear-gradient(135deg,var(--accent),var(--accent2));color:#fff;font-weight:600;cursor:pointer}
20
+ .meta{color:var(--dim);font-size:.82rem;margin:.2rem .2rem 1.3rem;display:flex;justify-content:space-between}
21
+ .seal{color:var(--dim);opacity:.7}
22
+ .group{margin:1.5rem 0 .5rem;font-size:.78rem;text-transform:uppercase;letter-spacing:.08em;color:var(--dim);font-weight:600}
23
+ a.res{display:block;text-decoration:none;color:inherit;padding:.8rem 1rem;border-radius:12px;border:1px solid transparent}
24
+ a.res:hover{background:var(--card);border-color:var(--line)}
25
+ .res .t{font-size:1.02rem;color:#cfe0ff;margin-bottom:.15rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
26
+ .res .u{color:#6f8db5;font-size:.82rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
27
+ .res .why{display:inline-block;margin-top:.35rem;font-size:.74rem;color:var(--dim);background:#0f1620;border:1px solid var(--line);border-radius:999px;padding:.1rem .55rem}
28
+ .escape{margin-top:1.6rem;padding-top:1.1rem;border-top:1px solid var(--line)}
29
+ .empty{color:var(--dim);text-align:center;margin:2rem 0}
30
+ </style></head>
31
+ <body><div class="wrap">
32
+ <div class="brand"><svg viewBox="0 0 24 24" fill="none" stroke="url(#g)" stroke-width="1.7"><defs><linearGradient id="g" x1="0" y1="0" x2="24" y2="24"><stop stop-color="#7b68ee"/><stop offset="1" stop-color="#3b6ee0"/></linearGradient></defs><circle cx="12" cy="12" r="8.4"/><path d="M3.6 12h16.8"/><path d="M12 3.6c2.4 2.3 3.7 5.3 3.7 8.4s-1.3 6.1-3.7 8.4c-2.4-2.3-3.7-5.3-3.7-8.4S9.6 5.9 12 3.6Z"/></svg><b>Holo Search</b></div>
33
+ <form id="f"><input type="search" id="q" autocomplete="off" spellcheck="false" placeholder="Search your web, the Hologram universe, or the internet"><button class="go" type="submit">Search</button></form>
34
+ <div class="meta"><span id="stat"></span><span class="seal">on-device Β· zero-egress Β· ΞΊ-ranked</span></div>
35
+ <div id="out"></div>
36
+ </div>
37
+ <script src="../player/holo-rank.js"></script>
38
+ <script>
39
+ (function () {
40
+ "use strict";
41
+ var params = new URL(location.href).searchParams;
42
+ var q0 = params.get("q") || "";
43
+ var qEl = document.getElementById("q"), out = document.getElementById("out"), stat = document.getElementById("stat");
44
+ qEl.value = q0;
45
+
46
+ // navigation goes back to the BROWSER page (it owns the loading seam); fall back to top nav.
47
+ function nav(url) {
48
+ try { window.parent.postMessage({ type: "holonav", url: url }, location.origin); } catch (e) {}
49
+ try { window.parent.postMessage({ type: "holonav", url: url }, "*"); } catch (e) {}
50
+ }
51
+ function esc(s) { return String(s == null ? "" : s).replace(/[<&>"]/g, function (c) { return { "<": "&lt;", "&": "&amp;", ">": "&gt;", '"': "&quot;" }[c]; }); }
52
+ function tokens(s) { return String(s || "").toLowerCase().split(/[^a-z0-9]+/).filter(function (t) { return t.length > 1; }); }
53
+ function host(u) { try { return new URL(u).hostname.replace(/^www\./, ""); } catch (e) { return u; } }
54
+
55
+ // ── the personal ΞΊ-graph: your visit history (persisted by the browser, on-device) ──
56
+ function history() { try { var h = JSON.parse(localStorage.getItem("holo.browser.history.v1") || "[]"); return Array.isArray(h) ? h : []; } catch (e) { return []; } }
57
+ // collapse repeat visits to one candidate per URL, counting frequency + keeping the most recent.
58
+ function historyCandidates() {
59
+ var by = {}, h = history();
60
+ for (var i = 0; i < h.length; i++) {
61
+ var e = h[i]; if (!e || !e.url || !/^https?:\/\//.test(e.url)) continue; // real web pages only β€” never our own app/holo:// pages
62
+ var k = e.url; if (!by[k]) by[k] = { url: e.url, title: e.title || host(e.url), visits: 0, ts: 0, source: "history" };
63
+ by[k].visits++; by[k].ts = Math.max(by[k].ts, e.ts || 0);
64
+ }
65
+ return Object.keys(by).map(function (k) { return by[k]; });
66
+ }
67
+ // ── the Hologram universe: the app catalog (JSON-LD, same-origin). Mount-base aware: on a /<repo>/
68
+ // static host this page is at <base>/apps/browser/holo-search.html, so the catalog is <base>/apps/…. ──
69
+ var APPS_BASE = location.pathname.replace(/\/apps\/browser\/[^/]*$/, "/apps/");
70
+ if (!/\/apps\/$/.test(APPS_BASE)) APPS_BASE = "/apps/";
71
+ var catalogP = fetch(APPS_BASE + "index.jsonld").then(function (r) { return r.json(); }).then(function (j) {
72
+ var MOUNT = APPS_BASE.replace(/apps\/$/, ""); // "<base>/" β€” so app links resolve on any mount
73
+ var ds = j["dcat:dataset"] || j.dataset || [];
74
+ return ds.map(function (d) {
75
+ var land = d["dcat:landingPage"] || d["schema:mainEntityOfPage"] || "";
76
+ return { url: land ? (MOUNT + String(land).replace(/^\/+/, "")) : "", title: d["schema:name"] || "", desc: d["schema:description"] || "", source: "app" };
77
+ }).filter(function (c) { return c.url && c.title; });
78
+ }).catch(function () { return []; });
79
+
80
+ // ── rank: term match (primary) + personal frequency + recency, fused; holo-rank's learned term
81
+ // profile contributes a personal boost + we record the search so taste improves over time. Every
82
+ // pick carries a WHY (legible, no black box). ──
83
+ function scoreOne(c, qt, profileTerms, now) {
84
+ var hay = (c.title + " " + (c.url || "") + " " + (c.desc || "")).toLowerCase();
85
+ var term = 0, hits = 0;
86
+ for (var i = 0; i < qt.length; i++) { if (hay.indexOf(qt[i]) >= 0) { term += (c.title.toLowerCase().indexOf(qt[i]) >= 0 ? 2 : 1); hits++; } }
87
+ var termScore = qt.length ? (term / qt.length) * (hits / qt.length) : 0; // coverage-weighted
88
+ var freq = c.visits ? Math.min(1, Math.log(1 + c.visits) / Math.log(8)) : 0;
89
+ var rec = c.ts ? Math.max(0, 1 - (now - c.ts) / (30 * 864e5)) : 0; // 30-day recency
90
+ var learned = 0; for (var t in profileTerms) if (hay.indexOf(t) >= 0) learned += profileTerms[t];
91
+ return { c: c, term: termScore, personal: freq * 0.7 + rec * 0.3, learned: Math.min(1, learned), _hits: hits };
92
+ }
93
+ function whyFor(s) {
94
+ if (s.c.source === "app") return "Hologram app";
95
+ if (s.c.visits >= 3) return "Visited " + s.c.visits + "Γ—";
96
+ if (s.personal > 0.4) return "Recently visited";
97
+ if (s.term >= 1) return "Strong match";
98
+ return "From your web";
99
+ }
100
+ function rankAll(q, cands) {
101
+ var qt = tokens(q), now = Date.now();
102
+ var prof = (window.HoloRank && window.HoloRank.profile) ? (window.HoloRank.profile().terms || {}) : {};
103
+ var scored = cands.map(function (c) { return scoreOne(c, qt, prof, now); }).filter(function (s) { return s.term > 0 || (!qt.length && s.personal > 0); });
104
+ scored.sort(function (a, b) { return (b.term * 3 + b.personal * 1.4 + b.learned * 0.8) - (a.term * 3 + a.personal * 1.4 + a.learned * 0.8); });
105
+ return scored;
106
+ }
107
+
108
+ function render(q) {
109
+ stat.textContent = "";
110
+ // record the search so the on-device profile learns (holo-rank), then rank.
111
+ try { if (window.HoloRank && window.HoloRank.signal) window.HoloRank.signal({ type: "search", term: q }); } catch (e) {}
112
+ catalogP.then(function (apps) {
113
+ var cands = historyCandidates().concat(apps);
114
+ var scored = rankAll(q, cands);
115
+ var yours = scored.filter(function (s) { return s.c.source === "history"; }).slice(0, 8);
116
+ var appHits = scored.filter(function (s) { return s.c.source === "app"; }).slice(0, 6);
117
+ var html = "";
118
+ if (yours.length) { html += '<div class="group">From your web</div>'; yours.forEach(function (s) { html += row(s); }); }
119
+ if (appHits.length) { html += '<div class="group">Hologram universe</div>'; appHits.forEach(function (s) { html += row(s); }); }
120
+ // the web escape β€” ALWAYS present, so the long tail resolves in one tap (no third party until asked).
121
+ var looksDomain = /^[a-z0-9-]+(\.[a-z0-9-]+)+(\/.*)?$/i.test(q.trim()) && !/\s/.test(q.trim());
122
+ html += '<div class="escape"><div class="group">Search the web</div>';
123
+ if (looksDomain) html += rowRaw("https://" + q.trim(), "Go to " + esc(q.trim()), "Direct address");
124
+ html += rowRaw("https://html.duckduckgo.com/html/?q=" + encodeURIComponent(q), "Search β€œ" + esc(q) + "” on the web", "Private web search Β· DuckDuckGo");
125
+ html += "</div>";
126
+ out.innerHTML = html;
127
+ var n = yours.length + appHits.length;
128
+ stat.textContent = q ? (n ? (n + " result" + (n > 1 ? "s" : "") + " from your ΞΊ-graph") : "Nothing in your ΞΊ-graph yet β€” search the web below") : "Type to search your web and the Hologram universe";
129
+ wire();
130
+ });
131
+ }
132
+ function row(s) { return rowRaw(s.c.url, s.c.title || host(s.c.url), whyFor(s), s.c.source === "app" ? s.c.url : host(s.c.url)); }
133
+ function rowRaw(url, title, why, sub) {
134
+ return '<a class="res" href="' + esc(url) + '" data-url="' + esc(url) + '"><div class="t">' + esc(title) + '</div><div class="u">' + esc(sub || url) + '</div><span class="why">' + esc(why) + '</span></a>';
135
+ }
136
+ function wire() {
137
+ var links = out.querySelectorAll("a.res");
138
+ for (var i = 0; i < links.length; i++) links[i].addEventListener("click", function (e) { e.preventDefault(); nav(this.getAttribute("data-url")); });
139
+ }
140
+
141
+ document.getElementById("f").addEventListener("submit", function (e) { e.preventDefault(); var q = qEl.value.trim(); if (q) { history.pushState ? 0 : 0; render(q); } });
142
+ render(q0);
143
+ qEl.focus(); if (q0) qEl.setSelectionRange(q0.length, q0.length);
144
+ })();
145
+ </script>
146
+ </body></html>
b/1f0258122adee3b1a865da516c2ab2a91f22939cba63f999cc159118b753370b ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "v": 1,
3
+ "kind": "holo-kappa-audio",
4
+ "title": "Content Address",
5
+ "artist": "Hologram Collective",
6
+ "album": "Kappa Sessions",
7
+ "mime": "audio/wav",
8
+ "sampleRate": 44100,
9
+ "channels": 1,
10
+ "bits": 16,
11
+ "durationSec": 12.727,
12
+ "bytes": 1122724,
13
+ "trackKappa": "did:holo:sha256:b2e5bef4fdb398147c9e36af427cbe385c14153f755ee9f113416c065ed55fb2",
14
+ "chunkBytes": 262144,
15
+ "loudness": {
16
+ "lufs": -10.53,
17
+ "samplePeakDbfs": -3.07,
18
+ "truePeakDbtp": -3.07
19
+ },
20
+ "normalizeDb": -5.47,
21
+ "targetLufs": -16,
22
+ "chunks": [
23
+ {
24
+ "file": "chunk-0000.bin",
25
+ "kappa": "did:holo:sha256:9b621f5b8bde7cb7ae5a01cdd9ab6bc4f4abae09844d30eff342b427bcd9f88f",
26
+ "bytes": 262144
27
+ },
28
+ {
29
+ "file": "chunk-0001.bin",
30
+ "kappa": "did:holo:sha256:546e96e238291c87da8ea049c1f27c7b5cd41963e413b59db2dabc18ba063607",
31
+ "bytes": 262144
32
+ },
33
+ {
34
+ "file": "chunk-0002.bin",
35
+ "kappa": "did:holo:sha256:6cf954fb612ff84b639de62ec6deaf9ed2c6d2f581a58bc464d71a84fd904587",
36
+ "bytes": 262144
37
+ },
38
+ {
39
+ "file": "chunk-0003.bin",
40
+ "kappa": "did:holo:sha256:73c0b6b62c440decc5e0f05bd5a27a04bc36ed737908b35c0c3141e7a3073756",
41
+ "bytes": 262144
42
+ },
43
+ {
44
+ "file": "chunk-0004.bin",
45
+ "kappa": "did:holo:sha256:79bdb636e618678599190cf0e8d9a72fed757e164ce7eee8820a0332535282ba",
46
+ "bytes": 74148
47
+ }
48
+ ]
49
+ }
b/1f1976bb4609e4bf91e616016ac1af4f81ad9d5d8b8c07360763e97db0b6029b ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-media-cache.mjs β€” every metadata response is a content-addressed ΞΊ-object.
2
+ //
3
+ // This is the "very, very fast" + "100% ΞΊ-addressable substrate-native" answer for discovery. A TMDb
4
+ // response (a trending page, a title, a season, a poster manifest) is fetched ONCE, sealed under its own
5
+ // content hash (ΞΊ), and read back O(1) β€” across reloads (IndexedDB) and, by export, across devices. Cold =
6
+ // network; warm = memory-speed from the resident ΞΊ-set; offline = still works. Law L5 holds: a cached
7
+ // entry only returns bytes that RE-DERIVE to the requested ΞΊ β€” a tampered entry fails verification and is
8
+ // refused, never silently trusted (the same discipline as holo-q-vision-store, kept self-contained here so
9
+ // the module is app-local + Node-witnessable with zero cross-tree imports).
10
+ //
11
+ // ONE KV, TWO ROLES: "obj:<ΞΊ>" β†’ the sealed response (immutable, content-addressed, verifiable)
12
+ // "req:<key>" β†’ the ΞΊ for a request key (so the same request short-circuits to the ΞΊ)
13
+
14
+ // Stable (canonical) serialization so the hash is deterministic regardless of key order.
15
+ function jcs(v) {
16
+ if (v === null || typeof v !== "object") return JSON.stringify(v);
17
+ if (Array.isArray(v)) return "[" + v.map(jcs).join(",") + "]";
18
+ return "{" + Object.keys(v).sort().map((k) => JSON.stringify(k) + ":" + jcs(v[k])).join(",") + "}";
19
+ }
20
+
21
+ // sha-256 hex β€” SubtleCrypto in the browser, node:crypto in Node. Both deterministic; this IS the ΞΊ.
22
+ async function sha256hex(s) {
23
+ if (typeof crypto !== "undefined" && crypto.subtle) {
24
+ const buf = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(s));
25
+ return [...new Uint8Array(buf)].map((b) => b.toString(16).padStart(2, "0")).join("");
26
+ }
27
+ const { createHash } = await import("node:crypto");
28
+ return createHash("sha256").update(s).digest("hex");
29
+ }
30
+
31
+ export async function address(body) { return "sha256:" + (await sha256hex(jcs(body))); }
32
+ export async function seal(body) { return { id: await address(body), body }; }
33
+ export async function verify(obj) { return !!obj && typeof obj.id === "string" && obj.id === (await address(obj.body)); }
34
+
35
+ // createMediaCache({ kv }) β€” kv: { get(key)->string|null (maybe async), set(key,val) }.
36
+ export function createMediaCache({ kv } = {}) {
37
+ if (!kv || typeof kv.get !== "function" || typeof kv.set !== "function") throw new Error("holo-media-cache: kv required");
38
+ const stats = { puts: 0, hits: 0, misses: 0, refused: 0 };
39
+
40
+ async function put(key, body) {
41
+ const sealed = await seal(body);
42
+ await kv.set("obj:" + sealed.id, jcs(sealed));
43
+ await kv.set("req:" + key, sealed.id);
44
+ stats.puts++;
45
+ return sealed.id;
46
+ }
47
+ // resolve(ΞΊ) β†’ body | null, verify-before-trust (Law L5).
48
+ async function resolveKappa(kappa) {
49
+ const s = await kv.get("obj:" + kappa);
50
+ if (s == null) return null;
51
+ let obj; try { obj = JSON.parse(s); } catch { return null; }
52
+ if (!obj || obj.id !== kappa || !(await verify(obj))) { stats.refused++; return null; }
53
+ return obj.body;
54
+ }
55
+ // get(key) β†’ { kappa, body } | null. The O(1) "fetched this request before?" lookup (survives reloads).
56
+ async function get(key) {
57
+ const kappa = await kv.get("req:" + key);
58
+ if (!kappa) { stats.misses++; return null; }
59
+ const body = await resolveKappa(kappa);
60
+ if (body == null) { stats.misses++; return null; }
61
+ stats.hits++;
62
+ return { kappa, body };
63
+ }
64
+ // through(key, fetcher) β€” the one call sites use: a hit serves from ΞΊ (no fetcher run); a miss runs the
65
+ // fetcher, seals + stores, and returns. Offline + rate-limited paths fall through to the cached ΞΊ.
66
+ async function through(key, fetcher) {
67
+ const hit = await get(key);
68
+ if (hit) return { ...hit, fromCache: true };
69
+ const body = await fetcher();
70
+ const kappa = await put(key, body);
71
+ return { kappa, body, fromCache: false };
72
+ }
73
+ return { get, put, resolveKappa, through, stats: () => ({ ...stats }) };
74
+ }
75
+
76
+ // ── persistent IndexedDB KV (browser only; no dependency) β€” same shape as holo-q-vision-store's idbKV ──────
77
+ function idbKV(dbName = "holo-media", storeName = "kv") {
78
+ let dbp = null;
79
+ const open = () => dbp || (dbp = new Promise((res, rej) => {
80
+ const r = indexedDB.open(dbName, 1);
81
+ r.onupgradeneeded = () => { try { r.result.createObjectStore(storeName); } catch {} };
82
+ r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error);
83
+ }));
84
+ const tx = async (mode, fn) => { const db = await open(); return new Promise((res, rej) => { const t = db.transaction(storeName, mode); const s = t.objectStore(storeName); const rq = fn(s); rq.onsuccess = () => res(rq.result); rq.onerror = () => rej(rq.error); }); };
85
+ return {
86
+ get: (k) => tx("readonly", (s) => s.get(k)).then((v) => (v == null ? null : v)).catch(() => null),
87
+ set: (k, v) => tx("readwrite", (s) => s.put(v, k)).catch(() => {}),
88
+ };
89
+ }
90
+ // in-memory fallback (Node default / private-mode browser) β€” session-scoped, still ΞΊ-correct.
91
+ export function memKV() { const m = new Map(); return { get: (k) => (m.has(k) ? m.get(k) : null), set: (k, v) => { m.set(k, v); } }; }
92
+
93
+ if (typeof window !== "undefined") {
94
+ window.HoloMediaCache = {
95
+ createMediaCache, memKV,
96
+ live() { try { return createMediaCache({ kv: idbKV() }); } catch { return createMediaCache({ kv: memKV() }); } },
97
+ };
98
+ }
99
+
100
+ export default { createMediaCache, memKV, seal, verify, address };
b/20317750d4063d3c9b8e4350458d28f0a021d419c46a4cb422a02089ff039005 ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-source-ia.mjs β€” Internet Archive as a SourceProvider: a massive, LEGAL, key-free catalogue of
2
+ // public-domain films (and more), shipped enabled-by-default so the wall is full of real, beautiful,
3
+ // legal content with zero user setup. Uses IA's open JSON APIs (no key):
4
+ // advancedsearch.php?q=…&output=json β†’ { response:{ docs:[{identifier,title,year,description}] } }
5
+ // metadata/{identifier} β†’ { files:[{name,format}], metadata:{} }
6
+ // services/img/{identifier} β†’ poster
7
+ // download/{identifier}/{file} β†’ the stream (HTTP, instant-play)
8
+ //
9
+ // createIA({ fetch, cache }) β€” injected (Node-witnessable). Implements the SourceProvider contract.
10
+
11
+ import { browseOnly } from "./holo-media-item.mjs";
12
+
13
+ const IA = "https://archive.org";
14
+ // curated, legal shelves (each is an IA search query).
15
+ // Curated, LEGAL, key-free public-domain shelves β€” the out-of-box catalogue you can stream right away with
16
+ // zero setup. Every collection id is verified to return streamable films.
17
+ const SHELVES = [
18
+ { id: "feature_films", name: "Feature Films Β· Internet Archive", q: "collection:(feature_films) AND mediatype:(movies)" },
19
+ { id: "comedy", name: "Comedy Classics Β· Internet Archive", q: "collection:(feature_films) AND subject:(comedy) AND mediatype:(movies)" },
20
+ { id: "scifi_horror", name: "Sci-Fi & Horror Β· Internet Archive", q: "collection:(SciFi_Horror) AND mediatype:(movies)" },
21
+ { id: "westerns", name: "Westerns Β· Internet Archive", q: "collection:(feature_films) AND subject:(western) AND mediatype:(movies)" },
22
+ { id: "classic_tv", name: "TV Classics Β· Internet Archive", q: "collection:(classic_tv) AND mediatype:(movies)" },
23
+ { id: "war", name: "War Films Β· Internet Archive", q: "collection:(feature_films) AND subject:(war) AND mediatype:(movies)" },
24
+ { id: "animation", name: "Classic Animation Β· Internet Archive", q: "collection:(animationandcartoons) AND mediatype:(movies)" },
25
+ { id: "noir", name: "Film Noir Β· Internet Archive", q: "collection:(film_noir) AND mediatype:(movies)" },
26
+ { id: "silent", name: "Silent Cinema Β· Internet Archive", q: "collection:(silent_films) AND mediatype:(movies)" },
27
+ { id: "documentary", name: "Documentaries Β· Internet Archive", q: "collection:(opensource_movies) AND subject:(documentary) AND mediatype:(movies)" },
28
+ ];
29
+ const VIDEO_FMT = /h\.264|mpeg4|mp4|ogg video|ogv|matroska|webm/i;
30
+ const isVideoFile = (f) => VIDEO_FMT.test(f.format || "") || /\.(mp4|ogv|webm|mkv)$/i.test(f.name || "");
31
+ const qualityOf = (name) => /2160|4k/i.test(name) ? 2160 : /1080/.test(name) ? 1080 : /720/.test(name) ? 720 : 0;
32
+
33
+ function normalizeDoc(d) {
34
+ return {
35
+ id: "ia:" + d.identifier, _iaId: d.identifier, kind: "movie", name: d.title || d.identifier,
36
+ year: d.year ? (String(d.year).match(/\d{4}/) ? +String(d.year).match(/\d{4}/)[0] : null) : null,
37
+ overview: Array.isArray(d.description) ? d.description[0] : (d.description || ""),
38
+ blurb: Array.isArray(d.description) ? d.description[0] : (d.description || ""),
39
+ posterUrl: `${IA}/services/img/${encodeURIComponent(d.identifier)}`,
40
+ backdrop: `${IA}/services/img/${encodeURIComponent(d.identifier)}`,
41
+ topics: [], genres: [], rating: null, runtimeSec: 0,
42
+ channel: "Internet Archive", quality: 0.7, license: "Public Domain",
43
+ source: "tmdb", provider: "ia", kappa: "", holoKappa: "ia:" + d.identifier,
44
+ availability: browseOnly(),
45
+ };
46
+ }
47
+
48
+ export function createIA({ fetch: f, cache } = {}) {
49
+ const doFetch = f || (typeof fetch !== "undefined" ? fetch.bind(globalThis) : null);
50
+ if (!doFetch) throw new Error("holo-source-ia: fetch required");
51
+ async function api(url) {
52
+ const fetcher = async () => { const r = await doFetch(url); if (!r.ok) throw new Error("ia " + r.status); return r.json(); };
53
+ if (!cache) return fetcher();
54
+ const { body } = await cache.through("ia|" + url, fetcher);
55
+ return body;
56
+ }
57
+ async function searchDocs(q, { rows = 24, page = 1 } = {}) {
58
+ const url = `${IA}/advancedsearch.php?q=${encodeURIComponent(q)}&fl[]=identifier&fl[]=title&fl[]=year&fl[]=description&sort[]=downloads+desc&rows=${rows}&page=${page}&output=json`;
59
+ const data = await api(url);
60
+ return (data && data.response && data.response.docs) || [];
61
+ }
62
+ return {
63
+ id: "builtin:ia", name: "Internet Archive", kind: "open", enabled: true, trust: 3,
64
+ async catalogs() { return SHELVES.map((s) => ({ id: s.id, type: "movie", name: s.name })); },
65
+ async browse(catalogId, opts = {}) {
66
+ const shelf = SHELVES.find((s) => s.id === catalogId) || SHELVES[0];
67
+ const q = opts.search ? `${shelf.q} AND (title:(${opts.search}))` : shelf.q;
68
+ return (await searchDocs(q, { page: (opts.skip ? Math.floor(opts.skip / 24) + 1 : 1) })).map(normalizeDoc);
69
+ },
70
+ async search(q) { return (await searchDocs(`mediatype:(movies) AND (title:(${q}))`)).map(normalizeDoc); },
71
+ async resolve(item) {
72
+ const id = item._iaId || String(item.id).replace(/^ia:/, "");
73
+ const meta = await api(`${IA}/metadata/${encodeURIComponent(id)}`);
74
+ const files = (meta && meta.files) || [];
75
+ const vids = files.filter(isVideoFile).sort((a, b) => qualityOf(b.name) - qualityOf(a.name) || (+b.size || 0) - (+a.size || 0));
76
+ if (!vids.length) return [];
77
+ return vids.slice(0, 3).map((file) => ({
78
+ playSrc: `${IA}/download/${encodeURIComponent(id)}/${encodeURIComponent(file.name)}`,
79
+ type: /\.ogv$/i.test(file.name) ? "video/ogg" : /\.webm$/i.test(file.name) ? "video/webm" : "video/mp4",
80
+ quality: qualityOf(file.name), hdr: false, kind: "open", httpDirect: true,
81
+ provenance: { resolver: "Internet Archive", kind: "open", label: "Public Domain Β· Internet Archive" },
82
+ }));
83
+ },
84
+ };
85
+ }
86
+
87
+ export default { createIA };
88
+ if (typeof window !== "undefined") window.HoloSourceIA = { createIA };
b/214e51f0f754eec5fb6bde71dc5011a5ec69a42420bc52156babaf7b6e27da26 ADDED
The diff for this file is too large to render. See raw diff
 
b/24338bb3184e12efd6b3e94683130a283c7a584f2f04e6ef85efec036371f230 ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "@type": "earl:TestResult",
3
+ "spec": "holo-watchlist β€” 'My List' as an append-only, hash-linked ΞΊ-chain you own: idempotent adds, head attests the whole list, remove re-links the tail, a clean export adopts (verify-before-trust round-trip), and a tampered or reordered export is refused. Replaces Jellyseerr's request DB with a portable, shareable, roamable strand.",
4
+ "authority": "rests on #holo-watchlist (Holo-strand discipline) β€” Phase 2 of the streaming/metadata plan",
5
+ "witnessed": true,
6
+ "covers": [
7
+ "add-and-list",
8
+ "hash-linked",
9
+ "remove-relinks",
10
+ "export-adopt",
11
+ "tamper-refused",
12
+ "reorder-refused"
13
+ ],
14
+ "checks": {
15
+ "addAndList": true,
16
+ "hashLinked": true,
17
+ "removeRelinks": true,
18
+ "exportAdopt": true,
19
+ "tamperRefused": true,
20
+ "reorderRefused": true
21
+ },
22
+ "failed": []
23
+ }
b/248172b3ccb76e102c66fec90bd358f3bb6ae35165e9016d3a18b4e0a8aa6f48 ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ async function QuickNESCore(moduleArg={}){var Module=moduleArg;var ENVIRONMENT_IS_WEB=!!globalThis.window;var ENVIRONMENT_IS_WORKER=!!globalThis.WorkerGlobalScope;var ENVIRONMENT_IS_NODE=globalThis.process?.versions?.node&&globalThis.process?.type!="renderer";if(ENVIRONMENT_IS_NODE){const{createRequire}=await import("node:module");var require=createRequire(import.meta.url)}var programArgs=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var _scriptName=import.meta.url;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("node:fs");if(_scriptName.startsWith("file:")){scriptDirectory=require("node:path").dirname(require("node:url").fileURLToPath(_scriptName))+"/"}readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename);return ret};readAsync=async(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):filename;var ret=fs.readFileSync(filename,binary?undefined:"utf8");return ret};if(process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}programArgs=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){try{scriptDirectory=new URL(".",_scriptName).href}catch{}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=async url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}var response=await fetch(url,{credentials:"same-origin"});if(response.ok){return response.arrayBuffer()}throw new Error(response.status+" : "+response.url)}}}else{}var out=console.log.bind(console);var err=console.error.bind(console);var wasmBinary;var ABORT=false;var isFileURI=filename=>filename.startsWith("file://");class EmscriptenEH{}class EmscriptenSjLj extends EmscriptenEH{}var runtimeInitialized=false;function updateMemoryViews(){var b=wasmMemory.buffer;HEAP8=new Int8Array(b);HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);HEAPU32=new Uint32Array(b);HEAPF32=new Float32Array(b);HEAPF64=new Float64Array(b);HEAP64=new BigInt64Array(b);HEAPU64=new BigUint64Array(b)}function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(onPreRuns)}function initRuntime(){runtimeInitialized=true;wasmExports["g"]()}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(onPostRuns)}function abort(what){Module["onAbort"]?.(what);what=`Aborted(${what})`;err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var wasmBinaryFile;function findWasmBinary(){if(Module["locateFile"]){return locateFile("quicknes.wasm")}return new URL("quicknes.wasm",import.meta.url).href}function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}async function getWasmBinary(binaryFile){if(!wasmBinary){try{var response=await readAsync(binaryFile);return new Uint8Array(response)}catch{}}return getBinarySync(binaryFile)}async function instantiateArrayBuffer(binaryFile,imports){try{var binary=await getWasmBinary(binaryFile);var instance=await WebAssembly.instantiate(binary,imports);return instance}catch(reason){err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)}}async function instantiateAsync(binary,binaryFile,imports){if(!binary&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE){try{var response=fetch(binaryFile,{credentials:"same-origin"});var instantiationResult=await WebAssembly.instantiateStreaming(response,imports);return instantiationResult}catch(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation")}}return instantiateArrayBuffer(binaryFile,imports)}function getWasmImports(){var imports={a:wasmImports};return imports}async function createWasm(){function receiveInstance(instance,module){wasmExports=instance.exports;assignWasmExports(wasmExports);updateMemoryViews();return wasmExports}function receiveInstantiationResult(result){return receiveInstance(result["instance"])}var info=getWasmImports();if(Module["instantiateWasm"]){return new Promise((resolve,reject)=>{Module["instantiateWasm"](info,(inst,mod)=>{resolve(receiveInstance(inst,mod))})})}wasmBinaryFile??=findWasmBinary();var result=await instantiateAsync(wasmBinary,wasmBinaryFile,info);var exports=receiveInstantiationResult(result);return exports}class ExitStatus{name="ExitStatus";constructor(status){this.message=`Program terminated with exit(${status})`;this.status=status}}var HEAP16;var HEAP32;var HEAP64;var HEAP8;var HEAPF32;var HEAPF64;var HEAPU16;var HEAPU32;var HEAPU64;var HEAPU8;var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var onPostRuns=[];var addOnPostRun=cb=>onPostRuns.push(cb);var onPreRuns=[];var addOnPreRun=cb=>onPreRuns.push(cb);function getValue(ptr,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":return HEAP8[ptr];case"i8":return HEAP8[ptr];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP64[ptr>>3];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];case"*":return HEAPU32[ptr>>2];default:abort(`invalid type for getValue: ${type}`)}}var noExitRuntime=true;function setValue(ptr,value,type="i8"){if(type.endsWith("*"))type="*";switch(type){case"i1":HEAP8[ptr]=value;break;case"i8":HEAP8[ptr]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":HEAP64[ptr>>3]=BigInt(value);break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;case"*":HEAPU32[ptr>>2]=value;break;default:abort(`invalid type for setValue: ${type}`)}}var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var __abort_js=()=>abort("");var getHeapMax=()=>2147483648;var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var growMemory=size=>{var oldHeapSize=wasmMemory.buffer.byteLength;var pages=(size-oldHeapSize+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var _fd_close=fd=>52;var INT53_MAX=9007199254740992;var INT53_MIN=-9007199254740992;var bigintToI53Checked=num=>num<INT53_MIN||num>INT53_MAX?NaN:Number(num);function _fd_seek(fd,offset,whence,newOffset){offset=bigintToI53Checked(offset);return 70}var printCharBuffers=[null,[],[]];var UTF8Decoder=globalThis.TextDecoder&&new TextDecoder;var findStringEnd=(heapOrArray,idx,maxBytesToRead,ignoreNul)=>{var maxIdx=idx+maxBytesToRead;if(ignoreNul)return maxIdx;while(heapOrArray[idx]&&!(idx>=maxIdx))++idx;return idx};var UTF8ArrayToString=(heapOrArray,idx=0,maxBytesToRead,ignoreNul)=>{var endPtr=findStringEnd(heapOrArray,idx,maxBytesToRead,ignoreNul);if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var printChar=(stream,curr)=>{var buffer=printCharBuffers[stream];if(curr===0||curr===10){(stream===1?out:err)(UTF8ArrayToString(buffer));buffer.length=0}else{buffer.push(curr)}};var UTF8ToString=(ptr,maxBytesToRead,ignoreNul)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead,ignoreNul):"";var _fd_write=(fd,iov,iovcnt,pnum)=>{var num=0;for(var i=0;i<iovcnt;i++){var ptr=HEAPU32[iov>>2];var len=HEAPU32[iov+4>>2];iov+=8;for(var j=0;j<len;j++){printChar(fd,HEAPU8[ptr+j])}num+=len}HEAPU32[pnum>>2]=num;return 0};var wasmTableMirror=[];var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var updateTableMap=(offset,count)=>{if(functionsInTableMap){for(var i=offset;i<offset+count;i++){var item=getWasmTableEntry(i);if(item){functionsInTableMap.set(item,i)}}}};var functionsInTableMap;var getFunctionAddress=func=>{if(!functionsInTableMap){functionsInTableMap=new WeakMap;updateTableMap(0,wasmTable.length)}return functionsInTableMap.get(func)||0};var freeTableIndexes=[];var getEmptyTableSlot=()=>{if(freeTableIndexes.length){return freeTableIndexes.pop()}return wasmTable["grow"](1)};var setWasmTableEntry=(idx,func)=>{wasmTable.set(idx,func);wasmTableMirror[idx]=wasmTable.get(idx)};var uleb128EncodeWithLen=arr=>{const n=arr.length;return[n%128|128,n>>7,...arr]};var wasmTypeCodes={i:127,p:127,j:126,f:125,d:124,e:111};var generateTypePack=types=>uleb128EncodeWithLen(Array.from(types,type=>{var code=wasmTypeCodes[type];return code}));var convertJsFunctionToWasm=(func,sig)=>{var bytes=Uint8Array.of(0,97,115,109,1,0,0,0,1,...uleb128EncodeWithLen([1,96,...generateTypePack(sig.slice(1)),...generateTypePack(sig[0]==="v"?"":sig[0])]),2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);var module=new WebAssembly.Module(bytes);var instance=new WebAssembly.Instance(module,{e:{f:func}});var wrappedFunc=instance.exports["f"];return wrappedFunc};var addFunction=(func,sig)=>{var rtn=getFunctionAddress(func);if(rtn){return rtn}var ret=getEmptyTableSlot();try{setWasmTableEntry(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);setWasmTableEntry(ret,wrapped)}functionsInTableMap.set(func,ret);return ret};var removeFunction=index=>{functionsInTableMap.delete(getWasmTableEntry(index));setWasmTableEntry(index,null);freeTableIndexes.push(index)};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.codePointAt(i);if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63;i++}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var cwrap=(ident,returnType,argTypes,opts)=>{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};{if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(Module["print"])out=Module["print"];if(Module["printErr"])err=Module["printErr"];if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];if(Module["arguments"])programArgs=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].shift()()}}}Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["removeFunction"]=removeFunction;Module["setValue"]=setValue;Module["getValue"]=getValue;Module["UTF8ToString"]=UTF8ToString;var _retro_init,_retro_deinit,_retro_api_version,_retro_get_system_info,_retro_get_system_av_info,_retro_set_environment,_retro_set_audio_sample,_retro_set_audio_sample_batch,_retro_set_input_poll,_retro_set_input_state,_retro_set_video_refresh,_retro_reset,_retro_run,_retro_load_game,_retro_unload_game,_retro_serialize_size,_retro_serialize,_retro_unserialize,_malloc,_free,__emscripten_stack_restore,__emscripten_stack_alloc,_emscripten_stack_get_current,memory,__indirect_function_table,wasmMemory,wasmTable;function assignWasmExports(wasmExports){_retro_init=Module["_retro_init"]=wasmExports["h"];_retro_deinit=Module["_retro_deinit"]=wasmExports["i"];_retro_api_version=Module["_retro_api_version"]=wasmExports["j"];_retro_get_system_info=Module["_retro_get_system_info"]=wasmExports["k"];_retro_get_system_av_info=Module["_retro_get_system_av_info"]=wasmExports["l"];_retro_set_environment=Module["_retro_set_environment"]=wasmExports["m"];_retro_set_audio_sample=Module["_retro_set_audio_sample"]=wasmExports["n"];_retro_set_audio_sample_batch=Module["_retro_set_audio_sample_batch"]=wasmExports["o"];_retro_set_input_poll=Module["_retro_set_input_poll"]=wasmExports["p"];_retro_set_input_state=Module["_retro_set_input_state"]=wasmExports["q"];_retro_set_video_refresh=Module["_retro_set_video_refresh"]=wasmExports["r"];_retro_reset=Module["_retro_reset"]=wasmExports["s"];_retro_run=Module["_retro_run"]=wasmExports["t"];_retro_load_game=Module["_retro_load_game"]=wasmExports["u"];_retro_unload_game=Module["_retro_unload_game"]=wasmExports["v"];_retro_serialize_size=Module["_retro_serialize_size"]=wasmExports["w"];_retro_serialize=Module["_retro_serialize"]=wasmExports["x"];_retro_unserialize=Module["_retro_unserialize"]=wasmExports["y"];_malloc=Module["_malloc"]=wasmExports["z"];_free=Module["_free"]=wasmExports["A"];__emscripten_stack_restore=wasmExports["B"];__emscripten_stack_alloc=wasmExports["C"];_emscripten_stack_get_current=wasmExports["D"];memory=wasmMemory=wasmExports["f"];__indirect_function_table=wasmTable=wasmExports["E"]}var wasmImports={d:__abort_js,e:_emscripten_resize_heap,c:_fd_close,b:_fd_seek,a:_fd_write};async function run(){preRun();var setStatus=Module["setStatus"];if(setStatus){setStatus("Running...");await new Promise(resolve=>setTimeout(resolve,1));setTimeout(setStatus,1,"")}if(ABORT)return;initRuntime();Module["onRuntimeInitialized"]?.();postRun()}var wasmExports;wasmExports=await createWasm();await run();
2
+ ;return Module}export default QuickNESCore;
b/2796065b82a04e8d3d3e5045070f564cbdddc3b3a7f6716d1ea1c241e4f69daa ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "v": 1,
3
+ "kind": "holo-kappa-audio",
4
+ "title": "Merkle Dance",
5
+ "artist": "Hologram Collective",
6
+ "album": "Kappa Sessions",
7
+ "mime": "audio/wav",
8
+ "sampleRate": 44100,
9
+ "channels": 1,
10
+ "bits": 16,
11
+ "durationSec": 10.909,
12
+ "bytes": 962362,
13
+ "trackKappa": "did:holo:sha256:9a68453f141d66fe138ef33d709e8cf359bcf896070049d8b5834832704a92a6",
14
+ "chunkBytes": 262144,
15
+ "loudness": {
16
+ "lufs": -10.66,
17
+ "samplePeakDbfs": -3.04,
18
+ "truePeakDbtp": -3.04
19
+ },
20
+ "normalizeDb": -5.34,
21
+ "targetLufs": -16,
22
+ "chunks": [
23
+ {
24
+ "file": "chunk-0000.bin",
25
+ "kappa": "did:holo:sha256:74602bbb82bf85d76e3f106bb0512b7b5258fa4aaea1438e671218032d784930",
26
+ "bytes": 262144
27
+ },
28
+ {
29
+ "file": "chunk-0001.bin",
30
+ "kappa": "did:holo:sha256:ce68b84c65c5d3274c5b0706a9f26f4615220b3063148d9bceb7ca3946544371",
31
+ "bytes": 262144
32
+ },
33
+ {
34
+ "file": "chunk-0002.bin",
35
+ "kappa": "did:holo:sha256:62665f788b438909c888b405b4c06e334b6345135800e3ca216a079bd46cff2a",
36
+ "bytes": 262144
37
+ },
38
+ {
39
+ "file": "chunk-0003.bin",
40
+ "kappa": "did:holo:sha256:fcf4f9bbf7648384a1e5a6b7c43e1ca71c11258fa3de196a258af0de6ea563f4",
41
+ "bytes": 175930
42
+ }
43
+ ]
44
+ }
b/28b4f28cbaa7bce7335085d8d5d44b04e0abd42a41fe8141f90cbf47aa06d41b ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-onion-endpoint.node.mjs β€” the HOST half of rung 1. Mount this next to the host's /web handler so
2
+ // the browser page's hostOnionFetch("/onion?url=…") reaches a real Tor/Arti client running in this process.
3
+ //
4
+ // Split of concerns: RTC + the exit-peer framing live in the browser page (peer-host.html); the raw SOCKS5
5
+ // socket to Arti lives HERE, in the host process, behind one same-origin endpoint. Keeps the browser bundle
6
+ // free of node:net and the host free of RTC.
7
+ //
8
+ // Mount (any node:http server that already serves /web):
9
+ // import { onionRequestHandler } from "./_shared/holo-onion-endpoint.node.mjs";
10
+ // const onion = onionRequestHandler(); // or ({ socksHost, socksPort })
11
+ // // inside your request router:
12
+ // if (url.pathname === "/onion") return onion(req, res);
13
+ //
14
+ // Requires a running Arti with a SOCKS listener: arti proxy (default 127.0.0.1:9150).
15
+ // If Arti is down / the onion is unreachable, responds 502 so hostOnionFetch throws and the ladder falls
16
+ // past this device to arti-wasm. STATUS: UNVERIFIED in this build env (no Arti daemon here).
17
+
18
+ import { nodeArtiFetch } from "./holo-onion-arti.node.mjs";
19
+ import { rewriteHtml, rewriteCss, defaultProxy } from "./holo-onion-rewrite.mjs";
20
+
21
+ // CORS so the deployed https origin (github.io/Q) can read the x-holo-onion markers from this http loopback
22
+ // host (127.0.0.1 is a secure context, so the fetch itself is allowed; CORS lets the SW READ the response).
23
+ const CORS = {
24
+ "access-control-allow-origin": "*",
25
+ "access-control-expose-headers": "x-holo-onion, x-holo-onion-verified, content-type",
26
+ };
27
+
28
+ export function onionRequestHandler({ socksHost = "127.0.0.1", socksPort = 9150 } = {}) {
29
+ const onionFetch = nodeArtiFetch({ socksHost, socksPort });
30
+ return async function handle(req, res) {
31
+ try {
32
+ const u = new URL(req.url, "http://host");
33
+ const target = u.searchParams.get("url");
34
+ const rw = u.searchParams.get("rw") === "1"; // standalone-demo mode: rewrite the graph to this endpoint
35
+ // probe / invalid: still advertise x-holo-onion so the SW's probeOnionHosts DISCOVERS this endpoint.
36
+ if (!target || !/^[a-z2-7]{16}\.onion$|^[a-z2-7]{56}\.onion$/i.test(target.replace(/^[a-z]+:\/\//i, "").split(/[/?#]/)[0].split(":")[0].toLowerCase())) {
37
+ res.writeHead(target ? 400 : 200, { "content-type": "text/plain", "x-holo-onion": "ready", ...CORS });
38
+ return res.end(target ? "not a .onion" : "holo onion endpoint ready");
39
+ }
40
+
41
+ const r = await onionFetch(target);
42
+ const headers = Object.assign({}, r.headers || {}, CORS);
43
+ headers["x-holo-onion"] = "routed";
44
+ headers["x-holo-onion-verified"] = r.verified ? "1" : "0";
45
+
46
+ // By DEFAULT serve raw onion bytes β€” the caller (deployed browser SW) does its own <base>+rewrite of the
47
+ // whole graph. Only when a standalone caller passes rw=1 do we rewrite links/subresources back to THIS
48
+ // endpoint (so an iframe pointed straight at /onion is browsable without the SW).
49
+ let bytes = Buffer.from(r.bytes);
50
+ const ct = (headers["content-type"] || "").toLowerCase();
51
+ let rewritten = false;
52
+ if (rw) {
53
+ const proxy = (abs) => "/onion?url=" + encodeURIComponent(abs) + "&rw=1";
54
+ if (ct.includes("text/html")) { bytes = Buffer.from(rewriteHtml(bytes.toString("utf8"), target, proxy), "utf8"); rewritten = true; }
55
+ else if (ct.includes("text/css")) { bytes = Buffer.from(rewriteCss(bytes.toString("utf8"), target, proxy), "utf8"); rewritten = true; }
56
+ }
57
+
58
+ delete headers["content-length"]; delete headers["transfer-encoding"];
59
+ if (rewritten) delete headers["content-encoding"];
60
+ if (rw) { delete headers["content-security-policy"]; delete headers["x-frame-options"]; } // let the demo iframe render
61
+ res.writeHead(r.status || 200, headers);
62
+ res.end(bytes);
63
+ } catch (e) {
64
+ // Arti down or onion unreachable β†’ 502 so the caller falls past this device to the next rung.
65
+ res.writeHead(502, { "content-type": "text/plain", "x-holo-onion": "unavailable", ...CORS });
66
+ res.end("onion route failed: " + (e && e.message || e));
67
+ }
68
+ };
69
+ }
70
+
71
+ export default { onionRequestHandler };
b/295b87c6e792570b60ebbe5053f59b66418e28d5afefb263fbffb5a2b0d66900 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ <svg width="251" height="83" viewBox="0 0 251 83" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M153.602 82.4612H173.519V0.015625H153.602V82.4612ZM118.632 58.9549C118.632 61.8498 116.201 64.2815 113.306 64.2815H101.726C98.8314 64.2815 96.3998 61.8498 96.3998 58.9549C96.3998 57.797 96.3998 25.722 96.3998 25.722H76.4831V60.5761C76.4831 74.8188 85.6309 82.3454 99.063 82.3454H118.516C130.906 82.3454 138.433 73.4292 138.433 60.5761V25.722H118.516C118.632 25.722 118.632 57.9128 118.632 58.9549ZM230.605 25.722C230.605 25.722 230.605 57.9128 230.605 58.9549C230.605 61.8498 228.174 64.2815 225.279 64.2815H213.699C210.805 64.2815 208.373 61.8498 208.373 58.9549C208.373 57.797 208.373 25.722 208.373 25.722H188.456V60.5761C188.456 74.8188 197.604 82.3454 211.036 82.3454H230.49C242.88 82.3454 250.406 73.4292 250.406 60.5761V25.722H230.605ZM39.7763 25.722C39.7763 25.722 29.4706 25.722 27.039 25.722C22.523 25.722 20.3229 26.8799 20.3229 26.8799V0.015625H0.40625V82.3454H20.2071V49.2282C20.2071 46.3334 22.6388 43.9017 25.5336 43.9017H37.1131C40.0079 43.9017 42.4396 46.3334 42.4396 49.2282V82.4612H62.3562V46.5649C62.3562 31.5117 52.2821 25.722 39.7763 25.722Z" fill="#ffffff"/>
3
+ </svg>
b/29656a5df86aef555c69787686c3ded84c3187dc1d3f50cd0d1ffd514daade6c ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "@type": "earl:TestResult",
3
+ "spec": "holo-source-jellyfin — a linked Jellyfin/Emby server as a full SourceProvider: catalogs→rails, /Items→the one shape, search via SearchTerm, resolve→master.m3u8 with server provenance. Federated + deduped with every other library under one interface.",
4
+ "authority": "rests on #holo-source-jellyfin β€” Phase 3 of the Stremio+RD+Jellyfin plan (full library interop)",
5
+ "witnessed": true,
6
+ "covers": [
7
+ "catalogs",
8
+ "browse-normalizes",
9
+ "search-term",
10
+ "resolve-stream",
11
+ "series-kind"
12
+ ],
13
+ "checks": {
14
+ "catalogs": true,
15
+ "browseNormalizes": true,
16
+ "searchTerm": true,
17
+ "resolveStream": true,
18
+ "seriesKind": true
19
+ },
20
+ "failed": []
21
+ }
b/2cd5d13e57edc5d4658bdb65efb329cb677f758f5c87d77cb9e95de87ba28821 ADDED
Binary file (15 kB). View file
 
b/300bc8c5fe7cf6dfcfc61a0601b4e16082990da558f93b2408c74ab451228d59 ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600" role="img" aria-label="Kappa Sessions by Hologram Collective">
2
+ <defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="hsl(293 64% 22%)"/><stop offset="1" stop-color="hsl(335 70% 12%)"/></linearGradient></defs>
3
+ <rect width="600" height="600" fill="url(#g)"/>
4
+ <circle cx="164" cy="286" r="94" fill="none" stroke="hsl(293 80% 72%)" stroke-width="2" opacity="0.23"/>
5
+ <circle cx="286" cy="157" r="162" fill="none" stroke="hsl(293 80% 72%)" stroke-width="2" opacity="0.12"/>
6
+ <circle cx="157" cy="208" r="216" fill="none" stroke="hsl(293 80% 72%)" stroke-width="2" opacity="0.24"/>
7
+ <circle cx="208" cy="238" r="259" fill="none" stroke="hsl(293 80% 72%)" stroke-width="2" opacity="0.23"/>
8
+ <circle cx="238" cy="287" r="314" fill="none" stroke="hsl(293 80% 72%)" stroke-width="2" opacity="0.23"/>
9
+ <rect x="36" y="192" width="18" height="238" rx="3" fill="hsl(293 80% 72%)" opacity="0.46"/>
10
+ <rect x="69" y="356" width="18" height="74" rx="3" fill="hsl(293 80% 72%)" opacity="0.27"/>
11
+ <rect x="102" y="312" width="18" height="118" rx="3" fill="hsl(293 80% 72%)" opacity="0.28"/>
12
+ <rect x="135" y="381" width="18" height="49" rx="3" fill="hsl(293 80% 72%)" opacity="0.32"/>
13
+ <rect x="168" y="312" width="18" height="118" rx="3" fill="hsl(293 80% 72%)" opacity="0.34"/>
14
+ <rect x="201" y="205" width="18" height="225" rx="3" fill="hsl(293 80% 72%)" opacity="0.25"/>
15
+ <rect x="234" y="337" width="18" height="93" rx="3" fill="hsl(293 80% 72%)" opacity="0.46"/>
16
+ <rect x="267" y="193" width="18" height="237" rx="3" fill="hsl(293 80% 72%)" opacity="0.46"/>
17
+ <rect x="300" y="205" width="18" height="225" rx="3" fill="hsl(293 80% 72%)" opacity="0.35"/>
18
+ <rect x="333" y="228" width="18" height="202" rx="3" fill="hsl(293 80% 72%)" opacity="0.47"/>
19
+ <rect x="366" y="325" width="18" height="105" rx="3" fill="hsl(293 80% 72%)" opacity="0.36"/>
20
+ <rect x="399" y="391" width="18" height="39" rx="3" fill="hsl(293 80% 72%)" opacity="0.36"/>
21
+ <rect x="432" y="234" width="18" height="196" rx="3" fill="hsl(293 80% 72%)" opacity="0.46"/>
22
+ <rect x="465" y="282" width="18" height="148" rx="3" fill="hsl(293 80% 72%)" opacity="0.44"/>
23
+ <rect x="498" y="262" width="18" height="168" rx="3" fill="hsl(293 80% 72%)" opacity="0.20"/>
24
+ <rect x="531" y="284" width="18" height="146" rx="3" fill="hsl(293 80% 72%)" opacity="0.33"/>
25
+ <text x="40" y="500" font-family="system-ui, sans-serif" font-size="40" font-weight="800" fill="#fff">Kappa Sessions</text>
26
+ <text x="40" y="540" font-family="ui-monospace, monospace" font-size="22" fill="hsl(293 80% 72%)">Hologram Collective</text>
27
+ <text x="40" y="572" font-family="ui-monospace, monospace" font-size="16" fill="#ffffffaa">Chiptune Β· Hologram OS</text>
28
+ </svg>
b/308bb1c176791ad8b0390e37036819cae063f6ee656e06839c1db6eca0b35430 ADDED
The diff for this file is too large to render. See raw diff
 
b/3409f443106e4dafbe11d69d59436026892e04e0a23b9533cf444c8ab13d837e ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-onion-arti.node.mjs β€” PRODUCTION onionFetch for the desktop host (Node/Tauri side of rung 1).
2
+ //
3
+ // Routes a .onion through a locally-running Tor/Arti SOCKS5 proxy using ONLY Node built-ins (net, tls) β€”
4
+ // zero npm deps. socks5h semantics: we hand the .onion to the PROXY as a DOMAIN so Tor resolves +
5
+ // rendezvous-connects it; the host never leaks a lookup. Reaching a v3 .onion means Tor authenticated the
6
+ // service's ed25519 key (the address IS the key) β†’ verified:true.
7
+ //
8
+ // SPEED: a page pulls ~15-30 subresources. Opening a fresh Tor stream (SOCKS handshake + new rendezvous
9
+ // stream) per resource is the latency killer. So we POOL keep-alive connections per onion origin and reuse
10
+ // them, with a small per-origin concurrency cap so we don't flood one circuit. Repeat visits are served
11
+ // from the browser's ΞΊ-store (zero Tor) β€” this pool is only about making the FIRST paint fast.
12
+ //
13
+ // Requires a running Arti/tor SOCKS listener (Arti default 127.0.0.1:9150 Β· `arti proxy`).
14
+
15
+ import net from "node:net";
16
+ import tls from "node:tls";
17
+
18
+ const MAX_BODY = 24 * 1024 * 1024; // SEC-8: bound by RECEIVED bytes
19
+ const REQ_TIMEOUT = 30000; // per-request ceiling
20
+ const IDLE_MS = 20000; // keep an idle pooled socket this long
21
+ const MAX_PER_ORIGIN = 6; // in-flight streams per onion origin (browser-like)
22
+
23
+ function socks5Connect({ socksHost, socksPort, host, port }) {
24
+ return new Promise((resolve, reject) => {
25
+ const sock = net.connect({ host: socksHost, port: socksPort });
26
+ const fail = (e) => { try { sock.destroy(); } catch {} reject(e instanceof Error ? e : new Error(String(e))); };
27
+ sock.once("error", fail);
28
+ sock.once("connect", () => {
29
+ sock.write(Buffer.from([0x05, 0x01, 0x00])); // VER5, NO-AUTH
30
+ sock.once("data", (m) => {
31
+ if (m[0] !== 0x05 || m[1] !== 0x00) return fail("socks5: no acceptable auth method");
32
+ const h = Buffer.from(host, "ascii");
33
+ sock.write(Buffer.concat([Buffer.from([0x05, 0x01, 0x00, 0x03, h.length]), h, Buffer.from([(port >> 8) & 0xff, port & 0xff])]));
34
+ sock.once("data", (r) => {
35
+ if (r[0] !== 0x05 || r[1] !== 0x00) return fail("socks5: connect refused (rep=" + r[1] + ") β€” Arti running + onion reachable?");
36
+ sock.removeListener("error", fail);
37
+ resolve(sock);
38
+ });
39
+ });
40
+ });
41
+ });
42
+ }
43
+
44
+ // Read ONE full HTTP/1.1 response off a (possibly keep-alive) socket. Handles content-length, chunked
45
+ // transfer-encoding, and close-delimited bodies. Resolves { status, headers, bytes, keepAlive }.
46
+ function readResponse(sock) {
47
+ return new Promise((resolve, reject) => {
48
+ let buf = Buffer.alloc(0), phase = "head", status = 0; const headers = {};
49
+ let mode = null, contentLength = 0, received = 0, keepAlive = true;
50
+ const chunks = []; let chunkRemain = 0, chunkPhase = "size";
51
+ const timer = setTimeout(() => finish(new Error("onion request timeout")), REQ_TIMEOUT);
52
+ const cleanup = () => { clearTimeout(timer); sock.removeListener("data", onData); sock.removeListener("error", onErr); sock.removeListener("end", onEnd); };
53
+ const done = (ka) => { cleanup(); resolve({ status, headers, bytes: Buffer.concat(chunks), keepAlive: ka }); };
54
+ const finish = (e) => { cleanup(); reject(e); };
55
+ const onErr = (e) => finish(e);
56
+ const onEnd = () => { if (mode === "close") done(false); else finish(new Error("connection closed mid-response")); };
57
+ function onData(d) {
58
+ buf = Buffer.concat([buf, d]);
59
+ if (phase === "head") {
60
+ const i = buf.indexOf("\r\n\r\n"); if (i < 0) return;
61
+ const lines = buf.slice(0, i).toString("latin1").split("\r\n");
62
+ status = parseInt(lines[0].split(" ")[1] || "0", 10);
63
+ for (const line of lines.slice(1)) { const k = line.indexOf(":"); if (k > 0) headers[line.slice(0, k).trim().toLowerCase()] = line.slice(k + 1).trim(); }
64
+ keepAlive = (headers["connection"] || "").toLowerCase() !== "close";
65
+ buf = buf.slice(i + 4); phase = "body";
66
+ const te = (headers["transfer-encoding"] || "").toLowerCase();
67
+ if (status === 204 || status === 304) return done(keepAlive);
68
+ if (te.includes("chunked")) mode = "chunked";
69
+ else if (headers["content-length"] != null) { mode = "length"; contentLength = parseInt(headers["content-length"], 10) || 0; if (contentLength === 0) return done(keepAlive); }
70
+ else mode = "close";
71
+ }
72
+ if (phase !== "body") return;
73
+ if (mode === "length") {
74
+ const take = buf.slice(0, contentLength - received); chunks.push(take); received += take.length; buf = buf.slice(take.length);
75
+ if (received > MAX_BODY) return onErr(new Error("over cap"));
76
+ if (received >= contentLength) return done(keepAlive);
77
+ } else if (mode === "chunked") {
78
+ while (buf.length) {
79
+ if (chunkPhase === "size") {
80
+ const j = buf.indexOf("\r\n"); if (j < 0) break;
81
+ const size = parseInt(buf.slice(0, j).toString("latin1").split(";")[0].trim(), 16); buf = buf.slice(j + 2);
82
+ if (!size) { chunkPhase = "trailer"; } else { chunkRemain = size; chunkPhase = "data"; }
83
+ } else if (chunkPhase === "data") {
84
+ const take = buf.slice(0, chunkRemain); chunks.push(take); chunkRemain -= take.length; received += take.length; buf = buf.slice(take.length);
85
+ if (received > MAX_BODY) return onErr(new Error("over cap"));
86
+ if (chunkRemain === 0) { if (buf.length < 2) break; buf = buf.slice(2); chunkPhase = "size"; } else break;
87
+ } else { const j = buf.indexOf("\r\n"); if (j < 0) break; buf = buf.slice(j + 2); return done(keepAlive); }
88
+ }
89
+ } else { chunks.push(buf); received += buf.length; buf = Buffer.alloc(0); if (received > MAX_BODY) return onErr(new Error("over cap")); }
90
+ }
91
+ sock.on("data", onData); sock.once("error", onErr); sock.once("end", onEnd);
92
+ });
93
+ }
94
+
95
+ export function nodeArtiFetch({ socksHost = "127.0.0.1", socksPort = 9150 } = {}) {
96
+ const pool = new Map(); // originKey β†’ [{ sock, idleTimer }]
97
+ const inflight = new Map(); // originKey β†’ count
98
+ const waiters = new Map(); // originKey β†’ [resolve]
99
+ const keyOf = (u) => (u.protocol === "https:" ? "https" : "http") + "://" + u.hostname + ":" + (u.port || (u.protocol === "https:" ? 443 : 80));
100
+ const dec = (key) => { const n = Math.max(0, (inflight.get(key) || 1) - 1); inflight.set(key, n); const w = waiters.get(key); if (w && w.length) w.shift()(); };
101
+
102
+ async function acquire(u, forceFresh = false) {
103
+ const key = keyOf(u);
104
+ const idle = pool.get(key);
105
+ // forceFresh (a retry) skips the pool: a reused keep-alive socket whose Tor circuit died silently still
106
+ // looks writable, so a stale socket is exactly what a retry must avoid β€” go straight to a new circuit.
107
+ while (!forceFresh && idle && idle.length) { const e = idle.pop(); clearTimeout(e.idleTimer); if (e.sock.writable && !e.sock.destroyed) return e.sock; try { e.sock.destroy(); } catch {} }
108
+ if ((inflight.get(key) || 0) >= MAX_PER_ORIGIN) await new Promise((res) => { const w = waiters.get(key) || []; w.push(res); waiters.set(key, w); });
109
+ inflight.set(key, (inflight.get(key) || 0) + 1);
110
+ try {
111
+ const https = u.protocol === "https:";
112
+ const port = u.port ? parseInt(u.port, 10) : (https ? 443 : 80);
113
+ let sock = await socks5Connect({ socksHost, socksPort, host: u.hostname, port });
114
+ if (https) sock = tls.connect({ socket: sock, servername: u.hostname, rejectUnauthorized: false }); // onion self-auths via rendezvous
115
+ sock.setMaxListeners(0);
116
+ return sock;
117
+ } catch (e) { dec(key); throw e; }
118
+ }
119
+ function release(u, sock, keepAlive) {
120
+ const key = keyOf(u); dec(key);
121
+ if (keepAlive && sock && sock.writable && !sock.destroyed) {
122
+ const idle = pool.get(key) || [];
123
+ const idleTimer = setTimeout(() => { const arr = pool.get(key); if (arr) { const i = arr.findIndex((x) => x.sock === sock); if (i >= 0) arr.splice(i, 1); } try { sock.destroy(); } catch {} }, IDLE_MS);
124
+ idle.push({ sock, idleTimer }); pool.set(key, idle);
125
+ } else if (sock) { try { sock.destroy(); } catch {} }
126
+ }
127
+
128
+ return async function onionFetch(url) {
129
+ const u = new URL(/^https?:\/\//i.test(url) ? url : "http://" + url);
130
+ const path = (u.pathname + u.search) || "/";
131
+ // Retry so an IDLE/rebuilt Tor circuit is transparent: after Tor drops circuits (long idle), the first
132
+ // stream fails or a stale pooled socket dies β€” attempt 2+ forces a fresh circuit with a short backoff so
133
+ // Tor has time to rebuild. GETs are idempotent, so retry is safe. This kills the "first request β†’ 502".
134
+ let lastErr = null;
135
+ for (let attempt = 0; attempt < 3; attempt++) {
136
+ let sock = null, keep = false;
137
+ try {
138
+ sock = await acquire(u, attempt > 0);
139
+ const onTimeout = () => { try { sock.destroy(new Error("onion request timeout")); } catch {} };
140
+ sock.setTimeout(REQ_TIMEOUT, onTimeout);
141
+ sock.write(`GET ${path} HTTP/1.1\r\nHost: ${u.hostname}\r\nUser-Agent: Hologram/onion\r\nAccept: */*\r\nAccept-Encoding: identity\r\nConnection: keep-alive\r\n\r\n`);
142
+ const res = await readResponse(sock);
143
+ keep = res.keepAlive; sock.setTimeout(0);
144
+ return { status: res.status, headers: res.headers, bytes: new Uint8Array(res.bytes), verified: true };
145
+ } catch (e) {
146
+ lastErr = e;
147
+ if (sock) { const key = keyOf(u); try { sock.destroy(); } catch {} dec(key); sock = null; }
148
+ if (attempt < 2) await new Promise((r) => setTimeout(r, 350 + attempt * 600)); // let Tor rebuild
149
+ } finally { if (sock) release(u, sock, keep); }
150
+ }
151
+ throw lastErr;
152
+ };
153
+ }
154
+
155
+ export default { nodeArtiFetch };
b/34a4996e7fc708c03acccb3efd9a39b995b81ba23fdfe440c81a4e598453803d ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:600304eed836177785f7cfa4b0747db9bf02153502f8c26a1f0e6b23dd3fd29f
3
+ size 131072
b/359adc26b17b4a3c1e8927a61fb169b4d6c757c7fc45d00669621aee4d2e93c2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ce68b84c65c5d3274c5b0706a9f26f4615220b3063148d9bceb7ca3946544371
3
+ size 262144
b/362148d3ecaabd71460e10f06bb75bb6b13535f17924fe4ea7ef8d73f9fe7c54 ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-chapters.mjs β€” "Skip Intro / Skip Credits" as chapter markers bound to a title ΞΊ (Intro-Skipper's
2
+ // job, ΞΊ-native). A chapter set is content-addressed: detect once, seal under the title's ΞΊ, reuse forever
3
+ // across devices (Law-L5 verifiable). The player asks activeSkip(position) every status tick and shows one
4
+ // button when the playhead is inside a skippable window.
5
+ //
6
+ // Pure ESM β€” Node witnesses the geometry exactly.
7
+
8
+ function jcs(v) {
9
+ if (v === null || typeof v !== "object") return JSON.stringify(v);
10
+ if (Array.isArray(v)) return "[" + v.map(jcs).join(",") + "]";
11
+ return "{" + Object.keys(v).sort().map((k) => JSON.stringify(k) + ":" + jcs(v[k])).join(",") + "}";
12
+ }
13
+ async function sha256hex(s) {
14
+ if (typeof crypto !== "undefined" && crypto.subtle) { const b = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(s)); return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join(""); }
15
+ const { createHash } = await import("node:crypto"); return createHash("sha256").update(s).digest("hex");
16
+ }
17
+
18
+ const LABEL = { intro: "Skip Intro", recap: "Skip Recap", credits: "Skip Credits" };
19
+
20
+ // sealChapters(titleKappa, chapters) β†’ a ΞΊ-addressed chapter object. chapters: [{kind,start,end}] (seconds).
21
+ export async function sealChapters(titleKappa, chapters) {
22
+ const clean = (chapters || []).filter((c) => c && c.end > c.start).map((c) => ({ kind: c.kind, start: +c.start, end: +c.end })).sort((a, b) => a.start - b.start);
23
+ const body = { titleKappa: titleKappa || "", chapters: clean };
24
+ return { id: "sha256:" + (await sha256hex(jcs(body))), ...body };
25
+ }
26
+ export async function verifyChapters(obj) {
27
+ if (!obj || !Array.isArray(obj.chapters)) return false;
28
+ const re = await sealChapters(obj.titleKappa, obj.chapters);
29
+ return re.id === obj.id;
30
+ }
31
+
32
+ // activeSkip(position, chapters) β†’ { label, target } when the playhead is inside a skippable window, else
33
+ // null. Intro/recap skip to the window end; credits skip to its end (next-episode/up-next takes over there).
34
+ export function activeSkip(position, chapters) {
35
+ for (const c of chapters || []) {
36
+ if (position >= c.start && position < c.end - 0.3) return { label: LABEL[c.kind] || "Skip", target: c.end, kind: c.kind };
37
+ }
38
+ return null;
39
+ }
40
+
41
+ export default { sealChapters, verifyChapters, activeSkip };
42
+ if (typeof window !== "undefined") window.HoloChapters = { sealChapters, verifyChapters, activeSkip };
b/364687deaf3342b8e0f9126100aade8f739a1650e6939d334b4eda67ba8e0058 ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-onion-exit.mjs β€” S1 rung 1: make the DEVICE-MESH exit peer onion-aware.
2
+ //
3
+ // Produces a `fetchImpl` for serveAsExitPeer (holo-peer-egress.mjs): a `.onion` URL routes through a REAL
4
+ // Tor/Arti client running on THIS device (the desktop host, which has egress); everything else falls
5
+ // through to the host's own /web β€” the exact default the exit peer already uses. So the phone browsing on
6
+ // the serverless bundle reaches the onion web through the owner's desktop, over the SAME mesh, dialed by ΞΊ.
7
+ //
8
+ // TRUST is unchanged: bytes are still minted + re-derived against their ΞΊ by browser-sw (Law L5); the peer
9
+ // is untrusted transport. What onion adds: the first hop for a .onion is a Tor circuit, and because a v3
10
+ // .onion IS its ed25519 pubkey (self-authenticating), the Arti client verifies the service END-TO-END β€”
11
+ // no gateway is trusted for authenticity. The privacy signal travels as response headers so the UI can
12
+ // show the truth of the route in one calm glance (x-holo-onion, x-holo-onion-verified).
13
+ //
14
+ // Detection reuses the ONE onion source of truth β€” holo-dweb.js `onionHost` β€” so classify() and the exit
15
+ // peer can never disagree about what an onion is.
16
+
17
+ import { onionHost } from "./holo-dweb.js";
18
+
19
+ const ONION_V3_RE = /^[a-z2-7]{56}\.onion$/i;
20
+ const ONION_V2_RE = /^[a-z2-7]{16}\.onion$/i;
21
+
22
+ export function isOnionUrl(url) {
23
+ const h = onionHost(url);
24
+ return ONION_V3_RE.test(h) || ONION_V2_RE.test(h);
25
+ }
26
+
27
+ // webViaHost(webBase) β†’ the exit peer's normal clearnet path (mirrors serveAsExitPeer's private `viaWeb`),
28
+ // so an onion-aware fetchImpl is a COMPLETE fetchImpl: onion β†’ Tor, everything else β†’ the host's /web.
29
+ export function webViaHost(webBase = "/apps/browser/web?url=") {
30
+ return async (url, { doc, op } = {}) => {
31
+ const q = encodeURIComponent(url) + (doc ? "&doc=1" : "") + (op ? "&op=" + encodeURIComponent(op) : "");
32
+ const r = await fetch(webBase + q);
33
+ const headers = {}; r.headers.forEach((v, k) => { headers[k] = v; });
34
+ return { status: r.status, headers, bytes: new Uint8Array(await r.arrayBuffer()) };
35
+ };
36
+ }
37
+
38
+ // onionExitFetch({ onionFetch, webFetch, peerOnly }) β†’ fetchImpl(url,{doc,op}) β†’ { status, headers, bytes }.
39
+ // onionFetch(url,{doc,op}) β†’ { status, headers, bytes, verified } β€” the REAL Tor client (nodeArtiFetch
40
+ // in production; a stub in the witness). `verified` = the onion's ed25519 key checked end-to-end.
41
+ // webFetch β€” clearnet path; defaults to webViaHost(). Ignored for .onion.
42
+ // peerOnly β€” reserved: in PEER_ONLY the ladder already forbids the clearnet gateway; the exit peer never
43
+ // was a gateway, so this only documents intent here.
44
+ export function onionExitFetch({ onionFetch, webFetch = webViaHost() } = {}) {
45
+ return async function fetchImpl(url, opts = {}) {
46
+ if (isOnionUrl(url)) {
47
+ if (typeof onionFetch !== "function") {
48
+ // No Tor client on this device β€” refuse the onion cleanly so the ladder can fall past to arti-wasm.
49
+ return { status: 502, headers: { "x-holo-onion": "unavailable" }, bytes: new Uint8Array() };
50
+ }
51
+ const r = await onionFetch(url, opts);
52
+ const headers = Object.assign({}, r.headers || {});
53
+ headers["x-holo-onion"] = "routed"; // real onion routing on this device (rung 1)
54
+ headers["x-holo-onion-verified"] = r.verified ? "1" : "0"; // end-to-end key check (self-authentication)
55
+ return { status: r.status | 0, headers, bytes: r.bytes instanceof Uint8Array ? r.bytes : new Uint8Array(r.bytes || []) };
56
+ }
57
+ return webFetch(url, opts);
58
+ };
59
+ }
60
+
61
+ // hostOnionFetch(base) β†’ onionFetch for the BROWSER page half of rung 1. RTC lives in the page; raw sockets
62
+ // (SOCKS5 to Arti) live in the host PROCESS. So the page's onion transport is a thin call to a same-origin
63
+ // host endpoint (`/onion?url=…`) that runs nodeArtiFetch (holo-onion-arti.node.mjs). If the endpoint is
64
+ // absent (no Arti wired on this host), it THROWS β€” the exit peer replies error and browser-sw's ladder
65
+ // falls past this device to the next rung (arti-wasm), exactly as intended.
66
+ export function hostOnionFetch(base = "/onion?url=") {
67
+ return async (url, { doc, op } = {}) => {
68
+ const q = encodeURIComponent(url) + (doc ? "&doc=1" : "") + (op ? "&op=" + encodeURIComponent(op) : "");
69
+ let r;
70
+ try { r = await fetch(base + q); } catch (e) { throw new Error("onion endpoint unreachable β€” no Arti on this host? (" + (e && e.message || e) + ")"); }
71
+ if (r.status === 404 || r.status === 501) throw new Error("onion endpoint not mounted on this host (status " + r.status + ")");
72
+ const headers = {}; r.headers.forEach((v, k) => { headers[k] = v; });
73
+ return { status: r.status, headers, bytes: new Uint8Array(await r.arrayBuffer()), verified: headers["x-holo-onion-verified"] === "1" };
74
+ };
75
+ }
76
+
77
+ export default { isOnionUrl, webViaHost, onionExitFetch, hostOnionFetch };
b/368419920dea19a3102a3c57db6b17a38001850aed2e4b73149ce44380701d07 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e4ae4f2bdd0c22810557f30eaabebdd9a831e5a9c44d2b09711546fbdc969963
3
+ size 262144
b/379cdc501c2fe6824601fa6ccfd64d32d7f23a9a19a5edad4ae4e25e4f93cdda ADDED
Binary file (16.7 kB). View file
 
b/37a53f9635d37c43c9cff03a30e51d91c058ab0ac6f86d82fe82fb379093bc60 ADDED
@@ -0,0 +1 @@
 
 
1
+ <svg fill="#ffffff" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Crunchyroll</title><path d="M2.909 13.436C2.914 7.61 7.642 2.893 13.468 2.898c5.576.005 10.137 4.339 10.51 9.819q.021-.351.022-.706C24.007 5.385 18.64.006 12.012 0S.007 5.36 0 11.988 5.36 23.994 11.988 24q.412 0 .815-.027c-5.526-.338-9.9-4.928-9.894-10.538Zm16.284.155a4.1 4.1 0 0 1-4.095-4.103 4.1 4.1 0 0 1 2.712-3.855 8.95 8.95 0 0 0-4.187-1.037 9.007 9.007 0 1 0 8.997 9.016q-.001-.847-.15-1.651a4.1 4.1 0 0 1-3.278 1.63Z"/></svg>
b/37af9d7eda050c8fd1658e20475de9958012e8d128273512e0136c9a446f1535 ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "@type": "earl:TestResult",
3
+ "spec": "holo-tmdb β€” the metadata provider auto-populates Holo Player (TMDb β†’ the one item shape: trending/popular/search/title/season, series enriched with trailer+cast+where-to-watch) and is ΞΊ-cached: repeats are served O(1) from a content-addressed ΞΊ (no fetch), tampering is refused (Law L5), and a warm cache works offline.",
4
+ "authority": "rests on #holo-tmdb + #holo-media-cache + #holo-media-item β€” Phase 1 of the streaming/metadata plan",
5
+ "witnessed": true,
6
+ "covers": [
7
+ "populates-rails",
8
+ "series-enriched",
9
+ "season-episodes",
10
+ "kappa-cache-hit",
11
+ "verify-before-trust",
12
+ "works-offline",
13
+ "deep-metadata"
14
+ ],
15
+ "checks": {
16
+ "populatesRails": true,
17
+ "seriesEnriched": true,
18
+ "seasonEpisodes": true,
19
+ "kappaCacheHit": true,
20
+ "verifyBeforeTrust": true,
21
+ "deepMetadata": true,
22
+ "worksOffline": true
23
+ },
24
+ "failed": []
25
+ }
b/38c718bfb8248e8f2a7ea814eb19d0e06895b691dc2ba01453a3c864caafcd1a ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:74602bbb82bf85d76e3f106bb0512b7b5258fa4aaea1438e671218032d784930
3
+ size 262144
b/391b5487b1ea161f001c35bfe921d71a1f4f21ca95f795600cdaa63ff1f3af11 ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$comment": "HoloRank candidate catalog high-quality OPEN-SOURCE video streamed DIRECTLY through Holo Video (the hologram-native WebGPU/HLS/DASH engine), not third-party embeds. Every `src` was verified live (HTTP 200/206 + CORS Access-Control-Allow-Origin + content-type) on 2026-06-11 so the cross-origin fetch works under the page's COEP. All Creative Commons β†’ genuinely ownable (Make native develops to ΞΊ). Showcases all four formats Holo Video plays: HLS Β· DASH Β· MP4 Β· WebM.",
3
+ "version": 2,
4
+ "generatedAt": "2026-06-11",
5
+ "topics": ["animation", "scifi", "fantasy", "comedy", "action", "adventure", "surreal", "nature", "music", "focus", "creative-commons", "short-film", "4k"],
6
+ "enrich": {
7
+ "holo-video": { "topics": ["comedy", "animation", "nature", "creative-commons"], "channel": "Blender Foundation", "quality": 0.82, "blurb": "A gentle giant rabbit turns the tables on three bullying rodents." },
8
+ "aqz-KE-bpKQ": { "topics": ["comedy", "animation", "nature", "creative-commons", "4k"], "channel": "Blender", "quality": 0.86, "blurb": "The beloved Blender short, in 60fps 4K a big bunny's sweet revenge." },
9
+ "SkVqJ1SGeL0": { "topics": ["comedy", "animation", "nature", "creative-commons"], "channel": "Blender Studio", "quality": 0.85, "blurb": "Koro the llama schemes to win a tasty treat from a stubborn gull." },
10
+ "-kf9XnXM4xE": { "topics": ["music", "focus"], "channel": "Focus Music", "quality": 0.7, "blurb": "Hours of calm instrumental music for deep focus and concentration." }
11
+ },
12
+ "items": [
13
+ { "id": "tos-hls", "title": "Tears of Steel", "channel": "Blender Β· Mango", "format": "hls", "type": "application/x-mpegURL",
14
+ "src": "https://test-streams.mux.dev/tos_ismc/main.m3u8", "poster": "https://i.ytimg.com/vi/R6MlUcmOul8/hqdefault.jpg",
15
+ "page": "https://mango.blender.org/", "topics": ["scifi", "action", "animation", "creative-commons", "short-film"], "tags": ["vfx", "robots", "adaptive"], "runtimeSec": 734, "quality": 0.93, "license": "CC BY 3.0" },
16
+
17
+ { "id": "ed-dash", "title": "Elephants Dream", "channel": "Blender Β· Orange", "format": "dash", "type": "application/dash+xml",
18
+ "src": "https://dash.akamaized.net/akamai/test/caption_test/ElephantsDream/elephants_dream_480p_heaac5_1.mpd", "poster": "https://i.ytimg.com/vi/TLkA0RELQ1g/hqdefault.jpg",
19
+ "page": "https://orange.blender.org/", "topics": ["scifi", "surreal", "fantasy", "animation", "creative-commons", "short-film"], "tags": ["first", "machine"], "runtimeSec": 654, "quality": 0.85, "license": "CC BY 2.5" },
20
+
21
+ { "id": "sintel-dash", "title": "Sintel", "channel": "Blender Β· Durian", "format": "dash", "type": "application/dash+xml",
22
+ "src": "https://storage.googleapis.com/shaka-demo-assets/sintel-mp4-only/dash.mpd", "poster": "https://i.ytimg.com/vi/eRsGyueVLvQ/hqdefault.jpg",
23
+ "page": "https://durian.blender.org/", "topics": ["fantasy", "adventure", "animation", "creative-commons", "short-film"], "tags": ["dragon", "epic", "adaptive"], "runtimeSec": 888, "quality": 0.95, "license": "CC BY 3.0" },
24
+
25
+ { "id": "bbb4k-webm", "title": "Big Buck Bunny 4K", "channel": "Blender Β· Peach", "format": "webm", "type": "video/webm",
26
+ "src": "https://upload.wikimedia.org/wikipedia/commons/transcoded/c/c0/Big_Buck_Bunny_4K.webm/Big_Buck_Bunny_4K.webm.720p.vp9.webm", "poster": "https://i.ytimg.com/vi/aqz-KE-bpKQ/hqdefault.jpg",
27
+ "page": "https://commons.wikimedia.org/wiki/File:Big_Buck_Bunny_4K.webm", "topics": ["comedy", "nature", "animation", "creative-commons", "4k", "short-film"], "tags": ["bunny", "vp9", "4k"], "runtimeSec": 634, "quality": 0.9, "license": "CC BY 3.0" },
28
+
29
+ { "id": "spring-webm", "title": "Spring", "channel": "Blender Studio Β· Spring", "format": "webm", "type": "video/webm",
30
+ "src": "https://upload.wikimedia.org/wikipedia/commons/transcoded/a/a5/Spring_-_Blender_Open_Movie.webm/Spring_-_Blender_Open_Movie.webm.720p.vp9.webm", "poster": "https://i.ytimg.com/vi/WhWc3b3KhnY/hqdefault.jpg",
31
+ "page": "https://www.blender.org/press/spring-open-movie/", "topics": ["fantasy", "nature", "animation", "creative-commons", "short-film"], "tags": ["seasons", "shepherd", "vp9"], "runtimeSec": 464, "quality": 0.94, "license": "CC BY 4.0" },
32
+
33
+ { "id": "charge-webm", "title": "Charge", "channel": "Blender Studio Β· Charge", "format": "webm", "type": "video/webm",
34
+ "src": "https://upload.wikimedia.org/wikipedia/commons/transcoded/7/7a/Charge_-_Blender_Open_Movie-full_movie.webm/Charge_-_Blender_Open_Movie-full_movie.webm.720p.vp9.webm", "poster": "https://i.ytimg.com/vi/UXqq0ZvbOnk/hqdefault.jpg",
35
+ "page": "https://studio.blender.org/projects/charge/", "topics": ["scifi", "action", "animation", "creative-commons", "short-film"], "tags": ["military", "eevee", "vp9"], "runtimeSec": 176, "quality": 0.91, "license": "CC BY 4.0" },
36
+
37
+ { "id": "angel-one-hls", "title": "Angel One", "channel": "Shaka Β· multi-audio demo", "format": "hls", "type": "application/x-mpegURL",
38
+ "src": "https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8", "poster": null,
39
+ "page": "https://github.com/shaka-project/shaka-player", "topics": ["scifi", "drama", "creative-commons", "short-film"], "tags": ["multi-audio", "subtitles", "languages"], "runtimeSec": 0, "quality": 0.74, "license": "CC BY 3.0" }
40
+ ]
41
+ }
b/3933995a0bb0fe7dcbed795b3d0ef19b070833c1743a70c3c61939cf3c2eaf40 ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-source-subsonic.mjs β€” a Subsonic/Navidrome/Airsonic server as a SourceProvider (its video library).
2
+ // Subsonic REST (f=json): getVideos β†’ videos; stream?id= β†’ direct stream; getCoverArt?id= β†’ art.
3
+ // auth: token (t=md5(password+salt), s=salt) or plain p; injected fetch β†’ Node-witnessable.
4
+
5
+ export function createSubsonicProvider({ base, user, token, salt, password, name, fetch: f, cache } = {}) {
6
+ base = String(base || "").replace(/\/+$/, "");
7
+ const doFetch = f || (typeof fetch !== "undefined" ? fetch.bind(globalThis) : null);
8
+ if (!doFetch) throw new Error("holo-source-subsonic: fetch required");
9
+ const label = name || base.replace(/^https?:\/\//, "");
10
+ const common = `u=${encodeURIComponent(user || "")}&${token ? `t=${token}&s=${salt}` : `p=${encodeURIComponent(password || "")}`}&v=1.16.1&c=HoloPlayer&f=json`;
11
+ const ep = (view, params = "") => `${base}/rest/${view}.view?${common}${params ? "&" + params : ""}`;
12
+
13
+ async function api(view, params) {
14
+ const url = ep(view, params);
15
+ const go = async () => { const r = await doFetch(url); if (!r.ok) throw new Error("subsonic " + r.status); return r.json(); };
16
+ if (!cache) return go();
17
+ const { body } = await cache.through("sub|" + url, go);
18
+ return body;
19
+ }
20
+ const norm = (v) => ({
21
+ id: "sub:" + v.id, _subId: v.id, kind: "movie", name: v.title || v.name || "Untitled", year: v.year || null,
22
+ overview: "", blurb: "", posterUrl: v.coverArt ? ep("getCoverArt", "id=" + encodeURIComponent(v.coverArt)) : null,
23
+ backdrop: v.coverArt ? ep("getCoverArt", "id=" + encodeURIComponent(v.coverArt)) : null,
24
+ runtimeSec: v.duration || 0, rating: null, genres: v.genre ? [v.genre] : [], topics: v.genre ? [String(v.genre).toLowerCase()] : [],
25
+ channel: label, quality: 0.7, license: "", source: "tmdb", provider: "subsonic", kappa: "", holoKappa: "sub:" + v.id,
26
+ availability: { playable: false, source: null, kappa: "", playSrc: "", type: "" },
27
+ });
28
+ const provider = {
29
+ id: "subsonic:" + base, name: label, kind: "subsonic", enabled: true, trust: 2,
30
+ async catalogs() { return [{ id: "videos", type: "movie", name: "Videos Β· " + label }]; },
31
+ async browse() { const d = await api("getVideos"); const vids = (d["subsonic-response"] && d["subsonic-response"].videos && d["subsonic-response"].videos.video) || []; return vids.map(norm); },
32
+ async search(q) { const items = await provider.browse(); return items.filter((x) => x.name.toLowerCase().includes(String(q).toLowerCase())); },
33
+ async resolve(item) { const id = item._subId || String(item.id).replace(/^sub:/, ""); return [{ playSrc: ep("stream", "id=" + encodeURIComponent(id)), type: "video/mp4", kind: "subsonic", httpDirect: true, quality: 720, provenance: { resolver: label, kind: "subsonic", label: "Subsonic Β· " + label } }]; },
34
+ };
35
+ return provider;
36
+ }
37
+ export default { createSubsonicProvider };
38
+ if (typeof window !== "undefined") window.HoloSourceSubsonic = { createSubsonicProvider };
b/3ad6f7a995b02b7307ee62d7867693bcfbd60388aac1964c8002452407e80dbe ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-art-cache.mjs β€” every poster/backdrop/logo/still is a content-addressed ΞΊ-object.
2
+ //
3
+ // The same poster is identical bytes for everyone, so it should be fetched ONCE and re-served forever. This
4
+ // caches image bytes under their content hash (ΞΊ): a warm image paints in <1 frame from the resident set, a
5
+ // reload paints with zero network, and it works offline. Law L5 holds β€” stored bytes that don't re-derive to
6
+ // their ΞΊ are refused (a poisoned poster fails closed, never silently painted). Bytes are stored as base64 so
7
+ // the SAME store works in Node (Map, witnessable) and the browser (IndexedDB); `objectURL()` hands the UI a
8
+ // blob: URL with no re-encode round-trip on the hot path.
9
+ //
10
+ // TWO ROLES: "u:<url>" β†’ the ΞΊ for an image URL (so the same URL short-circuits to its ΞΊ)
11
+ // "b:<ΞΊ>" β†’ the image bytes (base64), content-addressed + verifiable
12
+
13
+ async function sha256hex(bytes) {
14
+ if (typeof crypto !== "undefined" && crypto.subtle) {
15
+ const b = await crypto.subtle.digest("SHA-256", bytes);
16
+ return [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join("");
17
+ }
18
+ const { createHash } = await import("node:crypto");
19
+ return createHash("sha256").update(Buffer.from(bytes)).digest("hex");
20
+ }
21
+ const b64 = {
22
+ enc: (u8) => { if (typeof Buffer !== "undefined") return Buffer.from(u8).toString("base64"); let s = ""; for (let i = 0; i < u8.length; i++) s += String.fromCharCode(u8[i]); return btoa(s); },
23
+ dec: (s) => { if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(s, "base64")); const bin = atob(s); const u8 = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) u8[i] = bin.charCodeAt(i); return u8; },
24
+ };
25
+
26
+ // createArtCache({ kv, fetch }) β€” kv: { get(k)->string|null (maybe async), set(k,v) }; fetch optional (else global).
27
+ export function createArtCache({ kv, fetch: f } = {}) {
28
+ if (!kv || typeof kv.get !== "function" || typeof kv.set !== "function") throw new Error("holo-art-cache: kv required");
29
+ const doFetch = f || (typeof fetch !== "undefined" ? fetch.bind(globalThis) : null);
30
+ const stats = { puts: 0, hits: 0, misses: 0, refused: 0, dedup: 0 };
31
+
32
+ async function put(url, bytes) {
33
+ const u8 = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
34
+ const kappa = "sha256:" + (await sha256hex(u8));
35
+ const had = await kv.get("b:" + kappa);
36
+ if (had != null) stats.dedup++; else await kv.set("b:" + kappa, b64.enc(u8)); // identical bytes from any URL stored once
37
+ await kv.set("u:" + url, kappa);
38
+ stats.puts++;
39
+ return kappa;
40
+ }
41
+ // bytesFor(ΞΊ) β†’ Uint8Array | null, verify-before-trust (Law L5).
42
+ async function bytesFor(kappa) {
43
+ const s = await kv.get("b:" + kappa);
44
+ if (s == null) return null;
45
+ const u8 = b64.dec(s);
46
+ if ("sha256:" + (await sha256hex(u8)) !== kappa) { stats.refused++; return null; }
47
+ return u8;
48
+ }
49
+ async function get(url) {
50
+ const kappa = await kv.get("u:" + url);
51
+ if (!kappa) { stats.misses++; return null; }
52
+ const bytes = await bytesFor(kappa);
53
+ if (!bytes) { stats.misses++; return null; }
54
+ stats.hits++;
55
+ return { kappa, bytes };
56
+ }
57
+ // resolve(url) β€” the call the UI uses: a hit serves from ΞΊ (no network); a miss fetches the bytes, stores
58
+ // them, returns. Throws only if the fetch itself fails on a cold miss (caller falls back to the direct URL).
59
+ async function resolve(url) {
60
+ const hit = await get(url);
61
+ if (hit) return { ...hit, fromCache: true };
62
+ if (!doFetch) throw new Error("no fetch");
63
+ const res = await doFetch(url);
64
+ if (!res.ok) throw new Error("img " + res.status);
65
+ const bytes = new Uint8Array(await res.arrayBuffer());
66
+ const kappa = await put(url, bytes);
67
+ return { kappa, bytes, fromCache: false };
68
+ }
69
+ return { put, get, bytesFor, resolve, stats: () => ({ ...stats }) };
70
+ }
71
+
72
+ // ── browser: persistent IndexedDB store + blob:URL minting ───────────────────────────────────────────────
73
+ function idbKV(dbName = "holo-art", storeName = "kv") {
74
+ let dbp = null;
75
+ const open = () => dbp || (dbp = new Promise((res, rej) => { const r = indexedDB.open(dbName, 1); r.onupgradeneeded = () => { try { r.result.createObjectStore(storeName); } catch {} }; r.onsuccess = () => res(r.result); r.onerror = () => rej(r.error); }));
76
+ const tx = async (mode, fn) => { const db = await open(); return new Promise((res, rej) => { const t = db.transaction(storeName, mode); const s = t.objectStore(storeName); const rq = fn(s); rq.onsuccess = () => res(rq.result); rq.onerror = () => rej(rq.error); }); };
77
+ return { get: (k) => tx("readonly", (s) => s.get(k)).then((v) => (v == null ? null : v)).catch(() => null), set: (k, v) => tx("readwrite", (s) => s.put(v, k)).catch(() => {}) };
78
+ }
79
+ export function memKV() { const m = new Map(); return { get: (k) => (m.has(k) ? m.get(k) : null), set: (k, v) => { m.set(k, v); } }; }
80
+
81
+ // sniff an image MIME from magic bytes β€” a typeless Blob won't decode reliably as an <img> src.
82
+ function sniffImageType(b) {
83
+ if (!b || b.length < 12) return "image/jpeg";
84
+ if (b[0] === 0xff && b[1] === 0xd8) return "image/jpeg";
85
+ if (b[0] === 0x89 && b[1] === 0x50) return "image/png";
86
+ if (b[0] === 0x47 && b[1] === 0x49) return "image/gif";
87
+ if (b[0] === 0x52 && b[1] === 0x49 && b[8] === 0x57 && b[9] === 0x45) return "image/webp"; // RIFF…WEBP
88
+ if (b[0] === 0x3c) return "image/svg+xml"; // '<'
89
+ return "image/jpeg";
90
+ }
91
+
92
+ if (typeof window !== "undefined") {
93
+ const objURLs = new Map(); // ΞΊ β†’ blob: URL (minted once, reused)
94
+ window.HoloArtCache = {
95
+ createArtCache, memKV,
96
+ _cache: null,
97
+ live() { if (!this._cache) { try { this._cache = createArtCache({ kv: idbKV() }); } catch { this._cache = createArtCache({ kv: memKV() }); } } return this._cache; },
98
+ // url β†’ a blob: URL served from ΞΊ (warm), fetching+caching on a cold miss. Null if it can't be had.
99
+ async url(imageUrl) {
100
+ const c = this.live();
101
+ let r; try { r = await c.resolve(imageUrl); } catch { return null; }
102
+ if (objURLs.has(r.kappa)) return objURLs.get(r.kappa);
103
+ const u = URL.createObjectURL(new Blob([r.bytes], { type: sniffImageType(r.bytes) }));
104
+ objURLs.set(r.kappa, u);
105
+ return u;
106
+ },
107
+ };
108
+ }
109
+
110
+ export default { createArtCache, memKV };