@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.
- package/comlink.js +1 -7
- package/droid.mjs +191 -61
- package/index.d.ts +29 -0
- package/index.js +9054 -2763
- 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 +291 -145
- 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 +2680 -2147
- 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 +107 -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 +9337 -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 +12635 -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 +11 -0
- package/lib/workers/ws-worker.d.ts.map +1 -0
- package/lib/workers/ws-worker.js +174 -115
- package/main.js +4875 -0
- package/package.json +17 -6
- package/reference.js +688 -228
- package/src/index.ts +38 -13
- package/src/lib/droid.spec.ts +14 -19
- package/src/lib/droid.ts +48 -4
- package/src/lib/gateway/SupabaseGateway.ts +183 -0
- package/src/lib/gateway/WorkerCommunication.ts +132 -0
- package/src/lib/gateway/WorkerPool.ts +304 -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 +112 -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 +68 -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 +30 -0
- package/src/lib/state/index.ts +34 -0
- package/src/lib/state/overlay-manager.ts +125 -0
- package/src/lib/state/router.ts +24 -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.ts +76 -0
- package/src/lib/types/bento.spec.ts +116 -0
- package/src/lib/types/bento.ts +94 -55
- package/src/lib/types/event-types.spec.ts +93 -0
- package/src/lib/types/event-types.ts +39 -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 +59 -36
- package/src/lib/workers/db-worker.ts +79 -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 +510 -316
- package/src/lib/workers/supabase-db-worker.ts +221 -0
- package/src/lib/workers/supabase-shared-worker.ts +572 -0
- package/src/lib/workers/validate.ts +64 -0
- package/src/lib/workers/ws-worker.ts +156 -25
- 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 +10 -1123
- package/src/types.d.ts +0 -31
package/src/index.ts
CHANGED
|
@@ -1,21 +1,46 @@
|
|
|
1
|
+
export type { KBVEGlobal } from './types';
|
|
2
|
+
|
|
1
3
|
export * from './lib/droid';
|
|
2
4
|
export * from './lib/types/bento';
|
|
5
|
+
export * from './lib/types/event-types';
|
|
6
|
+
export * from './lib/types/panel-types';
|
|
7
|
+
export * from './lib/types/ui-event-types';
|
|
8
|
+
export * from './lib/workers/events';
|
|
3
9
|
export * from './lib/mod/mod-urls';
|
|
10
|
+
export * from './lib/gateway';
|
|
11
|
+
export * from './lib/state';
|
|
12
|
+
|
|
13
|
+
export type { VirtualNode } from './lib/types/modules';
|
|
14
|
+
|
|
15
|
+
// Worker-side validation
|
|
16
|
+
export { validateUIMessage } from './lib/workers/validate';
|
|
17
|
+
export type {
|
|
18
|
+
ValidatedWorkerMessage,
|
|
19
|
+
UIMessageType,
|
|
20
|
+
} from './lib/workers/validate';
|
|
21
|
+
|
|
22
|
+
// Canvas UI renderer (for direct worker usage)
|
|
23
|
+
export { CanvasUIRenderer } from './lib/workers/canvas-ui-renderer';
|
|
24
|
+
|
|
25
|
+
// Vite ?worker&url imports — bundles each worker as JS and returns the URL string
|
|
26
|
+
import canvasWorkerUrl from './lib/workers/canvas-worker?worker&url';
|
|
27
|
+
import dbWorkerUrl from './lib/workers/db-worker?worker&url';
|
|
28
|
+
import wsWorkerUrl from './lib/workers/ws-worker?worker&url';
|
|
29
|
+
import supabaseSharedWorkerUrl from './lib/workers/supabase-shared-worker?worker&url';
|
|
30
|
+
import supabaseDbWorkerUrl from './lib/workers/supabase-db-worker?worker&url';
|
|
4
31
|
|
|
5
32
|
export const workerURLs = {
|
|
6
|
-
canvasWorker:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
wsWorker: new URL('./lib/workers/ws-worker.js', import.meta.url),
|
|
33
|
+
canvasWorker: canvasWorkerUrl,
|
|
34
|
+
dbWorker: dbWorkerUrl,
|
|
35
|
+
wsWorker: wsWorkerUrl,
|
|
36
|
+
supabaseSharedWorker: supabaseSharedWorkerUrl,
|
|
37
|
+
supabaseDbWorker: supabaseDbWorkerUrl,
|
|
12
38
|
};
|
|
13
39
|
|
|
14
40
|
export const workerStrings = {
|
|
15
|
-
canvasWorker:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
};
|
|
41
|
+
canvasWorker: canvasWorkerUrl,
|
|
42
|
+
dbWorker: dbWorkerUrl,
|
|
43
|
+
wsWorker: wsWorkerUrl,
|
|
44
|
+
supabaseSharedWorker: supabaseSharedWorkerUrl,
|
|
45
|
+
supabaseDbWorker: supabaseDbWorkerUrl,
|
|
46
|
+
};
|
package/src/lib/droid.spec.ts
CHANGED
|
@@ -1,26 +1,21 @@
|
|
|
1
|
-
import { describe, it, expect,
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
2
|
import { droid } from './droid';
|
|
3
3
|
|
|
4
4
|
beforeEach(() => {
|
|
5
|
-
delete (window as
|
|
5
|
+
delete (window as unknown as { kbve?: unknown }).kbve;
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
describe('droid', () => {
|
|
9
|
-
it(
|
|
10
|
-
|
|
11
|
-
async () => {
|
|
12
|
-
const result = await droid();
|
|
9
|
+
it('should initialize and attach to window.kbve', async () => {
|
|
10
|
+
const result = await droid();
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
10_000,
|
|
25
|
-
);
|
|
26
|
-
});
|
|
12
|
+
expect(result).toEqual({ initialized: true });
|
|
13
|
+
expect(window.kbve).toBeDefined();
|
|
14
|
+
expect(window.kbve?.api).toBeDefined();
|
|
15
|
+
expect(window.kbve?.i18n).toBeDefined();
|
|
16
|
+
expect(window.kbve?.uiux).toBeDefined();
|
|
17
|
+
expect(window.kbve?.ws).toBeDefined();
|
|
18
|
+
expect(window.kbve?.data).toBeDefined();
|
|
19
|
+
expect(window.kbve?.mod).toBeDefined();
|
|
20
|
+
}, 10_000);
|
|
21
|
+
});
|
package/src/lib/droid.ts
CHANGED
|
@@ -1,7 +1,51 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {
|
|
2
|
+
downscale,
|
|
3
|
+
upscale,
|
|
4
|
+
getScaleLevel,
|
|
5
|
+
type DroidScaleLevel,
|
|
6
|
+
} from './workers/main.js';
|
|
7
|
+
|
|
8
|
+
export async function droid(opts?: {
|
|
9
|
+
workerURLs?: Record<string, string>;
|
|
10
|
+
workerRefs?: {
|
|
11
|
+
canvasWorker?: Worker;
|
|
12
|
+
dbWorker?: SharedWorker;
|
|
13
|
+
wsWorker?: SharedWorker;
|
|
14
|
+
};
|
|
15
|
+
i18nPath?: string;
|
|
16
|
+
dataPath?: string;
|
|
17
|
+
/** Maximum time (ms) to wait for worker initialization before rejecting. Default: no timeout. */
|
|
18
|
+
initTimeout?: number;
|
|
19
|
+
}): Promise<{ initialized: boolean }> {
|
|
2
20
|
console.log('[DROID]: droid<T>');
|
|
21
|
+
|
|
3
22
|
const { main } = await import('./workers/main.js');
|
|
4
|
-
|
|
5
|
-
|
|
23
|
+
|
|
24
|
+
const initPromise = main({
|
|
25
|
+
workerURLs: opts?.workerURLs,
|
|
26
|
+
workerRefs: opts?.workerRefs,
|
|
27
|
+
i18nPath: opts?.i18nPath,
|
|
28
|
+
dataPath: opts?.dataPath,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (opts?.initTimeout && opts.initTimeout > 0) {
|
|
32
|
+
const timeout = new Promise<never>((_, reject) =>
|
|
33
|
+
setTimeout(
|
|
34
|
+
() =>
|
|
35
|
+
reject(
|
|
36
|
+
new Error(
|
|
37
|
+
`[DROID] Worker init timed out after ${opts.initTimeout}ms`,
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
opts.initTimeout,
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
await Promise.race([initPromise, timeout]);
|
|
44
|
+
} else {
|
|
45
|
+
await initPromise;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log('[DROID]: droid<T> => await WorkerRefs + URLs');
|
|
49
|
+
|
|
6
50
|
return { initialized: true };
|
|
7
|
-
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Unified gateway for Supabase operations with automatic strategy selection
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ISupabaseStrategy,
|
|
5
|
+
SelectOptions,
|
|
6
|
+
SessionResponse,
|
|
7
|
+
WebSocketStatus,
|
|
8
|
+
StrategyType,
|
|
9
|
+
GatewayConfig,
|
|
10
|
+
} from './types';
|
|
11
|
+
import {
|
|
12
|
+
detectCapabilities,
|
|
13
|
+
selectStrategy,
|
|
14
|
+
logCapabilities,
|
|
15
|
+
getStrategyDescription,
|
|
16
|
+
} from './capabilities';
|
|
17
|
+
import { SharedWorkerStrategy } from './strategies/SharedWorkerStrategy';
|
|
18
|
+
import { WebWorkerStrategy } from './strategies/WebWorkerStrategy';
|
|
19
|
+
import { DirectStrategy } from './strategies/DirectStrategy';
|
|
20
|
+
|
|
21
|
+
// Vite ?worker&url imports — bundles workers as JS and returns their URL
|
|
22
|
+
import defaultSharedWorkerUrl from '../workers/supabase-shared-worker?worker&url';
|
|
23
|
+
import defaultDbWorkerUrl from '../workers/supabase-db-worker?worker&url';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* SupabaseGateway: Unified interface for all Supabase operations
|
|
27
|
+
*
|
|
28
|
+
* Automatically selects the best strategy based on browser capabilities:
|
|
29
|
+
* 1. SharedWorker + DB Worker Pool (desktop: Chrome, Firefox, Edge)
|
|
30
|
+
* 2. WebSocket Worker + DB Worker Pool (Android, Safari)
|
|
31
|
+
* 3. Direct/Main thread (fallback)
|
|
32
|
+
*
|
|
33
|
+
* All strategies implement the same interface, so application code is unchanged.
|
|
34
|
+
*/
|
|
35
|
+
export class SupabaseGateway implements ISupabaseStrategy {
|
|
36
|
+
private strategy: ISupabaseStrategy;
|
|
37
|
+
private strategyType: StrategyType;
|
|
38
|
+
|
|
39
|
+
constructor(config: GatewayConfig = {}) {
|
|
40
|
+
const capabilities = detectCapabilities();
|
|
41
|
+
logCapabilities(capabilities);
|
|
42
|
+
|
|
43
|
+
this.strategyType =
|
|
44
|
+
config.forceStrategy || selectStrategy(capabilities);
|
|
45
|
+
console.log(
|
|
46
|
+
`[SupabaseGateway] Selected strategy: ${getStrategyDescription(this.strategyType)}`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const poolSize = config.poolSize ?? 3;
|
|
50
|
+
const workerUrls = config.workerUrls ?? {};
|
|
51
|
+
|
|
52
|
+
switch (this.strategyType) {
|
|
53
|
+
case 'shared-worker': {
|
|
54
|
+
const sharedUrl =
|
|
55
|
+
workerUrls.sharedWorker ?? defaultSharedWorkerUrl;
|
|
56
|
+
const dbUrl = workerUrls.dbWorker ?? defaultDbWorkerUrl;
|
|
57
|
+
this.strategy = new SharedWorkerStrategy(
|
|
58
|
+
sharedUrl,
|
|
59
|
+
dbUrl,
|
|
60
|
+
poolSize,
|
|
61
|
+
);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'web-worker': {
|
|
65
|
+
const wsUrl =
|
|
66
|
+
workerUrls.webSocketWorker ?? defaultSharedWorkerUrl;
|
|
67
|
+
const dbUrl = workerUrls.dbWorker ?? defaultDbWorkerUrl;
|
|
68
|
+
this.strategy = new WebWorkerStrategy(wsUrl, dbUrl, poolSize);
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
case 'direct':
|
|
72
|
+
this.strategy = new DirectStrategy();
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getStrategyType(): StrategyType {
|
|
78
|
+
return this.strategyType;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getStrategyDescription(): string {
|
|
82
|
+
return getStrategyDescription(this.strategyType);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Delegate all methods to the selected strategy
|
|
86
|
+
|
|
87
|
+
init(
|
|
88
|
+
url: string,
|
|
89
|
+
anonKey: string,
|
|
90
|
+
options?: Record<string, unknown>,
|
|
91
|
+
): Promise<SessionResponse> {
|
|
92
|
+
return this.strategy.init(url, anonKey, options);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
on(event: string, callback: (payload: unknown) => void): () => void {
|
|
96
|
+
return this.strategy.on(event, callback);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getSession(): Promise<SessionResponse> {
|
|
100
|
+
return this.strategy.getSession();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
signInWithPassword(
|
|
104
|
+
email: string,
|
|
105
|
+
password: string,
|
|
106
|
+
): Promise<SessionResponse> {
|
|
107
|
+
return this.strategy.signInWithPassword(email, password);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
signOut(): Promise<void> {
|
|
111
|
+
return this.strategy.signOut();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
select(table: string, opts?: SelectOptions): Promise<unknown[]> {
|
|
115
|
+
return this.strategy.select(table, opts);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
insert(
|
|
119
|
+
table: string,
|
|
120
|
+
data: Record<string, unknown> | Record<string, unknown>[],
|
|
121
|
+
): Promise<unknown[]> {
|
|
122
|
+
return this.strategy.insert(table, data);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
update(
|
|
126
|
+
table: string,
|
|
127
|
+
data: Record<string, unknown>,
|
|
128
|
+
match: Record<string, unknown>,
|
|
129
|
+
): Promise<unknown[]> {
|
|
130
|
+
return this.strategy.update(table, data, match);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
upsert(
|
|
134
|
+
table: string,
|
|
135
|
+
data: Record<string, unknown> | Record<string, unknown>[],
|
|
136
|
+
): Promise<unknown[]> {
|
|
137
|
+
return this.strategy.upsert(table, data);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
delete(table: string, match: Record<string, unknown>): Promise<unknown[]> {
|
|
141
|
+
return this.strategy.delete(table, match);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
rpc(fn: string, args?: Record<string, unknown>): Promise<unknown> {
|
|
145
|
+
return this.strategy.rpc(fn, args);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
subscribePostgres(
|
|
149
|
+
key: string,
|
|
150
|
+
params: Record<string, unknown>,
|
|
151
|
+
callback: (payload: unknown) => void,
|
|
152
|
+
): () => void {
|
|
153
|
+
return this.strategy.subscribePostgres(key, params, callback);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
connectWebSocket(wsUrl?: string): Promise<WebSocketStatus> {
|
|
157
|
+
return this.strategy.connectWebSocket(wsUrl);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
disconnectWebSocket(): Promise<void> {
|
|
161
|
+
return this.strategy.disconnectWebSocket();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
sendWebSocketMessage(data: unknown): Promise<void> {
|
|
165
|
+
return this.strategy.sendWebSocketMessage(data);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getWebSocketStatus(): Promise<WebSocketStatus> {
|
|
169
|
+
return this.strategy.getWebSocketStatus();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
onWebSocketMessage(callback: (message: unknown) => void): () => void {
|
|
173
|
+
return this.strategy.onWebSocketMessage(callback);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
onWebSocketStatus(callback: (status: WebSocketStatus) => void): () => void {
|
|
177
|
+
return this.strategy.onWebSocketStatus(callback);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
terminate() {
|
|
181
|
+
this.strategy.terminate();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// Unified communication layer for all workers (SharedWorker, WebWorker, main thread)
|
|
2
|
+
// Uses BroadcastChannel for real-time events
|
|
3
|
+
|
|
4
|
+
import type { BroadcastEvent } from './types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WorkerCommunication: Unified communication layer for workers
|
|
8
|
+
*
|
|
9
|
+
* Features:
|
|
10
|
+
* - BroadcastChannel for real-time events
|
|
11
|
+
* - Works in SharedWorker, WebWorker, and main thread
|
|
12
|
+
*
|
|
13
|
+
* Note: This layer does NOT open IndexedDB. The SharedWorker is the single
|
|
14
|
+
* writer to the auth database (sb-auth-v2). All other contexts receive
|
|
15
|
+
* auth state via BroadcastChannel or postMessage.
|
|
16
|
+
*/
|
|
17
|
+
export class WorkerCommunication {
|
|
18
|
+
private bc: BroadcastChannel | null = null;
|
|
19
|
+
private listeners = new Map<string, Set<(payload: unknown) => void>>();
|
|
20
|
+
private channelName: string;
|
|
21
|
+
|
|
22
|
+
constructor(channelName = 'supabase_events') {
|
|
23
|
+
this.channelName = channelName;
|
|
24
|
+
this.initBroadcastChannel();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private initBroadcastChannel() {
|
|
28
|
+
try {
|
|
29
|
+
if (typeof BroadcastChannel !== 'undefined') {
|
|
30
|
+
this.bc = new BroadcastChannel(this.channelName);
|
|
31
|
+
this.bc.onmessage = (e: MessageEvent) =>
|
|
32
|
+
this.handleBroadcast(e.data);
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error(
|
|
36
|
+
'[WorkerCommunication] Failed to initialize BroadcastChannel:',
|
|
37
|
+
err,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Broadcast an event to all workers and tabs
|
|
44
|
+
*/
|
|
45
|
+
broadcast(event: BroadcastEvent) {
|
|
46
|
+
if (this.bc) {
|
|
47
|
+
try {
|
|
48
|
+
this.bc.postMessage(event);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error('[WorkerCommunication] Broadcast failed:', err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private handleBroadcast(event: BroadcastEvent) {
|
|
56
|
+
const { type, key } = event;
|
|
57
|
+
|
|
58
|
+
if (type === 'realtime' && key) {
|
|
59
|
+
this.emit(`realtime:${key}`, event.payload);
|
|
60
|
+
} else {
|
|
61
|
+
this.emit(type, event);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Subscribe to events (local and broadcast)
|
|
67
|
+
*/
|
|
68
|
+
on(event: string, callback: (payload: unknown) => void): () => void {
|
|
69
|
+
if (!this.listeners.has(event)) {
|
|
70
|
+
this.listeners.set(event, new Set());
|
|
71
|
+
}
|
|
72
|
+
this.listeners.get(event)?.add(callback);
|
|
73
|
+
|
|
74
|
+
return () => {
|
|
75
|
+
const set = this.listeners.get(event);
|
|
76
|
+
if (set) {
|
|
77
|
+
set.delete(callback);
|
|
78
|
+
if (set.size === 0) {
|
|
79
|
+
this.listeners.delete(event);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private emit(event: string, payload: unknown) {
|
|
86
|
+
const listeners = this.listeners.get(event);
|
|
87
|
+
if (listeners) {
|
|
88
|
+
listeners.forEach((cb) => {
|
|
89
|
+
try {
|
|
90
|
+
cb(payload);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error(
|
|
93
|
+
`[WorkerCommunication] Listener error for ${event}:`,
|
|
94
|
+
err,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if BroadcastChannel is available
|
|
103
|
+
*/
|
|
104
|
+
hasBroadcastChannel(): boolean {
|
|
105
|
+
return this.bc !== null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Close connections
|
|
110
|
+
*/
|
|
111
|
+
close() {
|
|
112
|
+
if (this.bc) {
|
|
113
|
+
this.bc.close();
|
|
114
|
+
this.bc = null;
|
|
115
|
+
}
|
|
116
|
+
this.listeners.clear();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Create a singleton instance for use across workers
|
|
122
|
+
*/
|
|
123
|
+
let _instance: WorkerCommunication | null = null;
|
|
124
|
+
|
|
125
|
+
export function getWorkerCommunication(
|
|
126
|
+
channelName?: string,
|
|
127
|
+
): WorkerCommunication {
|
|
128
|
+
if (!_instance) {
|
|
129
|
+
_instance = new WorkerCommunication(channelName);
|
|
130
|
+
}
|
|
131
|
+
return _instance;
|
|
132
|
+
}
|