reflectdb 0.1.0 → 0.1.2
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/README.md +500 -40
- package/dist/cjs/client/index.cjs +32 -17
- package/dist/cjs/client/index.d.cts +18 -3
- package/dist/cjs/client/storage/indexeddb.d.cts +1 -1
- package/dist/cjs/core/index.cjs +28 -28
- package/dist/cjs/core/index.d.cts +21 -9
- package/dist/cjs/react/index.cjs +48 -33
- package/dist/cjs/react/index.d.cts +23 -5
- package/dist/cjs/server/drizzle.cjs +2 -2
- package/dist/cjs/server/drizzle.d.cts +1 -1
- package/dist/cjs/server/ephemeral/index.cjs +180 -0
- package/dist/cjs/server/ephemeral/index.d.cts +102 -0
- package/dist/cjs/server/ephemeral/redis.cjs +226 -0
- package/dist/cjs/server/ephemeral/redis.d.cts +136 -0
- package/dist/cjs/server/index.cjs +238 -87
- package/dist/cjs/server/index.d.cts +196 -5
- package/dist/cjs/svelte/index.cjs +34 -19
- package/dist/cjs/svelte/index.d.cts +18 -3
- package/dist/cjs/transport/bun-ws.cjs +9 -9
- package/dist/cjs/transport/bun-ws.d.cts +1 -1
- package/dist/cjs/transport/polling.cjs +11 -11
- package/dist/cjs/transport/polling.d.cts +1 -1
- package/dist/cjs/transport/sse.cjs +11 -11
- package/dist/cjs/transport/sse.d.cts +1 -1
- package/dist/cjs/transport/ws.cjs +11 -11
- package/dist/cjs/transport/ws.d.cts +1 -1
- package/dist/cjs/vanilla/index.cjs +34 -19
- package/dist/cjs/vanilla/index.d.cts +18 -3
- package/dist/client/index.d.ts +18 -3
- package/dist/client/index.js +7 -7
- package/dist/client/storage/indexeddb.d.ts +1 -1
- package/dist/client/storage/indexeddb.js +1 -1
- package/dist/core/index.d.ts +21 -9
- package/dist/core/index.js +20 -20
- package/dist/react/index.d.ts +23 -5
- package/dist/react/index.js +23 -23
- package/dist/server/drizzle.d.ts +1 -1
- package/dist/server/drizzle.js +3 -3
- package/dist/server/ephemeral/index.d.ts +102 -0
- package/dist/server/ephemeral/index.js +9 -0
- package/dist/server/ephemeral/redis.d.ts +136 -0
- package/dist/server/ephemeral/redis.js +186 -0
- package/dist/server/index.d.ts +196 -5
- package/dist/server/index.js +184 -163
- package/dist/shared/{esm-ytrd3hbq.js → esm-8qbr4y0d.js} +18 -3
- package/dist/shared/{esm-wkwx6bd9.js → esm-ck88h30s.js} +10 -10
- package/dist/shared/esm-g0marxk7.js +134 -0
- package/dist/svelte/index.d.ts +18 -3
- package/dist/svelte/index.js +9 -9
- package/dist/transport/bun-ws.d.ts +1 -1
- package/dist/transport/bun-ws.js +1 -1
- package/dist/transport/polling.d.ts +1 -1
- package/dist/transport/polling.js +3 -3
- package/dist/transport/sse.d.ts +1 -1
- package/dist/transport/sse.js +3 -3
- package/dist/transport/ws.d.ts +1 -1
- package/dist/transport/ws.js +3 -3
- package/dist/vanilla/index.d.ts +18 -3
- package/dist/vanilla/index.js +9 -9
- package/package.json +66 -44
- /package/dist/shared/{esm-b7xs9cde.js → esm-k7kedp3y.js} +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// src/server/ephemeral/memory.ts
|
|
2
|
+
var DEFAULT_MAX_ENTRIES = 1e4;
|
|
3
|
+
function indexKey(room, key, clientId) {
|
|
4
|
+
return `${room}\x00${key}\x00${clientId}`;
|
|
5
|
+
}
|
|
6
|
+
function parseIndexKey(packed) {
|
|
7
|
+
const [room, key, clientId] = packed.split("\x00");
|
|
8
|
+
return { room, key, clientId };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class EphemeralManager {
|
|
12
|
+
store = new Map;
|
|
13
|
+
clientIndex = new Map;
|
|
14
|
+
entryCount = 0;
|
|
15
|
+
maxEntries;
|
|
16
|
+
constructor(maxEntries) {
|
|
17
|
+
this.maxEntries = maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
18
|
+
}
|
|
19
|
+
set(room, key, clientId, userId, data, ttlMs) {
|
|
20
|
+
const roomMap = this.store.get(room) ?? new Map;
|
|
21
|
+
const keyMap = roomMap.get(key) ?? new Map;
|
|
22
|
+
const existing = keyMap.get(clientId);
|
|
23
|
+
if (!existing && this.entryCount >= this.maxEntries) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const state = {
|
|
27
|
+
clientId,
|
|
28
|
+
userId,
|
|
29
|
+
key,
|
|
30
|
+
data,
|
|
31
|
+
updatedAt: Date.now(),
|
|
32
|
+
ttlMs
|
|
33
|
+
};
|
|
34
|
+
keyMap.set(clientId, state);
|
|
35
|
+
roomMap.set(key, keyMap);
|
|
36
|
+
this.store.set(room, roomMap);
|
|
37
|
+
if (!existing) {
|
|
38
|
+
this.entryCount++;
|
|
39
|
+
this.addToClientIndex(clientId, indexKey(room, key, clientId));
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
addToClientIndex(clientId, packed) {
|
|
44
|
+
const clientSet = this.clientIndex.get(clientId) ?? new Set;
|
|
45
|
+
clientSet.add(packed);
|
|
46
|
+
this.clientIndex.set(clientId, clientSet);
|
|
47
|
+
}
|
|
48
|
+
removeFromClientIndex(clientId, packed) {
|
|
49
|
+
const clientSet = this.clientIndex.get(clientId);
|
|
50
|
+
if (!clientSet)
|
|
51
|
+
return;
|
|
52
|
+
clientSet.delete(packed);
|
|
53
|
+
if (clientSet.size === 0)
|
|
54
|
+
this.clientIndex.delete(clientId);
|
|
55
|
+
}
|
|
56
|
+
get(room, key) {
|
|
57
|
+
const roomMap = this.store.get(room);
|
|
58
|
+
if (!roomMap)
|
|
59
|
+
return {};
|
|
60
|
+
const keyMap = roomMap.get(key);
|
|
61
|
+
if (!keyMap)
|
|
62
|
+
return {};
|
|
63
|
+
return Object.fromEntries(keyMap.entries());
|
|
64
|
+
}
|
|
65
|
+
getRoom(room) {
|
|
66
|
+
const roomMap = this.store.get(room);
|
|
67
|
+
if (!roomMap)
|
|
68
|
+
return {};
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const [key, keyMap] of roomMap) {
|
|
71
|
+
out[key] = Object.fromEntries(keyMap.entries());
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
remove(room, key, clientId) {
|
|
76
|
+
const roomMap = this.store.get(room);
|
|
77
|
+
if (!roomMap)
|
|
78
|
+
return;
|
|
79
|
+
const keyMap = roomMap.get(key);
|
|
80
|
+
if (!keyMap)
|
|
81
|
+
return;
|
|
82
|
+
if (keyMap.has(clientId)) {
|
|
83
|
+
keyMap.delete(clientId);
|
|
84
|
+
this.entryCount--;
|
|
85
|
+
this.removeFromClientIndex(clientId, indexKey(room, key, clientId));
|
|
86
|
+
}
|
|
87
|
+
if (keyMap.size === 0) {
|
|
88
|
+
roomMap.delete(key);
|
|
89
|
+
}
|
|
90
|
+
if (roomMap.size === 0) {
|
|
91
|
+
this.store.delete(room);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
removeClient(clientId) {
|
|
95
|
+
const indices = this.clientIndex.get(clientId);
|
|
96
|
+
if (!indices)
|
|
97
|
+
return;
|
|
98
|
+
const entries = [...indices];
|
|
99
|
+
for (const packed of entries) {
|
|
100
|
+
const { room, key, clientId: owner } = parseIndexKey(packed);
|
|
101
|
+
this.remove(room, key, owner);
|
|
102
|
+
}
|
|
103
|
+
this.clientIndex.delete(clientId);
|
|
104
|
+
}
|
|
105
|
+
cleanupExpired() {
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const toRemove = [];
|
|
108
|
+
for (const [room, roomMap] of this.store) {
|
|
109
|
+
for (const [key, keyMap] of roomMap) {
|
|
110
|
+
for (const [clientId, state] of keyMap) {
|
|
111
|
+
if (state.ttlMs && now - state.updatedAt > state.ttlMs) {
|
|
112
|
+
toRemove.push({ room, key, clientId });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
for (const { room, key, clientId } of toRemove) {
|
|
118
|
+
this.remove(room, key, clientId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
size() {
|
|
122
|
+
return this.entryCount;
|
|
123
|
+
}
|
|
124
|
+
destroy() {
|
|
125
|
+
this.store.clear();
|
|
126
|
+
this.clientIndex.clear();
|
|
127
|
+
this.entryCount = 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function createMemoryEphemeral(maxEntries) {
|
|
131
|
+
return new EphemeralManager(maxEntries);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export { EphemeralManager, createMemoryEphemeral };
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -267,6 +267,17 @@ declare class ClientStore {
|
|
|
267
267
|
clearTable(table: string): void;
|
|
268
268
|
applySnapshot(table: string, rows: Record<string, unknown>[], colClocks: Record<string, Record<string, string>>, append?: boolean, pk?: string): void;
|
|
269
269
|
applyDelta(table: string, op: string, rowId: string, payload: Record<string, unknown> | null, hlc: string, colClocks?: Record<string, string>): void;
|
|
270
|
+
/**
|
|
271
|
+
* Guarantee a materialized row carries its own primary key.
|
|
272
|
+
*
|
|
273
|
+
* A delta's payload is not always a whole row: eager broadcasts forward the
|
|
274
|
+
* writer's payload verbatim, and the typed API omits the pk from what a
|
|
275
|
+
* client may write — so an eagerly-broadcast insert describes a row with no
|
|
276
|
+
* id in it. Rows are addressed by `rowId` everywhere in the protocol, so the
|
|
277
|
+
* key is never in doubt; only the materialized object was missing it, and
|
|
278
|
+
* `rows.map(r => r.id)` came back undefined.
|
|
279
|
+
*/
|
|
280
|
+
private withPk;
|
|
270
281
|
revertOp(opId: string, serverRow: Record<string, unknown> | null | undefined): void;
|
|
271
282
|
setTableMeta(meta: Record<string, {
|
|
272
283
|
serverSet: string[];
|
|
@@ -496,11 +507,15 @@ interface DrizzleTableLike {
|
|
|
496
507
|
/**
|
|
497
508
|
* Value supplied per serverSet field in the schema's object form.
|
|
498
509
|
* Either a static value or a function that receives the request context.
|
|
510
|
+
*
|
|
511
|
+
* The static arm is enumerated rather than written as `unknown`: a union with
|
|
512
|
+
* `unknown` collapses to `unknown`, which strips the contextual type from the
|
|
513
|
+
* callback arm and makes every `(ctx) => …` an implicit `any` under `strict`.
|
|
499
514
|
*/
|
|
500
|
-
type ServerSetSchemaValue =
|
|
515
|
+
type ServerSetSchemaValue = ((ctx: {
|
|
501
516
|
auth: unknown;
|
|
502
517
|
params: unknown;
|
|
503
|
-
}) => unknown)
|
|
518
|
+
}) => unknown) | string | number | bigint | boolean | symbol | null | undefined | Date | readonly unknown[] | Record<string, unknown>;
|
|
504
519
|
/**
|
|
505
520
|
* Schema-side `serverSet` declaration. Two shapes:
|
|
506
521
|
* - `string[]` — keys only; values are supplied at `implement(...)` time.
|
|
@@ -642,4 +657,4 @@ interface SyncSvelteHooks<TQueries extends SyncQueryMap> {
|
|
|
642
657
|
createStore: (config: SyncStoreConfig) => TypedSyncStore<TQueries>;
|
|
643
658
|
}
|
|
644
659
|
declare function createSyncSvelte<TQueries extends SyncQueryMap>(): SyncSvelteHooks<TQueries>;
|
|
645
|
-
export {
|
|
660
|
+
export { Readable, SyncStore, SyncStoreConfig, SyncSvelteHooks, TypedSyncStore, createBrowserWsTransport, createSyncStore, createSyncSvelte };
|
package/dist/svelte/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SyncClient,
|
|
3
3
|
pushSafely
|
|
4
|
-
} from "../shared/esm-
|
|
4
|
+
} from "../shared/esm-8qbr4y0d.js";
|
|
5
5
|
import"../shared/esm-rw7jjtrv.js";
|
|
6
6
|
import"../shared/esm-3tkwvysa.js";
|
|
7
|
-
import"../shared/esm-
|
|
7
|
+
import"../shared/esm-k7kedp3y.js";
|
|
8
8
|
|
|
9
9
|
// src/svelte/stores.ts
|
|
10
10
|
function createBrowserWsTransport(url) {
|
|
@@ -166,21 +166,21 @@ function createSyncStore(config) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
const unsub = client.subscribeEphemeral(cfg.key, (event) => {
|
|
169
|
-
current = { ...current, [event.
|
|
169
|
+
current = { ...current, [event.clientId]: event.data };
|
|
170
170
|
notifySubscribers();
|
|
171
171
|
if (cfg.ttlMs) {
|
|
172
|
-
const existing = timers.get(event.
|
|
172
|
+
const existing = timers.get(event.clientId);
|
|
173
173
|
if (existing) {
|
|
174
174
|
clearTimeout(existing);
|
|
175
175
|
}
|
|
176
176
|
const timer = setTimeout(() => {
|
|
177
177
|
const next = { ...current };
|
|
178
|
-
delete next[event.
|
|
178
|
+
delete next[event.clientId];
|
|
179
179
|
current = next;
|
|
180
|
-
timers.delete(event.
|
|
180
|
+
timers.delete(event.clientId);
|
|
181
181
|
notifySubscribers();
|
|
182
182
|
}, cfg.ttlMs);
|
|
183
|
-
timers.set(event.
|
|
183
|
+
timers.set(event.clientId, timer);
|
|
184
184
|
}
|
|
185
185
|
});
|
|
186
186
|
const events = {
|
|
@@ -256,7 +256,7 @@ function createSyncSvelte() {
|
|
|
256
256
|
return { createStore };
|
|
257
257
|
}
|
|
258
258
|
export {
|
|
259
|
-
|
|
259
|
+
createBrowserWsTransport,
|
|
260
260
|
createSyncStore,
|
|
261
|
-
|
|
261
|
+
createSyncSvelte
|
|
262
262
|
};
|
|
@@ -212,4 +212,4 @@ declare function createBunWsServerTransport(cfg?: BunWsServerConfig): {
|
|
|
212
212
|
pong?(ws: BunServerWebSocket<BunWsTransportData>): void;
|
|
213
213
|
};
|
|
214
214
|
};
|
|
215
|
-
export {
|
|
215
|
+
export { BunWsServerConfig, BunWsTransportData, createBunWsServerTransport };
|
package/dist/transport/bun-ws.js
CHANGED
|
@@ -207,4 +207,4 @@ interface PollingClientConfig {
|
|
|
207
207
|
headers?: Record<string, string>;
|
|
208
208
|
}
|
|
209
209
|
declare function createPollingClientTransport(config: PollingClientConfig): ClientTransport;
|
|
210
|
-
export {
|
|
210
|
+
export { PollingClientConfig, PollingServerConfig, createPollingClientTransport, createPollingServerTransport, pollingBodyTooLarge };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TransportSendError
|
|
3
3
|
} from "../shared/esm-3tkwvysa.js";
|
|
4
|
-
import"../shared/esm-
|
|
4
|
+
import"../shared/esm-k7kedp3y.js";
|
|
5
5
|
|
|
6
6
|
// src/transport/polling.ts
|
|
7
7
|
function pollingBodyTooLarge(body, limit = 1e6) {
|
|
@@ -175,7 +175,7 @@ function createPollingClientTransport(config) {
|
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
export {
|
|
178
|
-
|
|
178
|
+
createPollingClientTransport,
|
|
179
179
|
createPollingServerTransport,
|
|
180
|
-
|
|
180
|
+
pollingBodyTooLarge
|
|
181
181
|
};
|
package/dist/transport/sse.d.ts
CHANGED
|
@@ -202,4 +202,4 @@ interface SseClientConfig {
|
|
|
202
202
|
headers?: Record<string, string>;
|
|
203
203
|
}
|
|
204
204
|
declare function createSseClientTransport(config: SseClientConfig): ClientTransport;
|
|
205
|
-
export {
|
|
205
|
+
export { SseClientConfig, SseServerConfig, createSseClientTransport, createSseServerTransport };
|
package/dist/transport/sse.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TransportSendError
|
|
3
3
|
} from "../shared/esm-3tkwvysa.js";
|
|
4
|
-
import"../shared/esm-
|
|
4
|
+
import"../shared/esm-k7kedp3y.js";
|
|
5
5
|
|
|
6
6
|
// src/transport/sse.ts
|
|
7
7
|
function createSseServerTransport(cfg = {}) {
|
|
@@ -203,6 +203,6 @@ function createSseClientTransport(config) {
|
|
|
203
203
|
};
|
|
204
204
|
}
|
|
205
205
|
export {
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
createSseClientTransport,
|
|
207
|
+
createSseServerTransport
|
|
208
208
|
};
|
package/dist/transport/ws.d.ts
CHANGED
|
@@ -233,4 +233,4 @@ interface WebSocketLike {
|
|
|
233
233
|
/** Bytes queued but not yet flushed. Used for outbound backpressure. */
|
|
234
234
|
bufferedAmount?: number;
|
|
235
235
|
}
|
|
236
|
-
export {
|
|
236
|
+
export { WebSocketLike, WsClientConfig, WsServerConfig, WsServerTransportConfig, createWsClientTransport, createWsServerTransport, isOriginAllowed };
|
package/dist/transport/ws.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
TransportSendError
|
|
3
3
|
} from "../shared/esm-3tkwvysa.js";
|
|
4
|
-
import"../shared/esm-
|
|
4
|
+
import"../shared/esm-k7kedp3y.js";
|
|
5
5
|
|
|
6
6
|
// src/transport/ws.ts
|
|
7
7
|
function trySend(clientId, ws, data, maxBufferedBytes) {
|
|
@@ -220,7 +220,7 @@ function createWsClientTransport(config) {
|
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
222
|
export {
|
|
223
|
-
|
|
223
|
+
createWsClientTransport,
|
|
224
224
|
createWsServerTransport,
|
|
225
|
-
|
|
225
|
+
isOriginAllowed
|
|
226
226
|
};
|
package/dist/vanilla/index.d.ts
CHANGED
|
@@ -267,6 +267,17 @@ declare class ClientStore {
|
|
|
267
267
|
clearTable(table: string): void;
|
|
268
268
|
applySnapshot(table: string, rows: Record<string, unknown>[], colClocks: Record<string, Record<string, string>>, append?: boolean, pk?: string): void;
|
|
269
269
|
applyDelta(table: string, op: string, rowId: string, payload: Record<string, unknown> | null, hlc: string, colClocks?: Record<string, string>): void;
|
|
270
|
+
/**
|
|
271
|
+
* Guarantee a materialized row carries its own primary key.
|
|
272
|
+
*
|
|
273
|
+
* A delta's payload is not always a whole row: eager broadcasts forward the
|
|
274
|
+
* writer's payload verbatim, and the typed API omits the pk from what a
|
|
275
|
+
* client may write — so an eagerly-broadcast insert describes a row with no
|
|
276
|
+
* id in it. Rows are addressed by `rowId` everywhere in the protocol, so the
|
|
277
|
+
* key is never in doubt; only the materialized object was missing it, and
|
|
278
|
+
* `rows.map(r => r.id)` came back undefined.
|
|
279
|
+
*/
|
|
280
|
+
private withPk;
|
|
270
281
|
revertOp(opId: string, serverRow: Record<string, unknown> | null | undefined): void;
|
|
271
282
|
setTableMeta(meta: Record<string, {
|
|
272
283
|
serverSet: string[];
|
|
@@ -504,11 +515,15 @@ interface DrizzleTableLike {
|
|
|
504
515
|
/**
|
|
505
516
|
* Value supplied per serverSet field in the schema's object form.
|
|
506
517
|
* Either a static value or a function that receives the request context.
|
|
518
|
+
*
|
|
519
|
+
* The static arm is enumerated rather than written as `unknown`: a union with
|
|
520
|
+
* `unknown` collapses to `unknown`, which strips the contextual type from the
|
|
521
|
+
* callback arm and makes every `(ctx) => …` an implicit `any` under `strict`.
|
|
507
522
|
*/
|
|
508
|
-
type ServerSetSchemaValue =
|
|
523
|
+
type ServerSetSchemaValue = ((ctx: {
|
|
509
524
|
auth: unknown;
|
|
510
525
|
params: unknown;
|
|
511
|
-
}) => unknown)
|
|
526
|
+
}) => unknown) | string | number | bigint | boolean | symbol | null | undefined | Date | readonly unknown[] | Record<string, unknown>;
|
|
512
527
|
/**
|
|
513
528
|
* Schema-side `serverSet` declaration. Two shapes:
|
|
514
529
|
* - `string[]` — keys only; values are supplied at `implement(...)` time.
|
|
@@ -654,4 +669,4 @@ interface SyncVanillaHooks<TQueries extends SyncQueryMap> {
|
|
|
654
669
|
createSync: (config: VanillaSyncConfig) => TypedVanillaSync<TQueries>;
|
|
655
670
|
}
|
|
656
671
|
declare function createSyncVanilla<TQueries extends SyncQueryMap>(): SyncVanillaHooks<TQueries>;
|
|
657
|
-
export {
|
|
672
|
+
export { EphemeralBinding, SyncVanillaHooks, TableBinding, TypedVanillaSync, VanillaSync, VanillaSyncConfig, createBrowserWsTransport, createSync2 as createSync, createSyncVanilla };
|
package/dist/vanilla/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SyncClient,
|
|
3
3
|
pushSafely
|
|
4
|
-
} from "../shared/esm-
|
|
4
|
+
} from "../shared/esm-8qbr4y0d.js";
|
|
5
5
|
import"../shared/esm-rw7jjtrv.js";
|
|
6
6
|
import"../shared/esm-3tkwvysa.js";
|
|
7
|
-
import"../shared/esm-
|
|
7
|
+
import"../shared/esm-k7kedp3y.js";
|
|
8
8
|
|
|
9
9
|
// src/vanilla/sync.ts
|
|
10
10
|
function createBrowserWsTransport(url) {
|
|
@@ -184,20 +184,20 @@ function createSync(config) {
|
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
const unsub = client.subscribeEphemeral(ephConfig.key, (event) => {
|
|
187
|
-
events = { ...events, [event.
|
|
187
|
+
events = { ...events, [event.clientId]: event.data };
|
|
188
188
|
if (ephConfig.ttlMs) {
|
|
189
|
-
const existing = timers.get(event.
|
|
189
|
+
const existing = timers.get(event.clientId);
|
|
190
190
|
if (existing) {
|
|
191
191
|
clearTimeout(existing);
|
|
192
192
|
}
|
|
193
193
|
const timer = setTimeout(() => {
|
|
194
194
|
const next = { ...events };
|
|
195
|
-
delete next[event.
|
|
195
|
+
delete next[event.clientId];
|
|
196
196
|
events = next;
|
|
197
|
-
timers.delete(event.
|
|
197
|
+
timers.delete(event.clientId);
|
|
198
198
|
notifyChange();
|
|
199
199
|
}, ephConfig.ttlMs);
|
|
200
|
-
timers.set(event.
|
|
200
|
+
timers.set(event.clientId, timer);
|
|
201
201
|
}
|
|
202
202
|
notifyChange();
|
|
203
203
|
});
|
|
@@ -272,7 +272,7 @@ function createSyncVanilla() {
|
|
|
272
272
|
};
|
|
273
273
|
}
|
|
274
274
|
export {
|
|
275
|
-
|
|
275
|
+
createBrowserWsTransport,
|
|
276
276
|
createSync,
|
|
277
|
-
|
|
277
|
+
createSyncVanilla
|
|
278
278
|
};
|
package/package.json
CHANGED
|
@@ -1,70 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reflectdb",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Real-time sync engine for TypeScript — a server database and offline-first browser clients stay in sync, with optimistic writes and typed queries.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"data-sync",
|
|
10
|
-
"state-sync",
|
|
11
|
-
"real-time",
|
|
12
|
-
"realtime",
|
|
13
|
-
"replication",
|
|
14
|
-
"live-query",
|
|
15
|
-
"local-first",
|
|
16
|
-
"offline-first",
|
|
17
|
-
"offline",
|
|
18
|
-
"optimistic-updates",
|
|
6
|
+
"bun",
|
|
7
|
+
"collaborative",
|
|
8
|
+
"collaborative-editing",
|
|
19
9
|
"conflict-resolution",
|
|
20
|
-
"
|
|
10
|
+
"data-sync",
|
|
11
|
+
"drizzle",
|
|
12
|
+
"drizzle-orm",
|
|
21
13
|
"eventual-consistency",
|
|
22
|
-
"hybrid-logical-clock",
|
|
23
14
|
"hlc",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
15
|
+
"hybrid-logical-clock",
|
|
16
|
+
"indexeddb",
|
|
17
|
+
"kysely",
|
|
18
|
+
"last-write-wins",
|
|
19
|
+
"live-query",
|
|
20
|
+
"local-first",
|
|
28
21
|
"long-polling",
|
|
29
22
|
"multiplayer",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
23
|
+
"offline",
|
|
24
|
+
"offline-first",
|
|
25
|
+
"op-log",
|
|
26
|
+
"optimistic-updates",
|
|
27
|
+
"postgres",
|
|
32
28
|
"presence",
|
|
33
|
-
"typescript",
|
|
34
|
-
"type-safe",
|
|
35
29
|
"react",
|
|
36
30
|
"react-hooks",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"indexeddb",
|
|
42
|
-
"postgres",
|
|
31
|
+
"real-time",
|
|
32
|
+
"realtime",
|
|
33
|
+
"replication",
|
|
34
|
+
"server-sent-events",
|
|
43
35
|
"sqlite",
|
|
44
|
-
"
|
|
36
|
+
"sse",
|
|
37
|
+
"state-sync",
|
|
38
|
+
"svelte",
|
|
39
|
+
"sync",
|
|
40
|
+
"sync-engine",
|
|
41
|
+
"sync-server",
|
|
42
|
+
"type-safe",
|
|
43
|
+
"typescript",
|
|
44
|
+
"websocket"
|
|
45
45
|
],
|
|
46
46
|
"homepage": "https://github.com/TimMikeladze/reflectdb#readme",
|
|
47
47
|
"bugs": {
|
|
48
48
|
"url": "https://github.com/TimMikeladze/reflectdb/issues"
|
|
49
49
|
},
|
|
50
|
+
"license": "MIT",
|
|
51
|
+
"author": "Tim Mikeladze <tim.mikeladze@gmail.com> (https://github.com/TimMikeladze)",
|
|
50
52
|
"repository": {
|
|
51
53
|
"type": "git",
|
|
52
54
|
"url": "git+https://github.com/TimMikeladze/reflectdb.git"
|
|
53
55
|
},
|
|
54
56
|
"funding": "https://github.com/sponsors/TimMikeladze",
|
|
55
|
-
"license": "MIT",
|
|
56
|
-
"author": "Tim Mikeladze <tim.mikeladze@gmail.com> (https://github.com/TimMikeladze)",
|
|
57
|
-
"type": "module",
|
|
58
|
-
"sideEffects": false,
|
|
59
|
-
"engines": {
|
|
60
|
-
"node": ">=22"
|
|
61
|
-
},
|
|
62
57
|
"files": [
|
|
63
58
|
"dist"
|
|
64
59
|
],
|
|
65
|
-
"
|
|
66
|
-
"access": "public"
|
|
67
|
-
},
|
|
60
|
+
"type": "module",
|
|
68
61
|
"main": "./dist/cjs/core/index.cjs",
|
|
69
62
|
"module": "./dist/core/index.js",
|
|
70
63
|
"types": "./dist/core/index.d.ts",
|
|
@@ -199,25 +192,50 @@
|
|
|
199
192
|
"default": "./dist/cjs/client/storage/indexeddb.cjs"
|
|
200
193
|
}
|
|
201
194
|
},
|
|
195
|
+
"./server/ephemeral": {
|
|
196
|
+
"import": {
|
|
197
|
+
"types": "./dist/server/ephemeral/index.d.ts",
|
|
198
|
+
"default": "./dist/server/ephemeral/index.js"
|
|
199
|
+
},
|
|
200
|
+
"require": {
|
|
201
|
+
"types": "./dist/cjs/server/ephemeral/index.d.cts",
|
|
202
|
+
"default": "./dist/cjs/server/ephemeral/index.cjs"
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
"./server/ephemeral/redis": {
|
|
206
|
+
"import": {
|
|
207
|
+
"types": "./dist/server/ephemeral/redis.d.ts",
|
|
208
|
+
"default": "./dist/server/ephemeral/redis.js"
|
|
209
|
+
},
|
|
210
|
+
"require": {
|
|
211
|
+
"types": "./dist/cjs/server/ephemeral/redis.d.cts",
|
|
212
|
+
"default": "./dist/cjs/server/ephemeral/redis.cjs"
|
|
213
|
+
}
|
|
214
|
+
},
|
|
202
215
|
"./package.json": "./package.json"
|
|
203
216
|
},
|
|
217
|
+
"publishConfig": {
|
|
218
|
+
"access": "public"
|
|
219
|
+
},
|
|
204
220
|
"scripts": {
|
|
205
|
-
"build": "bunup --filter esm && bunup --filter cjs",
|
|
221
|
+
"build": "NODE_ENV=production bunup --filter esm && NODE_ENV=production bunup --filter cjs && bun run verify:jsx",
|
|
206
222
|
"format": "oxfmt",
|
|
207
223
|
"lint": "oxlint",
|
|
208
224
|
"prepare": "bun simple-git-hooks",
|
|
209
225
|
"release": "bumpp --commit --push --tag",
|
|
210
226
|
"test": "bun test",
|
|
211
227
|
"test:coverage": "bun test --coverage",
|
|
212
|
-
"test:watch": "bun test --watch",
|
|
213
|
-
"test:ui": "bun test/ui/server.tsx",
|
|
214
228
|
"test:sync": "bun test/ui/test-sync.ts",
|
|
229
|
+
"test:ui": "bun test/ui/server.tsx",
|
|
230
|
+
"test:watch": "bun test --watch",
|
|
215
231
|
"type-check": "tsc --noEmit",
|
|
216
232
|
"verify:exports": "bun scripts/verify-exports.ts",
|
|
233
|
+
"verify:jsx": "bun scripts/verify-jsx.ts",
|
|
217
234
|
"verify:node": "bun scripts/verify-node-consumer.ts"
|
|
218
235
|
},
|
|
219
236
|
"devDependencies": {
|
|
220
237
|
"@types/bun": "^1.3.10",
|
|
238
|
+
"@types/pg": "^8.21.0",
|
|
221
239
|
"@types/react": "^19.2.14",
|
|
222
240
|
"@types/react-dom": "^19.2.3",
|
|
223
241
|
"bumpp": "^10.4.1",
|
|
@@ -227,6 +245,7 @@
|
|
|
227
245
|
"kysely": "^0.28.12",
|
|
228
246
|
"oxfmt": "^0.36.0",
|
|
229
247
|
"oxlint": "^1.51.0",
|
|
248
|
+
"pg": "^8.23.0",
|
|
230
249
|
"react-dom": "^19.2.4",
|
|
231
250
|
"simple-git-hooks": "^2.13.1",
|
|
232
251
|
"typescript": "^5.9.3"
|
|
@@ -249,5 +268,8 @@
|
|
|
249
268
|
},
|
|
250
269
|
"simple-git-hooks": {
|
|
251
270
|
"pre-commit": "bun run lint && bun run type-check"
|
|
271
|
+
},
|
|
272
|
+
"engines": {
|
|
273
|
+
"node": ">=22"
|
|
252
274
|
}
|
|
253
275
|
}
|
|
File without changes
|