reflectdb 0.1.2 → 0.2.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/README.md +483 -17
- package/dist/cjs/client/index.cjs +0 -1
- package/dist/cjs/client/storage/indexeddb.cjs +0 -1
- package/dist/cjs/core/index.cjs +0 -1
- package/dist/cjs/htmx/index.cjs +1720 -0
- package/dist/cjs/htmx/index.d.cts +760 -0
- package/dist/cjs/react/index.cjs +0 -1
- package/dist/cjs/server/drizzle.cjs +15 -4
- package/dist/cjs/server/ephemeral/index.cjs +0 -1
- package/dist/cjs/server/ephemeral/redis.cjs +0 -1
- package/dist/cjs/server/index.cjs +34 -13
- package/dist/cjs/server/index.d.cts +14 -0
- package/dist/cjs/server/storage/object/index.cjs +2284 -0
- package/dist/cjs/server/storage/object/index.d.cts +578 -0
- package/dist/cjs/svelte/index.cjs +0 -1
- package/dist/cjs/transport/bun-ws.cjs +0 -1
- package/dist/cjs/transport/polling.cjs +0 -1
- package/dist/cjs/transport/sse.cjs +52 -2
- package/dist/cjs/transport/sse.d.cts +34 -0
- package/dist/cjs/transport/ws.cjs +0 -1
- package/dist/cjs/vanilla/index.cjs +0 -1
- package/dist/client/index.js +1 -1
- package/dist/client/storage/indexeddb.js +1 -1
- package/dist/core/index.js +1 -1
- package/dist/htmx/index.d.ts +760 -0
- package/dist/htmx/index.js +297 -0
- package/dist/react/index.js +1 -1
- package/dist/server/drizzle.js +3 -2
- package/dist/server/ephemeral/index.js +1 -1
- package/dist/server/ephemeral/redis.js +1 -1
- package/dist/server/index.d.ts +14 -0
- package/dist/server/index.js +19 -6
- package/dist/server/storage/object/index.d.ts +578 -0
- package/dist/server/storage/object/index.js +2232 -0
- package/dist/shared/{esm-z1xse19c.js → esm-5ahpq25j.js} +4 -4
- package/dist/shared/esm-dcs8qa5n.js +263 -0
- package/dist/shared/esm-f11s9zpb.js +15 -0
- package/dist/shared/esm-g5h4a88j.js +9 -0
- package/dist/shared/{esm-ck88h30s.js → esm-xtkhzxg2.js} +1 -1
- package/dist/svelte/index.js +1 -1
- package/dist/transport/bun-ws.js +1 -1
- package/dist/transport/polling.js +1 -1
- package/dist/transport/sse.d.ts +34 -0
- package/dist/transport/sse.js +53 -2
- package/dist/transport/ws.js +1 -1
- package/dist/vanilla/index.js +5 -261
- package/package.json +26 -3
- package/dist/shared/esm-k7kedp3y.js +0 -4
package/dist/vanilla/index.js
CHANGED
|
@@ -1,267 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from "../shared/esm-
|
|
2
|
+
createBrowserWsTransport,
|
|
3
|
+
createSync
|
|
4
|
+
} from "../shared/esm-dcs8qa5n.js";
|
|
5
|
+
import"../shared/esm-8qbr4y0d.js";
|
|
5
6
|
import"../shared/esm-rw7jjtrv.js";
|
|
6
7
|
import"../shared/esm-3tkwvysa.js";
|
|
7
|
-
import"../shared/esm-
|
|
8
|
-
|
|
9
|
-
// src/vanilla/sync.ts
|
|
10
|
-
function createBrowserWsTransport(url) {
|
|
11
|
-
let ws = null;
|
|
12
|
-
let handler = null;
|
|
13
|
-
let intentionalClose = false;
|
|
14
|
-
const sendQueue = [];
|
|
15
|
-
function ensureConnected() {
|
|
16
|
-
if (ws && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) {
|
|
17
|
-
return ws;
|
|
18
|
-
}
|
|
19
|
-
const socket = new WebSocket(url);
|
|
20
|
-
socket.onopen = () => {
|
|
21
|
-
for (const msg of sendQueue) {
|
|
22
|
-
socket.send(JSON.stringify(msg));
|
|
23
|
-
}
|
|
24
|
-
sendQueue.length = 0;
|
|
25
|
-
};
|
|
26
|
-
socket.onmessage = (event) => {
|
|
27
|
-
handler?.(JSON.parse(event.data));
|
|
28
|
-
};
|
|
29
|
-
socket.onclose = () => {
|
|
30
|
-
ws = null;
|
|
31
|
-
if (!intentionalClose) {
|
|
32
|
-
handler?.({ type: "disconnect", reason: "transport_closed" });
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
ws = socket;
|
|
36
|
-
return socket;
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
async send(message) {
|
|
40
|
-
const socket = ensureConnected();
|
|
41
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
42
|
-
socket.send(JSON.stringify(message));
|
|
43
|
-
} else {
|
|
44
|
-
sendQueue.push(message);
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
subscribe(h) {
|
|
48
|
-
handler = h;
|
|
49
|
-
},
|
|
50
|
-
async close() {
|
|
51
|
-
intentionalClose = true;
|
|
52
|
-
if (ws) {
|
|
53
|
-
ws.close();
|
|
54
|
-
ws = null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
function createSync(config) {
|
|
60
|
-
const clientId = config.clientId ?? `browser-${crypto.randomUUID().slice(0, 8)}`;
|
|
61
|
-
const transport = createBrowserWsTransport(config.url);
|
|
62
|
-
const managedTables = new Set(config.tables ?? []);
|
|
63
|
-
const unmanagedRefCounts = new Map;
|
|
64
|
-
const unmanagedParamKeys = new Map;
|
|
65
|
-
function stableKey(obj) {
|
|
66
|
-
if (!obj)
|
|
67
|
-
return "";
|
|
68
|
-
const keys = Object.keys(obj).sort();
|
|
69
|
-
return keys.map((k) => `${k}:${JSON.stringify(obj[k])}`).join("|");
|
|
70
|
-
}
|
|
71
|
-
const client = new SyncClient({
|
|
72
|
-
clientId,
|
|
73
|
-
transport,
|
|
74
|
-
token: config.token,
|
|
75
|
-
storage: config.storage,
|
|
76
|
-
onReauth: () => config.onReauth?.() ?? Promise.reject(new Error("No onReauth handler")),
|
|
77
|
-
onError: (err) => config.onError?.(err)
|
|
78
|
-
});
|
|
79
|
-
async function connect() {
|
|
80
|
-
if (config.storage) {
|
|
81
|
-
await client.init();
|
|
82
|
-
}
|
|
83
|
-
await client.connect();
|
|
84
|
-
for (const table of managedTables) {
|
|
85
|
-
await client.sync(table);
|
|
86
|
-
}
|
|
87
|
-
await client.resume();
|
|
88
|
-
}
|
|
89
|
-
async function close() {
|
|
90
|
-
await client.close();
|
|
91
|
-
}
|
|
92
|
-
function getState() {
|
|
93
|
-
return client.getState();
|
|
94
|
-
}
|
|
95
|
-
function onStateChange(cb) {
|
|
96
|
-
let lastState = client.getState();
|
|
97
|
-
return client.subscribe(() => {
|
|
98
|
-
const current = client.getState();
|
|
99
|
-
if (current !== lastState) {
|
|
100
|
-
lastState = current;
|
|
101
|
-
cb(current);
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
function getPendingCount() {
|
|
106
|
-
return client.getPendingCount();
|
|
107
|
-
}
|
|
108
|
-
function onPendingChange(cb) {
|
|
109
|
-
let lastCount = client.getPendingCount();
|
|
110
|
-
return client.subscribe(() => {
|
|
111
|
-
const current = client.getPendingCount();
|
|
112
|
-
if (current !== lastCount) {
|
|
113
|
-
lastCount = current;
|
|
114
|
-
cb(current);
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
function sync(table, options) {
|
|
119
|
-
const isManaged = managedTables.has(table);
|
|
120
|
-
const paramKey = stableKey(options?.params);
|
|
121
|
-
if (!isManaged) {
|
|
122
|
-
const currentParamKey = unmanagedParamKeys.get(table);
|
|
123
|
-
const count = unmanagedRefCounts.get(table) ?? 0;
|
|
124
|
-
if (count === 0 || currentParamKey !== paramKey) {
|
|
125
|
-
if (currentParamKey !== undefined && currentParamKey !== paramKey) {
|
|
126
|
-
client.unsync(table);
|
|
127
|
-
}
|
|
128
|
-
client.sync(table, options?.params, options?.window ? { window: options.window } : undefined);
|
|
129
|
-
client.scheduleBootstrap();
|
|
130
|
-
unmanagedParamKeys.set(table, paramKey);
|
|
131
|
-
}
|
|
132
|
-
unmanagedRefCounts.set(table, count + 1);
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
getRows(getOptions) {
|
|
136
|
-
return client.getRows(table, {
|
|
137
|
-
includeDeleted: getOptions?.includeDeleted ?? options?.includeDeleted
|
|
138
|
-
});
|
|
139
|
-
},
|
|
140
|
-
insert(rowId, payload) {
|
|
141
|
-
client.insert(table, rowId, payload);
|
|
142
|
-
pushSafely(client);
|
|
143
|
-
},
|
|
144
|
-
update(rowId, payload) {
|
|
145
|
-
client.update(table, rowId, payload);
|
|
146
|
-
pushSafely(client);
|
|
147
|
-
},
|
|
148
|
-
remove(rowId) {
|
|
149
|
-
client.delete(table, rowId);
|
|
150
|
-
pushSafely(client);
|
|
151
|
-
},
|
|
152
|
-
onChange(cb) {
|
|
153
|
-
return client.subscribeTable(table, cb);
|
|
154
|
-
},
|
|
155
|
-
destroy() {
|
|
156
|
-
if (isManaged)
|
|
157
|
-
return;
|
|
158
|
-
const count = (unmanagedRefCounts.get(table) ?? 1) - 1;
|
|
159
|
-
if (count <= 0) {
|
|
160
|
-
unmanagedRefCounts.delete(table);
|
|
161
|
-
unmanagedParamKeys.delete(table);
|
|
162
|
-
client.unsync(table);
|
|
163
|
-
} else {
|
|
164
|
-
unmanagedRefCounts.set(table, count);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function getRow(table, rowId) {
|
|
170
|
-
return client.getRow(table, rowId);
|
|
171
|
-
}
|
|
172
|
-
function onRowChange(table, rowId, cb) {
|
|
173
|
-
return client.subscribeTable(table, () => {
|
|
174
|
-
cb(client.getRow(table, rowId));
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
function ephemeral(ephConfig) {
|
|
178
|
-
let events = {};
|
|
179
|
-
const timers = new Map;
|
|
180
|
-
const changeListeners = new Set;
|
|
181
|
-
function notifyChange() {
|
|
182
|
-
for (const listener of changeListeners) {
|
|
183
|
-
listener(events);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const unsub = client.subscribeEphemeral(ephConfig.key, (event) => {
|
|
187
|
-
events = { ...events, [event.clientId]: event.data };
|
|
188
|
-
if (ephConfig.ttlMs) {
|
|
189
|
-
const existing = timers.get(event.clientId);
|
|
190
|
-
if (existing) {
|
|
191
|
-
clearTimeout(existing);
|
|
192
|
-
}
|
|
193
|
-
const timer = setTimeout(() => {
|
|
194
|
-
const next = { ...events };
|
|
195
|
-
delete next[event.clientId];
|
|
196
|
-
events = next;
|
|
197
|
-
timers.delete(event.clientId);
|
|
198
|
-
notifyChange();
|
|
199
|
-
}, ephConfig.ttlMs);
|
|
200
|
-
timers.set(event.clientId, timer);
|
|
201
|
-
}
|
|
202
|
-
notifyChange();
|
|
203
|
-
});
|
|
204
|
-
return {
|
|
205
|
-
getEvents() {
|
|
206
|
-
return events;
|
|
207
|
-
},
|
|
208
|
-
broadcast(data) {
|
|
209
|
-
return client.sendEphemeral({
|
|
210
|
-
key: ephConfig.key,
|
|
211
|
-
userId: ephConfig.userId,
|
|
212
|
-
data,
|
|
213
|
-
ttlMs: ephConfig.ttlMs
|
|
214
|
-
});
|
|
215
|
-
},
|
|
216
|
-
onChange(cb) {
|
|
217
|
-
changeListeners.add(cb);
|
|
218
|
-
return () => {
|
|
219
|
-
changeListeners.delete(cb);
|
|
220
|
-
};
|
|
221
|
-
},
|
|
222
|
-
destroy() {
|
|
223
|
-
unsub();
|
|
224
|
-
for (const timer of timers.values()) {
|
|
225
|
-
clearTimeout(timer);
|
|
226
|
-
}
|
|
227
|
-
timers.clear();
|
|
228
|
-
changeListeners.clear();
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
function getTotalCount(table) {
|
|
233
|
-
return client.getTotalCount(table);
|
|
234
|
-
}
|
|
235
|
-
function onTotalCountChange(table, cb) {
|
|
236
|
-
let lastCount = client.getTotalCount(table);
|
|
237
|
-
return client.subscribeTable(table, () => {
|
|
238
|
-
const current = client.getTotalCount(table);
|
|
239
|
-
if (current !== lastCount) {
|
|
240
|
-
lastCount = current;
|
|
241
|
-
cb(current);
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
function loadMore(table, count) {
|
|
246
|
-
client.loadMore(table, count);
|
|
247
|
-
}
|
|
248
|
-
return {
|
|
249
|
-
client,
|
|
250
|
-
connect,
|
|
251
|
-
close,
|
|
252
|
-
getState,
|
|
253
|
-
onStateChange,
|
|
254
|
-
getPendingCount,
|
|
255
|
-
onPendingChange,
|
|
256
|
-
sync,
|
|
257
|
-
getRow,
|
|
258
|
-
onRowChange,
|
|
259
|
-
ephemeral,
|
|
260
|
-
getTotalCount,
|
|
261
|
-
onTotalCountChange,
|
|
262
|
-
loadMore
|
|
263
|
-
};
|
|
264
|
-
}
|
|
8
|
+
import"../shared/esm-g5h4a88j.js";
|
|
265
9
|
// src/vanilla/typed.ts
|
|
266
10
|
function createSyncVanilla() {
|
|
267
11
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reflectdb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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
6
|
"bun",
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"drizzle-orm",
|
|
13
13
|
"eventual-consistency",
|
|
14
14
|
"hlc",
|
|
15
|
+
"htmx",
|
|
16
|
+
"hypermedia",
|
|
15
17
|
"hybrid-logical-clock",
|
|
16
18
|
"indexeddb",
|
|
17
19
|
"kysely",
|
|
@@ -172,6 +174,16 @@
|
|
|
172
174
|
"default": "./dist/cjs/svelte/index.cjs"
|
|
173
175
|
}
|
|
174
176
|
},
|
|
177
|
+
"./htmx": {
|
|
178
|
+
"import": {
|
|
179
|
+
"types": "./dist/htmx/index.d.ts",
|
|
180
|
+
"default": "./dist/htmx/index.js"
|
|
181
|
+
},
|
|
182
|
+
"require": {
|
|
183
|
+
"types": "./dist/cjs/htmx/index.d.cts",
|
|
184
|
+
"default": "./dist/cjs/htmx/index.cjs"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
175
187
|
"./vanilla": {
|
|
176
188
|
"import": {
|
|
177
189
|
"types": "./dist/vanilla/index.d.ts",
|
|
@@ -212,6 +224,16 @@
|
|
|
212
224
|
"default": "./dist/cjs/server/ephemeral/redis.cjs"
|
|
213
225
|
}
|
|
214
226
|
},
|
|
227
|
+
"./server/storage/object": {
|
|
228
|
+
"import": {
|
|
229
|
+
"types": "./dist/server/storage/object/index.d.ts",
|
|
230
|
+
"default": "./dist/server/storage/object/index.js"
|
|
231
|
+
},
|
|
232
|
+
"require": {
|
|
233
|
+
"types": "./dist/cjs/server/storage/object/index.d.cts",
|
|
234
|
+
"default": "./dist/cjs/server/storage/object/index.cjs"
|
|
235
|
+
}
|
|
236
|
+
},
|
|
215
237
|
"./package.json": "./package.json"
|
|
216
238
|
},
|
|
217
239
|
"publishConfig": {
|
|
@@ -231,7 +253,8 @@
|
|
|
231
253
|
"type-check": "tsc --noEmit",
|
|
232
254
|
"verify:exports": "bun scripts/verify-exports.ts",
|
|
233
255
|
"verify:jsx": "bun scripts/verify-jsx.ts",
|
|
234
|
-
"verify:node": "bun scripts/verify-node-consumer.ts"
|
|
256
|
+
"verify:node": "bun scripts/verify-node-consumer.ts",
|
|
257
|
+
"verify:presence": "bun scripts/verify-presence.ts"
|
|
235
258
|
},
|
|
236
259
|
"devDependencies": {
|
|
237
260
|
"@types/bun": "^1.3.10",
|
|
@@ -270,6 +293,6 @@
|
|
|
270
293
|
"pre-commit": "bun run lint && bun run type-check"
|
|
271
294
|
},
|
|
272
295
|
"engines": {
|
|
273
|
-
"node": ">=22"
|
|
296
|
+
"node": ">=22.3"
|
|
274
297
|
}
|
|
275
298
|
}
|