- commit
- 86c250b8556925e7fda3ea5d3639777290c5f57c
- author
- Dami <dami.etoile@gmail.com>
| A | .gitignore | 4 | + |
| A | LICENSE | 24 | ++ |
| A | deno.json | 9 | + |
| A | deno.lock | 335 | ++++++++++++++++++++++++++++++ |
| A | scripts/ | 16 | + |
| A | scripts/ | 13 | + |
| A | server.ts | 57 | +++++ |
@@ -0,0 +1,24 @@
1+This is free and unencumbered software released into the public domain.2+3+Anyone is free to copy, modify, publish, use, compile, sell, or4+distribute this software, either in source code form or as a compiled5+binary, for any purpose, commercial or non-commercial, and by any6+means.7+8+In jurisdictions that recognize copyright laws, the author or authors9+of this software dedicate any and all copyright interest in the10+software to the public domain. We make this dedication for the benefit11+of the public at large and to the detriment of our heirs and12+successors. We intend this dedication to be an overt act of13+relinquishment in perpetuity of all present and future rights to this14+software under copyright law.15+16+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,17+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF18+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.19+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR20+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,21+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR22+OTHER DEALINGS IN THE SOFTWARE.23+24+For more information, please refer to <https://unlicense.org/>@@ -0,0 +1,335 @@
1+{2+ "version": "5",3+ "specifiers": {4+ "jsr:@std/assert@1": "1.0.19",5+ "jsr:@std/cli@^1.0.31": "1.0.32",6+ "jsr:@std/encoding@^1.0.11": "1.0.11",7+ "jsr:@std/fmt@^1.0.10": "1.0.10",8+ "jsr:@std/fs@1": "1.0.24",9+ "jsr:@std/fs@^1.0.24": "1.0.24",10+ "jsr:@std/html@^1.0.7": "1.0.7",11+ "jsr:@std/http@1": "1.1.2",12+ "jsr:@std/internal@^1.0.12": "1.0.14",13+ "jsr:@std/internal@^1.0.14": "1.0.14",14+ "jsr:@std/media-types@^1.1.0": "1.1.0",15+ "jsr:@std/net@^1.0.6": "1.0.6",16+ "jsr:@std/path@^1.1.5": "1.1.6",17+ "jsr:@std/path@^1.1.6": "1.1.6",18+ "jsr:@std/streams@^1.1.1": "1.1.1",19+ "npm:@noble/curves@1.9.7": "1.9.7",20+ "npm:@noble/hashes@1.8.0": "1.8.0",21+ "npm:esbuild@0.25": "0.25.12",22+ "npm:fake-indexeddb@6": "6.2.5",23+ "npm:nostr-tools@2.17.0": "2.17.0",24+ "npm:playwright@1.58": "1.58.2"25+ },26+ "jsr": {27+ "@std/assert@1.0.19": {28+ "integrity": "eaada96ee120cb980bc47e040f82814d786fe8162ecc53c91d8df60b8755991e",29+ "dependencies": [30+ "jsr:@std/internal@^1.0.12"31+ ]32+ },33+ "@std/cli@1.0.32": {34+ "integrity": "188b3a100d6202d64e3f5bd3d799c7fa4f6d77f92cc65eb7f641c1fa0aa92a66"35+ },36+ "@std/encoding@1.0.11": {37+ "integrity": "e7cef2f0b3153bccc17431e7c864a1de03a4b6d9647389c43e08aaa775d37385"38+ },39+ "@std/fmt@1.0.10": {40+ "integrity": "90dfba288802ac6de82fb31d0917eb9e4450b9925b954d5e51fc29ac07419db5"41+ },42+ "@std/fs@1.0.24": {43+ "integrity": "f3061b45b81673a2bece689da041df32d174be064c89eb6397fb5718d3fb7877",44+ "dependencies": [45+ "jsr:@std/internal@^1.0.14",46+ "jsr:@std/path@^1.1.5"47+ ]48+ },49+ "@std/html@1.0.7": {50+ "integrity": "175c818905a6e75743c69c251395e273d82698e58cfe7dd76268d70e28b8d8fe"51+ },52+ "@std/http@1.1.2": {53+ "integrity": "1c0473590d5e5b75b30b2be4833c08047dd799aeea35624fd34ec58d67fc9240",54+ "dependencies": [55+ "jsr:@std/cli",56+ "jsr:@std/encoding",57+ "jsr:@std/fmt",58+ "jsr:@std/fs@^1.0.24",59+ "jsr:@std/html",60+ "jsr:@std/media-types",61+ "jsr:@std/net",62+ "jsr:@std/path@^1.1.6",63+ "jsr:@std/streams"64+ ]65+ },66+ "@std/internal@1.0.14": {67+ "integrity": "291516b3d4c35024d6ffbc0a9df5bf4c64116e05b50012cf846710152d2ffdf7"68+ },69+ "@std/media-types@1.1.0": {70+ "integrity": "c9d093f0c05c3512932b330e3cc1fe1d627b301db33a4c2c2185c02471d6eaa4"71+ },72+ "@std/net@1.0.6": {73+ "integrity": "110735f93e95bb9feb95790a8b1d1bf69ec0dc74f3f97a00a76ea5efea25500c"74+ },75+ "@std/path@1.1.6": {76+ "integrity": "c68485c2a4dfbb5ae3cc74fae4e8c4e5d874cf8a8ed12927917235c758b46cbe",77+ "dependencies": [78+ "jsr:@std/internal@^1.0.14"79+ ]80+ },81+ "@std/streams@1.1.1": {82+ "integrity": "92556d350e537e9dce527a6d08f6f15be3ff65e656079dea69d15252187c7613"83+ }84+ },85+ "npm": {86+ "@esbuild/aix-ppc64@0.25.12": {87+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",88+ "os": ["aix"],89+ "cpu": ["ppc64"]90+ },91+ "@esbuild/android-arm64@0.25.12": {92+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",93+ "os": ["android"],94+ "cpu": ["arm64"]95+ },96+ "@esbuild/android-arm@0.25.12": {97+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",98+ "os": ["android"],99+ "cpu": ["arm"]100+ },101+ "@esbuild/android-x64@0.25.12": {102+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",103+ "os": ["android"],104+ "cpu": ["x64"]105+ },106+ "@esbuild/darwin-arm64@0.25.12": {107+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",108+ "os": ["darwin"],109+ "cpu": ["arm64"]110+ },111+ "@esbuild/darwin-x64@0.25.12": {112+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",113+ "os": ["darwin"],114+ "cpu": ["x64"]115+ },116+ "@esbuild/freebsd-arm64@0.25.12": {117+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",118+ "os": ["freebsd"],119+ "cpu": ["arm64"]120+ },121+ "@esbuild/freebsd-x64@0.25.12": {122+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",123+ "os": ["freebsd"],124+ "cpu": ["x64"]125+ },126+ "@esbuild/linux-arm64@0.25.12": {127+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",128+ "os": ["linux"],129+ "cpu": ["arm64"]130+ },131+ "@esbuild/linux-arm@0.25.12": {132+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",133+ "os": ["linux"],134+ "cpu": ["arm"]135+ },136+ "@esbuild/linux-ia32@0.25.12": {137+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",138+ "os": ["linux"],139+ "cpu": ["ia32"]140+ },141+ "@esbuild/linux-loong64@0.25.12": {142+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",143+ "os": ["linux"],144+ "cpu": ["loong64"]145+ },146+ "@esbuild/linux-mips64el@0.25.12": {147+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",148+ "os": ["linux"],149+ "cpu": ["mips64el"]150+ },151+ "@esbuild/linux-ppc64@0.25.12": {152+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",153+ "os": ["linux"],154+ "cpu": ["ppc64"]155+ },156+ "@esbuild/linux-riscv64@0.25.12": {157+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",158+ "os": ["linux"],159+ "cpu": ["riscv64"]160+ },161+ "@esbuild/linux-s390x@0.25.12": {162+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",163+ "os": ["linux"],164+ "cpu": ["s390x"]165+ },166+ "@esbuild/linux-x64@0.25.12": {167+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",168+ "os": ["linux"],169+ "cpu": ["x64"]170+ },171+ "@esbuild/netbsd-arm64@0.25.12": {172+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",173+ "os": ["netbsd"],174+ "cpu": ["arm64"]175+ },176+ "@esbuild/netbsd-x64@0.25.12": {177+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",178+ "os": ["netbsd"],179+ "cpu": ["x64"]180+ },181+ "@esbuild/openbsd-arm64@0.25.12": {182+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",183+ "os": ["openbsd"],184+ "cpu": ["arm64"]185+ },186+ "@esbuild/openbsd-x64@0.25.12": {187+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",188+ "os": ["openbsd"],189+ "cpu": ["x64"]190+ },191+ "@esbuild/openharmony-arm64@0.25.12": {192+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",193+ "os": ["openharmony"],194+ "cpu": ["arm64"]195+ },196+ "@esbuild/sunos-x64@0.25.12": {197+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",198+ "os": ["sunos"],199+ "cpu": ["x64"]200+ },201+ "@esbuild/win32-arm64@0.25.12": {202+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",203+ "os": ["win32"],204+ "cpu": ["arm64"]205+ },206+ "@esbuild/win32-ia32@0.25.12": {207+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",208+ "os": ["win32"],209+ "cpu": ["ia32"]210+ },211+ "@esbuild/win32-x64@0.25.12": {212+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",213+ "os": ["win32"],214+ "cpu": ["x64"]215+ },216+ "@noble/ciphers@0.5.3": {217+ "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w=="218+ },219+ "@noble/curves@1.1.0": {220+ "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",221+ "dependencies": [222+ "@noble/hashes@1.3.1"223+ ]224+ },225+ "@noble/curves@1.2.0": {226+ "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",227+ "dependencies": [228+ "@noble/hashes@1.3.2"229+ ]230+ },231+ "@noble/curves@1.9.7": {232+ "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==",233+ "dependencies": [234+ "@noble/hashes@1.8.0"235+ ]236+ },237+ "@noble/hashes@1.3.1": {238+ "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA=="239+ },240+ "@noble/hashes@1.3.2": {241+ "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="242+ },243+ "@noble/hashes@1.8.0": {244+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="245+ },246+ "@scure/base@1.1.1": {247+ "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="248+ },249+ "@scure/bip32@1.3.1": {250+ "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",251+ "dependencies": [252+ "@noble/curves@1.1.0",253+ "@noble/hashes@1.3.2",254+ "@scure/base"255+ ]256+ },257+ "@scure/bip39@1.2.1": {258+ "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",259+ "dependencies": [260+ "@noble/hashes@1.3.2",261+ "@scure/base"262+ ]263+ },264+ "esbuild@0.25.12": {265+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",266+ "optionalDependencies": [267+ "@esbuild/aix-ppc64",268+ "@esbuild/android-arm",269+ "@esbuild/android-arm64",270+ "@esbuild/android-x64",271+ "@esbuild/darwin-arm64",272+ "@esbuild/darwin-x64",273+ "@esbuild/freebsd-arm64",274+ "@esbuild/freebsd-x64",275+ "@esbuild/linux-arm",276+ "@esbuild/linux-arm64",277+ "@esbuild/linux-ia32",278+ "@esbuild/linux-loong64",279+ "@esbuild/linux-mips64el",280+ "@esbuild/linux-ppc64",281+ "@esbuild/linux-riscv64",282+ "@esbuild/linux-s390x",283+ "@esbuild/linux-x64",284+ "@esbuild/netbsd-arm64",285+ "@esbuild/netbsd-x64",286+ "@esbuild/openbsd-arm64",287+ "@esbuild/openbsd-x64",288+ "@esbuild/openharmony-arm64",289+ "@esbuild/sunos-x64",290+ "@esbuild/win32-arm64",291+ "@esbuild/win32-ia32",292+ "@esbuild/win32-x64"293+ ],294+ "scripts": true,295+ "bin": true296+ },297+ "fake-indexeddb@6.2.5": {298+ "integrity": "sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w=="299+ },300+ "fsevents@2.3.2": {301+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",302+ "os": ["darwin"],303+ "scripts": true304+ },305+ "nostr-tools@2.17.0": {306+ "integrity": "sha512-lrvHM7cSaGhz7F0YuBvgHMoU2s8/KuThihDoOYk8w5gpVHTy0DeUCAgCN8uLGeuSl5MAWekJr9Dkfo5HClqO9w==",307+ "dependencies": [308+ "@noble/ciphers",309+ "@noble/curves@1.2.0",310+ "@noble/hashes@1.3.1",311+ "@scure/base",312+ "@scure/bip32",313+ "@scure/bip39",314+ "nostr-wasm"315+ ]316+ },317+ "nostr-wasm@0.1.0": {318+ "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA=="319+ },320+ "playwright-core@1.58.2": {321+ "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",322+ "bin": true323+ },324+ "playwright@1.58.2": {325+ "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",326+ "dependencies": [327+ "playwright-core"328+ ],329+ "optionalDependencies": [330+ "fsevents"331+ ],332+ "bin": true333+ }334+ }335+}@@ -0,0 +1,16 @@
1+// Concatenates the libraries into dist/nobel.js and minifies it into dist/nobel.min.js.2+import { transform } from "npm:esbuild@0.25";3+import { brotliCompressSync } from "node:zlib";4+5+const order = ["neon", "krypton", "fluorine", "chlorine", "argon", "hydrogen", "helium", "gold", "mercury", "xenon", "radon", "iodine", "oganesson", "europium", "praseodymium", "cerium"];6+const root = new URL("../", import.meta.url);7+const parts = await Promise.all(order.map((n) => Deno.readTextFile(new URL(`${n}/${n}.js`, root))));8+const src = order.map((n, i) => `// ${n}.js\n${parts[i].trim()}\n`).join("");9+const { code } = await transform(src, { minify: true, target: "es2022" });10+11+await Deno.mkdir(new URL("dist/", root), { recursive: true });12+await Deno.writeTextFile(new URL("dist/nobel.js", root), src);13+await Deno.writeTextFile(new URL("dist/nobel.min.js", root), code);14+const br = brotliCompressSync(new TextEncoder().encode(code)).length;15+console.log(`dist/nobel.js ${src.length} raw | dist/nobel.min.js ${code.length} min, ${br} br`);16+Deno.exit(0);@@ -0,0 +1,13 @@
1+// Prints raw / gzip / brotli sizes and fails if a library exceeds the 5kb raw budget.2+import { brotliCompressSync, gzipSync } from "node:zlib";3+4+const BUDGET = 5120;5+const libs = ["hydrogen", "xenon", "krypton", "fluorine", "chlorine", "argon", "helium", "neon", "radon", "iodine", "oganesson", "gold", "mercury", "europium", "praseodymium", "cerium"];6+let over = false;7+for (const name of libs) {8+ const src = await Deno.readFile(new URL(`../${name}/${name}.js`, import.meta.url));9+ const raw = src.length, gz = gzipSync(src).length, br = brotliCompressSync(src).length;10+ over ||= raw > BUDGET;11+ console.log(`${`${name}.js`.padEnd(11)} raw ${String(raw).padStart(5)} gzip ${String(gz).padStart(5)} br ${String(br).padStart(5)}${raw > BUDGET ? " OVER BUDGET" : ""}`);12+}13+if (over) Deno.exit(1);@@ -0,0 +1,57 @@
1+// Dev server: static files plus a tiny in-memory nostr relay at /relay.2+// The relay reuses krypton.js to check signatures and argon.js to match filters.3+import { serveDir } from "jsr:@std/http@1/file-server";4+import "./krypton/krypton.js";5+import "./argon/argon.js";6+7+type Event = { id: string; pubkey: string; kind: number; created_at: number; tags: string[][]; content: string; sig: string };8+type Filter = Record<string, unknown> & { limit?: number };9+// deno-lint-ignore no-explicit-any10+const { krypton, argon } = globalThis as any;11+12+export function serve(port = 8000) {13+ const events: Event[] = [];14+ const clients = new Set<{ ws: WebSocket; subs: Map<string, Filter[]> }>();15+16+ const relay = (req: Request) => {17+ const { socket: ws, response } = Deno.upgradeWebSocket(req);18+ const client = { ws, subs: new Map<string, Filter[]>() };19+ const send = (m: unknown[]) => ws.readyState === 1 && ws.send(JSON.stringify(m));20+ clients.add(client);21+ ws.onclose = () => clients.delete(client);22+ ws.onmessage = async (m) => {23+ let msg;24+ try { msg = JSON.parse(m.data); } catch { return; }25+ const [type, a, ...filters] = msg;26+ if (type === "REQ") {27+ client.subs.set(a, filters);28+ const newest = [...events].reverse(), out = new Map<string, Event>();29+ for (const f of filters) {30+ newest.filter((e) => argon.match(f, e)).slice(0, f.limit ?? Infinity).forEach((e) => out.set(e.id, e));31+ }32+ out.forEach((e) => send(["EVENT", a, e]));33+ send(["EOSE", a]);34+ } else if (type === "CLOSE") {35+ client.subs.delete(a);36+ } else if (type === "EVENT") {37+ const ok = await krypton.check(a);38+ send(["OK", a?.id, ok, ok ? "" : "invalid: bad id or signature"]);39+ if (!ok || events.some((e) => e.id === a.id)) return;40+ if (a.kind < 20000 || a.kind >= 30000) events.push(a);41+ for (const c of clients) {42+ for (const [id, fs] of c.subs) {43+ if (fs.some((f) => argon.match(f, a))) c.ws.send(JSON.stringify(["EVENT", id, a]));44+ }45+ }46+ }47+ };48+ return response;49+ };50+51+ return Deno.serve({ port, onListen: ({ port }) => console.log(`nobel: http://localhost:${port}/demo/`) }, (req) => {52+ if (new URL(req.url).pathname === "/relay" && req.headers.get("upgrade") === "websocket") return relay(req);53+ return serveDir(req, { fsRoot: new URL(".", import.meta.url).pathname, quiet: true });54+ });55+}56+57+if (import.meta.main) serve(Number(Deno.env.get("PORT") ?? 8000));