@kbve/droid 0.0.2 → 0.0.4

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.
@@ -9,12 +9,25 @@ import { getModManager } from '../mod/mod-manager';
9
9
  import { scopeData } from './data';
10
10
  import { dispatchAsync, renderVNode } from './tools';
11
11
  import type { PanelPayload, PanelId } from '../types/panel-types';
12
- import { DroidEvents } from './events';
12
+ import { DroidEvents } from './events';
13
13
 
14
14
  const EXPECTED_DB_VERSION = '1.0.3';
15
+ let initialized = false;
15
16
 
16
- // * DeepProxy
17
+ export function resolveWorkerURL(name: string, fallback?: string): string {
18
+ if (!name)
19
+ throw new Error('[resolveWorkerURL] Worker name must be defined');
20
+
21
+ if (typeof window !== 'undefined') {
22
+ const globalMap = (window as any).kbveWorkerURLs;
23
+ if (globalMap?.[name]) return globalMap[name];
24
+ }
17
25
 
26
+ // No bundler-specific resolution: just return fallback or root-relative
27
+ return fallback ?? `/workers/${name}`;
28
+ }
29
+
30
+ // * DeepProxy
18
31
  function deepProxy<T>(obj: T): T {
19
32
  if (typeof obj === 'function') return proxy(obj) as T;
20
33
 
@@ -29,21 +42,69 @@ function deepProxy<T>(obj: T): T {
29
42
  return obj;
30
43
  }
31
44
 
32
- // * WebSocket
33
- async function initWsComlink(): Promise<Remote<WSInstance>> {
34
- const worker = new SharedWorker(new URL('./ws-worker', import.meta.url), {
35
- type: 'module',
36
- });
37
- worker.port.start();
38
- return wrap<WSInstance>(worker.port);
45
+ async function initWsComlink(opts?: {
46
+ workerRef?: SharedWorker;
47
+ workerURL?: string;
48
+ }): Promise<Remote<WSInstance>> {
49
+ let api: Remote<WSInstance> | null = null;
50
+
51
+ // 1. Try Vite-style import
52
+ try {
53
+ const worker = new SharedWorker(new URL('./ws-worker.ts', import.meta.url), {
54
+ type: 'module',
55
+ });
56
+ worker.port.start();
57
+ api = wrap<WSInstance>(worker.port);
58
+ } catch (err) {
59
+ console.warn('[DROID] ws-worker import.meta fallback failed:', err);
60
+ }
61
+
62
+ // 2. Try hardcoded path
63
+ if (!api) {
64
+ try {
65
+ const worker = new SharedWorker('/ws-worker.js', { type: 'module' });
66
+ worker.port.start();
67
+ api = wrap<WSInstance>(worker.port);
68
+ } catch (err) {
69
+ console.warn('[DROID] ws-worker hardcoded fallback failed:', err);
70
+ }
71
+ }
72
+
73
+ // 3. Try provided SharedWorker reference
74
+ if (!api && opts?.workerRef) {
75
+ try {
76
+ opts.workerRef.port.start();
77
+ api = wrap<WSInstance>(opts.workerRef.port);
78
+ } catch (err) {
79
+ console.warn('[DROID] ws-worker workerRef failed:', err);
80
+ }
81
+ }
82
+
83
+ // 4. Try provided URL
84
+ if (!api && opts?.workerURL) {
85
+ try {
86
+ const worker = new SharedWorker(opts.workerURL, { type: 'module' });
87
+ worker.port.start();
88
+ api = wrap<WSInstance>(worker.port);
89
+ } catch (err) {
90
+ console.warn('[DROID] ws-worker workerURL failed:', err);
91
+ }
92
+ }
93
+
94
+ // 5. Final failure
95
+ if (!api) {
96
+ console.error('[DROID] No WS Worker Comlink Initialized');
97
+ throw new Error('[DROID] Failed to initialize ws-worker');
98
+ }
99
+
100
+ return api;
39
101
  }
40
102
 
41
- // * Interface -> Moved to the panels.ts
42
103
 
43
104
  // * UIUX
44
-
45
105
  const uiuxState = persistentMap<{
46
- panelManager: Record<PanelId,
106
+ panelManager: Record<
107
+ PanelId,
47
108
  {
48
109
  open: boolean;
49
110
  payload?: PanelPayload;
@@ -71,13 +132,55 @@ const uiuxState = persistentMap<{
71
132
  },
72
133
  );
73
134
 
74
- const canvasWorker = wrap<CanvasWorkerAPI>(
75
- new Worker(new URL('./canvas-worker', import.meta.url), { type: 'module' }),
76
- );
135
+ async function initCanvasComlink(opts?: {
136
+ workerRef?: Worker;
137
+ workerURL?: string;
138
+ }): Promise<Remote<CanvasWorkerAPI>> {
139
+ // 1. Try Vite-style import resolution
140
+ try {
141
+ const worker = new Worker(
142
+ new URL('./canvas-worker.ts', import.meta.url),
143
+ { type: 'module' },
144
+ );
145
+ return wrap<CanvasWorkerAPI>(worker);
146
+ } catch (err) {
147
+ console.warn('[DROID] Vite-style canvas-worker import failed:', err);
148
+ }
149
+
150
+ // 2. Try hardcoded path fallback
151
+ try {
152
+ const worker = new Worker('./canvas-worker.js', { type: 'module' });
153
+ return wrap<CanvasWorkerAPI>(worker);
154
+ } catch (err) {
155
+ console.warn('[DROID] Fallback /canvas-worker.js failed:', err);
156
+ }
157
+
158
+ // 3. Try direct Worker instance
159
+ if (opts?.workerRef) {
160
+ try {
161
+ return wrap<CanvasWorkerAPI>(opts.workerRef);
162
+ } catch (err) {
163
+ console.warn('[DROID] Provided workerRef failed:', err);
164
+ }
165
+ }
166
+
167
+ // 4. Try provided URL
168
+ if (opts?.workerURL) {
169
+ try {
170
+ const worker = new Worker(opts.workerURL, { type: 'module' });
171
+ return wrap<CanvasWorkerAPI>(worker);
172
+ } catch (err) {
173
+ console.warn('[DROID] Provided workerURL failed:', err);
174
+ }
175
+ }
176
+
177
+ // 5. Failure
178
+ console.error('[DROID] No Canvas Comlink Initialized');
179
+ throw new Error('[DROID] Failed to initialize canvas worker');
180
+ }
77
181
 
78
182
  export const uiux = {
79
183
  state: uiuxState,
80
- worker: canvasWorker,
81
184
  openPanel(id: PanelId, payload?: PanelPayload) {
82
185
  const panels = { ...uiuxState.get().panelManager };
83
186
  panels[id] = { open: true, payload };
@@ -90,10 +193,7 @@ export const uiux = {
90
193
  uiuxState.setKey('panelManager', panels);
91
194
  },
92
195
 
93
- togglePanel(
94
- id: PanelId,
95
- payload?: PanelPayload,
96
- ) {
196
+ togglePanel(id: PanelId, payload?: PanelPayload) {
97
197
  const panels = { ...uiuxState.get().panelManager };
98
198
  const isOpen = panels[id]?.open ?? false;
99
199
  panels[id] = { open: !isOpen, payload: !isOpen ? payload : undefined };
@@ -114,23 +214,20 @@ export const uiux = {
114
214
  delete toasts[id];
115
215
  uiuxState.setKey('toastManager', toasts);
116
216
  },
117
-
118
217
  async dispatchCanvasRequest(
119
218
  panelId: PanelId,
120
219
  canvasEl: HTMLCanvasElement,
121
220
  mode: 'static' | 'animated' | 'dynamic' = 'animated',
122
221
  ) {
123
222
  const offscreen = canvasEl.transferControlToOffscreen();
124
- await this.worker.bindCanvas(panelId, offscreen, mode);
223
+ await window.kbve?.uiux?.worker?.bindCanvas(panelId, offscreen, mode);
125
224
  },
126
225
 
127
226
  closeAllPanels() {
128
227
  const panels = { ...uiuxState.get().panelManager };
129
228
  console.log('error panel is closing');
130
229
 
131
- for (const id of Object.keys(panels) as Array<
132
- PanelId
133
- >) {
230
+ for (const id of Object.keys(panels) as Array<PanelId>) {
134
231
  panels[id] = { open: false, payload: undefined };
135
232
  }
136
233
 
@@ -235,12 +332,64 @@ function initSWComlink() {
235
332
  channel.port1.start();
236
333
  }
237
334
 
238
- async function initStorageComlink(): Promise<Remote<LocalStorageAPI>> {
239
- const worker = new SharedWorker(new URL('./db-worker', import.meta.url), {
240
- type: 'module',
241
- });
242
- worker.port.start();
243
- const api = wrap<LocalStorageAPI>(worker.port);
335
+ async function initStorageComlink(opts?: {
336
+ workerRef?: SharedWorker;
337
+ workerURL?: string;
338
+ }): Promise<Remote<LocalStorageAPI>> {
339
+ let api: Remote<LocalStorageAPI> | null = null;
340
+ // 1. Try Vite-style import
341
+ try {
342
+ const worker = new SharedWorker(
343
+ new URL('./db-worker.ts', import.meta.url),
344
+ {
345
+ type: 'module',
346
+ },
347
+ );
348
+ worker.port.start();
349
+ api = wrap<LocalStorageAPI>(worker.port);
350
+ } catch (err) {
351
+ console.warn('[DROID] db-worker import.meta fallback failed:', err);
352
+ }
353
+
354
+ // 2. Try hardcoded path
355
+ if (!api) {
356
+ try {
357
+ const worker = new SharedWorker('/db-worker.js', {
358
+ type: 'module',
359
+ });
360
+ worker.port.start();
361
+ api = wrap<LocalStorageAPI>(worker.port);
362
+ } catch (err) {
363
+ console.warn('[DROID] db-worker hardcoded fallback failed:', err);
364
+ }
365
+ }
366
+
367
+ // 3. Try provided SharedWorker reference
368
+ if (!api && opts?.workerRef) {
369
+ try {
370
+ opts.workerRef.port.start();
371
+ api = wrap<LocalStorageAPI>(opts.workerRef.port);
372
+ } catch (err) {
373
+ console.warn('[DROID] db-worker workerRef failed:', err);
374
+ }
375
+ }
376
+
377
+ // 4. Try provided URL
378
+ if (!api && opts?.workerURL) {
379
+ try {
380
+ const worker = new SharedWorker(opts.workerURL, { type: 'module' });
381
+ worker.port.start();
382
+ api = wrap<LocalStorageAPI>(worker.port);
383
+ } catch (err) {
384
+ console.warn('[DROID] db-worker workerURL failed:', err);
385
+ }
386
+ }
387
+
388
+ // 5. Final failure
389
+ if (!api) {
390
+ console.error('[DROID] No DB Worker Comlink Initialized');
391
+ throw new Error('[DROID] Failed to initialize db-worker');
392
+ }
244
393
 
245
394
  const version = await api.getVersion();
246
395
  if (version !== EXPECTED_DB_VERSION) {
@@ -255,8 +404,6 @@ async function initStorageComlink(): Promise<Remote<LocalStorageAPI>> {
255
404
  return api;
256
405
  }
257
406
 
258
- let initialized = false;
259
-
260
407
  // * Bridge
261
408
  export function bridgeWsToDb(
262
409
  ws: Remote<WSInstance>,
@@ -273,7 +420,16 @@ export function bridgeWsToDb(
273
420
  }
274
421
 
275
422
  // * MAIN
276
- export async function main() {
423
+ export async function main(opts?: {
424
+ workerURLs?: Record<string, string>;
425
+ workerRefs?: {
426
+ canvasWorker?: Worker;
427
+ dbWorker?: SharedWorker;
428
+ wsWorker?: SharedWorker;
429
+ };
430
+ }) {
431
+ console.log('[DROID]: Main<T>');
432
+
277
433
  if (!initialized) {
278
434
  initialized = true;
279
435
 
@@ -288,68 +444,87 @@ export async function main() {
288
444
  }
289
445
  }
290
446
 
447
+ console.log('[DROID] Main<T> => Worker URLs', opts?.workerURLs);
448
+
291
449
  const needsInit =
292
450
  !window.kbve?.api || !window.kbve?.i18n || !window.kbve?.uiux;
293
451
 
294
452
  if (needsInit) {
295
- const api = await initStorageComlink();
296
- const ws = await initWsComlink();
297
- const mod = await getModManager();
298
- const events = DroidEvents;
299
-
300
- for (const handle of Object.values(mod.registry)) {
301
- if (typeof handle.instance.init === 'function') {
302
- await handle.instance.init({
303
- emitFromWorker: uiux.emitFromWorker,
304
- });
305
- }
306
- console.log('[Event] -> Fire Mod Ready');
307
- events.emit('droid-mod-ready', {
308
- meta: handle.meta,
309
- timestamp: Date.now(),
453
+ try {
454
+ console.log('[DROID] Main<T> => Worker => CanvasComlink');
455
+ const canvas = await initCanvasComlink({
456
+ workerRef: opts?.workerRefs?.canvasWorker,
457
+ workerURL: opts?.workerURLs?.['canvasWorker'],
310
458
  });
311
- }
459
+ console.log('[DROID] Main<T> => Worker => StorageComlink');
460
+ const api = await initStorageComlink({
461
+ workerURL: typeof opts?.workerURLs?.['dbWorker'] === 'string'
462
+ ? opts.workerURLs['dbWorker']
463
+ : undefined,
464
+ workerRef: opts?.workerRefs?.dbWorker,
465
+ });
466
+ console.log('[DROID] Main<T> => Worker => WsComlink');
467
+ const ws = await initWsComlink({
468
+ workerRef: opts?.workerRefs?.wsWorker,
469
+ workerURL: opts?.workerURLs?.['wsWorker'],
470
+ });
471
+
472
+ console.log('[DROID] Main<T> => Worker => ModManager');
473
+ const mod = await getModManager(
474
+ (url) => opts?.workerURLs?.[url] ?? url,
475
+ );
476
+ const events = DroidEvents;
312
477
 
313
- bridgeWsToDb(ws, api);
478
+ for (const handle of Object.values(mod.registry)) {
479
+ if (typeof handle.instance.init === 'function') {
480
+ await handle.instance.init({
481
+ emitFromWorker: uiux.emitFromWorker,
482
+ });
483
+ }
484
+ console.log('[Event] -> Fire Mod Ready');
485
+ events.emit('droid-mod-ready', {
486
+ meta: handle.meta,
487
+ timestamp: Date.now(),
488
+ });
489
+ }
314
490
 
315
- const data = scopeData;
316
- i18n.api = api;
317
- i18n.ready = i18n.hydrateLocale('en');
491
+ bridgeWsToDb(ws, api);
318
492
 
319
- window.kbve = {
320
- ...(window.kbve || {}),
321
- api,
322
- i18n,
323
- uiux,
324
- ws,
325
- data,
326
- mod,
327
- events,
328
- };
493
+ const data = scopeData;
494
+ i18n.api = api;
495
+ i18n.ready = i18n.hydrateLocale('en');
329
496
 
330
- //window.kbve = deepProxy(window.kbve);
497
+ window.kbve = {
498
+ ...(window.kbve || {}),
499
+ api,
500
+ i18n,
501
+ uiux: { ...uiux, worker: canvas },
502
+ ws,
503
+ data,
504
+ mod,
505
+ events,
506
+ };
331
507
 
332
- await i18n.ready;
508
+ await i18n.ready;
333
509
 
334
- window.kbve.events.emit('droid-ready', {
335
- timestamp: Date.now(),
336
- });
337
-
338
- document.addEventListener('astro:page-load', () => {
339
- console.debug('[KBVE] Re-dispatched droid-ready after astro:page-load');
340
- window.kbve?.events.emit('droid-ready', {
510
+ window.kbve.events.emit('droid-ready', {
341
511
  timestamp: Date.now(),
342
512
  });
343
- });
344
513
 
345
- // document.addEventListener('astro:page-load', () => {
346
- // console.debug('[KBVE] Re-dispatched droid-ready after DomContentLoaded');
347
- // window.kbve?.events.emit('droid-ready', {
348
- // timestamp: Date.now(),
349
- // });
350
- // });
514
+ document.addEventListener('astro:page-load', () => {
515
+ console.debug(
516
+ '[KBVE] Re-dispatched droid-ready after astro:page-load',
517
+ );
518
+ window.kbve?.events.emit('droid-ready', {
519
+ timestamp: Date.now(),
520
+ });
521
+ });
351
522
 
352
- console.log('[KBVE] Global API ready');
523
+ console.log('[KBVE] Global API ready');
524
+ } catch (err) {
525
+ console.error('[DROID] Initialization error:', err);
526
+ throw err;
527
+ }
353
528
  } else {
354
529
  console.log('[KBVE] Already initialized');
355
530
  }