commit 861cc9c86cfeab19e844cd24f3884a5d97c9f291
parent 746bda9041244de0127f438b78fd1a5328397952
Author: finwo <finwo@pm.me>
Date: Sat, 13 Sep 2025 18:18:08 +0200
Creating account seed+name combo
Diffstat:
10 files changed, 419 insertions(+), 248 deletions(-)
diff --git a/package-lock.json b/package-lock.json
@@ -454,6 +454,25 @@
"node": ">=18"
}
},
+ "node_modules/@jspm/core": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@jspm/core/-/core-2.1.0.tgz",
+ "integrity": "sha512-3sRl+pkyFY/kLmHl0cgHiFp2xEqErA8N3ECjMs7serSUBmoJ70lBa0PG5t0IM6WJgdZNyyI0R8YFfi5wM8+mzg==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+ "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -505,53 +524,37 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "node_modules/@types/mithril": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@types/mithril/-/mithril-2.2.7.tgz",
+ "integrity": "sha512-uetxoYizBMHPELl6DSZUfO6Q/aOm+h0NUCv9bVAX2iAxfrdBSOvU9KKFl+McTtxR13F+BReYLY814pJsZvnSxg==",
"dev": true,
- "license": "ISC",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
+ "license": "MIT"
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
},
"engines": {
- "node": ">= 8"
+ "node": ">=0.4.0"
}
},
"node_modules/app": {
"resolved": "packages/app",
"link": true
},
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/brace-expansion": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
- "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
- "dev": true,
- "license": "MIT",
+ "node_modules/bip39": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz",
+ "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==",
+ "license": "ISC",
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "@noble/hashes": "^1.2.0"
}
},
"node_modules/braces": {
@@ -567,35 +570,10 @@
"node": ">=8"
}
},
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "node_modules/confbox": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
+ "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
"dev": true,
"license": "MIT"
},
@@ -637,24 +615,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
"node_modules/esbuild": {
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
@@ -697,6 +657,31 @@
"@esbuild/win32-x64": "0.25.9"
}
},
+ "node_modules/esbuild-plugins-node-modules-polyfill": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/esbuild-plugins-node-modules-polyfill/-/esbuild-plugins-node-modules-polyfill-1.7.1.tgz",
+ "integrity": "sha512-IEaUhaS1RukGGcatDzqJmR+AzUWJ2upTJZP2i7FzR37Iw5Lk0LReCTnWnPMWnGG9lO4MWTGKEGGLWEOPegTRJA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jspm/core": "^2.1.0",
+ "local-pkg": "^1.1.1",
+ "resolve.exports": "^2.0.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "esbuild": ">=0.14.0 <=0.25.x"
+ }
+ },
+ "node_modules/exsolve": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz",
+ "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -737,21 +722,6 @@
"node": ">=8"
}
},
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -793,16 +763,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
@@ -813,26 +773,6 @@
"node": ">= 4"
}
},
- "node_modules/ignore-by-default": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
- "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -879,6 +819,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/local-pkg": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz",
+ "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mlly": "^1.7.4",
+ "pkg-types": "^2.3.0",
+ "quansync": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -903,69 +861,42 @@
"node": ">=8.6"
}
},
- "node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/mithril": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/mithril/-/mithril-2.3.7.tgz",
"integrity": "sha512-igRr/o79tNDTL2wa4ta16V7B0A0KdtjPcRVBdw2BMHF0c08xpCk4GxN+Ws04NYq2kGDBA0En1QHF/dgCw1oIIw==",
"license": "MIT"
},
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/nodemon": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
- "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
+ "node_modules/mlly": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+ "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "chokidar": "^3.5.2",
- "debug": "^4",
- "ignore-by-default": "^1.0.1",
- "minimatch": "^3.1.2",
- "pstree.remy": "^1.1.8",
- "semver": "^7.5.3",
- "simple-update-notifier": "^2.0.0",
- "supports-color": "^5.5.0",
- "touch": "^3.1.0",
- "undefsafe": "^2.0.5"
- },
- "bin": {
- "nodemon": "bin/nodemon.js"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/nodemon"
+ "acorn": "^8.15.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^1.3.1",
+ "ufo": "^1.6.1"
}
},
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "node_modules/mlly/node_modules/confbox": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+ "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mlly/node_modules/pkg-types": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+ "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">=0.10.0"
+ "dependencies": {
+ "confbox": "^0.1.8",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.1"
}
},
"node_modules/p-event": {
@@ -1039,6 +970,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -1052,11 +990,33 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/pstree.remy": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
- "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "node_modules/pkg-types": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
+ "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.2.2",
+ "exsolve": "^1.0.7",
+ "pathe": "^2.0.3"
+ }
+ },
+ "node_modules/quansync": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
+ "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
"dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/antfu"
+ },
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ ],
"license": "MIT"
},
"node_modules/queue-microtask": {
@@ -1080,17 +1040,14 @@
],
"license": "MIT"
},
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "node_modules/resolve.exports": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz",
+ "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
"engines": {
- "node": ">=8.10.0"
+ "node": ">=10"
}
},
"node_modules/reusify": {
@@ -1128,32 +1085,6 @@
"queue-microtask": "^1.2.2"
}
},
- "node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/simple-update-notifier": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
- "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "semver": "^7.5.3"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/slash": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
@@ -1167,19 +1098,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -1193,20 +1111,10 @@
"node": ">=8.0"
}
},
- "node_modules/touch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
- "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "nodetouch": "bin/nodetouch.js"
- }
- },
- "node_modules/undefsafe": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
- "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+ "node_modules/ufo": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
+ "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
"dev": true,
"license": "MIT"
},
@@ -1227,12 +1135,14 @@
"version": "0.0.1",
"license": "MIT",
"dependencies": {
+ "bip39": "^3.1.0",
"mithril": "^2.3.7"
},
"devDependencies": {
+ "@types/mithril": "^2.2.7",
"cpy": "^12.0.1",
"esbuild": "^0.25.9",
- "nodemon": "^3.1.10"
+ "esbuild-plugins-node-modules-polyfill": "^1.7.1"
}
}
}
diff --git a/packages/app/esbuild.js b/packages/app/esbuild.js
@@ -5,6 +5,7 @@ const fs = require('node:fs');
const { dirname } = require('node:path');
const { fileURLToPath } = require('node:url');
const esbuild = require('esbuild');
+const { nodeModulesPolyfillPlugin } = require('esbuild-plugins-node-modules-polyfill');
// const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -35,6 +36,14 @@ const config = {
'.woff' : 'dataurl',
'.woff2': 'dataurl',
},
+
+ plugins: [
+ nodeModulesPolyfillPlugin({
+ globals: {
+ Buffer: true
+ }
+ })
+ ],
};
const buildList = [];
diff --git a/packages/app/package.json b/packages/app/package.json
@@ -14,10 +14,13 @@
"license": "MIT",
"description": "",
"devDependencies": {
+ "@types/mithril": "^2.2.7",
"cpy": "^12.0.1",
- "esbuild": "^0.25.9"
+ "esbuild": "^0.25.9",
+ "esbuild-plugins-node-modules-polyfill": "^1.7.1"
},
"dependencies": {
+ "bip39": "^3.1.0",
"mithril": "^2.3.7"
}
}
diff --git a/packages/app/src/component/app.tsx b/packages/app/src/component/app.tsx
@@ -1,5 +1,5 @@
export default {
- view: function() {
+ view() {
return (
<div>
Hello World
diff --git a/packages/app/src/component/screen-account-create.tsx b/packages/app/src/component/screen-account-create.tsx
@@ -0,0 +1,115 @@
+import { generateMnemonic } from 'bip39';
+import { Vnode } from 'mithril';
+
+import { getDirectoryHandle, getFileHandle } from '../util/opfs';
+
+type _Vnode = Vnode<{}, {
+ accountId : typeof AccountCreateScreen['accountId' ],
+ accountName : typeof AccountCreateScreen['accountName'],
+ seedPhrase : typeof AccountCreateScreen['seedPhrase' ],
+ step : typeof AccountCreateScreen['step' ],
+ steps : typeof AccountCreateScreen['steps' ],
+ handler : typeof AccountCreateScreen['handler' ],
+}>;
+
+export const AccountCreateScreen = {
+ routePath: '/account/create',
+
+ accountId : '',
+ accountName: '',
+ seedPhrase : '',
+
+ handler: {
+ setAccountName(vnode: _Vnode) {
+ return () => {
+ const el = document.getElementById('accountName') as HTMLInputElement;
+ vnode.state.accountName = el.value;
+ vnode.state.step++;
+ };
+ },
+ goto: (vnode: _Vnode, step) => {
+ return () => vnode.state.step = step;
+ },
+ confirmPhrase: (vnode: _Vnode) => {
+ return async () => {
+ const el = document.getElementById('phraseConfirm') as HTMLTextAreaElement;
+ if (el.value !== vnode.state.seedPhrase) {
+ alert("Phrase incorrect");
+ return;
+ }
+ vnode.state.step++;
+
+ const accountSeedHandle = await getFileHandle(`/accounts/${vnode.state.accountId}/seed.txt`, { create: true })
+ const accountSeedWritable = await accountSeedHandle.createWritable();
+ await accountSeedWritable.write(vnode.state.seedPhrase);
+ await accountSeedWritable.close();
+
+ const accountConfigHandle = await getFileHandle(`/accounts/${vnode.state.accountId}/config.json`, { create: true })
+ const accountConfigWritable = await accountConfigHandle.createWritable();
+ await accountConfigWritable.write(JSON.stringify({
+ name: vnode.state.accountName
+ }, null, 2));
+ await accountConfigWritable.close();
+ }
+ },
+ taGrow() {
+ const el = this as HTMLTextAreaElement;
+ el.style.height = (Math.max(el.scrollHeight, 38)+2)+'px';
+ }
+ },
+
+ step: 0,
+ steps: [
+ (vnode: _Vnode) => (
+ <div class="main">
+ <p>
+ Give your new account a name you'll recognize (not public)
+ </p>
+ <input id="accountName" value={vnode.state.accountName} style="display: block; background: transparent; padding: 0.5em 0px; color: inherit; border-width: medium medium 2px; border-style: none none solid; border-color: currentcolor currentcolor rgb(255, 255, 255); border-image: none;width: calc(100% - 2rem);" type="text"/>
+ <button onclick={vnode.state.handler.setAccountName(vnode)}>Next</button>
+ </div>
+ ),
+ (vnode: _Vnode) => (
+ <div class="main">
+ <p>
+ Here's your new account's seed phrase. Please write it down and store it securely:
+ </p>
+ <pre style="white-space:break-spaces;">
+ {vnode.state.seedPhrase}
+ </pre>
+ <button onclick={vnode.state.handler.goto(vnode, 0)}>Back</button>
+ <button onclick={vnode.state.handler.goto(vnode, 2)}>Next</button>
+ </div>
+ ),
+ (vnode: _Vnode) => (
+ <div class="main">
+ <p>
+ Please repeat the seed phrase to confirm you got it stored:
+ </p>
+ <textarea id="phraseConfirm" style="display:block;width:calc(100% - 2rem);background:transparent;padding:0.5em 0;color:inherit;border:none;border-bottom:2px solid #FFF;height:40px;" onkeyup={vnode.state.handler.taGrow} onkeydown={vnode.state.handler.taGrow}></textarea>
+ <button onclick={vnode.state.handler.goto(vnode, 1)}>Back</button>
+ <button onclick={vnode.state.handler.confirmPhrase(vnode)}>Next</button>
+ </div>
+ ),
+ (vnode: _Vnode) => (
+ <div class="main">
+ Building account...
+ </div>
+ )
+ ],
+
+ async oninit(vnode: _Vnode) {
+ vnode.state.seedPhrase = generateMnemonic();
+
+ vnode.state.accountId = '';
+ while(vnode.state.accountId.length < 32) {
+ vnode.state.accountId += Math.random().toString(36).slice(2);
+ }
+ vnode.state.accountId = vnode.state.accountId.slice(0,32);
+ },
+
+ view(vnode: _Vnode) {
+ return vnode.state.steps[vnode.state.step](vnode);
+ },
+
+};
diff --git a/packages/app/src/component/screen-account-select.tsx b/packages/app/src/component/screen-account-select.tsx
@@ -0,0 +1,32 @@
+import { AccountCreateScreen } from './screen-account-create.tsx';
+
+import { getDirectoryHandle } from '../util/opfs';
+
+export const AccountSelectScreen = {
+ routePath: '/account/select',
+
+ accounts: [],
+
+ async oninit(vnode) {
+ vnode.state.accounts = [];
+
+ const accountsHandle = await getDirectoryHandle('/accounts');
+ for await (const [name, handle] of accountsHandle.entries()) {
+ vnode.state.accounts.push(name);
+ }
+
+ if (!vnode.state.accounts.length) {
+ document.location.href = `#!${AccountCreateScreen.routePath}`;
+ return;
+ }
+
+ },
+
+ view() {
+ return (
+ <div>
+ ACCOUNT SELECT
+ </div>
+ );
+ },
+};
diff --git a/packages/app/src/component/screen-home.tsx b/packages/app/src/component/screen-home.tsx
@@ -0,0 +1,20 @@
+import { AccountSelectScreen } from "./screen-account-select";
+
+export default {
+ oninit() {
+
+ // Redirect to account selection if not there
+ if (!localStorage.selectedAccount) {
+ document.location.href = `#!${AccountSelectScreen.routePath}`;
+ return;
+ }
+
+ console.log("INIT HOME");
+ },
+
+ view() {
+ return (
+ <a href="#!/app">Enter!</a>
+ );
+ },
+};
diff --git a/packages/app/src/global.css b/packages/app/src/global.css
@@ -2,4 +2,60 @@
background: #000000;
color: #FFFFFF;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-size: x-large;
+}
+
+* {
+ box-sizing: border-box;
+ font-size: inherit;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+}
+
+h1 { font-size: 2.0rem; }
+h2 { font-size: 1.6rem; }
+h3 { font-size: 1.2rem; }
+h4 { font-size: 1.0rem; }
+h5 { font-size: 0.8rem; }
+h6 { font-size: 0.6rem; }
+
+h1, h2, h3, h4, h5, h6, legend, label {
+ font-weight: 500;
+}
+
+a[href] {
+ color: inherit;
+ text-decoration: underline;
+}
+
+
+input:focus,
+select:focus,
+textarea:focus,
+button:focus {
+ outline: none;
+}
+
+button {
+ border: 2px solid #FFFFFF;
+ background: #FFFFFF;
+ color: #000000;
+ margin: 1em;
+ padding: 0.25em 0.5em;
+ cursor: pointer;
+ &:active {
+ background: #000000;
+ color: #FFFFFF;
+ }
+}
+
+.main {
+ max-width: 640px;
+ margin: 0 auto;
+ > * {
+ margin: 1rem;
+ }
}
diff --git a/packages/app/src/main.ts b/packages/app/src/main.ts
@@ -1,2 +1,12 @@
globalThis.m = require('mithril');
-m.mount(document.body, require('./component/app.tsx').default);
+
+import { AccountCreateScreen } from './component/screen-account-create.tsx';
+import { AccountSelectScreen } from './component/screen-account-select.tsx';
+
+
+m.route(document.body, "/", {
+ "/" : require('./component/screen-home.tsx' ).default,
+ "/app" : require('./component/app.tsx' ).default,
+ [AccountSelectScreen.routePath]: AccountSelectScreen,
+ [AccountCreateScreen.routePath]: AccountCreateScreen,
+});
diff --git a/packages/app/src/util/opfs.ts b/packages/app/src/util/opfs.ts
@@ -0,0 +1,16 @@
+export async function getDirectoryHandle(path: string) {
+ const tokens = path.split('/') as string[];
+ if (tokens[0] == '') tokens.shift();
+ let reference = await navigator.storage.getDirectory();
+ while(tokens.length) reference = await reference.getDirectoryHandle(tokens.shift() as string, { create: true })
+ return reference;
+}
+
+export async function getFileHandle(path: string, options?: FileSystemGetFileOptions) {
+ const tokens = path.split('/') as string[];
+ if (tokens[0] == '') tokens.shift();
+ const filename = tokens.pop() as string;
+ let reference = await navigator.storage.getDirectory();
+ while(tokens.length) reference = await reference.getDirectoryHandle(tokens.shift() as string, { create: true })
+ return await reference.getFileHandle(filename, options);
+}