wireweave 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,71 +0,0 @@
1
- # legacy/
2
-
3
- browser-globals port of the full zellous networking + voice stack. these files are the original scripts copied verbatim from `zellous/docs/js/`. they assign to `window.*` (`window.nostrNet`, `window.nostrVoice`, `window.auth`, etc.) and expect co-resident globals (`state`, `XState`, `NostrTools`, `ui`, `auth`, `message`).
4
-
5
- this is the honest "move everything" checkpoint. the SDK-shaped ES-module exports in `../` are the forward path — `relay-pool.js` + `auth.js` today, voice / chat / channels / servers to follow.
6
-
7
- ## load order
8
-
9
- scripts must load in this order (same order zellous uses):
10
-
11
- ```
12
- nostr-state-patch.js
13
- nostr-fsm.js
14
- nostr-network.js
15
- nostr-auth.js
16
- nostr-message.js
17
- nostr-bans.js
18
- nostr-roles.js
19
- nostr-settings.js
20
- nostr-voice.js
21
- nostr-voice-rtc.js
22
- nostr-voice-sfu.js
23
- nostr-voice-camera.js
24
- nostr-chat.js
25
- nostr-channels.js
26
- nostr-servers.js
27
- nostr-media.js
28
- nostr-pages.js
29
- ```
30
-
31
- ## prerequisites already on window
32
-
33
- - `window.state`, `window.stateSignals`, `window.config` (zellous/state.js or equivalent)
34
- - `window.XState` — `{ createMachine, createActor }` from xstate v5
35
- - `window.NostrTools` — `nostr-tools` (generateSecretKey, getPublicKey, nip19, finalizeEvent, verifyEvent)
36
- - preact signals for reactive state
37
- - `window.ui` bindings (optional for headless)
38
-
39
- ## files
40
-
41
- | file | bytes | assigns |
42
- |---|---|---|
43
- | nostr-state-patch.js | 507 | state signal patches |
44
- | nostr-fsm.js | 1018 | `voiceMachine`, `peerMachine` |
45
- | nostr-network.js | 7595 | `nostrNet` relay pool |
46
- | nostr-auth.js | 8501 | `auth` key mgmt |
47
- | nostr-message.js | 529 | `message` system msg dispatch |
48
- | nostr-bans.js | 2422 | `nostrBans` moderation |
49
- | nostr-roles.js | 3077 | `serverRoles` |
50
- | nostr-settings.js | 4231 | `serverSettings` encoder config |
51
- | nostr-voice.js | 13458 | `nostrVoice` session |
52
- | nostr-voice-rtc.js | 11636 | `nostrVoiceRtc` perfect-negotiation |
53
- | nostr-voice-sfu.js | 4773 | `nostrVoiceSfu` hub election |
54
- | nostr-voice-camera.js | 2355 | `nostrVoiceCamera` |
55
- | nostr-chat.js | 6513 | `chat` kind-28 messaging |
56
- | nostr-channels.js | 5369 | `channelManager` |
57
- | nostr-servers.js | 10718 | `serverManager` communities |
58
- | nostr-media.js | 3524 | `nostrMedia` blossom uploads |
59
- | nostr-pages.js | 7909 | `serverPages` |
60
-
61
- ## CDN load (via jsDelivr)
62
-
63
- ```html
64
- <script src="https://cdn.jsdelivr.net/npm/wireweave@latest/src/legacy/nostr-fsm.js"></script>
65
- <script src="https://cdn.jsdelivr.net/npm/wireweave@latest/src/legacy/nostr-network.js"></script>
66
- <!-- etc in order above -->
67
- ```
68
-
69
- ## status
70
-
71
- probably emerging 🌀
@@ -1,49 +0,0 @@
1
- export const LEGACY_LOAD_ORDER = [
2
- 'nostr-state-patch.js',
3
- 'nostr-fsm.js',
4
- 'nostr-network.js',
5
- 'nostr-auth.js',
6
- 'nostr-message.js',
7
- 'nostr-bans.js',
8
- 'nostr-roles.js',
9
- 'nostr-settings.js',
10
- 'nostr-voice.js',
11
- 'nostr-voice-rtc.js',
12
- 'nostr-voice-sfu.js',
13
- 'nostr-voice-camera.js',
14
- 'nostr-chat.js',
15
- 'nostr-channels.js',
16
- 'nostr-servers.js',
17
- 'nostr-media.js',
18
- 'nostr-pages.js'
19
- ];
20
-
21
- export const loadLegacy = async ({ base = 'https://cdn.jsdelivr.net/npm/wireweave@latest/src/legacy/', prereqsReady = () => true } = {}) => {
22
- if (!prereqsReady()) throw new Error('wireweave/legacy: prereqs not ready (state, XState, NostrTools must be on window)');
23
- for (const f of LEGACY_LOAD_ORDER) {
24
- await new Promise((resolve, reject) => {
25
- const s = document.createElement('script');
26
- s.src = base + f;
27
- s.onload = resolve;
28
- s.onerror = () => reject(new Error('failed: ' + f));
29
- document.head.appendChild(s);
30
- });
31
- }
32
- return {
33
- net: window.nostrNet,
34
- auth: window.auth,
35
- voice: window.nostrVoice,
36
- chat: window.chat,
37
- channels: window.channelManager,
38
- servers: window.serverManager,
39
- message: window.message,
40
- bans: window.nostrBans,
41
- roles: window.serverRoles,
42
- settings: window.serverSettings,
43
- pages: window.serverPages,
44
- media: window.nostrMedia,
45
- voiceRtc: window.nostrVoiceRtc,
46
- voiceSfu: window.nostrVoiceSfu,
47
- voiceCamera: window.nostrVoiceCamera
48
- };
49
- };
@@ -1,217 +0,0 @@
1
- const auth = {
2
- get user() {
3
- const pk = state.nostrPubkey;
4
- if (!pk) return null;
5
- const short = auth.npubShort(pk);
6
- return { id: pk, username: short, displayName: state.nostrProfile?.name || short };
7
- },
8
-
9
- init() {
10
- const skHex = localStorage.getItem('zn_sk');
11
- const pkHex = localStorage.getItem('zn_pk');
12
- if (!skHex || !pkHex) return false;
13
- try {
14
- state.nostrPrivkey = auth._hex2b(skHex);
15
- state.nostrPubkey = pkHex;
16
- const short = auth.npubShort(pkHex);
17
- const nameEl = document.getElementById('userPanelName');
18
- const tagEl = document.getElementById('userPanelTag');
19
- const avatarEl = document.getElementById('userPanelAvatar');
20
- if (nameEl) nameEl.textContent = short;
21
- if (tagEl) tagEl.textContent = short;
22
- if (avatarEl) { const n = avatarEl.childNodes[0]; if (n?.nodeType === 3) n.textContent = short[0].toUpperCase(); }
23
- document.getElementById('userStatusDot')?.classList.add('online');
24
- return true;
25
- } catch { return false; }
26
- },
27
-
28
- generateKey() {
29
- const NT = window.NostrTools;
30
- const sk = NT.generateSecretKey();
31
- const pk = NT.getPublicKey(sk);
32
- localStorage.setItem('zn_sk', auth._b2hex(sk));
33
- localStorage.setItem('zn_pk', pk);
34
- state.nostrPrivkey = sk;
35
- state.nostrPubkey = pk;
36
- return { pubkey: pk, privkey: sk };
37
- },
38
-
39
- importKey(input) {
40
- try {
41
- const NT = window.NostrTools;
42
- const sk = input.startsWith('nsec')
43
- ? (() => { const d = NT.nip19.decode(input); if (d.type !== 'nsec') throw 0; return d.data; })()
44
- : auth._hex2b(input);
45
- const pk = NT.getPublicKey(sk);
46
- localStorage.setItem('zn_sk', auth._b2hex(sk));
47
- localStorage.setItem('zn_pk', pk);
48
- state.nostrPrivkey = sk;
49
- state.nostrPubkey = pk;
50
- return true;
51
- } catch { return false; }
52
- },
53
-
54
- async loginWithExtension() {
55
- const pk = await window.nostr.getPublicKey();
56
- state.nostrPubkey = pk;
57
- state.nostrPrivkey = null;
58
- return pk;
59
- },
60
-
61
- async sign(eventTemplate) {
62
- if (state.nostrPrivkey) return window.NostrTools.finalizeEvent(eventTemplate, state.nostrPrivkey);
63
- return window.nostr.signEvent(eventTemplate);
64
- },
65
-
66
- async setDisplayName(name) {
67
- if (!state.nostrPubkey) throw new Error('Not logged in');
68
- if (!name || typeof name !== 'string') throw new Error('Invalid display name');
69
- const template = {
70
- kind: 0,
71
- created_at: Math.floor(Date.now() / 1000),
72
- tags: [],
73
- content: JSON.stringify({ name: name.trim(), ...(state.nostrProfile || {}) })
74
- };
75
- const signed = await auth.sign(template);
76
- if (window.nostrNet && window.nostrNet.publish) {
77
- await window.nostrNet.publish(signed);
78
- }
79
- state.nostrProfile = { ...(state.nostrProfile || {}), name: name.trim() };
80
- const nameEl = document.getElementById('userPanelName');
81
- const avatarEl = document.getElementById('userPanelAvatar');
82
- if (nameEl) nameEl.textContent = state.nostrProfile.name;
83
- if (avatarEl) { const n = avatarEl.childNodes[0]; if (n?.nodeType === 3) n.textContent = state.nostrProfile.name[0].toUpperCase(); }
84
- if (window.chat) chat.updateProfile(state.nostrPubkey, state.nostrProfile);
85
- },
86
-
87
- logout() {
88
- localStorage.removeItem('zn_sk');
89
- localStorage.removeItem('zn_pk');
90
- state.nostrPubkey = '';
91
- state.nostrPrivkey = null;
92
- state.nostrProfile = null;
93
- const nameEl = document.getElementById('userPanelName');
94
- const tagEl = document.getElementById('userPanelTag');
95
- const avatarEl = document.getElementById('userPanelAvatar');
96
- if (nameEl) nameEl.textContent = 'Not logged in';
97
- if (tagEl) tagEl.textContent = 'Click to login';
98
- if (avatarEl) { const n = avatarEl.childNodes[0]; if (n?.nodeType === 3) n.textContent = '?'; }
99
- document.getElementById('userStatusDot')?.classList.remove('online');
100
- if (window.nostrNet?.disconnect) nostrNet.disconnect();
101
- },
102
-
103
- getToken() { return state.nostrPubkey || null; },
104
- isLoggedIn() { return !!state.nostrPubkey; },
105
-
106
- npubShort(pubkey) {
107
- if (!pubkey) return '';
108
- const npub = window.NostrTools.nip19.npubEncode(pubkey);
109
- return npub.slice(0, 8) + '...' + npub.slice(-4);
110
- },
111
-
112
- showModal() {
113
- const modal = document.getElementById('authModal');
114
- if (!modal) return;
115
- modal.style.display = 'flex';
116
- const cv = document.getElementById('nostrConnectView');
117
- const lv = document.getElementById('nostrLoggedInView');
118
- const loggedIn = auth.isLoggedIn();
119
- if (cv) cv.style.display = loggedIn ? 'none' : 'flex';
120
- if (lv) lv.style.display = loggedIn ? 'flex' : 'none';
121
- if (loggedIn) {
122
- const d = document.getElementById('nostrNpubDisplay');
123
- if (d) d.textContent = auth.npubShort(state.nostrPubkey);
124
- const inp = document.getElementById('displayNameInput');
125
- if (inp) inp.value = state.nostrProfile?.name || '';
126
- }
127
- },
128
-
129
- hideModal() {
130
- const modal = document.getElementById('authModal');
131
- if (modal) modal.style.display = 'none';
132
- },
133
-
134
- _afterLogin() {
135
- const d = document.getElementById('nostrNpubDisplay');
136
- if (d) d.textContent = auth.npubShort(state.nostrPubkey);
137
- const cv = document.getElementById('nostrConnectView');
138
- const lv = document.getElementById('nostrLoggedInView');
139
- if (cv) cv.style.display = 'none';
140
- if (lv) lv.style.display = 'flex';
141
- const err = document.getElementById('nostrAuthError');
142
- if (err) err.textContent = '';
143
- const short = auth.npubShort(state.nostrPubkey);
144
- const nameEl = document.getElementById('userPanelName');
145
- const tagEl = document.getElementById('userPanelTag');
146
- const avatarEl = document.getElementById('userPanelAvatar');
147
- if (nameEl) nameEl.textContent = state.nostrProfile?.name || short;
148
- if (tagEl) tagEl.textContent = short;
149
- if (avatarEl) { const n = avatarEl.childNodes[0]; if (n?.nodeType === 3) n.textContent = (state.nostrProfile?.name || short)[0].toUpperCase(); }
150
- document.getElementById('userStatusDot')?.classList.add('online');
151
- document.dispatchEvent(new CustomEvent('nostr:login'));
152
- setTimeout(() => auth.hideModal(), 1000);
153
- },
154
-
155
- _err(msg) {
156
- const el = document.getElementById('nostrAuthError');
157
- if (el) el.textContent = msg;
158
- },
159
-
160
- bindUI() {
161
- const $ = id => document.getElementById(id);
162
- const on = (id, fn) => { const el = $(id); if (el) el.addEventListener('click', fn); };
163
-
164
- on('connectExtensionBtn', async () => {
165
- try {
166
- if (!window.nostr) throw new Error('No Nostr extension found');
167
- await auth.loginWithExtension();
168
- auth._afterLogin();
169
- } catch (e) { auth._err(e.message); }
170
- });
171
-
172
- on('generateKeyBtn', () => {
173
- try { auth.generateKey(); auth._afterLogin(); }
174
- catch (e) { auth._err(e.message); }
175
- });
176
-
177
- on('importKeyBtn', () => {
178
- const inp = $('importKeyInput');
179
- const val = inp ? inp.value.trim() : '';
180
- if (!val) { auth._err('Enter a key'); return; }
181
- auth.importKey(val) ? auth._afterLogin() : auth._err('Invalid key');
182
- });
183
-
184
- on('copyNpubBtn', () => {
185
- const pk = state.nostrPubkey;
186
- if (pk) navigator.clipboard.writeText(window.NostrTools.nip19.npubEncode(pk)).catch(() => {});
187
- });
188
-
189
- on('saveDisplayNameBtn', async () => {
190
- const inp = $('displayNameInput');
191
- const val = inp ? inp.value.trim() : '';
192
- if (!val) { auth._err('Enter a display name'); return; }
193
- try {
194
- await auth.setDisplayName(val);
195
- if (inp) inp.value = val;
196
- } catch (e) { auth._err(e.message); }
197
- });
198
-
199
- on('nostrLogoutBtn', () => { auth.logout(); auth.showModal(); });
200
-
201
- const modal = document.getElementById('authModal');
202
- if (modal) modal.addEventListener('click', (e) => { if (e.target === modal) auth.hideModal(); });
203
-
204
- const avatarArea = document.querySelector('.user-avatar, .username-area, [data-action="show-auth"]');
205
- if (avatarArea) avatarArea.addEventListener('click', () => auth.showModal());
206
- },
207
-
208
- _b2hex(bytes) { return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); },
209
- _hex2b(hex) {
210
- const a = new Uint8Array(hex.length / 2);
211
- for (let i = 0; i < a.length; i++) a[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
212
- return a;
213
- }
214
- };
215
-
216
- window.__zellous.auth = auth;
217
- window.auth = auth;
@@ -1,59 +0,0 @@
1
- var nostrBans = {
2
- _store: new Map(),
3
- _sub: null,
4
-
5
- isBanned(serverId, pubkey) {
6
- var bans = nostrBans._store.get(serverId) || {};
7
- return !!(bans.banned || []).includes(pubkey);
8
- },
9
-
10
- isTimedOut(serverId, pubkey) {
11
- var bans = nostrBans._store.get(serverId) || {};
12
- var timeout = bans.timeouts && bans.timeouts[pubkey];
13
- if (!timeout) return false;
14
- return timeout.expiry > Math.floor(Date.now() / 1000);
15
- },
16
-
17
- subscribe(serverId) {
18
- if (nostrBans._sub) { nostrNet.unsubscribe(nostrBans._sub); nostrBans._sub = null; }
19
- if (!serverId) return;
20
- var creator = serverId.split(':')[0];
21
- if (!creator) return;
22
- nostrBans._sub = 'bans-' + serverId;
23
- nostrNet.subscribe(nostrBans._sub,
24
- [{ kinds: [30078], authors: [creator], '#d': ['zellous-ban:' + serverId, 'zellous-timeout:' + serverId] }],
25
- function(event) {
26
- if (event.pubkey !== creator) return;
27
- try {
28
- var dTag = event.tags.find(function(t) { return t[0] === 'd'; });
29
- if (!dTag || !dTag[1]) return;
30
- var prefix = dTag[1].split(':')[0];
31
- var banData = nostrBans._store.get(serverId) || { banned: [], timeouts: {} };
32
- if (prefix === 'zellous-ban') {
33
- var pubkey = dTag[1].split(':')[2];
34
- if (pubkey && !banData.banned.includes(pubkey)) banData.banned.push(pubkey);
35
- } else if (prefix === 'zellous-timeout') {
36
- var pubkey = dTag[1].split(':')[2];
37
- if (pubkey) {
38
- var data = JSON.parse(event.content);
39
- if (data.expiry > Math.floor(Date.now() / 1000)) {
40
- if (!banData.timeouts) banData.timeouts = {};
41
- banData.timeouts[pubkey] = { expiry: data.expiry };
42
- } else if (banData.timeouts && banData.timeouts[pubkey]) {
43
- delete banData.timeouts[pubkey];
44
- }
45
- }
46
- }
47
- nostrBans._store.set(serverId, banData);
48
- if (window.__debug && window.__debug.bans) window.__debug.bans = Object.fromEntries(nostrBans._store);
49
- } catch(e) {}
50
- },
51
- function() {}
52
- );
53
- }
54
- };
55
-
56
- window.__zellous.bans = nostrBans;
57
- window.nostrBans = nostrBans;
58
- if (!window.__debug) window.__debug = {};
59
- Object.defineProperty(window.__debug, 'bans', { get: function() { return Object.fromEntries(nostrBans._store); }, configurable: true });
@@ -1,145 +0,0 @@
1
- var DEFAULT_CATEGORIES = [
2
- { id: 'general', name: 'TEXT CHANNELS', position: 0 },
3
- { id: 'voice', name: 'VOICE CHANNELS', position: 1 }
4
- ];
5
-
6
- var DEFAULT_CHANNELS = [
7
- { id: 'general', name: 'general', type: 'text', categoryId: 'general', position: 0 },
8
- { id: 'announcements', name: 'announcements', type: 'announcement', categoryId: 'general', position: 1 },
9
- { id: 'general-voice', name: 'General', type: 'voice', categoryId: 'voice', position: 0 }
10
- ];
11
-
12
- async function _hexChannelId(channelId, serverId) {
13
- var input = (serverId || '') + ':' + channelId;
14
- var buf = new TextEncoder().encode(input);
15
- var hash = await crypto.subtle.digest('SHA-256', buf);
16
- return Array.from(new Uint8Array(hash)).map(function(b) { return b.toString(16).padStart(2, '0'); }).join('');
17
- }
18
-
19
- var channelManager = {
20
- isOwner: function() {
21
- return state.nostrPubkey && state.currentServerId && state.nostrPubkey === state.currentServerId.split(':')[0];
22
- },
23
-
24
- loadChannels: function(serverId, onReady) {
25
- var parts = serverId.split(':');
26
- var ownerPubkey = parts[0];
27
- var dTag = 'zellous-channels:' + serverId;
28
- nostrNet.subscribe(
29
- 'channels-' + serverId,
30
- [{ kinds: [30078], authors: [ownerPubkey], '#d': [dTag] }],
31
- function(event) {
32
- var hasTag = event.tags && event.tags.some(function(t) { return t[0] === 'd' && t[1] === dTag; });
33
- if (!hasTag) return;
34
- try {
35
- var data = JSON.parse(event.content);
36
- state.channels = data.channels || [];
37
- state.categories = data.categories || [];
38
- ui.render.all();
39
- } catch (e) { console.warn('[nostr-channels] parse error:', e.message); }
40
- },
41
- function() {
42
- if (!state.channels.length) channelManager._setDefaults();
43
- if (onReady) onReady();
44
- }
45
- );
46
- },
47
-
48
- _setDefaults: function() {
49
- state.channels = DEFAULT_CHANNELS.map(function(c) { return Object.assign({}, c); });
50
- state.categories = DEFAULT_CATEGORIES.map(function(c) { return Object.assign({}, c); });
51
- ui.render.all();
52
- if (channelManager.isOwner()) channelManager._publishChannelList().catch(function(e) { console.warn('[nostr-channels] publish defaults failed:', e.message); });
53
- },
54
-
55
- _publishChannelList: async function() {
56
- if (!channelManager.isOwner()) return;
57
- var serverId = state.currentServerId;
58
- var template = {
59
- kind: 30078,
60
- created_at: Math.floor(Date.now() / 1000),
61
- tags: [['d', 'zellous-channels:' + serverId]],
62
- content: JSON.stringify({ channels: state.channels, categories: state.categories })
63
- };
64
- var signed = await auth.sign(template);
65
- await nostrNet.publish(signed);
66
- },
67
-
68
- create: async function(name, type, categoryId) {
69
- var id = 'ch-' + Date.now();
70
- state.channels = state.channels.concat([{
71
- id: id, name: name, type: type || 'text',
72
- categoryId: categoryId || 'general', position: state.channels.length
73
- }]);
74
- await channelManager._publishChannelList();
75
- ui.render.all();
76
- },
77
-
78
- rename: async function(id, name) {
79
- state.channels = state.channels.map(function(c) {
80
- return c.id === id ? Object.assign({}, c, { name: name }) : c;
81
- });
82
- await channelManager._publishChannelList();
83
- ui.render.all();
84
- },
85
-
86
- remove: async function(id) {
87
- state.channels = state.channels.filter(function(c) { return c.id !== id; });
88
- await channelManager._publishChannelList();
89
- ui.render.all();
90
- },
91
-
92
- createCategory: async function(name) {
93
- var id = 'cat-' + Date.now();
94
- state.categories = state.categories.concat([{ id: id, name: name, position: state.categories.length }]);
95
- await channelManager._publishChannelList();
96
- ui.render.all();
97
- },
98
-
99
- renameCategory: async function(id, name) {
100
- state.categories = state.categories.map(function(c) {
101
- return c.id === id ? Object.assign({}, c, { name: name }) : c;
102
- });
103
- await channelManager._publishChannelList();
104
- ui.render.all();
105
- },
106
-
107
- deleteCategory: async function(id) {
108
- state.categories = state.categories.filter(function(c) { return c.id !== id; });
109
- state.channels = state.channels.map(function(c) {
110
- return c.categoryId === id ? Object.assign({}, c, { categoryId: null }) : c;
111
- });
112
- await channelManager._publishChannelList();
113
- ui.render.all();
114
- },
115
-
116
- reorderChannels: async function(catId, ids) {
117
- ids.forEach(function(chId, idx) {
118
- state.channels = state.channels.map(function(c) {
119
- return c.id === chId ? Object.assign({}, c, { position: idx, categoryId: catId }) : c;
120
- });
121
- });
122
- await channelManager._publishChannelList();
123
- ui.render.all();
124
- },
125
-
126
- reorderCategories: async function(ids) {
127
- ids.forEach(function(catId, idx) {
128
- state.categories = state.categories.map(function(c) {
129
- return c.id === catId ? Object.assign({}, c, { position: idx }) : c;
130
- });
131
- });
132
- await channelManager._publishChannelList();
133
- ui.render.all();
134
- },
135
-
136
- hideContextMenu: function() {
137
- var m = document.getElementById('channelContextMenu');
138
- if (m) m.remove();
139
- var cm = document.getElementById('categoryContextMenu');
140
- if (cm) cm.remove();
141
- }
142
- };
143
-
144
- window.__zellous.channels = channelManager;
145
- window.channelManager = channelManager;