@kbve/droid 0.0.6 → 0.1.1

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.
Files changed (108) hide show
  1. package/comlink.js +1 -7
  2. package/droid.mjs +191 -61
  3. package/index.d.ts +29 -0
  4. package/index.js +9054 -2763
  5. package/lib/gateway/SupabaseGateway.d.ts +38 -0
  6. package/lib/gateway/SupabaseGateway.d.ts.map +1 -0
  7. package/lib/gateway/WorkerCommunication.d.ts +39 -0
  8. package/lib/gateway/WorkerCommunication.d.ts.map +1 -0
  9. package/lib/gateway/WorkerPool.d.ts +55 -0
  10. package/lib/gateway/WorkerPool.d.ts.map +1 -0
  11. package/lib/gateway/capabilities.d.ts +23 -0
  12. package/lib/gateway/capabilities.d.ts.map +1 -0
  13. package/lib/gateway/index.d.ts +9 -0
  14. package/lib/gateway/index.d.ts.map +1 -0
  15. package/lib/gateway/strategies/DirectStrategy.d.ts +52 -0
  16. package/lib/gateway/strategies/DirectStrategy.d.ts.map +1 -0
  17. package/lib/gateway/strategies/SharedWorkerStrategy.d.ts +46 -0
  18. package/lib/gateway/strategies/SharedWorkerStrategy.d.ts.map +1 -0
  19. package/lib/gateway/strategies/WebWorkerStrategy.d.ts +45 -0
  20. package/lib/gateway/strategies/WebWorkerStrategy.d.ts.map +1 -0
  21. package/lib/gateway/types.d.ts +81 -0
  22. package/lib/gateway/types.d.ts.map +1 -0
  23. package/lib/workers/canvas-ui-renderer.d.ts +25 -0
  24. package/lib/workers/canvas-ui-renderer.d.ts.map +1 -0
  25. package/lib/workers/canvas-worker.d.ts +14 -0
  26. package/lib/workers/canvas-worker.d.ts.map +1 -0
  27. package/lib/workers/canvas-worker.js +291 -145
  28. package/lib/workers/data.d.ts +40 -0
  29. package/lib/workers/data.d.ts.map +1 -0
  30. package/lib/workers/db-worker.d.ts +69 -0
  31. package/lib/workers/db-worker.d.ts.map +1 -0
  32. package/lib/workers/db-worker.js +2680 -2147
  33. package/lib/workers/events.d.ts +11 -0
  34. package/lib/workers/events.d.ts.map +1 -0
  35. package/lib/workers/flexbuilder.d.ts +4 -0
  36. package/lib/workers/flexbuilder.d.ts.map +1 -0
  37. package/lib/workers/init.d.ts +11 -0
  38. package/lib/workers/init.d.ts.map +1 -0
  39. package/lib/workers/main.d.ts +107 -0
  40. package/lib/workers/main.d.ts.map +1 -0
  41. package/lib/workers/supabase-db-worker.d.ts +2 -0
  42. package/lib/workers/supabase-db-worker.d.ts.map +1 -0
  43. package/lib/workers/supabase-db-worker.js +9337 -0
  44. package/lib/workers/supabase-shared-worker.d.ts +2 -0
  45. package/lib/workers/supabase-shared-worker.d.ts.map +1 -0
  46. package/lib/workers/supabase-shared-worker.js +12635 -0
  47. package/lib/workers/tools.d.ts +4 -0
  48. package/lib/workers/tools.d.ts.map +1 -0
  49. package/lib/workers/validate.d.ts +15 -0
  50. package/lib/workers/validate.d.ts.map +1 -0
  51. package/lib/workers/ws-worker.d.ts +11 -0
  52. package/lib/workers/ws-worker.d.ts.map +1 -0
  53. package/lib/workers/ws-worker.js +174 -115
  54. package/main.js +4875 -0
  55. package/package.json +17 -6
  56. package/reference.js +688 -228
  57. package/src/index.ts +38 -13
  58. package/src/lib/droid.spec.ts +14 -19
  59. package/src/lib/droid.ts +48 -4
  60. package/src/lib/gateway/SupabaseGateway.ts +183 -0
  61. package/src/lib/gateway/WorkerCommunication.ts +132 -0
  62. package/src/lib/gateway/WorkerPool.ts +304 -0
  63. package/src/lib/gateway/capabilities.ts +80 -0
  64. package/src/lib/gateway/index.ts +15 -0
  65. package/src/lib/gateway/strategies/DirectStrategy.ts +443 -0
  66. package/src/lib/gateway/strategies/SharedWorkerStrategy.ts +278 -0
  67. package/src/lib/gateway/strategies/WebWorkerStrategy.ts +278 -0
  68. package/src/lib/gateway/types.ts +112 -0
  69. package/src/lib/mod/mod-manager.ts +13 -8
  70. package/src/lib/mod/module/bento/mod-bento.worker.ts +1 -1
  71. package/src/lib/mod/module/supabase/mod-supabase.spec.ts +21 -25
  72. package/src/lib/mod/module/supabase/mod-supabase.ts +68 -0
  73. package/src/lib/mod/module/supabase/mod-supabase.worker.ts +1 -56
  74. package/src/lib/state/auth.spec.ts +58 -0
  75. package/src/lib/state/auth.ts +30 -0
  76. package/src/lib/state/index.ts +34 -0
  77. package/src/lib/state/overlay-manager.ts +125 -0
  78. package/src/lib/state/router.ts +24 -0
  79. package/src/lib/state/theme-sync.ts +75 -0
  80. package/src/lib/state/toasts.spec.ts +106 -0
  81. package/src/lib/state/toasts.ts +34 -0
  82. package/src/lib/state/ui.spec.ts +148 -0
  83. package/src/lib/state/ui.ts +45 -0
  84. package/src/lib/state/welcome-toast.ts +76 -0
  85. package/src/lib/types/bento.spec.ts +116 -0
  86. package/src/lib/types/bento.ts +94 -55
  87. package/src/lib/types/event-types.spec.ts +93 -0
  88. package/src/lib/types/event-types.ts +39 -3
  89. package/src/lib/types/modules.ts +12 -5
  90. package/src/lib/types/ui-event-types.ts +66 -0
  91. package/src/lib/workers/canvas-ui-renderer.ts +266 -0
  92. package/src/lib/workers/canvas-worker.ts +94 -9
  93. package/src/lib/workers/data.ts +59 -36
  94. package/src/lib/workers/db-worker.ts +79 -32
  95. package/src/lib/workers/events.spec.ts +208 -0
  96. package/src/lib/workers/events.ts +27 -14
  97. package/src/lib/workers/flexbuilder.ts +2 -2
  98. package/src/lib/workers/init.ts +25 -17
  99. package/src/lib/workers/main.ts +510 -316
  100. package/src/lib/workers/supabase-db-worker.ts +221 -0
  101. package/src/lib/workers/supabase-shared-worker.ts +572 -0
  102. package/src/lib/workers/validate.ts +64 -0
  103. package/src/lib/workers/ws-worker.ts +156 -25
  104. package/src/setup-vitest.ts +4 -2
  105. package/src/types.ts +30 -0
  106. package/src/vite-env.d.ts +1 -0
  107. package/workers/main.js +10 -1123
  108. package/src/types.d.ts +0 -31
@@ -1,45 +1,148 @@
1
- import { wrap, expose, type Remote } from 'comlink';
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;
9
+ let onMessageCallback: ((data: string | ArrayBuffer) => void) | null = null;
10
+ let onStatusCallback: ((status: string) => void) | null = null;
11
+
12
+ // Heartbeat
13
+ let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
14
+ let lastPongTime = 0;
15
+ const HEARTBEAT_INTERVAL_MS = 30000;
16
+ const HEARTBEAT_TIMEOUT_MS = 60000;
17
+
18
+ // Reconnect
19
+ let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
20
+ let reconnectAttempts = 0;
21
+ let lastUrl: string | null = null;
22
+ const MAX_RECONNECT_ATTEMPTS = 5;
23
+ const RECONNECT_DELAY_MS = 3000;
24
+
25
+ function startHeartbeat() {
26
+ stopHeartbeat();
27
+ heartbeatTimer = setInterval(() => {
28
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
29
+ stopHeartbeat();
30
+ return;
31
+ }
32
+ if (
33
+ lastPongTime > 0 &&
34
+ Date.now() - lastPongTime > HEARTBEAT_TIMEOUT_MS
35
+ ) {
36
+ console.warn('[WS] Heartbeat timeout — closing connection');
37
+ stopHeartbeat();
38
+ ws.close(1001, 'Heartbeat timeout');
39
+ return;
40
+ }
41
+ try {
42
+ ws.send(JSON.stringify({ type: 'ping' }));
43
+ } catch (err) {
44
+ console.error('[WS] Failed to send heartbeat:', err);
45
+ }
46
+ }, HEARTBEAT_INTERVAL_MS);
47
+ }
14
48
 
49
+ function stopHeartbeat() {
50
+ if (heartbeatTimer) {
51
+ clearInterval(heartbeatTimer);
52
+ heartbeatTimer = null;
53
+ }
54
+ }
55
+
56
+ function broadcastStatus(status: string) {
57
+ onStatusCallback?.(status);
58
+ }
59
+
60
+ function attemptReconnect() {
61
+ if (!lastUrl || reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
62
+ if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
63
+ console.error(
64
+ `[WS] Max reconnect attempts (${MAX_RECONNECT_ATTEMPTS}) reached`,
65
+ );
66
+ broadcastStatus('failed');
67
+ }
68
+ return;
69
+ }
70
+
71
+ reconnectAttempts++;
72
+ console.log(
73
+ `[WS] Reconnecting (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`,
74
+ );
75
+ broadcastStatus('reconnecting');
76
+
77
+ reconnectTimer = setTimeout(() => {
78
+ if (lastUrl) wsInstanceAPI.connect(lastUrl);
79
+ }, RECONNECT_DELAY_MS);
80
+ }
15
81
 
16
82
  // --- WebSocket API ---
17
83
  const wsInstanceAPI = {
18
84
  async connect(url: string) {
19
- if (ws) return;
85
+ if (ws && ws.readyState === WebSocket.OPEN) return;
86
+
87
+ // Clean up any existing connection
88
+ if (ws) {
89
+ ws.onclose = null;
90
+ ws.close();
91
+ ws = null;
92
+ }
20
93
 
94
+ lastUrl = url;
21
95
  ws = new WebSocket(url);
22
96
  ws.binaryType = 'arraybuffer';
23
97
 
24
- ws.onopen = () => console.log('[WS] Connected:', url);
98
+ ws.onopen = () => {
99
+ console.log('[WS] Connected:', url);
100
+ reconnectAttempts = 0;
101
+ lastPongTime = Date.now();
102
+ startHeartbeat();
103
+ broadcastStatus('connected');
104
+ };
105
+
106
+ ws.onmessage = (e) => {
107
+ try {
108
+ // Handle JSON pong responses
109
+ if (typeof e.data === 'string') {
110
+ try {
111
+ const parsed = JSON.parse(e.data);
112
+ if (parsed.type === 'pong') {
113
+ lastPongTime = Date.now();
114
+ return;
115
+ }
116
+ } catch {
117
+ // Not JSON, pass through
118
+ }
119
+ }
25
120
 
26
- ws.onmessage = (e) => {
27
- try {
28
- console.log('[WS] Received binary message');
29
-
30
121
  if (e.data instanceof ArrayBuffer) {
31
- onMessageCallback?.(e.data);
122
+ onMessageCallback?.(e.data);
123
+ } else {
124
+ onMessageCallback?.(e.data);
32
125
  }
33
- } catch (err) {
34
- console.error('[WS] Failed to forward message', err);
35
- }
36
- };
37
-
38
-
39
- ws.onerror = (e) => console.error('[WS] Error:', e);
40
- ws.onclose = () => {
41
- console.log('[WS] Disconnected');
126
+ } catch (err) {
127
+ console.error('[WS] Failed to forward message', err);
128
+ }
129
+ };
130
+
131
+ ws.onerror = (e) => {
132
+ console.error('[WS] Error:', e);
133
+ broadcastStatus('error');
134
+ };
135
+
136
+ ws.onclose = (event) => {
137
+ console.log('[WS] Disconnected:', event.code, event.reason);
42
138
  ws = null;
139
+ stopHeartbeat();
140
+ broadcastStatus('disconnected');
141
+
142
+ // Auto-reconnect on abnormal closure
143
+ if (event.code !== 1000) {
144
+ attemptReconnect();
145
+ }
43
146
  };
44
147
  },
45
148
 
@@ -52,22 +155,50 @@ const wsInstanceAPI = {
52
155
  },
53
156
 
54
157
  async close() {
55
- ws?.close();
158
+ if (reconnectTimer) {
159
+ clearTimeout(reconnectTimer);
160
+ reconnectTimer = null;
161
+ }
162
+ stopHeartbeat();
163
+ reconnectAttempts = 0;
164
+ lastUrl = null;
165
+ ws?.close(1000, 'Client request');
56
166
  ws = null;
167
+ broadcastStatus('disconnected');
57
168
  },
58
169
 
59
- onMessage(callback: (data: any) => void) {
170
+ onMessage(callback: (data: string | ArrayBuffer) => void) {
60
171
  onMessageCallback = callback;
61
172
  },
62
173
 
174
+ onStatus(callback: (status: string) => void) {
175
+ onStatusCallback = callback;
176
+ },
63
177
 
178
+ async getStatus(): Promise<string> {
179
+ if (!ws) return 'disconnected';
180
+ const map: Record<number, string> = {
181
+ [WebSocket.CONNECTING]: 'connecting',
182
+ [WebSocket.OPEN]: 'connected',
183
+ [WebSocket.CLOSING]: 'closing',
184
+ [WebSocket.CLOSED]: 'disconnected',
185
+ };
186
+ return map[ws.readyState] || 'unknown';
187
+ },
64
188
  };
65
189
 
66
190
  export type WSInstance = typeof wsInstanceAPI;
67
191
 
192
+ const ports = new Set<MessagePort>();
193
+
68
194
  self.onconnect = (event: MessageEvent) => {
69
195
  const port = event.ports[0];
196
+ const isFirst = ports.size === 0;
197
+ ports.add(port);
70
198
  port.start();
199
+ port.postMessage({ type: isFirst ? 'first-connect' : 'reconnect' });
200
+ port.addEventListener('message', (e: MessageEvent) => {
201
+ if (e.data?.type === 'close') ports.delete(port);
202
+ });
71
203
  expose(wsInstanceAPI, port);
72
204
  };
73
-
@@ -1,2 +1,4 @@
1
- import '@vitest/web-worker';
2
- import 'fake-indexeddb/auto';
1
+ import { defineWebWorkers } from '@vitest/web-worker/pure';
2
+ import 'fake-indexeddb/auto';
3
+
4
+ defineWebWorkers({ clone: 'native' });
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" />