@firtoz/drizzle-indexeddb 0.6.2 → 2.0.0

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,174 +0,0 @@
1
- import type {
2
- IDBProxyRequest,
3
- IDBProxyResponse,
4
- IDBProxySyncMessage,
5
- } from "./idb-proxy-types";
6
-
7
- /**
8
- * Client-side transport interface.
9
- * Implement this to connect to an IDB proxy server via any messaging system.
10
- *
11
- * Examples of transports:
12
- * - Chrome extension: chrome.runtime.sendMessage
13
- * - WebSocket: ws.send + onmessage
14
- * - MessageChannel: port.postMessage
15
- * - In-memory (for testing): direct function call
16
- */
17
- export interface IDBProxyClientTransport {
18
- /**
19
- * Send a request to the server and wait for a response.
20
- * The transport is responsible for correlating request/response by ID.
21
- */
22
- sendRequest(request: IDBProxyRequest): Promise<IDBProxyResponse>;
23
-
24
- /**
25
- * Register a handler for sync messages from the server.
26
- * These are broadcasts when other clients modify data.
27
- */
28
- onSync(handler: (message: IDBProxySyncMessage) => void): void;
29
-
30
- /**
31
- * Optional: Clean up resources when the client is done.
32
- */
33
- dispose?(): void;
34
- }
35
-
36
- /**
37
- * Server-side transport interface.
38
- * Implement this to receive requests from IDB proxy clients.
39
- */
40
- export interface IDBProxyServerTransport {
41
- /**
42
- * Register a handler for incoming requests.
43
- * The handler should process the request and return a response.
44
- * The transport is responsible for sending the response back to the client.
45
- */
46
- onRequest(
47
- handler: (request: IDBProxyRequest) => Promise<IDBProxyResponse>,
48
- ): void;
49
-
50
- /**
51
- * Broadcast a sync message to all clients except the one specified.
52
- * @param message The sync message to broadcast
53
- * @param excludeClientId Client ID to exclude from the broadcast (the initiator)
54
- */
55
- broadcast(message: IDBProxySyncMessage, excludeClientId: string): void;
56
-
57
- /**
58
- * Optional: Clean up resources when the server is done.
59
- */
60
- dispose?(): void;
61
- }
62
-
63
- /**
64
- * A simple in-memory transport for testing.
65
- * Connects a client and server directly without any actual messaging.
66
- */
67
- export function createInMemoryTransport(): {
68
- clientTransport: IDBProxyClientTransport;
69
- serverTransport: IDBProxyServerTransport;
70
- } {
71
- let requestHandler:
72
- | ((request: IDBProxyRequest) => Promise<IDBProxyResponse>)
73
- | null = null;
74
- let syncHandler: ((message: IDBProxySyncMessage) => void) | null = null;
75
- const clientId = `single-client-${Date.now()}`;
76
-
77
- const clientTransport: IDBProxyClientTransport = {
78
- async sendRequest(request): Promise<IDBProxyResponse> {
79
- if (!requestHandler) {
80
- return {
81
- id: request.id,
82
- type: "error",
83
- error: "No server handler registered",
84
- };
85
- }
86
- return requestHandler(request);
87
- },
88
- onSync(handler): void {
89
- syncHandler = handler;
90
- },
91
- };
92
-
93
- const serverTransport: IDBProxyServerTransport = {
94
- onRequest(handler): void {
95
- requestHandler = handler;
96
- },
97
- broadcast(message, excludeClientId): void {
98
- // In single-client transport, only send if the client isn't excluded
99
- if (syncHandler && excludeClientId !== clientId) {
100
- syncHandler(message);
101
- }
102
- },
103
- };
104
-
105
- return { clientTransport, serverTransport };
106
- }
107
-
108
- /**
109
- * A broadcast transport that supports multiple clients connecting to one server.
110
- * Useful for testing N-client scenarios.
111
- */
112
- export function createMultiClientTransport(): {
113
- createClientTransport: () => IDBProxyClientTransport;
114
- serverTransport: IDBProxyServerTransport;
115
- } {
116
- let requestHandler:
117
- | ((request: IDBProxyRequest) => Promise<IDBProxyResponse>)
118
- | null = null;
119
-
120
- // Track all connected clients and their sync handlers
121
- const clients = new Map<
122
- string,
123
- { syncHandler: ((message: IDBProxySyncMessage) => void) | null }
124
- >();
125
-
126
- const createClientTransport = (): IDBProxyClientTransport => {
127
- const clientId = `client-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
128
- clients.set(clientId, { syncHandler: null });
129
-
130
- return {
131
- async sendRequest(request: IDBProxyRequest): Promise<IDBProxyResponse> {
132
- if (!requestHandler) {
133
- return {
134
- id: request.id,
135
- type: "error",
136
- error: "No server handler registered",
137
- };
138
- }
139
- // Inject clientId into request
140
- return requestHandler({ ...request, clientId });
141
- },
142
- onSync(handler: (message: IDBProxySyncMessage) => void): void {
143
- const client = clients.get(clientId);
144
- if (client) {
145
- client.syncHandler = handler;
146
- }
147
- },
148
- dispose(): void {
149
- clients.delete(clientId);
150
- },
151
- };
152
- };
153
-
154
- const serverTransport: IDBProxyServerTransport = {
155
- onRequest(
156
- handler: (request: IDBProxyRequest) => Promise<IDBProxyResponse>,
157
- ): void {
158
- requestHandler = handler;
159
- },
160
- broadcast(message: IDBProxySyncMessage, excludeClientId: string): void {
161
- // Send to all clients except the one that initiated the change
162
- for (const [clientId, client] of clients) {
163
- if (clientId !== excludeClientId && client.syncHandler) {
164
- client.syncHandler(message);
165
- }
166
- }
167
- },
168
- dispose(): void {
169
- clients.clear();
170
- },
171
- };
172
-
173
- return { createClientTransport, serverTransport };
174
- }
@@ -1,77 +0,0 @@
1
- import type { KeyRangeSpec } from "../idb-types";
2
-
3
- /**
4
- * Request body types (without the common fields)
5
- */
6
- export type IDBProxyRequestBody =
7
- // Connection (client session, not database lifecycle)
8
- | { type: "connect" }
9
- | { type: "disconnect" }
10
- // Read operations
11
- | { type: "getAll"; storeName: string }
12
- | {
13
- type: "getAllByIndex";
14
- storeName: string;
15
- indexName: string;
16
- keyRange?: KeyRangeSpec;
17
- }
18
- | { type: "get"; storeName: string; key: IDBValidKey }
19
- // Write operations
20
- | { type: "add"; storeName: string; items: unknown[] }
21
- | { type: "put"; storeName: string; items: unknown[] }
22
- | { type: "delete"; storeName: string; keys: IDBValidKey[] }
23
- | { type: "clear"; storeName: string }
24
- // Metadata (read-only)
25
- | { type: "getVersion" }
26
- | { type: "hasStore"; storeName: string }
27
- | { type: "getStoreNames" }
28
- | { type: "getStoreIndexes"; storeName: string };
29
-
30
- /**
31
- * Full request type with all required fields
32
- */
33
- export type IDBProxyRequest = {
34
- /** Unique request ID for correlating responses */
35
- id: string;
36
- /** Unique client ID for tracking who sent the request */
37
- clientId: string;
38
- /** Database name */
39
- dbName: string;
40
- } & IDBProxyRequestBody;
41
-
42
- /**
43
- * Response types for IDB proxy operations
44
- */
45
- export type IDBProxyResponse =
46
- | { id: string; type: "success"; data?: unknown }
47
- | { id: string; type: "error"; error: string };
48
-
49
- /**
50
- * Sync messages broadcast to clients when data changes.
51
- * These are sent from server to clients to keep them in sync.
52
- */
53
- export type IDBProxySyncMessage = {
54
- /** Database that was modified */
55
- dbName: string;
56
- /** Store that was modified */
57
- storeName: string;
58
- } & (
59
- | { type: "sync:add"; items: unknown[] }
60
- | { type: "sync:put"; items: unknown[] }
61
- | { type: "sync:delete"; keys: IDBValidKey[] }
62
- | { type: "sync:truncate" }
63
- );
64
-
65
- /**
66
- * Generate a unique request ID
67
- */
68
- export function generateRequestId(): string {
69
- return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
70
- }
71
-
72
- /**
73
- * Generate a unique client ID
74
- */
75
- export function generateClientId(): string {
76
- return `client-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
77
- }
@@ -1,95 +0,0 @@
1
- import type { ExternalSyncHandler } from "@firtoz/drizzle-utils";
2
- import type { IDBProxySyncMessage } from "./idb-proxy-types";
3
-
4
- /**
5
- * Creates a sync message handler that translates proxy sync messages
6
- * into collection external sync events.
7
- *
8
- * @param storeName The store name to handle sync for
9
- * @param pushExternalSync The collection's external sync handler (from syncResult)
10
- * @param options Optional configuration
11
- *
12
- * @example
13
- * // Get the sync handler from the collection
14
- * const { pushExternalSync } = syncResult;
15
- *
16
- * // Create the adapter
17
- * const handleSync = createCollectionSyncHandler(
18
- * 'todo',
19
- * pushExternalSync,
20
- * { debug: true }
21
- * );
22
- *
23
- * // Connect to proxy client
24
- * proxyClient.onSync(handleSync);
25
- */
26
- export function createCollectionSyncHandler<T = unknown>(
27
- storeName: string,
28
- pushExternalSync: ExternalSyncHandler<T>,
29
- options?: { debug?: boolean },
30
- ): (message: IDBProxySyncMessage) => void {
31
- const debug = options?.debug ?? false;
32
-
33
- return (message: IDBProxySyncMessage) => {
34
- // Ignore messages for other stores
35
- if (message.storeName !== storeName) {
36
- return;
37
- }
38
-
39
- if (debug) {
40
- console.log(`[SyncAdapter:${storeName}]`, message.type, message);
41
- }
42
-
43
- switch (message.type) {
44
- case "sync:add":
45
- pushExternalSync({
46
- type: "insert",
47
- items: message.items as T[],
48
- });
49
- break;
50
-
51
- case "sync:put":
52
- pushExternalSync({
53
- type: "update",
54
- items: message.items as T[],
55
- });
56
- break;
57
-
58
- case "sync:delete":
59
- // For delete, we need the full items, but we only have keys
60
- // The collection will handle this via the key
61
- // We'll need to construct minimal items with just the id
62
- pushExternalSync({
63
- type: "delete",
64
- items: message.keys.map((key) => ({ id: key })) as T[],
65
- });
66
- break;
67
-
68
- case "sync:truncate":
69
- pushExternalSync({
70
- type: "truncate",
71
- });
72
- break;
73
- }
74
- };
75
- }
76
-
77
- /**
78
- * Combines multiple sync handlers into one.
79
- * Use when you have multiple stores to sync from the same proxy client.
80
- *
81
- * @example
82
- * const todoHandler = createCollectionSyncHandler('todo', todoSync);
83
- * const userHandler = createCollectionSyncHandler('user', userSync);
84
- *
85
- * proxyClient.onSync(combineSyncHandlers([todoHandler, userHandler]));
86
- */
87
- export function combineSyncHandlers(
88
- handlers: Array<(message: IDBProxySyncMessage) => void>,
89
- ): (message: IDBProxySyncMessage) => void {
90
- return (message: IDBProxySyncMessage) => {
91
- for (const handler of handlers) {
92
- handler(message);
93
- }
94
- };
95
- }
@@ -1,37 +0,0 @@
1
- // Proxy types
2
- export {
3
- type IDBProxyRequest,
4
- type IDBProxyRequestBody,
5
- type IDBProxyResponse,
6
- type IDBProxySyncMessage,
7
- generateRequestId,
8
- generateClientId,
9
- } from "./idb-proxy-types";
10
-
11
- // Transport interfaces
12
- export {
13
- type IDBProxyClientTransport,
14
- type IDBProxyServerTransport,
15
- createInMemoryTransport,
16
- createMultiClientTransport,
17
- } from "./idb-proxy-transport";
18
-
19
- // Proxy client
20
- export {
21
- IDBProxyClient,
22
- createProxyIDbCreator,
23
- type SyncHandler,
24
- } from "./idb-proxy-client";
25
-
26
- // Proxy server
27
- export {
28
- IDBProxyServer,
29
- createProxyServer,
30
- type IDBProxyServerOptions,
31
- } from "./idb-proxy-server";
32
-
33
- // Sync adapter (connects proxy sync to collection)
34
- export {
35
- createCollectionSyncHandler,
36
- combineSyncHandlers,
37
- } from "./idb-sync-adapter";