powersync-bridge 0.0.1-alpha.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/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/chunk-TANEBEGU.js +198 -0
- package/dist/chunk-TANEBEGU.js.map +1 -0
- package/dist/clock-BAt_Za0Q.d.cts +271 -0
- package/dist/clock-BAt_Za0Q.d.ts +271 -0
- package/dist/index.cjs +1601 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +496 -0
- package/dist/index.d.ts +496 -0
- package/dist/index.js +1385 -0
- package/dist/index.js.map +1 -0
- package/dist/testing.cjs +373 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +118 -0
- package/dist/testing.d.ts +118 -0
- package/dist/testing.js +354 -0
- package/dist/testing.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Q as QueryAdapter, B as BridgeDriver, h as PendingOpRef, d as StreamDescription, a as BridgeClock, u as PreparedQuery, D as DifferentialStream, g as CrudDelta, P as Params, G as StreamHandle, J as SyncStatusView, t as PendingOp, z as RowsBuilder } from './clock-BAt_Za0Q.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `powersync-bridge/testing` — a fake driver with VIRTUAL TIME for deterministic interleavings.
|
|
5
|
+
* Every invariant test (T1–T44) runs on this. Nothing here touches PowerSync.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
type Row = Record<string, unknown> & {
|
|
9
|
+
id: string;
|
|
10
|
+
};
|
|
11
|
+
declare class VirtualClock implements BridgeClock {
|
|
12
|
+
private t;
|
|
13
|
+
private timers;
|
|
14
|
+
private seq;
|
|
15
|
+
now(): number;
|
|
16
|
+
setTimeout(fn: () => void, ms: number): () => void;
|
|
17
|
+
/** Advance time, firing due timers in order. */
|
|
18
|
+
advance(ms: number): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/** Drain microtasks (the engines do async work after every emission). */
|
|
21
|
+
declare function flush(rounds?: number): Promise<void>;
|
|
22
|
+
interface FakeQuery<T> extends RowsBuilder<T> {
|
|
23
|
+
readonly __tables: readonly string[];
|
|
24
|
+
readonly __run: () => T[];
|
|
25
|
+
}
|
|
26
|
+
declare class FakeOrm {
|
|
27
|
+
readonly driver: FakeDriver;
|
|
28
|
+
constructor(driver: FakeDriver);
|
|
29
|
+
/** A query over `tables` computed by `fn`; thenable so TItem infers like a Drizzle builder. */
|
|
30
|
+
query<T>(tables: readonly string[], fn: (db: FakeDriver) => T[]): FakeQuery<T>;
|
|
31
|
+
rows<T extends Row = Row>(table: string): T[];
|
|
32
|
+
}
|
|
33
|
+
declare class FakeAdapter implements QueryAdapter {
|
|
34
|
+
readonly orm: FakeOrm;
|
|
35
|
+
constructor(driver: FakeDriver);
|
|
36
|
+
prepare<T>(builder: RowsBuilder<T>): PreparedQuery<T>;
|
|
37
|
+
}
|
|
38
|
+
interface FakeStream {
|
|
39
|
+
desc: StreamDescription;
|
|
40
|
+
refs: number;
|
|
41
|
+
expiresAt: number | null;
|
|
42
|
+
hasSynced: boolean;
|
|
43
|
+
lastSyncedAt: number | null;
|
|
44
|
+
firstSync: {
|
|
45
|
+
promise: Promise<void>;
|
|
46
|
+
resolve: () => void;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
declare class FakeDriver implements BridgeDriver {
|
|
50
|
+
readonly tables: Map<string, Map<string, Row>>;
|
|
51
|
+
readonly crud: Map<string, PendingOpRef>;
|
|
52
|
+
readonly streams: Map<string, FakeStream>;
|
|
53
|
+
readonly clock: VirtualClock;
|
|
54
|
+
private connected;
|
|
55
|
+
private downloading;
|
|
56
|
+
private hasSynced;
|
|
57
|
+
private lastSyncedAt;
|
|
58
|
+
private online;
|
|
59
|
+
private readonly crudListeners;
|
|
60
|
+
private readonly statusListeners;
|
|
61
|
+
private readonly diffSubs;
|
|
62
|
+
readonly presentIdsCalls: Array<{
|
|
63
|
+
table: string;
|
|
64
|
+
ids: readonly string[];
|
|
65
|
+
}>;
|
|
66
|
+
readonly subscribeLog: StreamDescription[];
|
|
67
|
+
streamTtlDefault: number;
|
|
68
|
+
constructor(clock?: VirtualClock);
|
|
69
|
+
query<T>(q: PreparedQuery<T>): Promise<T[]>;
|
|
70
|
+
diffs<T>(q: PreparedQuery<T>): DifferentialStream<T>;
|
|
71
|
+
pendingUploadIds(): Promise<PendingOpRef[]>;
|
|
72
|
+
onCrudChange(cb: (delta: CrudDelta) => void): () => void;
|
|
73
|
+
presentIds(table: string, ids: readonly string[]): Promise<Set<string>>;
|
|
74
|
+
onlineHint(): boolean | undefined;
|
|
75
|
+
syncStream(name: string, params: Params | null, opts?: {
|
|
76
|
+
ttl?: number;
|
|
77
|
+
}): Promise<StreamHandle>;
|
|
78
|
+
syncStatus(): SyncStatusView;
|
|
79
|
+
onStatusChanged(cb: () => void): () => void;
|
|
80
|
+
/** Open a previously synced DB: hasSynced is PERSISTED (true before any connect). */
|
|
81
|
+
openWarm(lastSyncedAt: number): void;
|
|
82
|
+
connect(): void;
|
|
83
|
+
disconnect(): void;
|
|
84
|
+
startDownload(): void;
|
|
85
|
+
/** A checkpoint completes: rows in `apply` land, lastSyncedAt advances, downloading ends unless `keepDownloading`. */
|
|
86
|
+
completeCheckpoint(apply?: () => void, opts?: {
|
|
87
|
+
keepDownloading?: boolean;
|
|
88
|
+
stream?: StreamDescription;
|
|
89
|
+
}): Promise<void>;
|
|
90
|
+
/** The service restarted: the SDK resets lastSyncedAt to null. */
|
|
91
|
+
serviceRestart(): void;
|
|
92
|
+
/** disconnectAndClear(): tables wiped, hasSynced → false. */
|
|
93
|
+
clear(): Promise<void>;
|
|
94
|
+
setOnline(v: boolean | undefined): void;
|
|
95
|
+
/** Resolve a stream subscription's first sync, landing `apply` rows. */
|
|
96
|
+
resolveFirstSync(desc: StreamDescription, apply?: () => void): Promise<void>;
|
|
97
|
+
/** Expire every TTL-pending stream whose expiry has passed and remove its rows (`ownerOf` maps a row to its stream). */
|
|
98
|
+
expireStreams(ownerOf: (table: string, row: Row) => StreamDescription | undefined): Promise<void>;
|
|
99
|
+
/** Get-or-create a table map (tests write rows directly through this). */
|
|
100
|
+
table(name: string): Map<string, Row>;
|
|
101
|
+
/** Rows arriving via sync (no crud entry). Emits diffs unless `silent`. */
|
|
102
|
+
synced(table: string, rows: Row[], opts?: {
|
|
103
|
+
silent?: boolean;
|
|
104
|
+
delete?: boolean;
|
|
105
|
+
}): Promise<void>;
|
|
106
|
+
/** A local write: row applied + crud entry + delta + diffs. */
|
|
107
|
+
writeLocal(table: string, op: PendingOp, row: Row): Promise<void>;
|
|
108
|
+
/** Uploads complete: crud drained, completed delta emitted. */
|
|
109
|
+
completeUploads(filter?: (r: PendingOpRef) => boolean): void;
|
|
110
|
+
/** Re-run every differential watch and emit changes (what a table change notification does). */
|
|
111
|
+
emitAll(): Promise<void>;
|
|
112
|
+
notifyStatus(): void;
|
|
113
|
+
private emitFor;
|
|
114
|
+
}
|
|
115
|
+
/** Build an unsigned JWT for identity tests. */
|
|
116
|
+
declare function fakeJwt(claims: Record<string, unknown>): string;
|
|
117
|
+
|
|
118
|
+
export { FakeAdapter, FakeDriver, FakeOrm, type FakeQuery, type Row, VirtualClock, fakeJwt, flush };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { streamKey, idOf } from './chunk-TANEBEGU.js';
|
|
2
|
+
|
|
3
|
+
// src/testing.ts
|
|
4
|
+
var VirtualClock = class {
|
|
5
|
+
t = 1e6;
|
|
6
|
+
timers = [];
|
|
7
|
+
seq = 0;
|
|
8
|
+
now() {
|
|
9
|
+
return this.t;
|
|
10
|
+
}
|
|
11
|
+
setTimeout(fn, ms) {
|
|
12
|
+
const id = ++this.seq;
|
|
13
|
+
this.timers.push({ at: this.t + ms, fn, id });
|
|
14
|
+
return () => {
|
|
15
|
+
this.timers = this.timers.filter((x) => x.id !== id);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Advance time, firing due timers in order. */
|
|
19
|
+
async advance(ms) {
|
|
20
|
+
const target = this.t + ms;
|
|
21
|
+
for (; ; ) {
|
|
22
|
+
const due = this.timers.filter((x) => x.at <= target).sort((a, b) => a.at - b.at)[0];
|
|
23
|
+
if (!due) break;
|
|
24
|
+
this.timers = this.timers.filter((x) => x.id !== due.id);
|
|
25
|
+
this.t = due.at;
|
|
26
|
+
due.fn();
|
|
27
|
+
await flush();
|
|
28
|
+
}
|
|
29
|
+
this.t = target;
|
|
30
|
+
await flush();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
async function flush(rounds = 8) {
|
|
34
|
+
for (let i = 0; i < rounds; i++) await new Promise((r) => setTimeout(r, 0));
|
|
35
|
+
}
|
|
36
|
+
var FakeOrm = class {
|
|
37
|
+
constructor(driver) {
|
|
38
|
+
this.driver = driver;
|
|
39
|
+
}
|
|
40
|
+
driver;
|
|
41
|
+
/** A query over `tables` computed by `fn`; thenable so TItem infers like a Drizzle builder. */
|
|
42
|
+
query(tables, fn) {
|
|
43
|
+
const driver = this.driver;
|
|
44
|
+
const q = {
|
|
45
|
+
__tables: tables,
|
|
46
|
+
__run: () => fn(driver),
|
|
47
|
+
then(onFul, onRej) {
|
|
48
|
+
return Promise.resolve(fn(driver)).then(onFul ?? void 0, onRej ?? void 0);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
return q;
|
|
52
|
+
}
|
|
53
|
+
rows(table) {
|
|
54
|
+
return Array.from(this.driver.tables.get(table)?.values() ?? []);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var FakeAdapter = class {
|
|
58
|
+
orm;
|
|
59
|
+
constructor(driver) {
|
|
60
|
+
this.orm = new FakeOrm(driver);
|
|
61
|
+
}
|
|
62
|
+
prepare(builder) {
|
|
63
|
+
const q = builder;
|
|
64
|
+
return { tables: q.__tables, run: async () => q.__run() };
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var FakeDriver = class {
|
|
68
|
+
tables = /* @__PURE__ */ new Map();
|
|
69
|
+
crud = /* @__PURE__ */ new Map();
|
|
70
|
+
streams = /* @__PURE__ */ new Map();
|
|
71
|
+
clock;
|
|
72
|
+
connected = false;
|
|
73
|
+
downloading = false;
|
|
74
|
+
hasSynced = false;
|
|
75
|
+
lastSyncedAt = null;
|
|
76
|
+
online = true;
|
|
77
|
+
crudListeners = /* @__PURE__ */ new Set();
|
|
78
|
+
statusListeners = /* @__PURE__ */ new Set();
|
|
79
|
+
diffSubs = /* @__PURE__ */ new Set();
|
|
80
|
+
presentIdsCalls = [];
|
|
81
|
+
subscribeLog = [];
|
|
82
|
+
streamTtlDefault = 864e5;
|
|
83
|
+
constructor(clock = new VirtualClock()) {
|
|
84
|
+
this.clock = clock;
|
|
85
|
+
}
|
|
86
|
+
// -- BridgeDriver -----------------------------------------------------------
|
|
87
|
+
async query(q) {
|
|
88
|
+
return q.run();
|
|
89
|
+
}
|
|
90
|
+
diffs(q) {
|
|
91
|
+
return {
|
|
92
|
+
subscribe: (onDiff, onError) => {
|
|
93
|
+
const sub = { q, prev: /* @__PURE__ */ new Map(), onDiff, first: true };
|
|
94
|
+
this.diffSubs.add(sub);
|
|
95
|
+
queueMicrotask(() => {
|
|
96
|
+
if (this.diffSubs.has(sub)) void this.emitFor(sub, onError);
|
|
97
|
+
});
|
|
98
|
+
return () => this.diffSubs.delete(sub);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async pendingUploadIds() {
|
|
103
|
+
return Array.from(this.crud.values());
|
|
104
|
+
}
|
|
105
|
+
onCrudChange(cb) {
|
|
106
|
+
this.crudListeners.add(cb);
|
|
107
|
+
return () => this.crudListeners.delete(cb);
|
|
108
|
+
}
|
|
109
|
+
async presentIds(table, ids) {
|
|
110
|
+
this.presentIdsCalls.push({ table, ids });
|
|
111
|
+
const t = this.tables.get(table);
|
|
112
|
+
return new Set(ids.filter((id) => t?.has(id)));
|
|
113
|
+
}
|
|
114
|
+
onlineHint() {
|
|
115
|
+
return this.online;
|
|
116
|
+
}
|
|
117
|
+
async syncStream(name, params, opts = {}) {
|
|
118
|
+
const desc = { name, params };
|
|
119
|
+
const key = streamKey(desc);
|
|
120
|
+
this.subscribeLog.push(desc);
|
|
121
|
+
let s = this.streams.get(key);
|
|
122
|
+
if (!s) {
|
|
123
|
+
s = { desc, refs: 0, expiresAt: null, hasSynced: false, lastSyncedAt: null, firstSync: deferred() };
|
|
124
|
+
this.streams.set(key, s);
|
|
125
|
+
s.ttl = (opts.ttl ?? this.streamTtlDefault / 1e3) * 1e3;
|
|
126
|
+
}
|
|
127
|
+
s.refs += 1;
|
|
128
|
+
s.expiresAt = null;
|
|
129
|
+
const stream = s;
|
|
130
|
+
let done = false;
|
|
131
|
+
return {
|
|
132
|
+
name,
|
|
133
|
+
params,
|
|
134
|
+
waitForFirstSync: (signal) => new Promise((resolve, reject) => {
|
|
135
|
+
stream.firstSync.promise.then(resolve);
|
|
136
|
+
signal?.addEventListener("abort", () => reject(signal.reason ?? new Error("aborted")), { once: true });
|
|
137
|
+
}),
|
|
138
|
+
unsubscribe: () => {
|
|
139
|
+
if (done) return;
|
|
140
|
+
done = true;
|
|
141
|
+
stream.refs -= 1;
|
|
142
|
+
if (stream.refs <= 0) stream.expiresAt = this.clock.now() + (stream.ttl ?? this.streamTtlDefault);
|
|
143
|
+
this.notifyStatus();
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
syncStatus() {
|
|
148
|
+
const now = this.clock.now();
|
|
149
|
+
const syncStreams = [];
|
|
150
|
+
for (const s of this.streams.values()) {
|
|
151
|
+
if (s.expiresAt !== null && now >= s.expiresAt) continue;
|
|
152
|
+
syncStreams.push({
|
|
153
|
+
name: s.desc.name,
|
|
154
|
+
params: s.desc.params,
|
|
155
|
+
active: s.refs > 0,
|
|
156
|
+
expiresAt: s.expiresAt === null ? null : new Date(s.expiresAt),
|
|
157
|
+
hasSynced: s.hasSynced,
|
|
158
|
+
lastSyncedAt: s.lastSyncedAt === null ? null : new Date(s.lastSyncedAt)
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const self = this;
|
|
162
|
+
return {
|
|
163
|
+
connected: this.connected,
|
|
164
|
+
downloading: this.downloading,
|
|
165
|
+
hasSynced: this.hasSynced,
|
|
166
|
+
lastSyncedAt: this.lastSyncedAt === null ? null : new Date(this.lastSyncedAt),
|
|
167
|
+
syncStreams,
|
|
168
|
+
forStream(desc) {
|
|
169
|
+
return syncStreams.find((x) => streamKey(x) === streamKey(desc));
|
|
170
|
+
},
|
|
171
|
+
statusForPriority() {
|
|
172
|
+
return { hasSynced: self.hasSynced === true, lastSyncedAt: self.lastSyncedAt === null ? null : new Date(self.lastSyncedAt) };
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
onStatusChanged(cb) {
|
|
177
|
+
this.statusListeners.add(cb);
|
|
178
|
+
return () => this.statusListeners.delete(cb);
|
|
179
|
+
}
|
|
180
|
+
// -- Test helpers: sync side -----------------------------------------------
|
|
181
|
+
/** Open a previously synced DB: hasSynced is PERSISTED (true before any connect). */
|
|
182
|
+
openWarm(lastSyncedAt) {
|
|
183
|
+
this.hasSynced = true;
|
|
184
|
+
this.lastSyncedAt = lastSyncedAt;
|
|
185
|
+
}
|
|
186
|
+
connect() {
|
|
187
|
+
this.connected = true;
|
|
188
|
+
this.notifyStatus();
|
|
189
|
+
}
|
|
190
|
+
disconnect() {
|
|
191
|
+
this.connected = false;
|
|
192
|
+
this.notifyStatus();
|
|
193
|
+
}
|
|
194
|
+
startDownload() {
|
|
195
|
+
this.downloading = true;
|
|
196
|
+
this.notifyStatus();
|
|
197
|
+
}
|
|
198
|
+
/** A checkpoint completes: rows in `apply` land, lastSyncedAt advances, downloading ends unless `keepDownloading`. */
|
|
199
|
+
async completeCheckpoint(apply = () => {
|
|
200
|
+
}, opts = {}) {
|
|
201
|
+
apply();
|
|
202
|
+
this.lastSyncedAt = this.clock.now();
|
|
203
|
+
this.hasSynced = true;
|
|
204
|
+
this.downloading = opts.keepDownloading ?? false;
|
|
205
|
+
if (opts.stream) {
|
|
206
|
+
const s = this.streams.get(streamKey(opts.stream));
|
|
207
|
+
if (s) {
|
|
208
|
+
s.hasSynced = true;
|
|
209
|
+
s.lastSyncedAt = this.clock.now();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
this.notifyStatus();
|
|
213
|
+
await this.emitAll();
|
|
214
|
+
}
|
|
215
|
+
/** The service restarted: the SDK resets lastSyncedAt to null. */
|
|
216
|
+
serviceRestart() {
|
|
217
|
+
this.lastSyncedAt = null;
|
|
218
|
+
this.notifyStatus();
|
|
219
|
+
}
|
|
220
|
+
/** disconnectAndClear(): tables wiped, hasSynced → false. */
|
|
221
|
+
async clear() {
|
|
222
|
+
this.tables.clear();
|
|
223
|
+
this.crud.clear();
|
|
224
|
+
this.hasSynced = false;
|
|
225
|
+
this.lastSyncedAt = null;
|
|
226
|
+
this.connected = false;
|
|
227
|
+
this.notifyStatus();
|
|
228
|
+
await this.emitAll();
|
|
229
|
+
}
|
|
230
|
+
setOnline(v) {
|
|
231
|
+
this.online = v;
|
|
232
|
+
}
|
|
233
|
+
/** Resolve a stream subscription's first sync, landing `apply` rows. */
|
|
234
|
+
async resolveFirstSync(desc, apply = () => {
|
|
235
|
+
}) {
|
|
236
|
+
const s = this.streams.get(streamKey(desc));
|
|
237
|
+
if (!s) throw new Error(`no subscription for ${streamKey(desc)}`);
|
|
238
|
+
apply();
|
|
239
|
+
s.hasSynced = true;
|
|
240
|
+
s.lastSyncedAt = this.clock.now();
|
|
241
|
+
s.firstSync.resolve();
|
|
242
|
+
this.notifyStatus();
|
|
243
|
+
await this.emitAll();
|
|
244
|
+
}
|
|
245
|
+
/** Expire every TTL-pending stream whose expiry has passed and remove its rows (`ownerOf` maps a row to its stream). */
|
|
246
|
+
async expireStreams(ownerOf) {
|
|
247
|
+
const now = this.clock.now();
|
|
248
|
+
for (const [key, s] of this.streams) {
|
|
249
|
+
if (s.expiresAt !== null && now >= s.expiresAt) {
|
|
250
|
+
for (const [table, rows] of this.tables) {
|
|
251
|
+
for (const [id, row] of rows) {
|
|
252
|
+
const owner = ownerOf(table, row);
|
|
253
|
+
if (owner && streamKey(owner) === key) rows.delete(id);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
this.streams.delete(key);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
this.notifyStatus();
|
|
260
|
+
await this.emitAll();
|
|
261
|
+
}
|
|
262
|
+
// -- Test helpers: data side ----------------------------------------------
|
|
263
|
+
/** Get-or-create a table map (tests write rows directly through this). */
|
|
264
|
+
table(name) {
|
|
265
|
+
let t = this.tables.get(name);
|
|
266
|
+
if (!t) {
|
|
267
|
+
t = /* @__PURE__ */ new Map();
|
|
268
|
+
this.tables.set(name, t);
|
|
269
|
+
}
|
|
270
|
+
return t;
|
|
271
|
+
}
|
|
272
|
+
/** Rows arriving via sync (no crud entry). Emits diffs unless `silent`. */
|
|
273
|
+
async synced(table, rows, opts = {}) {
|
|
274
|
+
const t = this.table(table);
|
|
275
|
+
for (const r of rows) {
|
|
276
|
+
if (opts.delete) t.delete(r.id);
|
|
277
|
+
else t.set(r.id, { ...r });
|
|
278
|
+
}
|
|
279
|
+
if (!opts.silent) await this.emitAll();
|
|
280
|
+
}
|
|
281
|
+
/** A local write: row applied + crud entry + delta + diffs. */
|
|
282
|
+
async writeLocal(table, op, row) {
|
|
283
|
+
const t = this.table(table);
|
|
284
|
+
if (op === "DELETE") t.delete(row.id);
|
|
285
|
+
else t.set(row.id, { ...t.get(row.id) ?? {}, ...row });
|
|
286
|
+
const ref = { table, id: row.id, op };
|
|
287
|
+
this.crud.set(`${table}\0${row.id}`, ref);
|
|
288
|
+
for (const l of this.crudListeners) l({ added: [ref], completed: [] });
|
|
289
|
+
await this.emitAll();
|
|
290
|
+
}
|
|
291
|
+
/** Uploads complete: crud drained, completed delta emitted. */
|
|
292
|
+
completeUploads(filter) {
|
|
293
|
+
const completed = [];
|
|
294
|
+
for (const [k, r] of this.crud) {
|
|
295
|
+
if (!filter || filter(r)) {
|
|
296
|
+
this.crud.delete(k);
|
|
297
|
+
completed.push(r);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (completed.length) for (const l of this.crudListeners) l({ added: [], completed });
|
|
301
|
+
}
|
|
302
|
+
/** Re-run every differential watch and emit changes (what a table change notification does). */
|
|
303
|
+
async emitAll() {
|
|
304
|
+
for (const sub of Array.from(this.diffSubs)) await this.emitFor(sub);
|
|
305
|
+
await flush(2);
|
|
306
|
+
}
|
|
307
|
+
notifyStatus() {
|
|
308
|
+
for (const l of Array.from(this.statusListeners)) l();
|
|
309
|
+
}
|
|
310
|
+
async emitFor(sub, onError) {
|
|
311
|
+
let rows;
|
|
312
|
+
try {
|
|
313
|
+
rows = await sub.q.run();
|
|
314
|
+
} catch (e) {
|
|
315
|
+
onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const next = /* @__PURE__ */ new Map();
|
|
319
|
+
const byId = /* @__PURE__ */ new Map();
|
|
320
|
+
for (const r of rows) {
|
|
321
|
+
const id = idOf(r);
|
|
322
|
+
next.set(id, JSON.stringify(r));
|
|
323
|
+
byId.set(id, r);
|
|
324
|
+
}
|
|
325
|
+
const added = [];
|
|
326
|
+
const updated = [];
|
|
327
|
+
const removed = [];
|
|
328
|
+
for (const [id, json] of next) {
|
|
329
|
+
const p = sub.prev.get(id);
|
|
330
|
+
if (p === void 0) added.push(byId.get(id));
|
|
331
|
+
else if (p !== json) updated.push(byId.get(id));
|
|
332
|
+
}
|
|
333
|
+
for (const id of sub.prev.keys()) if (!next.has(id)) removed.push(id);
|
|
334
|
+
const changed = sub.first || added.length || updated.length || removed.length;
|
|
335
|
+
sub.prev = next;
|
|
336
|
+
sub.first = false;
|
|
337
|
+
if (changed) sub.onDiff({ all: rows, added, updated, removed });
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
function deferred() {
|
|
341
|
+
let resolve;
|
|
342
|
+
const promise = new Promise((r) => {
|
|
343
|
+
resolve = r;
|
|
344
|
+
});
|
|
345
|
+
return { promise, resolve };
|
|
346
|
+
}
|
|
347
|
+
function fakeJwt(claims) {
|
|
348
|
+
const enc = (o) => Buffer.from(JSON.stringify(o)).toString("base64url");
|
|
349
|
+
return `${enc({ alg: "none" })}.${enc(claims)}.sig`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export { FakeAdapter, FakeDriver, FakeOrm, VirtualClock, fakeJwt, flush };
|
|
353
|
+
//# sourceMappingURL=testing.js.map
|
|
354
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts"],"names":[],"mappings":"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","file":"testing.js","sourcesContent":["/**\n * `powersync-bridge/testing` — a fake driver with VIRTUAL TIME for deterministic interleavings.\n * Every invariant test (T1–T44) runs on this. Nothing here touches PowerSync.\n */\nimport type { BridgeClock } from './clock';\nimport { streamKey } from './freshness';\nimport { idOf } from './keyset';\nimport type {\n BridgeDriver,\n CrudDelta,\n DifferentialStream,\n Params,\n PendingOp,\n PendingOpRef,\n PreparedQuery,\n PriorityStatus,\n QueryAdapter,\n RowDiff,\n RowsBuilder,\n StreamDescription,\n StreamHandle,\n StreamStatus,\n SyncStatusView,\n} from './types';\n\nexport type Row = Record<string, unknown> & { id: string };\n\n// ---------------------------------------------------------------------------\n// Virtual clock\n// ---------------------------------------------------------------------------\n\nexport class VirtualClock implements BridgeClock {\n private t = 1_000_000;\n private timers: Array<{ at: number; fn: () => void; id: number }> = [];\n private seq = 0;\n\n now(): number {\n return this.t;\n }\n\n setTimeout(fn: () => void, ms: number): () => void {\n const id = ++this.seq;\n this.timers.push({ at: this.t + ms, fn, id });\n return () => {\n this.timers = this.timers.filter((x) => x.id !== id);\n };\n }\n\n /** Advance time, firing due timers in order. */\n async advance(ms: number): Promise<void> {\n const target = this.t + ms;\n for (;;) {\n const due = this.timers.filter((x) => x.at <= target).sort((a, b) => a.at - b.at)[0];\n if (!due) break;\n this.timers = this.timers.filter((x) => x.id !== due.id);\n this.t = due.at;\n due.fn();\n await flush();\n }\n this.t = target;\n await flush();\n }\n}\n\n/** Drain microtasks (the engines do async work after every emission). */\nexport async function flush(rounds = 8): Promise<void> {\n for (let i = 0; i < rounds; i++) await new Promise<void>((r) => setTimeout(r, 0));\n}\n\n// ---------------------------------------------------------------------------\n// Fake ORM: queries are functions over in-memory tables\n// ---------------------------------------------------------------------------\n\nexport interface FakeQuery<T> extends RowsBuilder<T> {\n readonly __tables: readonly string[];\n readonly __run: () => T[];\n}\n\nexport class FakeOrm {\n constructor(readonly driver: FakeDriver) {}\n\n /** A query over `tables` computed by `fn`; thenable so TItem infers like a Drizzle builder. */\n query<T>(tables: readonly string[], fn: (db: FakeDriver) => T[]): FakeQuery<T> {\n const driver = this.driver;\n const q = {\n __tables: tables,\n __run: () => fn(driver),\n then<R1 = T[], R2 = never>(onFul?: ((v: T[]) => R1 | PromiseLike<R1>) | null, onRej?: ((e: unknown) => R2 | PromiseLike<R2>) | null): PromiseLike<R1 | R2> {\n return Promise.resolve(fn(driver)).then(onFul ?? undefined, onRej ?? undefined);\n },\n };\n return q as FakeQuery<T>;\n }\n\n rows<T extends Row = Row>(table: string): T[] {\n return Array.from(this.driver.tables.get(table)?.values() ?? []) as T[];\n }\n}\n\nexport class FakeAdapter implements QueryAdapter {\n readonly orm: FakeOrm;\n constructor(driver: FakeDriver) {\n this.orm = new FakeOrm(driver);\n }\n prepare<T>(builder: RowsBuilder<T>): PreparedQuery<T> {\n const q = builder as FakeQuery<T>;\n return { tables: q.__tables, run: async () => q.__run() };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Fake driver\n// ---------------------------------------------------------------------------\n\ninterface FakeStream {\n desc: StreamDescription;\n refs: number;\n expiresAt: number | null;\n hasSynced: boolean;\n lastSyncedAt: number | null;\n firstSync: { promise: Promise<void>; resolve: () => void };\n}\n\ninterface DiffSub<T> {\n q: PreparedQuery<T>;\n prev: Map<string, string>;\n onDiff: (d: RowDiff<T>) => void;\n first: boolean;\n}\n\nexport class FakeDriver implements BridgeDriver {\n readonly tables = new Map<string, Map<string, Row>>();\n readonly crud = new Map<string, PendingOpRef>();\n readonly streams = new Map<string, FakeStream>();\n readonly clock: VirtualClock;\n\n private connected = false;\n private downloading = false;\n private hasSynced: boolean | undefined = false;\n private lastSyncedAt: number | null = null;\n private online: boolean | undefined = true;\n private readonly crudListeners = new Set<(d: CrudDelta) => void>();\n private readonly statusListeners = new Set<() => void>();\n private readonly diffSubs = new Set<DiffSub<unknown>>();\n readonly presentIdsCalls: Array<{ table: string; ids: readonly string[] }> = [];\n readonly subscribeLog: StreamDescription[] = [];\n streamTtlDefault = 86_400_000;\n\n constructor(clock = new VirtualClock()) {\n this.clock = clock;\n }\n\n // -- BridgeDriver -----------------------------------------------------------\n\n async query<T>(q: PreparedQuery<T>): Promise<T[]> {\n return q.run();\n }\n\n diffs<T>(q: PreparedQuery<T>): DifferentialStream<T> {\n return {\n subscribe: (onDiff, onError) => {\n const sub: DiffSub<T> = { q, prev: new Map(), onDiff, first: true };\n this.diffSubs.add(sub as DiffSub<unknown>);\n queueMicrotask(() => {\n if (this.diffSubs.has(sub as DiffSub<unknown>)) void this.emitFor(sub, onError);\n });\n return () => this.diffSubs.delete(sub as DiffSub<unknown>);\n },\n };\n }\n\n async pendingUploadIds(): Promise<PendingOpRef[]> {\n return Array.from(this.crud.values());\n }\n\n onCrudChange(cb: (delta: CrudDelta) => void): () => void {\n this.crudListeners.add(cb);\n return () => this.crudListeners.delete(cb);\n }\n\n async presentIds(table: string, ids: readonly string[]): Promise<Set<string>> {\n this.presentIdsCalls.push({ table, ids });\n const t = this.tables.get(table);\n return new Set(ids.filter((id) => t?.has(id)));\n }\n\n onlineHint(): boolean | undefined {\n return this.online;\n }\n\n async syncStream(name: string, params: Params | null, opts: { ttl?: number } = {}): Promise<StreamHandle> {\n const desc = { name, params };\n const key = streamKey(desc);\n this.subscribeLog.push(desc);\n let s = this.streams.get(key);\n if (!s) {\n s = { desc, refs: 0, expiresAt: null, hasSynced: false, lastSyncedAt: null, firstSync: deferred() };\n this.streams.set(key, s);\n (s as FakeStream & { ttl?: number }).ttl = (opts.ttl ?? this.streamTtlDefault / 1000) * 1000; // first TTL wins\n }\n s.refs += 1;\n s.expiresAt = null;\n const stream = s;\n let done = false;\n return {\n name,\n params,\n waitForFirstSync: (signal?: AbortSignal) =>\n new Promise<void>((resolve, reject) => {\n stream.firstSync.promise.then(resolve);\n signal?.addEventListener('abort', () => reject(signal.reason ?? new Error('aborted')), { once: true });\n }),\n unsubscribe: () => {\n if (done) return;\n done = true;\n stream.refs -= 1;\n if (stream.refs <= 0) stream.expiresAt = this.clock.now() + ((stream as FakeStream & { ttl?: number }).ttl ?? this.streamTtlDefault);\n this.notifyStatus();\n },\n };\n }\n\n syncStatus(): SyncStatusView {\n const now = this.clock.now();\n const syncStreams: StreamStatus[] = [];\n for (const s of this.streams.values()) {\n if (s.expiresAt !== null && now >= s.expiresAt) continue; // expired: no longer tracked\n syncStreams.push({\n name: s.desc.name,\n params: s.desc.params,\n active: s.refs > 0,\n expiresAt: s.expiresAt === null ? null : new Date(s.expiresAt),\n hasSynced: s.hasSynced,\n lastSyncedAt: s.lastSyncedAt === null ? null : new Date(s.lastSyncedAt),\n });\n }\n const self = this;\n return {\n connected: this.connected,\n downloading: this.downloading,\n hasSynced: this.hasSynced,\n lastSyncedAt: this.lastSyncedAt === null ? null : new Date(this.lastSyncedAt),\n syncStreams,\n forStream(desc) {\n return syncStreams.find((x) => streamKey(x) === streamKey(desc));\n },\n statusForPriority(): PriorityStatus {\n return { hasSynced: self.hasSynced === true, lastSyncedAt: self.lastSyncedAt === null ? null : new Date(self.lastSyncedAt) };\n },\n };\n }\n\n onStatusChanged(cb: () => void): () => void {\n this.statusListeners.add(cb);\n return () => this.statusListeners.delete(cb);\n }\n\n // -- Test helpers: sync side -----------------------------------------------\n\n /** Open a previously synced DB: hasSynced is PERSISTED (true before any connect). */\n openWarm(lastSyncedAt: number): void {\n this.hasSynced = true;\n this.lastSyncedAt = lastSyncedAt;\n }\n\n connect(): void {\n this.connected = true;\n this.notifyStatus();\n }\n\n disconnect(): void {\n this.connected = false;\n this.notifyStatus();\n }\n\n startDownload(): void {\n this.downloading = true;\n this.notifyStatus();\n }\n\n /** A checkpoint completes: rows in `apply` land, lastSyncedAt advances, downloading ends unless `keepDownloading`. */\n async completeCheckpoint(apply: () => void = () => {}, opts: { keepDownloading?: boolean; stream?: StreamDescription } = {}): Promise<void> {\n apply();\n this.lastSyncedAt = this.clock.now();\n this.hasSynced = true;\n this.downloading = opts.keepDownloading ?? false;\n if (opts.stream) {\n const s = this.streams.get(streamKey(opts.stream));\n if (s) {\n s.hasSynced = true;\n s.lastSyncedAt = this.clock.now();\n }\n }\n this.notifyStatus();\n await this.emitAll();\n }\n\n /** The service restarted: the SDK resets lastSyncedAt to null. */\n serviceRestart(): void {\n this.lastSyncedAt = null;\n this.notifyStatus();\n }\n\n /** disconnectAndClear(): tables wiped, hasSynced → false. */\n async clear(): Promise<void> {\n this.tables.clear();\n this.crud.clear();\n this.hasSynced = false;\n this.lastSyncedAt = null;\n this.connected = false;\n this.notifyStatus();\n await this.emitAll();\n }\n\n setOnline(v: boolean | undefined): void {\n this.online = v;\n }\n\n /** Resolve a stream subscription's first sync, landing `apply` rows. */\n async resolveFirstSync(desc: StreamDescription, apply: () => void = () => {}): Promise<void> {\n const s = this.streams.get(streamKey(desc));\n if (!s) throw new Error(`no subscription for ${streamKey(desc)}`);\n apply();\n s.hasSynced = true;\n s.lastSyncedAt = this.clock.now();\n s.firstSync.resolve();\n this.notifyStatus();\n await this.emitAll();\n }\n\n /** Expire every TTL-pending stream whose expiry has passed and remove its rows (`ownerOf` maps a row to its stream). */\n async expireStreams(ownerOf: (table: string, row: Row) => StreamDescription | undefined): Promise<void> {\n const now = this.clock.now();\n for (const [key, s] of this.streams) {\n if (s.expiresAt !== null && now >= s.expiresAt) {\n for (const [table, rows] of this.tables) {\n for (const [id, row] of rows) {\n const owner = ownerOf(table, row);\n if (owner && streamKey(owner) === key) rows.delete(id);\n }\n }\n this.streams.delete(key);\n }\n }\n this.notifyStatus();\n await this.emitAll();\n }\n\n // -- Test helpers: data side ----------------------------------------------\n\n /** Get-or-create a table map (tests write rows directly through this). */\n table(name: string): Map<string, Row> {\n let t = this.tables.get(name);\n if (!t) {\n t = new Map();\n this.tables.set(name, t);\n }\n return t;\n }\n\n /** Rows arriving via sync (no crud entry). Emits diffs unless `silent`. */\n async synced(table: string, rows: Row[], opts: { silent?: boolean; delete?: boolean } = {}): Promise<void> {\n const t = this.table(table);\n for (const r of rows) {\n if (opts.delete) t.delete(r.id);\n else t.set(r.id, { ...r });\n }\n if (!opts.silent) await this.emitAll();\n }\n\n /** A local write: row applied + crud entry + delta + diffs. */\n async writeLocal(table: string, op: PendingOp, row: Row): Promise<void> {\n const t = this.table(table);\n if (op === 'DELETE') t.delete(row.id);\n else t.set(row.id, { ...(t.get(row.id) ?? {}), ...row });\n const ref: PendingOpRef = { table, id: row.id, op };\n this.crud.set(`${table}\\u0000${row.id}`, ref);\n for (const l of this.crudListeners) l({ added: [ref], completed: [] });\n await this.emitAll();\n }\n\n /** Uploads complete: crud drained, completed delta emitted. */\n completeUploads(filter?: (r: PendingOpRef) => boolean): void {\n const completed: PendingOpRef[] = [];\n for (const [k, r] of this.crud) {\n if (!filter || filter(r)) {\n this.crud.delete(k);\n completed.push(r);\n }\n }\n if (completed.length) for (const l of this.crudListeners) l({ added: [], completed });\n }\n\n /** Re-run every differential watch and emit changes (what a table change notification does). */\n async emitAll(): Promise<void> {\n for (const sub of Array.from(this.diffSubs)) await this.emitFor(sub);\n await flush(2);\n }\n\n notifyStatus(): void {\n for (const l of Array.from(this.statusListeners)) l();\n }\n\n private async emitFor<T>(sub: DiffSub<T>, onError?: (e: Error) => void): Promise<void> {\n let rows: T[];\n try {\n rows = await sub.q.run();\n } catch (e) {\n onError?.(e instanceof Error ? e : new Error(String(e)));\n return;\n }\n const next = new Map<string, string>();\n const byId = new Map<string, T>();\n for (const r of rows) {\n const id = idOf(r);\n next.set(id, JSON.stringify(r));\n byId.set(id, r);\n }\n const added: T[] = [];\n const updated: T[] = [];\n const removed: string[] = [];\n for (const [id, json] of next) {\n const p = sub.prev.get(id);\n if (p === undefined) added.push(byId.get(id) as T);\n else if (p !== json) updated.push(byId.get(id) as T);\n }\n for (const id of sub.prev.keys()) if (!next.has(id)) removed.push(id);\n const changed = sub.first || added.length || updated.length || removed.length;\n sub.prev = next;\n sub.first = false;\n if (changed) sub.onDiff({ all: rows, added, updated, removed });\n }\n}\n\nfunction deferred(): { promise: Promise<void>; resolve: () => void } {\n let resolve!: () => void;\n const promise = new Promise<void>((r) => {\n resolve = r;\n });\n return { promise, resolve };\n}\n\n/** Build an unsigned JWT for identity tests. */\nexport function fakeJwt(claims: Record<string, unknown>): string {\n const enc = (o: unknown) => Buffer.from(JSON.stringify(o)).toString('base64url');\n return `${enc({ alg: 'none' })}.${enc(claims)}.sig`;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "powersync-bridge",
|
|
3
|
+
"version": "0.0.1-alpha.0",
|
|
4
|
+
"description": "Client-side bridge between a PowerSync local SQLite database and a conventional HTTP API: raced first paint, pending-write-aware reconcile, bounded partition escalation.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/akpor-kofi/powersync-bridge.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/akpor-kofi/powersync-bridge#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/akpor-kofi/powersync-bridge/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"powersync",
|
|
19
|
+
"offline-first",
|
|
20
|
+
"local-first",
|
|
21
|
+
"sqlite",
|
|
22
|
+
"sync"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js",
|
|
34
|
+
"require": "./dist/index.cjs"
|
|
35
|
+
},
|
|
36
|
+
"./testing": {
|
|
37
|
+
"types": "./dist/testing.d.ts",
|
|
38
|
+
"import": "./dist/testing.js",
|
|
39
|
+
"require": "./dist/testing.cjs"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist"
|
|
44
|
+
],
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"tsup": "^8.5.1",
|
|
47
|
+
"typescript": "^5.9.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"typecheck": "tsc --noEmit"
|
|
52
|
+
}
|
|
53
|
+
}
|