@kbve/droid 0.0.6 → 0.1.2
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.
- package/droid.mjs +2312 -74
- package/index.d.ts +32 -0
- package/lib/agents/api-types.d.ts +121 -0
- package/lib/agents/api-types.d.ts.map +1 -0
- package/lib/agents/auth/init.d.ts +8 -0
- package/lib/agents/auth/init.d.ts.map +1 -0
- package/lib/agents/cache.d.ts +9 -0
- package/lib/agents/cache.d.ts.map +1 -0
- package/lib/agents/client/callGuildVault.d.ts +3 -0
- package/lib/agents/client/callGuildVault.d.ts.map +1 -0
- package/lib/agents/client/callJson.d.ts +3 -0
- package/lib/agents/client/callJson.d.ts.map +1 -0
- package/lib/agents/config.d.ts +36 -0
- package/lib/agents/config.d.ts.map +1 -0
- package/lib/agents/constants.d.ts +11 -0
- package/lib/agents/constants.d.ts.map +1 -0
- package/lib/agents/createAgents.d.ts +4 -0
- package/lib/agents/createAgents.d.ts.map +1 -0
- package/lib/agents/ctx.d.ts +46 -0
- package/lib/agents/ctx.d.ts.map +1 -0
- package/lib/agents/discord/bot.d.ts +21 -0
- package/lib/agents/discord/bot.d.ts.map +1 -0
- package/lib/agents/discord/guilds.d.ts +8 -0
- package/lib/agents/discord/guilds.d.ts.map +1 -0
- package/lib/agents/formDrafts.d.ts +5 -0
- package/lib/agents/formDrafts.d.ts.map +1 -0
- package/lib/agents/generated/agents-schema.d.ts +73 -0
- package/lib/agents/generated/agents-schema.d.ts.map +1 -0
- package/lib/agents/generated/discordsh-agents-schema.d.ts +71 -0
- package/lib/agents/generated/discordsh-agents-schema.d.ts.map +1 -0
- package/lib/agents/github/backfill.d.ts +23 -0
- package/lib/agents/github/backfill.d.ts.map +1 -0
- package/lib/agents/github/events.d.ts +10 -0
- package/lib/agents/github/events.d.ts.map +1 -0
- package/lib/agents/github/pat.d.ts +13 -0
- package/lib/agents/github/pat.d.ts.map +1 -0
- package/lib/agents/github/repoConfig.d.ts +25 -0
- package/lib/agents/github/repoConfig.d.ts.map +1 -0
- package/lib/agents/github/tokens.d.ts +22 -0
- package/lib/agents/github/tokens.d.ts.map +1 -0
- package/lib/agents/github/webhook.d.ts +37 -0
- package/lib/agents/github/webhook.d.ts.map +1 -0
- package/lib/agents/index.d.ts +8 -0
- package/lib/agents/index.d.ts.map +1 -0
- package/lib/agents/state/atoms.d.ts +122 -0
- package/lib/agents/state/atoms.d.ts.map +1 -0
- package/lib/agents/types.d.ts +65 -0
- package/lib/agents/types.d.ts.map +1 -0
- package/lib/gateway/SupabaseGateway.d.ts +38 -0
- package/lib/gateway/SupabaseGateway.d.ts.map +1 -0
- package/lib/gateway/WorkerCommunication.d.ts +39 -0
- package/lib/gateway/WorkerCommunication.d.ts.map +1 -0
- package/lib/gateway/WorkerPool.d.ts +55 -0
- package/lib/gateway/WorkerPool.d.ts.map +1 -0
- package/lib/gateway/capabilities.d.ts +23 -0
- package/lib/gateway/capabilities.d.ts.map +1 -0
- package/lib/gateway/index.d.ts +9 -0
- package/lib/gateway/index.d.ts.map +1 -0
- package/lib/gateway/strategies/DirectStrategy.d.ts +52 -0
- package/lib/gateway/strategies/DirectStrategy.d.ts.map +1 -0
- package/lib/gateway/strategies/SharedWorkerStrategy.d.ts +46 -0
- package/lib/gateway/strategies/SharedWorkerStrategy.d.ts.map +1 -0
- package/lib/gateway/strategies/WebWorkerStrategy.d.ts +45 -0
- package/lib/gateway/strategies/WebWorkerStrategy.d.ts.map +1 -0
- package/lib/gateway/types.d.ts +81 -0
- package/lib/gateway/types.d.ts.map +1 -0
- package/lib/workers/canvas-ui-renderer.d.ts +25 -0
- package/lib/workers/canvas-ui-renderer.d.ts.map +1 -0
- package/lib/workers/canvas-worker.d.ts +14 -0
- package/lib/workers/canvas-worker.d.ts.map +1 -0
- package/lib/workers/canvas-worker.js +5 -302
- package/lib/workers/data.d.ts +40 -0
- package/lib/workers/data.d.ts.map +1 -0
- package/lib/workers/db-worker.d.ts +69 -0
- package/lib/workers/db-worker.d.ts.map +1 -0
- package/lib/workers/db-worker.js +13 -3636
- package/lib/workers/events.d.ts +11 -0
- package/lib/workers/events.d.ts.map +1 -0
- package/lib/workers/flexbuilder.d.ts +4 -0
- package/lib/workers/flexbuilder.d.ts.map +1 -0
- package/lib/workers/init.d.ts +11 -0
- package/lib/workers/init.d.ts.map +1 -0
- package/lib/workers/main.d.ts +105 -0
- package/lib/workers/main.d.ts.map +1 -0
- package/lib/workers/supabase-db-worker.d.ts +2 -0
- package/lib/workers/supabase-db-worker.d.ts.map +1 -0
- package/lib/workers/supabase-db-worker.js +23 -0
- package/lib/workers/supabase-shared-worker.d.ts +2 -0
- package/lib/workers/supabase-shared-worker.d.ts.map +1 -0
- package/lib/workers/supabase-shared-worker.js +25 -0
- package/lib/workers/tools.d.ts +4 -0
- package/lib/workers/tools.d.ts.map +1 -0
- package/lib/workers/validate.d.ts +15 -0
- package/lib/workers/validate.d.ts.map +1 -0
- package/lib/workers/ws-worker.d.ts +10 -0
- package/lib/workers/ws-worker.d.ts.map +1 -0
- package/lib/workers/ws-worker.js +5 -273
- package/main.js +5711 -0
- package/package.json +18 -6
- package/src/index.ts +41 -13
- package/src/lib/agents/api-types.ts +170 -0
- package/src/lib/agents/auth/init.ts +200 -0
- package/src/lib/agents/cache.ts +127 -0
- package/src/lib/agents/client/callGuildVault.ts +71 -0
- package/src/lib/agents/client/callJson.ts +64 -0
- package/src/lib/agents/config.ts +83 -0
- package/src/lib/agents/constants.ts +11 -0
- package/src/lib/agents/createAgents.ts +62 -0
- package/src/lib/agents/ctx.ts +63 -0
- package/src/lib/agents/discord/bot.ts +169 -0
- package/src/lib/agents/discord/guilds.ts +106 -0
- package/src/lib/agents/formDrafts.ts +55 -0
- package/src/lib/agents/generated/agents-schema.ts +155 -0
- package/src/lib/agents/generated/discordsh-agents-schema.ts +96 -0
- package/src/lib/agents/github/backfill.ts +126 -0
- package/src/lib/agents/github/events.ts +131 -0
- package/src/lib/agents/github/pat.ts +150 -0
- package/src/lib/agents/github/repoConfig.ts +240 -0
- package/src/lib/agents/github/tokens.ts +222 -0
- package/src/lib/agents/github/webhook.ts +349 -0
- package/src/lib/agents/index.ts +35 -0
- package/src/lib/agents/state/atoms.ts +122 -0
- package/src/lib/agents/types.ts +83 -0
- package/src/lib/api.ts +71 -0
- package/src/lib/droid.spec.ts +14 -19
- package/src/lib/droid.ts +48 -4
- package/src/lib/gateway/SupabaseGateway.ts +236 -0
- package/src/lib/gateway/WorkerCommunication.ts +132 -0
- package/src/lib/gateway/WorkerPool.ts +304 -0
- package/src/lib/gateway/capabilities.spec.ts +144 -0
- package/src/lib/gateway/capabilities.ts +80 -0
- package/src/lib/gateway/index.ts +15 -0
- package/src/lib/gateway/strategies/DirectStrategy.ts +443 -0
- package/src/lib/gateway/strategies/SharedWorkerStrategy.ts +278 -0
- package/src/lib/gateway/strategies/WebWorkerStrategy.ts +278 -0
- package/src/lib/gateway/types.ts +139 -0
- package/src/lib/mod/mod-manager.ts +13 -8
- package/src/lib/mod/module/bento/mod-bento.worker.ts +1 -1
- package/src/lib/mod/module/supabase/mod-supabase.spec.ts +21 -25
- package/src/lib/mod/module/supabase/mod-supabase.ts +73 -0
- package/src/lib/mod/module/supabase/mod-supabase.worker.ts +1 -56
- package/src/lib/state/auth.spec.ts +58 -0
- package/src/lib/state/auth.ts +148 -0
- package/src/lib/state/bento.spec.ts +76 -0
- package/src/lib/state/bento.ts +51 -0
- package/src/lib/state/index.ts +98 -0
- package/src/lib/state/overlay-manager.ts +125 -0
- package/src/lib/state/profile-sync.ts +214 -0
- package/src/lib/state/profile.ts +155 -0
- package/src/lib/state/router.ts +24 -0
- package/src/lib/state/staff.ts +128 -0
- package/src/lib/state/sync-bus.ts +109 -0
- package/src/lib/state/theme-sync.ts +75 -0
- package/src/lib/state/toasts.spec.ts +106 -0
- package/src/lib/state/toasts.ts +34 -0
- package/src/lib/state/ui.spec.ts +148 -0
- package/src/lib/state/ui.ts +45 -0
- package/src/lib/state/welcome-toast.spec.ts +101 -0
- package/src/lib/state/welcome-toast.ts +76 -0
- package/src/lib/sw-recovery.spec.ts +165 -0
- package/src/lib/sw-recovery.ts +141 -0
- package/src/lib/types/bento.spec.ts +116 -0
- package/src/lib/types/bento.ts +94 -55
- package/src/lib/types/event-schemas.spec.ts +120 -0
- package/src/lib/types/event-types.spec.ts +96 -0
- package/src/lib/types/event-types.ts +113 -3
- package/src/lib/types/modules.ts +12 -5
- package/src/lib/types/ui-event-types.ts +66 -0
- package/src/lib/workers/canvas-ui-renderer.ts +266 -0
- package/src/lib/workers/canvas-worker.ts +94 -9
- package/src/lib/workers/data.ts +64 -37
- package/src/lib/workers/db-worker.ts +105 -32
- package/src/lib/workers/events.spec.ts +208 -0
- package/src/lib/workers/events.ts +27 -14
- package/src/lib/workers/flexbuilder.ts +2 -2
- package/src/lib/workers/init.ts +25 -17
- package/src/lib/workers/main.ts +506 -327
- package/src/lib/workers/supabase-db-worker.ts +243 -0
- package/src/lib/workers/supabase-shared-worker.ts +607 -0
- package/src/lib/workers/validate.ts +64 -0
- package/src/lib/workers/ws-worker.spec.ts +41 -0
- package/src/lib/workers/ws-worker.ts +255 -31
- package/src/setup-vitest.ts +4 -2
- package/src/types.ts +30 -0
- package/src/vite-env.d.ts +1 -0
- package/workers/main.js +2 -1125
- package/comlink.js +0 -253
- package/index.js +0 -2972
- package/reference.js +0 -259
- package/src/types.d.ts +0 -31
|
@@ -1,73 +1,297 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { LocalStorageAPI } from './db-worker';
|
|
3
|
-
import { toReference, builder } from './flexbuilder';
|
|
4
|
-
|
|
1
|
+
import { expose } from 'comlink';
|
|
5
2
|
|
|
6
3
|
interface SharedWorkerGlobalScope extends Worker {
|
|
7
4
|
onconnect: (event: MessageEvent) => void;
|
|
8
5
|
}
|
|
9
6
|
declare const self: SharedWorkerGlobalScope;
|
|
10
7
|
|
|
11
|
-
|
|
12
8
|
let ws: WebSocket | null = null;
|
|
13
|
-
let onMessageCallback: ((data: any) => void) | null = null;
|
|
14
9
|
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Transport channels
|
|
12
|
+
//
|
|
13
|
+
// `kbve_ws_data` — raw WebSocket payloads for the db-worker to persist.
|
|
14
|
+
// Payload: { type: 'ws.store', format, data, ts }
|
|
15
|
+
//
|
|
16
|
+
// `kbve_ws_events` — status + message events for UI consumers (chat service,
|
|
17
|
+
// presence, overlays, etc.).
|
|
18
|
+
// Payload:
|
|
19
|
+
// { type: 'status', status: string }
|
|
20
|
+
// { type: 'message', format: 'text', data: string }
|
|
21
|
+
// { type: 'message', format: 'binary', data: Uint8Array }
|
|
22
|
+
//
|
|
23
|
+
// Keeping persistence and UI on separate channels means each consumer only
|
|
24
|
+
// sees what it cares about, and a new subscriber (e.g. cross-tab presence)
|
|
25
|
+
// drops in without filtering db-bound payloads.
|
|
26
|
+
//
|
|
27
|
+
// Historical note: an earlier design exposed onMessage(cb) / onStatus(cb)
|
|
28
|
+
// through Comlink. That failed with DataCloneError because Comlink forwards
|
|
29
|
+
// method args via postMessage, and functions are not structured-cloneable.
|
|
30
|
+
// If a future strategy wants a different transport (SharedArrayBuffer,
|
|
31
|
+
// MessagePort, proxied-Comlink callbacks), add it alongside — don't remove
|
|
32
|
+
// the BroadcastChannel path, it's the fallback that works everywhere.
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
const dbChannel =
|
|
35
|
+
typeof BroadcastChannel !== 'undefined'
|
|
36
|
+
? new BroadcastChannel('kbve_ws_data')
|
|
37
|
+
: null;
|
|
38
|
+
|
|
39
|
+
const eventsChannel =
|
|
40
|
+
typeof BroadcastChannel !== 'undefined'
|
|
41
|
+
? new BroadcastChannel('kbve_ws_events')
|
|
42
|
+
: null;
|
|
43
|
+
|
|
44
|
+
// Heartbeat
|
|
45
|
+
let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
|
|
46
|
+
let lastPongTime = 0;
|
|
47
|
+
const HEARTBEAT_INTERVAL_MS = 30000;
|
|
48
|
+
const HEARTBEAT_TIMEOUT_MS = 60000;
|
|
49
|
+
|
|
50
|
+
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
51
|
+
let reconnectAttempts = 0;
|
|
52
|
+
let lastUrl: string | null = null;
|
|
53
|
+
const MAX_RECONNECT_ATTEMPTS = 5;
|
|
54
|
+
const RECONNECT_BASE_MS = 1000;
|
|
55
|
+
const RECONNECT_MAX_MS = 30000;
|
|
56
|
+
const RECONNECT_JITTER_MS = 500;
|
|
57
|
+
|
|
58
|
+
export function reconnectDelayMs(attempt: number): number {
|
|
59
|
+
const exp = Math.min(
|
|
60
|
+
RECONNECT_MAX_MS,
|
|
61
|
+
RECONNECT_BASE_MS * 2 ** Math.max(0, attempt - 1),
|
|
62
|
+
);
|
|
63
|
+
const jitter = Math.floor(Math.random() * RECONNECT_JITTER_MS);
|
|
64
|
+
return exp + jitter;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function startHeartbeat() {
|
|
68
|
+
stopHeartbeat();
|
|
69
|
+
heartbeatTimer = setInterval(() => {
|
|
70
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
71
|
+
stopHeartbeat();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (
|
|
75
|
+
lastPongTime > 0 &&
|
|
76
|
+
Date.now() - lastPongTime > HEARTBEAT_TIMEOUT_MS
|
|
77
|
+
) {
|
|
78
|
+
console.warn('[WS] Heartbeat timeout — closing connection');
|
|
79
|
+
stopHeartbeat();
|
|
80
|
+
ws.close(1001, 'Heartbeat timeout');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
85
|
+
} catch (err) {
|
|
86
|
+
console.error('[WS] Failed to send heartbeat:', err);
|
|
87
|
+
}
|
|
88
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function stopHeartbeat() {
|
|
92
|
+
if (heartbeatTimer) {
|
|
93
|
+
clearInterval(heartbeatTimer);
|
|
94
|
+
heartbeatTimer = null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function broadcastStatus(
|
|
99
|
+
status: string,
|
|
100
|
+
detail?: { code?: number; reason?: string; wasClean?: boolean },
|
|
101
|
+
) {
|
|
102
|
+
eventsChannel?.postMessage({ type: 'status', status, ...detail });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function attemptReconnect() {
|
|
106
|
+
if (!lastUrl || reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
107
|
+
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
108
|
+
console.error(
|
|
109
|
+
`[WS] Max reconnect attempts (${MAX_RECONNECT_ATTEMPTS}) reached`,
|
|
110
|
+
);
|
|
111
|
+
broadcastStatus('failed');
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
reconnectAttempts++;
|
|
117
|
+
const delay = reconnectDelayMs(reconnectAttempts);
|
|
118
|
+
console.log(
|
|
119
|
+
`[WS] Reconnecting (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}) in ${delay}ms...`,
|
|
120
|
+
);
|
|
121
|
+
broadcastStatus('reconnecting');
|
|
122
|
+
|
|
123
|
+
reconnectTimer = setTimeout(() => {
|
|
124
|
+
if (lastUrl) wsInstanceAPI.connect(lastUrl);
|
|
125
|
+
}, delay);
|
|
126
|
+
}
|
|
15
127
|
|
|
16
|
-
//
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// WebSocket API exposed via Comlink
|
|
130
|
+
//
|
|
131
|
+
// Only commands that carry structured-cloneable data cross this bridge
|
|
132
|
+
// (strings, typed arrays). Event subscription is deliberately NOT here —
|
|
133
|
+
// it lives on the BroadcastChannel side to avoid DataCloneError when a
|
|
134
|
+
// caller passes a raw function.
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
17
136
|
const wsInstanceAPI = {
|
|
18
137
|
async connect(url: string) {
|
|
19
|
-
if (ws) return;
|
|
138
|
+
if (ws && ws.readyState === WebSocket.OPEN) return;
|
|
20
139
|
|
|
140
|
+
// Clean up any existing connection
|
|
141
|
+
if (ws) {
|
|
142
|
+
ws.onclose = null;
|
|
143
|
+
ws.close();
|
|
144
|
+
ws = null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
lastUrl = url;
|
|
148
|
+
const safeUrl = url.replace(/(token=)[^&]+/i, '$1<redacted>');
|
|
149
|
+
console.log('[WS] Connecting to', safeUrl);
|
|
21
150
|
ws = new WebSocket(url);
|
|
22
151
|
ws.binaryType = 'arraybuffer';
|
|
23
152
|
|
|
24
|
-
ws.onopen = () =>
|
|
153
|
+
ws.onopen = () => {
|
|
154
|
+
console.log('[WS] Connected:', safeUrl);
|
|
155
|
+
reconnectAttempts = 0;
|
|
156
|
+
lastPongTime = Date.now();
|
|
157
|
+
startHeartbeat();
|
|
158
|
+
broadcastStatus('connected');
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
ws.onmessage = (e) => {
|
|
162
|
+
try {
|
|
163
|
+
// Handle JSON pong responses
|
|
164
|
+
if (typeof e.data === 'string') {
|
|
165
|
+
try {
|
|
166
|
+
const parsed = JSON.parse(e.data);
|
|
167
|
+
if (parsed.type === 'pong') {
|
|
168
|
+
lastPongTime = Date.now();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
// Not JSON, pass through
|
|
173
|
+
}
|
|
174
|
+
}
|
|
25
175
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
176
|
+
// Fan out to db-worker (persistence pipeline).
|
|
177
|
+
// Structured-cloneable payloads only — strings and
|
|
178
|
+
// Uint8Array are both safe across BroadcastChannel.
|
|
179
|
+
if (dbChannel) {
|
|
180
|
+
if (typeof e.data === 'string') {
|
|
181
|
+
dbChannel.postMessage({
|
|
182
|
+
type: 'ws.store',
|
|
183
|
+
format: 'text',
|
|
184
|
+
data: e.data,
|
|
185
|
+
ts: Date.now(),
|
|
186
|
+
});
|
|
187
|
+
} else if (e.data instanceof ArrayBuffer) {
|
|
188
|
+
dbChannel.postMessage({
|
|
189
|
+
type: 'ws.store',
|
|
190
|
+
format: 'binary',
|
|
191
|
+
data: new Uint8Array(e.data),
|
|
192
|
+
ts: Date.now(),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
32
195
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
196
|
+
|
|
197
|
+
// Fan out to UI consumers (chat service, presence, etc.).
|
|
198
|
+
// Same structured-cloneable constraint applies.
|
|
199
|
+
if (eventsChannel) {
|
|
200
|
+
if (typeof e.data === 'string') {
|
|
201
|
+
eventsChannel.postMessage({
|
|
202
|
+
type: 'message',
|
|
203
|
+
format: 'text',
|
|
204
|
+
data: e.data,
|
|
205
|
+
});
|
|
206
|
+
} else if (e.data instanceof ArrayBuffer) {
|
|
207
|
+
eventsChannel.postMessage({
|
|
208
|
+
type: 'message',
|
|
209
|
+
format: 'binary',
|
|
210
|
+
data: new Uint8Array(e.data),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.error('[WS] Failed to forward message', err);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
ws.onerror = (e) => {
|
|
220
|
+
console.error('[WS] Error event:', e);
|
|
221
|
+
broadcastStatus('error', {
|
|
222
|
+
reason: 'WebSocket error event (no detail in browser API)',
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
ws.onclose = (event) => {
|
|
227
|
+
console.log(
|
|
228
|
+
`[WS] Disconnected code=${event.code} reason="${event.reason}" wasClean=${event.wasClean}`,
|
|
229
|
+
);
|
|
42
230
|
ws = null;
|
|
231
|
+
stopHeartbeat();
|
|
232
|
+
broadcastStatus('disconnected', {
|
|
233
|
+
code: event.code,
|
|
234
|
+
reason: event.reason,
|
|
235
|
+
wasClean: event.wasClean,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
if (event.code !== 1000) {
|
|
239
|
+
attemptReconnect();
|
|
240
|
+
}
|
|
43
241
|
};
|
|
44
242
|
},
|
|
45
243
|
|
|
46
244
|
async send(payload: Uint8Array) {
|
|
47
245
|
if (ws?.readyState === WebSocket.OPEN) {
|
|
48
|
-
|
|
246
|
+
// Cast narrows Uint8Array<ArrayBufferLike> to BufferSource for
|
|
247
|
+
// WebSocket.send. Newer TS DOM lib parameterizes Uint8Array over
|
|
248
|
+
// ArrayBuffer | SharedArrayBuffer, but send() only accepts
|
|
249
|
+
// ArrayBuffer-backed views. Every caller here posts a freshly
|
|
250
|
+
// encoded payload (Uint8Array from TextEncoder or similar), so
|
|
251
|
+
// the backing buffer is always a plain ArrayBuffer.
|
|
252
|
+
ws.send(payload as BufferSource);
|
|
49
253
|
} else {
|
|
50
254
|
console.warn('[WS] Tried to send while disconnected');
|
|
51
255
|
}
|
|
52
256
|
},
|
|
53
257
|
|
|
54
258
|
async close() {
|
|
55
|
-
|
|
259
|
+
if (reconnectTimer) {
|
|
260
|
+
clearTimeout(reconnectTimer);
|
|
261
|
+
reconnectTimer = null;
|
|
262
|
+
}
|
|
263
|
+
stopHeartbeat();
|
|
264
|
+
reconnectAttempts = 0;
|
|
265
|
+
lastUrl = null;
|
|
266
|
+
ws?.close(1000, 'Client request');
|
|
56
267
|
ws = null;
|
|
268
|
+
broadcastStatus('disconnected');
|
|
57
269
|
},
|
|
58
270
|
|
|
59
|
-
|
|
60
|
-
|
|
271
|
+
async getStatus(): Promise<string> {
|
|
272
|
+
if (!ws) return 'disconnected';
|
|
273
|
+
const map: Record<number, string> = {
|
|
274
|
+
[WebSocket.CONNECTING]: 'connecting',
|
|
275
|
+
[WebSocket.OPEN]: 'connected',
|
|
276
|
+
[WebSocket.CLOSING]: 'closing',
|
|
277
|
+
[WebSocket.CLOSED]: 'disconnected',
|
|
278
|
+
};
|
|
279
|
+
return map[ws.readyState] || 'unknown';
|
|
61
280
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
281
|
};
|
|
65
282
|
|
|
66
283
|
export type WSInstance = typeof wsInstanceAPI;
|
|
67
284
|
|
|
285
|
+
const ports = new Set<MessagePort>();
|
|
286
|
+
|
|
68
287
|
self.onconnect = (event: MessageEvent) => {
|
|
69
288
|
const port = event.ports[0];
|
|
289
|
+
const isFirst = ports.size === 0;
|
|
290
|
+
ports.add(port);
|
|
70
291
|
port.start();
|
|
292
|
+
port.postMessage({ type: isFirst ? 'first-connect' : 'reconnect' });
|
|
293
|
+
port.addEventListener('message', (e: MessageEvent) => {
|
|
294
|
+
if (e.data?.type === 'close') ports.delete(port);
|
|
295
|
+
});
|
|
71
296
|
expose(wsInstanceAPI, port);
|
|
72
297
|
};
|
|
73
|
-
|
package/src/setup-vitest.ts
CHANGED
package/src/types.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DroidEvents } from './lib/workers/events';
|
|
2
|
+
import type { FlexDataAPI } from './lib/workers/data';
|
|
3
|
+
import type { ModManager } from './lib/types/modules';
|
|
4
|
+
import type { SupabaseGateway } from './lib/gateway/SupabaseGateway';
|
|
5
|
+
import type { ToastPayload } from './lib/types/ui-event-types';
|
|
6
|
+
import type { DroidScaleLevel } from './lib/workers/main';
|
|
7
|
+
import type { OverlayManager } from './lib/state/overlay-manager';
|
|
8
|
+
|
|
9
|
+
export interface KBVEGlobal {
|
|
10
|
+
api?: unknown;
|
|
11
|
+
i18n?: unknown;
|
|
12
|
+
uiux?: Record<string, unknown>;
|
|
13
|
+
ws?: unknown;
|
|
14
|
+
data?: FlexDataAPI;
|
|
15
|
+
mod?: ModManager;
|
|
16
|
+
events: typeof DroidEvents;
|
|
17
|
+
overlay?: OverlayManager;
|
|
18
|
+
gateway?: SupabaseGateway;
|
|
19
|
+
downscale?: () => Promise<void>;
|
|
20
|
+
upscale?: () => Promise<void>;
|
|
21
|
+
scaleLevel?: () => DroidScaleLevel;
|
|
22
|
+
[key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare global {
|
|
26
|
+
interface Window {
|
|
27
|
+
kbve: KBVEGlobal;
|
|
28
|
+
__kbveToastQueue?: ToastPayload[] | null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|