@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.
- package/CHANGELOG.md +31 -0
- package/package.json +10 -7
- package/src/collections/drizzle-indexeddb-collection.ts +310 -0
- package/src/context/DrizzleIndexedDBProvider.tsx +6 -91
- package/src/function-migrator.ts +3 -0
- package/src/idb-types.ts +2 -12
- package/src/index.ts +4 -32
- package/src/instrumented-idb-database.ts +1 -1
- package/src/native-idb-database.ts +4 -1
- package/src/standalone-collection.ts +8 -12
- package/src/collections/indexeddb-collection.ts +0 -579
- package/src/proxy/idb-proxy-client.ts +0 -341
- package/src/proxy/idb-proxy-server.ts +0 -313
- package/src/proxy/idb-proxy-transport.ts +0 -174
- package/src/proxy/idb-proxy-types.ts +0 -77
- package/src/proxy/idb-sync-adapter.ts +0 -95
- package/src/proxy/index.ts +0 -37
|
@@ -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
|
-
}
|
package/src/proxy/index.ts
DELETED
|
@@ -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";
|