Compare commits

..
10 Commits
Author SHA1 Message Date
vd 4369b7877d support vdpr 2026-10-09 02:02:38 +02:00
Marlon Erler 61503efcb9 Update README with local development information
Added note about local development and GitHub updates.
2026-10-04 21:53:39 +02:00
marlon-erler 8706330097 use ssh 2026-10-04 21:50:44 +02:00
marlon-erler ee0d0e2c36 public release 04OCT 2026-10-04 21:50:07 +02:00
Marlon Erler a5dcf40e15 Update README.md 2026-10-04 21:48:45 +02:00
marlon-erler 65f598f82b 1.0.2 2026-04-08 11:08:32 +00:00
marlon-erler 83176d3133 handle errors when sending pings 2026-04-08 11:08:13 +00:00
marlon-erler 1f6e5c613e show explicit ids and urls in stats 2026-04-02 21:25:39 +00:00
marlon-erler 57f79e16df 1.0.0 final 2026-04-02 19:26:39 +00:00
marlon-erler 3545606399 clean, add http ping to connected servers 2026-04-01 17:44:12 +00:00
12 changed files with 676 additions and 1648 deletions
+7 -61
View File
@@ -1,3 +1,10 @@
> [!NOTE]
> **LOCAL DEVELOPMENT**
>
> This project is developed on a locally run git service.
>
> The github repository is updated only in case of significant changes.
# universal-decentralized-network
The UDN project aims to provide decentralized and localhost-able infrastructure for messaging and communication. This infrastructure can be utilized in any scenario where data needs to be sent between devices, especially when no internet connection is present.
@@ -8,14 +15,9 @@ The UDN project aims to provide decentralized and localhost-able infrastructure
- [Channels](#channels)
- [Scalability](#scalability)
- [Example](#example)
- [User's guide](#users-guide)
- [Getting Connected](#getting-connected)
- [Using Apps](#using-apps)
- [Getting Apps](#getting-apps)
- [Administrator's Guide](#administrators-guide)
- [Getting Started](#getting-started)
- [Configuration](#configuration)
- [TLS](#tls)
- [Connecting Multiple Servers](#connecting-multiple-servers)
- [App Developer's Guide](#app-developers-guide)
- [Guidelines](#guidelines)
@@ -117,39 +119,6 @@ We could now add a third server, connect it to at least one of S1 and S2, config
As you can see, there are many ways to connect multiple servers and communicate across them, all with their own pros and cons.
# User's Guide
## Getting Connected
1. **Your administrator will give you a server address**. This address will likely look like `http://192.168.0.69` or `https://192.168.0.69`.
2. Make sure your device is **connected to the right Wi-Fi network**.
3. Access the server by **opening the address in a web browser**.
4. If the address starts with `https://` you may be warned that the connection is "insecure" or "not private". This is because your administrator created and signed the server's certificate locally, and because the certificate is not registered with an official certification authority. As long as you are connecting to the server through your local network, you can safely **ignore the risk and proceed to load the site**.
5. If you are using a smartphone, **add the site to your homescreen** (you can find this option in the share sheet or site menu), launch it, and **repeat step 3**. If you skip this, apps may refuse to connect to your server.
6. If you are on a computer, consider trusting the server's certificate on system level if you want to install apps as PWA. This process varies based on your operating system, please google how to do this on your system.
## Using Apps
1. Apps are likely hosted as websites on the internet or your local network. If this is not the case, follow instructions given by the developers or your administrator.
2. To use an app, **open it in your web browser**. Most apps will install offline support automatically so you can use them on local networks without an internet connection.
3. If you are using a smartphone, **add the app to your homescreen and launch it**.
4. If you are using a computer, you can **install the app as PWA** (this option may be called "Add to Dock" or "Create Shortcut"). Please google how to do this in your specific browser.
5. If the app requests your server address, note that:
- `http://` or `https://` (the secure variant) are protocols to request data and receive a single response
- To send messages, apps use so-called WebSockets instead. Their protocol is `ws://` or `wss://` (the secure variant)
- You will need to **replace `http` with `ws`**:
- `http://192.168.0.100:3000` -> `ws://192.168.0.100:3000`
- `https://192.168.0.200:3000` -> `wss://192.168.0.200:3000`
## Getting Apps
Anyone can develop apps for the UDN project. Examples of such apps are listed below. If you have developed an app for the UDN project and you want your app to be listed here, create an issue.
| App Name | Description | Links |
| -------- | ----------------- | ---------------------------------------------------------------------------------------------------- |
| Comms | Messenger for UDN | [more info](https://github.com/marlon-erler/udn-comms/) - [install](https://udn-comms.onrender.com/) |
---
# Administrator's Guide
@@ -192,18 +161,6 @@ The port and channels are self-explanatory. For servers, you'll need to provide
}
```
## TLS
TLS is a protocol that encrypts network traffic and ensures a secure context. Features like offline support and cryptography (needed i.e. for encryption) require this secure context.
Any website loaded through a secure context will refuse to connect to insecure WebSockets, including local ones like `ws://192.168.0.100:3000`. If you want your local server to work with these apps, your server must use TLS.
In local networks, you will likely need co create a self-signed certificate. This certificate is used to encrypt network traffic and identify your server.
1. Change into your server directory.
2. Create the certificate with `openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -sha256 -days 365`. This command will ask you a few questions and create the files required by your server.
3. Provide the passphrase via the `TLS_PASSPHRASE` environment variable (i.e. `export TLS_PASSPHRASE="my-securepass-phrase"`);
## Connecting Multiple Servers
1. Add the WebSocket addresses of servers you want to connect to the `connectedServers` array in the configuration file.
@@ -228,7 +185,6 @@ As explained in the _How it works_ section, you may want to use multiple servers
## Guidelines
- Connect to one server at a time
- When supporting networks with multiple servers, differentiate between primary and secondary channels according to the explanation above.
- Subscribe only to the primary channel, send on the primary and all secondary channels.
- Make sure to label these channels as primary and secondary (you may translate these along with the rest of your app) as users are instructed on these terms.
@@ -238,16 +194,6 @@ As explained in the _How it works_ section, you may want to use multiple servers
**Base**
For TypeScript-based projects, you can use the [udn-frontend](https://github.com/marlon-erler/udn-frontend) package to manage the connection. This project provides methods for connecting/disconnecting, subscribing/unsubscribing, and sending messages. If you want to/need to implement this yourself, follow the manual instructions.
**Interface**
The web interface of the server itself is based on my other two projects, [carbon-mini](https://github.com/marlon-erler/carbon-mini) (preconfigured foundation for static websites, including a complete stylesheet) and [bloatless-react](https://github.com/marlon-erler/bloatless-react) (a minimalist alternative to react).
Both of these projects are very minimal, bloat-free, and quick to get started with. I'd suggest you to build your app based on the same foundation for a consistent user experience, but you can use any framework you want.
1. Download [carbon-mini](https://github.com/marlon-erler/carbon-mini) from the releases page and read the docs
2. Install [bloatless-react](https://github.com/marlon-erler/bloatless-react) and read the docs
3. Install [udn-frontend](https://github.com/marlon-erler/udn-frontend) and read the docs
4. Generate your app icon [here](https://icongen.onrender.com/), move it to the `dist` directory
5. Start developing
## The Manual Way
UDN is based on WebSockets sending JSON strings.
+5
View File
@@ -0,0 +1,5 @@
{
"port": 3001,
"connectedServers": [],
"subscribedChannels": []
}
+12 -13
View File
@@ -13,8 +13,8 @@ import {
createFileResponse,
getVersionNumber,
writeError,
writeStatNumber,
writeStatString,
writeSeparator,
writeStat,
writeSuccess,
} from "./utility";
@@ -119,23 +119,22 @@ async function main() {
// CLI
function updateCLI() {
return;
console.clear();
console.log(Colors.bold.bgWhite("UNIVERSAL DECENTRALIZED NETWORK"));
console.log(Colors.bold.bgBlue("UNIVERSAL DECENTRALIZED NETWORK"));
console.log(
Colors.yellow("relevant output is available in the 'logs' file"),
);
console.log();
writeSeparator();
console.log(new Date().toLocaleString());
console.log();
console.log(new Date().toISOString());
writeSeparator();
getServerStats().forEach((entry) => writeStatString(...entry));
console.log();
getServerStats().forEach((entry) => writeStat(...entry));
writeSeparator();
getWebSocketStats().forEach((entry) => writeStatNumber(...entry));
console.log();
getWebSocketStats().forEach((entry) => writeStat(...entry));
writeSeparator();
}
// LOOP
@@ -146,10 +145,10 @@ async function main() {
}
removeExpiredMailboxes();
}, 2000);
}, 5000);
// FINISH
writeSuccess(`###\nstarted up ${new Date().toLocaleString()}`);
writeSuccess(`###\nstarted up ${new Date().toISOString()}`);
}
main();
+36
View File
@@ -0,0 +1,36 @@
###
started up 2026-08-25T23:45:32.799Z
###
started up 2026-08-26T19:11:49.405Z
###
started up 2026-08-26T19:18:44.954Z
###
started up 2026-09-04T22:06:15.779Z
###
started up 2026-09-05T00:07:05.962Z
###
started up 2026-09-05T00:07:47.330Z
###
started up 2026-09-05T09:15:35.190Z
###
started up 2026-09-05T09:23:22.301Z
###
started up 2026-09-22T18:59:27.227Z
###
started up 2026-09-22T19:02:46.210Z
###
started up 2026-09-25T23:18:59.694Z
###
started up 2026-09-25T23:20:20.977Z
###
started up 2026-09-25T23:29:07.632Z
###
started up 2026-09-26T16:46:24.849Z
###
started up 2026-09-27T13:45:31.815Z
###
started up 2026-10-01T06:53:25.050Z
###
started up 2026-10-01T06:53:56.456Z
###
started up 2026-10-01T07:50:51.435Z
-754
View File
@@ -1,754 +0,0 @@
{
"name": "udn",
"version": "1.0.0-beta13",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "udn",
"version": "1.0.0-beta13",
"license": "SEE LICENSE IN LICENSE",
"dependencies": {
"@types/bun": "^1.1.6",
"colors": "^1.4.0",
"express": "^4.19.2",
"ip": "^2.0.1"
},
"bin": {
"start": "index.ts"
}
},
"node_modules/@types/bun": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.6.tgz",
"integrity": "sha512-uJgKjTdX0GkWEHZzQzFsJkWp5+43ZS7HC8sZPFnOwnSo1AsNl2q9o2bFeS23disNDqbggEgyFkKCHl/w8iZsMA==",
"dependencies": {
"bun-types": "1.1.17"
}
},
"node_modules/@types/node": {
"version": "20.12.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz",
"integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/bun-types": {
"version": "1.1.17",
"resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.17.tgz",
"integrity": "sha512-Z4+OplcSd/YZq7ZsrfD00DKJeCwuNY96a1IDJyR73+cTBaFIS7SC6LhpY/W3AMEXO9iYq5NJ58WAwnwL1p5vKg==",
"dependencies": {
"@types/node": "~20.12.8",
"@types/ws": "~8.5.10"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/colors": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
"engines": {
"node": ">=0.1.90"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.19.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.2",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"get-intrinsic": "^1.1.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
"integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ=="
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-inspect": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
"integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/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=="
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"engines": {
"node": ">= 0.8"
}
}
}
}
+4 -1
View File
@@ -1,10 +1,13 @@
{
"name": "udn",
"version": "1.0.0-beta15",
"version": "1.0.2",
"description": "A websocket-based infrastructure for decentralized networks ",
"bin": {
"start": "index.ts"
},
"scripts": {
"build": "esbuild static/src/index.tsx --bundle --outdir=static/dist"
},
"repository": {
"type": "git",
"url": "git+https://github.com/marlon-erler/universal-decentralized-network.git"
+112 -344
View File
@@ -1,5 +1,5 @@
(() => {
// node_modules/bloatless-react/index.ts
// static/node_modules/bloatless-react/index.ts
var State = class {
_value;
_bindings = /* @__PURE__ */ new Set();
@@ -73,8 +73,8 @@
};
function createProxyState(statesToSubscibe, fn) {
const proxyState = new State(fn());
statesToSubscibe.forEach((state) =>
state.subscribe(() => (proxyState.value = fn())),
statesToSubscibe.forEach(
(state) => state.subscribe(() => proxyState.value = fn())
);
return proxyState;
}
@@ -85,9 +85,7 @@
});
}
function restoreState(localStorageKey, initialStateValue) {
const storedString =
localStorage.getItem(localStorageKey) ??
JSON.stringify(initialStateValue);
const storedString = localStorage.getItem(localStorageKey) ?? JSON.stringify(initialStateValue);
const convertedValue = JSON.parse(storedString);
const state = new State(convertedValue);
persistState(localStorageKey, state);
@@ -122,8 +120,9 @@
const [listState, toElement] = value;
listState.handleAddition((newItem) => {
const child = toElement(newItem);
listState.handleRemoval(newItem, () =>
child.remove(),
listState.handleRemoval(
newItem,
() => child.remove()
);
element.append(child);
element.scrollTop = element.scrollHeight;
@@ -134,8 +133,7 @@
} else {
const state = value;
state.subscribe(
(newValue) =>
(element[directiveValue] = newValue),
(newValue) => element[directiveValue] = newValue
);
}
break;
@@ -143,22 +141,19 @@
case "bind": {
const state = value;
state.subscribe(
(newValue) => (element[directiveValue] = newValue),
(newValue) => element[directiveValue] = newValue
);
element.addEventListener(
"input",
() => (state.value = element[directiveValue]),
() => state.value = element[directiveValue]
);
break;
}
case "toggle": {
if (value.subscribe) {
const state = value;
state.subscribe((newValue) =>
element.toggleAttribute(
directiveValue,
newValue,
),
state.subscribe(
(newValue) => element.toggleAttribute(directiveValue, newValue)
);
} else {
element.toggleAttribute(directiveValue, value);
@@ -167,8 +162,8 @@
}
case "set": {
const state = value;
state.subscribe((newValue) =>
element.setAttribute(directiveValue, newValue),
state.subscribe(
(newValue) => element.setAttribute(directiveValue, newValue)
);
break;
}
@@ -180,16 +175,22 @@
return element;
}
// node_modules/udn-frontend/index.ts
// static/node_modules/udn-frontend/index.ts
var UDNFrontend = class {
ws;
// HANDLERS
connectionHandler = () => {};
disconnectionHandler = () => {};
messageHandler = (data) => {};
mailboxHandler = (mailboxId2) => {};
mailboxConnectionHandler = (mailboxId2) => {};
mailboxDeleteHandler = (mailboxId2) => {};
connectionHandler = () => {
};
disconnectionHandler = () => {
};
messageHandler = (data) => {
};
mailboxHandler = (mailboxId2) => {
};
mailboxConnectionHandler = (mailboxId2) => {
};
mailboxDeleteHandler = (mailboxId2) => {
};
// INIT
set onconnect(handler) {
this.connectionHandler = handler;
@@ -230,9 +231,7 @@
if (data.assignedMailboxId) {
return this.mailboxHandler(data.assignedMailboxId);
} else if (data.connectedMailboxId) {
return this.mailboxConnectionHandler(
data.connectedMailboxId,
);
return this.mailboxConnectionHandler(data.connectedMailboxId);
} else if (data.deletedMailbox) {
return this.mailboxDeleteHandler(data.deletedMailbox);
} else {
@@ -250,45 +249,45 @@
sendMessage(channel, body) {
const messageObject = {
messageChannel: channel,
messageBody: body,
messageBody: body
};
return this.send(messageObject);
}
// subscription
subscribe(channel) {
const messageObject = {
subscribeChannel: channel,
subscribeChannel: channel
};
return this.send(messageObject);
}
unsubscribe(channel) {
const messageObject = {
unsubscribeChannel: channel,
unsubscribeChannel: channel
};
return this.send(messageObject);
}
// mailbox
requestMailbox() {
const messageObject = {
requestingMailboxSetup: true,
requestingMailboxSetup: true
};
return this.send(messageObject);
}
connectMailbox(mailboxId2) {
const messageObject = {
requestedMailbox: mailboxId2,
requestedMailbox: mailboxId2
};
return this.send(messageObject);
}
deleteMailbox(mailboxId2) {
const messageObject = {
deletingMailbox: mailboxId2,
deletingMailbox: mailboxId2
};
return this.send(messageObject);
}
};
// src/translations.tsx
// static/src/translations.tsx
var staticTextEnglish = {
mailbox: "Mailbox",
requestMailbox: "Request",
@@ -311,7 +310,7 @@
settings: "Settings",
subscribeToChannel: "Subscribe to channel",
subscribe: "Subscribe",
unsubscribe: "Unubscribe",
unsubscribe: "Unubscribe"
};
var allTranslations = {
en: staticTextEnglish,
@@ -337,7 +336,7 @@
settings: "Configuraci\xF3n",
subscribeToChannel: "Suscribirse a un canal",
subscribe: "Suscribirse",
unsubscribe: "Cancelar",
unsubscribe: "Cancelar"
},
de: {
mailbox: "Briefkasten",
@@ -361,13 +360,13 @@
settings: "Einstellungen",
subscribeToChannel: "Kanal beitreten",
subscribe: "Beitreten",
unsubscribe: "Verlassen",
},
unsubscribe: "Verlassen"
}
};
var language = navigator.language.substring(0, 2);
var translations = allTranslations[language];
// src/model.tsx
// static/src/model.tsx
var Message = class {
constructor(channel, body) {
this.channel = channel;
@@ -378,25 +377,18 @@
var mailboxId = restoreState("mailbox-id", "");
var isMailboxDisonnected = new State(true);
var messages = new ListState();
var lastReceivedMessage = new State(translations.noMessagesReceived);
var lastReceivedMessage = new State(
translations.noMessagesReceived
);
var statHTMLString = new State("");
async function updateStats() {
const response = await fetch(
`${window.location.protocol}//${window.location.host}/stats`,
`${window.location.protocol}//${window.location.host}/stats`
);
const data = await response.json();
let html = "";
data.forEach((item) => {
const tile = /* @__PURE__ */ createElement(
"div",
{ class: "tile" },
/* @__PURE__ */ createElement(
"span",
{ class: "secondary flex width-100" },
item[0].toString(),
),
/* @__PURE__ */ createElement("b", null, item[1].toString()),
);
const tile = /* @__PURE__ */ createElement("div", { class: "tile" }, /* @__PURE__ */ createElement("div", { class: "flex-column align-start" }, /* @__PURE__ */ createElement("span", { class: "secondary flex width-100" }, item[0].toString()), ...item[1].toString().split("\n").map((string) => /* @__PURE__ */ createElement("b", null, string))));
html += tile.outerHTML;
});
statHTMLString.value = html;
@@ -406,7 +398,7 @@
var newMessageBody = new State("");
var isMessageEmpty = createProxyState(
[newMessageChannel, newMessageBody],
() => newMessageChannel.value == "" || newMessageBody.value == "",
() => newMessageChannel.value == "" || newMessageBody.value == ""
);
var UDN = new UDNFrontend();
UDN.onconnect = () => {
@@ -442,7 +434,7 @@
};
function connect() {
UDN.connect(
`${window.location.protocol.replace("http", "ws")}//${window.location.host}`,
`${window.location.protocol.replace("http", "ws")}//${window.location.host}`
);
}
connect();
@@ -461,354 +453,130 @@
UDN.requestMailbox();
}
function deleteMailbox() {
console.log("test");
UDN.deleteMailbox(mailboxId.value);
}
// src/infoScreen.tsx
// static/src/infoScreen.tsx
function InfoScreen() {
return /* @__PURE__ */ createElement(
"article",
{ id: "info-screen" },
/* @__PURE__ */ createElement(
"header",
null,
translations.serverInfo,
/* @__PURE__ */ createElement(
"span",
null,
/* @__PURE__ */ createElement(
"button",
{ "on:click": updateStats },
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"refresh",
),
),
),
),
/* @__PURE__ */ createElement(
return /* @__PURE__ */ createElement("article", { id: "info-screen" }, /* @__PURE__ */ createElement("header", null, translations.serverInfo, /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement("button", { "on:click": updateStats }, /* @__PURE__ */ createElement("span", { class: "icon" }, "refresh")))), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement("div", {
{
class: "flex-column gap",
"subscribe:innerHTML": statHTMLString,
}),
),
);
"subscribe:innerHTML": statHTMLString
}
)));
}
// src/mainScreen.tsx
// static/src/mainScreen.tsx
function MainScreen() {
return /* @__PURE__ */ createElement(
"article",
{ id: "main-screen" },
/* @__PURE__ */ createElement(
"header",
null,
translations.settings,
),
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"div",
{ class: "tile width-input" },
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"b",
null,
translations.messageLastReceived,
),
/* @__PURE__ */ createElement("p", {
class: "secondary",
"subscribe:innerText": lastReceivedMessage,
}),
),
),
/* @__PURE__ */ createElement("hr", null),
/* @__PURE__ */ createElement(
"div",
{ class: "tile width-input" },
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"b",
null,
translations.mailbox,
),
/* @__PURE__ */ createElement(
return /* @__PURE__ */ createElement("article", { id: "main-screen" }, /* @__PURE__ */ createElement("header", null, translations.settings), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("div", { class: "tile width-input" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.messageLastReceived), /* @__PURE__ */ createElement(
"p",
{
class: "secondary",
"toggle:hidden": isMailboxDisonnected,
"subscribe:innerText": lastReceivedMessage
}
))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "tile width-input" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("b", null, translations.mailbox), /* @__PURE__ */ createElement(
"p",
{
class: "secondary",
"toggle:hidden": isMailboxDisonnected
},
translations.mailboxConnected,
),
),
),
/* @__PURE__ */ createElement(
"div",
{ class: "flex-row width-input justify-end" },
/* @__PURE__ */ createElement(
translations.mailboxConnected
))), /* @__PURE__ */ createElement("div", { class: "flex-row width-input justify-end" }, /* @__PURE__ */ createElement(
"button",
{
class: "danger width-50",
"on:click": deleteMailbox,
"toggle:disabled": isMailboxDisonnected,
"toggle:disabled": isMailboxDisonnected
},
translations.deleteMailbox,
),
/* @__PURE__ */ createElement(
translations.deleteMailbox
), /* @__PURE__ */ createElement(
"button",
{
class: "primary width-50",
"on:click": requestMailbox,
"toggle:disabled": isDisconnected,
"toggle:disabled": isDisconnected
},
translations.requestMailbox,
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"inbox",
),
),
),
/* @__PURE__ */ createElement("hr", null),
/* @__PURE__ */ createElement(
"label",
{ class: "tile" },
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"span",
null,
translations.subscribeToChannel,
),
/* @__PURE__ */ createElement("input", {
/* @__PURE__ */ createElement("span", { class: "icon" }, "inbox")
)), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.subscribeToChannel), /* @__PURE__ */ createElement(
"input",
{
placeholder: translations.channel_placeholder,
"bind:value": subscriptionChannel,
"on:enter": subscribe,
}),
),
),
/* @__PURE__ */ createElement(
"div",
{ class: "flex-row width-input justify-end" },
/* @__PURE__ */ createElement(
"button",
{ class: "danger width-50", "on:click": unsubscribe },
translations.unsubscribe,
),
/* @__PURE__ */ createElement(
"button",
{ class: "primary width-50", "on:click": subscribe },
translations.subscribe,
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"arrow_forward",
),
),
),
/* @__PURE__ */ createElement("hr", null),
/* @__PURE__ */ createElement(
"label",
{ class: "tile" },
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"span",
null,
translations.channel,
),
/* @__PURE__ */ createElement("input", {
"on:enter": subscribe
}
))), /* @__PURE__ */ createElement("div", { class: "flex-row width-input justify-end" }, /* @__PURE__ */ createElement("button", { class: "danger width-50", "on:click": unsubscribe }, translations.unsubscribe), /* @__PURE__ */ createElement("button", { class: "primary width-50", "on:click": subscribe }, translations.subscribe, /* @__PURE__ */ createElement("span", { class: "icon" }, "arrow_forward"))), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.channel), /* @__PURE__ */ createElement(
"input",
{
placeholder: translations.channel_placeholder,
"bind:value": newMessageChannel,
"on:enter": sendMessage,
}),
),
),
/* @__PURE__ */ createElement(
"label",
{ class: "tile" },
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"span",
null,
translations.message,
),
/* @__PURE__ */ createElement("input", {
"on:enter": sendMessage
}
))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", null, translations.message), /* @__PURE__ */ createElement(
"input",
{
placeholder: translations.message_placeholder,
"bind:value": newMessageBody,
"on:enter": sendMessage,
}),
),
),
/* @__PURE__ */ createElement(
"div",
{ class: "flex-row width-input justify-end" },
/* @__PURE__ */ createElement(
"on:enter": sendMessage
}
))), /* @__PURE__ */ createElement("div", { class: "flex-row width-input justify-end" }, /* @__PURE__ */ createElement(
"button",
{
class: "primary width-50",
"on:click": sendMessage,
"toggle:disabled": isMessageEmpty,
"toggle:disabled": isMessageEmpty
},
translations.send,
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"send",
),
),
),
),
);
/* @__PURE__ */ createElement("span", { class: "icon" }, "send")
))));
}
// src/messageScreen.tsx
// static/src/messageScreen.tsx
var convertMessageToElement = (message) => {
return /* @__PURE__ */ createElement(
"div",
{ class: "tile", style: "flex: 0" },
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"span",
{ class: "secondary" },
message.channel,
),
/* @__PURE__ */ createElement("b", null, message.body),
),
);
return /* @__PURE__ */ createElement("div", { class: "tile", style: "flex: 0" }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, message.channel), /* @__PURE__ */ createElement("b", null, message.body)));
};
function MessageScreen() {
return /* @__PURE__ */ createElement(
"article",
{ id: "message-screen" },
/* @__PURE__ */ createElement(
"header",
null,
translations.messagesReceived,
),
/* @__PURE__ */ createElement(
return /* @__PURE__ */ createElement("article", { id: "message-screen" }, /* @__PURE__ */ createElement("header", null, translations.messagesReceived), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement("div", {
{
class: "flex-column gap",
"subscribe:children": [messages, convertMessageToElement],
}),
),
/* @__PURE__ */ createElement(
"footer",
null,
/* @__PURE__ */ createElement("input", {
"subscribe:children": [messages, convertMessageToElement]
}
)), /* @__PURE__ */ createElement("footer", null, /* @__PURE__ */ createElement(
"input",
{
style: "max-width: unset",
placeholder: translations.message_placeholder_generic,
"bind:value": newMessageBody,
"on:enter": sendMessage,
}),
/* @__PURE__ */ createElement(
"on:enter": sendMessage
}
), /* @__PURE__ */ createElement(
"button",
{
class: "primary",
"on:click": sendMessage,
"toggle:disabled": isMessageEmpty,
"toggle:disabled": isMessageEmpty
},
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"send",
),
),
),
);
/* @__PURE__ */ createElement("span", { class: "icon" }, "send")
)));
}
// src/index.tsx
// static/src/index.tsx
document.body.prepend(
/* @__PURE__ */ createElement(
"menu",
null,
/* @__PURE__ */ createElement(
"a",
{ class: "tab-link", href: "#info-screen", active: true },
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"info",
),
translations.info,
),
/* @__PURE__ */ createElement(
"a",
{ class: "tab-link", href: "#main-screen" },
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"settings",
),
translations.settings,
),
/* @__PURE__ */ createElement(
"a",
{ class: "tab-link", href: "#message-screen" },
/* @__PURE__ */ createElement(
"span",
{ class: "icon" },
"chat",
),
translations.messages,
),
),
/* @__PURE__ */ createElement("menu", null, /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#info-screen", active: true }, /* @__PURE__ */ createElement("span", { class: "icon" }, "info"), translations.info), /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#main-screen" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "settings"), translations.settings), /* @__PURE__ */ createElement("a", { class: "tab-link", href: "#message-screen" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "chat"), translations.messages))
);
document
.querySelector("main")
.append(InfoScreen(), MainScreen(), MessageScreen());
document.querySelector("main").append(InfoScreen(), MainScreen(), MessageScreen());
document.body.append(
/* @__PURE__ */ createElement(
"div",
{ class: "modal", "toggle:open": isDisconnected },
/* @__PURE__ */ createElement(
"div",
null,
/* @__PURE__ */ createElement(
"main",
null,
/* @__PURE__ */ createElement(
/* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isDisconnected }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement(
"div",
{
class: "flex-column align-center justify-center width-100 height-100",
style: "gap: 1rem",
style: "gap: 1rem"
},
/* @__PURE__ */ createElement(
"span",
{ class: "icon error", style: "font-size: 3rem" },
"signal_disconnected",
),
/* @__PURE__ */ createElement(
"h1",
{ class: "error" },
translations.disconnected,
),
/* @__PURE__ */ createElement(
"p",
{ class: "secondary" },
translations.reconnecting,
),
),
),
),
),
/* @__PURE__ */ createElement("span", { class: "icon error", style: "font-size: 3rem" }, "signal_disconnected"),
/* @__PURE__ */ createElement("h1", { class: "error" }, translations.disconnected),
/* @__PURE__ */ createElement("p", { class: "secondary" }, translations.reconnecting)
))))
);
})();
+6 -1
View File
@@ -36,10 +36,15 @@ export async function updateStats() {
data.forEach((item) => {
const tile = (
<div class="tile">
<div class="flex-column align-start">
<span class="secondary flex width-100">
{item[0].toString()}
</span>
<b>{item[1].toString()}</b>
{...item[1]
.toString()
.split("\n")
.map((string) => <b>{string}</b>)}
</div>
</div>
);
html += tile.outerHTML;
-2
View File
@@ -93,8 +93,6 @@ export class Mailbox implements Subscriber {
send(message: string, _?: boolean, uuid?: string): void {
this._ws?.send(message);
console.log(this.id, uuid, message, "sending");
if (!uuid) return;
this.messages.set(uuid, message);
}
Executable
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
url=""
case $1 in
vd)
url=https://viridian.vd/VDMDI/UDN
;;
pr)
url=https://vdpr.org/VDMDI/UDN
;;
esac
git remote set-url origin $url
git remote get-url origin
+4 -6
View File
@@ -36,16 +36,14 @@ async function writeToLogFile(message: string): Promise<void> {
}
// stats
export function writeStatString(label: string, value: string): void {
export function writeStat(label: string, value: string | number): void {
console.log(
`${label.padEnd(STAT_INDENT, " ")} ${Colors.bold.green(value)}`,
`${Colors.bold.green(label.padEnd(STAT_INDENT, " "))}\n${value.toString()}\n`
);
}
export function writeStatNumber(label: string, value: number): void {
console.log(
`${label.padEnd(STAT_INDENT, " ")} ${Colors.bold.blue(value.toString())}`,
);
export function writeSeparator(): void {
console.log(Colors.gray("-----") + "\n")
}
// logging
+17 -8
View File
@@ -51,10 +51,6 @@ export const serversConnectedAsClient = new Set<WS>();
export const serversConnected = new Set<WebSocket>();
export const serversDisconnected = new Set<string>();
export function getServerCount() {
return serversConnected.size + serversConnectedAsClient.size;
}
export function trackConnection(ws: WS) {
clientsConnected.add(ws);
}
@@ -82,13 +78,18 @@ export function forgetSubscription(
}
// AUDIT
export function getWebSocketStats(): [string, number][] {
export function getWebSocketStats(): [string, string | number][] {
function listString<T>(list: Set<T> | Map<string, T>, map: (x: T) => string): string {
if (list.size == 0) return "<none>";
return [...list.values()].map(map).join("\n");
}
return [
["channels", subscriptionMap.subscribersPerChannel.size],
["clients connected", clientsConnected.size],
["mailboxes", mailboxes.size],
["servers connected", getServerCount()],
["servers disconnected", serversDisconnected.size],
["mailboxes", listString(mailboxes, x => x.id)],
["servers connected", listString(serversConnected, x => x.url)],
["servers disconnected", listString(serversDisconnected, x => x)],
];
}
@@ -100,6 +101,14 @@ export function connectServers(config: typeof defaultConfig): void {
}
export function reconnectServers(config: typeof defaultConfig): void {
config.connectedServers
.map(address => address.replace("ws", "http"))
.forEach(async address => {
try {
await fetch(address);
} catch (error) { writeError(`failed to ping server at "${address}": ${error}`) }
});
[...serversDisconnected.values()].forEach((address) => {
connectServer(address, config);
});