powersync-bridge 0.0.1-alpha.0 → 0.0.1-alpha.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/freshness.ts","../src/keyset.ts","../src/testing.ts"],"names":[],"mappings":"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","file":"testing.cjs","sourcesContent":["import type { BridgeDriver, StreamDescription, SyncStatusView } from './types';\n\nexport type Clock = () => number;\n\nexport interface CheckpointCompletion {\n /** Lower-bound stamp of the checkpoint's data position (download start), ms. */\n readonly stamp: number;\n readonly completedAt: number;\n /** Stream this completion was observed on; null for the global status. */\n readonly stream: string | null;\n}\n\nexport function streamKey(desc: StreamDescription): string {\n return `${desc.name}:${desc.params ? JSON.stringify(desc.params) : ''}`;\n}\n\n/**\n * Session-scoped, monotonic, transition-independent freshness (§5.2, R3/R5).\n *\n * stamp(checkpoint_n) = max(observed download start ≤ its completion,\n * completion time of checkpoint n−1,\n * sessionConnectAt when no other bound exists)\n * localFreshAt = max(previous, stamp) — a null observation never lowers it.\n */\nexport class FreshnessTracker {\n private sessionConnectAt: number | null = null;\n private localFreshAt = Number.NEGATIVE_INFINITY;\n private prevCompletionAt: number | null = null;\n private lastDownloadStart: number | null = null;\n private lastSeenSyncedAt: number | null = null;\n private lastDownloading = false;\n private lastConnected = false;\n private lastHasSynced: boolean | undefined = undefined;\n private readonly streamFreshAt = new Map<string, number>();\n private readonly streamSeenSyncedAt = new Map<string, number>();\n private readonly completions: CheckpointCompletion[] = [];\n private readonly completionListeners = new Set<(c: CheckpointCompletion) => void>();\n private readonly clearListeners = new Set<() => void>();\n private unsubscribe: (() => void) | null = null;\n\n constructor(\n private readonly driver: BridgeDriver,\n private readonly clock: Clock,\n ) {}\n\n /** Call once at engine init and again after every identity swap. */\n start(epochStartedAt: number): void {\n this.stop();\n this.reset();\n const status = this.driver.syncStatus();\n this.lastConnected = status.connected;\n this.lastDownloading = status.downloading;\n this.lastHasSynced = status.hasSynced;\n this.lastSeenSyncedAt = status.lastSyncedAt?.getTime() ?? null;\n if (status.connected) this.sessionConnectAt = epochStartedAt;\n // Boot mid-download: provisional bound for the FIRST completion, never applied to localFreshAt now.\n if (status.downloading) {\n this.prevCompletionAt = Math.max(epochStartedAt, this.lastSeenSyncedAt ?? Number.NEGATIVE_INFINITY);\n }\n for (const s of status.syncStreams) {\n if (s.lastSyncedAt) this.streamSeenSyncedAt.set(streamKey(s), s.lastSyncedAt.getTime());\n }\n this.unsubscribe = this.driver.onStatusChanged(() => this.onStatus(this.driver.syncStatus()));\n }\n\n stop(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n }\n\n private reset(): void {\n this.sessionConnectAt = null;\n this.localFreshAt = Number.NEGATIVE_INFINITY;\n this.prevCompletionAt = null;\n this.lastDownloadStart = null;\n this.lastSeenSyncedAt = null;\n this.streamFreshAt.clear();\n this.streamSeenSyncedAt.clear();\n this.completions.length = 0;\n }\n\n get sessionConnectAtMs(): number | null {\n return this.sessionConnectAt;\n }\n\n get localFreshAtMs(): number {\n return this.localFreshAt;\n }\n\n streamFreshAtMs(desc: StreamDescription): number {\n return this.streamFreshAt.get(streamKey(desc)) ?? Number.NEGATIVE_INFINITY;\n }\n\n /** Freshness for a list: the owning stream's stamp when declared, else the global stamp. */\n freshAtFor(stream: StreamDescription | undefined): number {\n if (!stream) return this.localFreshAt;\n return Math.max(this.streamFreshAtMs(stream), this.localFreshAt);\n }\n\n /** \"Answered\" = in-session freshness evidence (R5). */\n isFresh(stream: StreamDescription | undefined): boolean {\n if (this.sessionConnectAt === null) return false;\n return this.freshAtFor(stream) >= this.sessionConnectAt;\n }\n\n onCompletion(l: (c: CheckpointCompletion) => void): () => void {\n this.completionListeners.add(l);\n return () => this.completionListeners.delete(l);\n }\n\n onClear(l: () => void): () => void {\n this.clearListeners.add(l);\n return () => this.clearListeners.delete(l);\n }\n\n private onStatus(status: SyncStatusView): void {\n const now = this.clock();\n\n if (status.connected && !this.lastConnected && this.sessionConnectAt === null) {\n this.sessionConnectAt = now; // FIRST connect of the epoch — never per reconnect (R5)\n }\n this.lastConnected = status.connected;\n\n if (status.downloading && !this.lastDownloading) this.lastDownloadStart = now;\n this.lastDownloading = status.downloading;\n\n const syncedAt = status.lastSyncedAt?.getTime() ?? null;\n if (syncedAt !== null && syncedAt !== this.lastSeenSyncedAt) {\n this.recordCompletion(now, null);\n }\n if (syncedAt !== null) this.lastSeenSyncedAt = syncedAt; // null never lowers anything (R5)\n\n for (const s of status.syncStreams) {\n const key = streamKey(s);\n const at = s.lastSyncedAt?.getTime() ?? null;\n if (at !== null && at !== this.streamSeenSyncedAt.get(key)) {\n this.streamSeenSyncedAt.set(key, at);\n const stamp = this.stampFor(now);\n const prev = this.streamFreshAt.get(key) ?? Number.NEGATIVE_INFINITY;\n this.streamFreshAt.set(key, Math.max(prev, stamp));\n const c: CheckpointCompletion = { stamp: Math.max(prev, stamp), completedAt: now, stream: key };\n for (const l of this.completionListeners) l(c);\n }\n }\n\n if (this.lastHasSynced === true && status.hasSynced !== true) {\n for (const l of this.clearListeners) l();\n }\n this.lastHasSynced = status.hasSynced;\n }\n\n private stampFor(now: number): number {\n const candidates: number[] = [];\n if (this.lastDownloadStart !== null && this.lastDownloadStart <= now) candidates.push(this.lastDownloadStart);\n if (this.prevCompletionAt !== null) candidates.push(this.prevCompletionAt);\n if (candidates.length === 0 && this.sessionConnectAt !== null) candidates.push(this.sessionConnectAt);\n if (candidates.length === 0) return Number.NEGATIVE_INFINITY;\n return Math.max(...candidates);\n }\n\n private recordCompletion(now: number, stream: string | null): void {\n const stamp = this.stampFor(now);\n this.localFreshAt = Math.max(this.localFreshAt, stamp);\n this.prevCompletionAt = now;\n this.lastDownloadStart = null; // consumed; the next stamp derives from this completion\n const c: CheckpointCompletion = { stamp: this.localFreshAt, completedAt: now, stream };\n this.completions.push(c);\n for (const l of this.completionListeners) l(c);\n }\n}\n","import type { CursorOf, KeyOf, OpaqueCursor, OrderKey, SortDirection } from './types';\n\n/** `id` is always the implicit tiebreaker (§8.3): a scalar key K is treated as [K, 'id']. */\nexport function keyColumns<T>(orderKey: OrderKey<T>): [KeyOf<T>, KeyOf<T>] {\n if (typeof orderKey === 'string') return [orderKey, 'id' as KeyOf<T>];\n return [orderKey[0], orderKey[1]];\n}\n\nexport function idOf(row: unknown): string {\n const r = row as { id?: unknown };\n if (r == null || r.id === undefined || r.id === null) {\n throw new Error('powersync-bridge: every row must have an `id`');\n }\n return String(r.id);\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === null || a === undefined) return 1; // nulls last\n if (b === null || b === undefined) return -1;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n if (typeof a === 'boolean' && typeof b === 'boolean') return Number(a) - Number(b);\n const sa = String(a);\n const sb = String(b);\n return sa < sb ? -1 : sa > sb ? 1 : 0;\n}\n\n/** Comparator over rows for the declared order key + direction. Byte order for strings (SQLite BINARY). */\nexport function rowComparator<T>(orderKey: OrderKey<T>, direction: SortDirection = 'asc'): (a: T, b: T) => number {\n const [k1, k2] = keyColumns(orderKey);\n const sign = direction === 'desc' ? -1 : 1;\n return (a, b) => {\n const c1 = compareValues((a as Record<string, unknown>)[k1], (b as Record<string, unknown>)[k1]);\n if (c1 !== 0) return c1 * sign;\n return compareValues((a as Record<string, unknown>)[k2], (b as Record<string, unknown>)[k2]) * sign;\n };\n}\n\n/** The cursor a row yields, in the definition's cursor shape. */\nexport function cursorOfRow<T, K extends OrderKey<T>>(row: T, orderKey: K): CursorOf<T, K> {\n const r = row as Record<string, unknown>;\n if (typeof orderKey === 'string') return r[orderKey] as CursorOf<T, K>;\n return [r[orderKey[0]], r[orderKey[1]]] as CursorOf<T, K>;\n}\n\n/** Whether `row` sorts strictly after `cursor` in the list's order (i.e. is \"beyond\" it). */\nexport function isAfterCursor<T, K extends OrderKey<T>>(\n row: T,\n cursor: CursorOf<T, K>,\n orderKey: K,\n direction: SortDirection = 'asc',\n): boolean {\n const [k1, k2] = keyColumns(orderKey);\n const r = row as Record<string, unknown>;\n const sign = direction === 'desc' ? -1 : 1;\n const [c1, c2] = typeof orderKey === 'string' ? [cursor, undefined] : (cursor as [unknown, unknown]);\n const d1 = compareValues(r[k1], c1) * sign;\n if (d1 !== 0) return d1 > 0;\n if (c2 === undefined) return false;\n return compareValues(r[k2], c2) * sign > 0;\n}\n\nfunction toBase64(s: string): string {\n if (typeof btoa === 'function') return btoa(unescape(encodeURIComponent(s)));\n return Buffer.from(s, 'utf8').toString('base64');\n}\n\nfunction fromBase64(s: string): string {\n if (typeof atob === 'function') return decodeURIComponent(escape(atob(s)));\n return Buffer.from(s, 'base64').toString('utf8');\n}\n\nexport function encodeCursor(cursor: unknown): OpaqueCursor {\n return toBase64(JSON.stringify(cursor)) as OpaqueCursor;\n}\n\nexport function decodeCursor<C>(opaque: OpaqueCursor): C {\n return JSON.parse(fromBase64(opaque)) as C;\n}\n","/**\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"]}
1
+ {"version":3,"sources":["../src/freshness.ts","../src/keyset.ts","../src/testing.ts"],"names":[],"mappings":"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","file":"testing.cjs","sourcesContent":["import type { BridgeDriver, StreamDescription, SyncStatusView } from './types';\n\nexport type Clock = () => number;\n\nexport interface CheckpointCompletion {\n /** Lower-bound stamp of the checkpoint's data position (download start), ms. */\n readonly stamp: number;\n readonly completedAt: number;\n /** Stream this completion was observed on; null for the global status. */\n readonly stream: string | null;\n}\n\nexport function streamKey(desc: StreamDescription): string {\n return `${desc.name}:${desc.params ? JSON.stringify(desc.params) : ''}`;\n}\n\n/**\n * Session-scoped, monotonic, transition-independent freshness (§5.2, R3/R5).\n *\n * stamp(checkpoint_n) = max(observed download start ≤ its completion,\n * completion time of checkpoint n−1,\n * sessionConnectAt when no other bound exists)\n * localFreshAt = max(previous, stamp) — a null observation never lowers it.\n */\nexport class FreshnessTracker {\n private sessionConnectAt: number | null = null;\n private localFreshAt = Number.NEGATIVE_INFINITY;\n private prevCompletionAt: number | null = null;\n private lastDownloadStart: number | null = null;\n private lastSeenSyncedAt: number | null = null;\n private lastDownloading = false;\n private lastConnected = false;\n private lastHasSynced: boolean | undefined = undefined;\n private readonly streamFreshAt = new Map<string, number>();\n private readonly streamSeenSyncedAt = new Map<string, number>();\n private readonly completionListeners = new Set<(c: CheckpointCompletion) => void>();\n private readonly clearListeners = new Set<() => void>();\n private unsubscribe: (() => void) | null = null;\n\n constructor(\n private readonly driver: BridgeDriver,\n private readonly clock: Clock,\n ) {}\n\n /** Call once at engine init and again after every identity swap. */\n start(epochStartedAt: number): void {\n this.stop();\n this.reset();\n const status = this.driver.syncStatus();\n this.lastConnected = status.connected;\n this.lastDownloading = status.downloading;\n this.lastHasSynced = status.hasSynced;\n this.lastSeenSyncedAt = status.lastSyncedAt?.getTime() ?? null;\n if (status.connected) this.sessionConnectAt = epochStartedAt;\n // Boot mid-download: provisional bound for the FIRST completion, never applied to localFreshAt now.\n if (status.downloading) {\n this.prevCompletionAt = Math.max(epochStartedAt, this.lastSeenSyncedAt ?? Number.NEGATIVE_INFINITY);\n }\n for (const s of status.syncStreams) {\n if (s.lastSyncedAt) this.streamSeenSyncedAt.set(streamKey(s), s.lastSyncedAt.getTime());\n }\n this.unsubscribe = this.driver.onStatusChanged(() => this.onStatus(this.driver.syncStatus()));\n }\n\n stop(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n }\n\n private reset(): void {\n this.sessionConnectAt = null;\n this.localFreshAt = Number.NEGATIVE_INFINITY;\n this.prevCompletionAt = null;\n this.lastDownloadStart = null;\n this.lastSeenSyncedAt = null;\n this.streamFreshAt.clear();\n this.streamSeenSyncedAt.clear();\n }\n\n get sessionConnectAtMs(): number | null {\n return this.sessionConnectAt;\n }\n\n get localFreshAtMs(): number {\n return this.localFreshAt;\n }\n\n streamFreshAtMs(desc: StreamDescription): number {\n return this.streamFreshAt.get(streamKey(desc)) ?? Number.NEGATIVE_INFINITY;\n }\n\n /** Freshness for a list: the owning stream's stamp when declared, else the global stamp. */\n freshAtFor(stream: StreamDescription | undefined): number {\n if (!stream) return this.localFreshAt;\n return Math.max(this.streamFreshAtMs(stream), this.localFreshAt);\n }\n\n /** \"Answered\" = in-session freshness evidence (R5). */\n isFresh(stream: StreamDescription | undefined): boolean {\n if (this.sessionConnectAt === null) return false;\n return this.freshAtFor(stream) >= this.sessionConnectAt;\n }\n\n onCompletion(l: (c: CheckpointCompletion) => void): () => void {\n this.completionListeners.add(l);\n return () => this.completionListeners.delete(l);\n }\n\n onClear(l: () => void): () => void {\n this.clearListeners.add(l);\n return () => this.clearListeners.delete(l);\n }\n\n private onStatus(status: SyncStatusView): void {\n const now = this.clock();\n\n if (status.connected && !this.lastConnected && this.sessionConnectAt === null) {\n this.sessionConnectAt = now; // FIRST connect of the epoch — never per reconnect (R5)\n }\n this.lastConnected = status.connected;\n\n if (status.downloading && !this.lastDownloading) this.lastDownloadStart = now;\n this.lastDownloading = status.downloading;\n\n const syncedAt = status.lastSyncedAt?.getTime() ?? null;\n if (syncedAt !== null && syncedAt !== this.lastSeenSyncedAt) {\n this.recordCompletion(now, null);\n }\n if (syncedAt !== null) this.lastSeenSyncedAt = syncedAt; // null never lowers anything (R5)\n\n for (const s of status.syncStreams) {\n const key = streamKey(s);\n const at = s.lastSyncedAt?.getTime() ?? null;\n if (at !== null && at !== this.streamSeenSyncedAt.get(key)) {\n this.streamSeenSyncedAt.set(key, at);\n const stamp = this.stampFor(now);\n const prev = this.streamFreshAt.get(key) ?? Number.NEGATIVE_INFINITY;\n this.streamFreshAt.set(key, Math.max(prev, stamp));\n const c: CheckpointCompletion = { stamp: Math.max(prev, stamp), completedAt: now, stream: key };\n for (const l of this.completionListeners) l(c);\n }\n }\n\n if (this.lastHasSynced === true && status.hasSynced !== true) {\n for (const l of this.clearListeners) l();\n }\n this.lastHasSynced = status.hasSynced;\n }\n\n private stampFor(now: number): number {\n const candidates: number[] = [];\n if (this.lastDownloadStart !== null && this.lastDownloadStart <= now) candidates.push(this.lastDownloadStart);\n if (this.prevCompletionAt !== null) candidates.push(this.prevCompletionAt);\n if (candidates.length === 0 && this.sessionConnectAt !== null) candidates.push(this.sessionConnectAt);\n if (candidates.length === 0) return Number.NEGATIVE_INFINITY;\n return Math.max(...candidates);\n }\n\n private recordCompletion(now: number, stream: string | null): void {\n const stamp = this.stampFor(now);\n this.localFreshAt = Math.max(this.localFreshAt, stamp);\n this.prevCompletionAt = now;\n this.lastDownloadStart = null; // consumed; the next stamp derives from this completion\n const c: CheckpointCompletion = { stamp: this.localFreshAt, completedAt: now, stream };\n for (const l of this.completionListeners) l(c);\n }\n}\n","import type { CursorOf, KeyOf, OpaqueCursor, OrderKey, SortDirection } from './types';\n\n/** `id` is always the implicit tiebreaker (§8.3): a scalar key K is treated as [K, 'id']. */\nexport function keyColumns<T>(orderKey: OrderKey<T>): [KeyOf<T>, KeyOf<T>] {\n if (typeof orderKey === 'string') return [orderKey, 'id' as KeyOf<T>];\n return [orderKey[0], orderKey[1]];\n}\n\nexport function idOf(row: unknown): string {\n const r = row as { id?: unknown };\n if (r == null || r.id === undefined || r.id === null) {\n throw new Error('powersync-bridge: every row must have an `id`');\n }\n return String(r.id);\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === null || a === undefined) return 1; // nulls last\n if (b === null || b === undefined) return -1;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n if (typeof a === 'boolean' && typeof b === 'boolean') return Number(a) - Number(b);\n const sa = String(a);\n const sb = String(b);\n return sa < sb ? -1 : sa > sb ? 1 : 0;\n}\n\n/** Comparator over rows for the declared order key + direction. Byte order for strings (SQLite BINARY). */\nexport function rowComparator<T>(orderKey: OrderKey<T>, direction: SortDirection = 'asc'): (a: T, b: T) => number {\n const [k1, k2] = keyColumns(orderKey);\n const sign = direction === 'desc' ? -1 : 1;\n return (a, b) => {\n const c1 = compareValues((a as Record<string, unknown>)[k1], (b as Record<string, unknown>)[k1]);\n if (c1 !== 0) return c1 * sign;\n return compareValues((a as Record<string, unknown>)[k2], (b as Record<string, unknown>)[k2]) * sign;\n };\n}\n\n/** The cursor a row yields, in the definition's cursor shape. */\nexport function cursorOfRow<T, K extends OrderKey<T>>(row: T, orderKey: K): CursorOf<T, K> {\n const r = row as Record<string, unknown>;\n if (typeof orderKey === 'string') return r[orderKey] as CursorOf<T, K>;\n return [r[orderKey[0]], r[orderKey[1]]] as CursorOf<T, K>;\n}\n\n/** Whether `row` sorts strictly after `cursor` in the list's order (i.e. is \"beyond\" it). */\nexport function isAfterCursor<T, K extends OrderKey<T>>(\n row: T,\n cursor: CursorOf<T, K>,\n orderKey: K,\n direction: SortDirection = 'asc',\n): boolean {\n const [k1, k2] = keyColumns(orderKey);\n const r = row as Record<string, unknown>;\n const sign = direction === 'desc' ? -1 : 1;\n const [c1, c2] = typeof orderKey === 'string' ? [cursor, undefined] : (cursor as [unknown, unknown]);\n const d1 = compareValues(r[k1], c1) * sign;\n if (d1 !== 0) return d1 > 0;\n if (c2 === undefined) return false;\n return compareValues(r[k2], c2) * sign > 0;\n}\n\nfunction toBase64(s: string): string {\n if (typeof btoa === 'function') return btoa(unescape(encodeURIComponent(s)));\n return Buffer.from(s, 'utf8').toString('base64');\n}\n\nfunction fromBase64(s: string): string {\n if (typeof atob === 'function') return decodeURIComponent(escape(atob(s)));\n return Buffer.from(s, 'base64').toString('utf8');\n}\n\nexport function encodeCursor(cursor: unknown): OpaqueCursor {\n return toBase64(JSON.stringify(cursor)) as OpaqueCursor;\n}\n\nexport function decodeCursor<C>(opaque: OpaqueCursor): C {\n return JSON.parse(fromBase64(opaque)) as C;\n}\n","/**\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"]}
@@ -1,4 +1,4 @@
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.cjs';
1
+ import { Q as QueryAdapter, B as BridgeDriver, P as PendingOpRef, S as StreamDescription, d as BridgeClock, v as PreparedQuery, D as DifferentialStream, c as CrudDelta, k as Params, G as StreamHandle, J as SyncStatusView, u as PendingOp, g as RowsBuilder } from './clock-DANrBsZH.cjs';
2
2
 
3
3
  /**
4
4
  * `powersync-bridge/testing` — a fake driver with VIRTUAL TIME for deterministic interleavings.
package/dist/testing.d.ts CHANGED
@@ -1,4 +1,4 @@
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';
1
+ import { Q as QueryAdapter, B as BridgeDriver, P as PendingOpRef, S as StreamDescription, d as BridgeClock, v as PreparedQuery, D as DifferentialStream, c as CrudDelta, k as Params, G as StreamHandle, J as SyncStatusView, u as PendingOp, g as RowsBuilder } from './clock-DANrBsZH.js';
2
2
 
3
3
  /**
4
4
  * `powersync-bridge/testing` — a fake driver with VIRTUAL TIME for deterministic interleavings.
package/dist/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { streamKey, idOf } from './chunk-TANEBEGU.js';
1
+ import { streamKey, idOf } from './chunk-WJDQWVO5.js';
2
2
 
3
3
  // src/testing.ts
4
4
  var VirtualClock = class {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "powersync-bridge",
3
- "version": "0.0.1-alpha.0",
3
+ "version": "0.0.1-alpha.2",
4
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
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/freshness.ts","../src/keyset.ts"],"names":[],"mappings":"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","file":"chunk-TANEBEGU.js","sourcesContent":["import type { BridgeDriver, StreamDescription, SyncStatusView } from './types';\n\nexport type Clock = () => number;\n\nexport interface CheckpointCompletion {\n /** Lower-bound stamp of the checkpoint's data position (download start), ms. */\n readonly stamp: number;\n readonly completedAt: number;\n /** Stream this completion was observed on; null for the global status. */\n readonly stream: string | null;\n}\n\nexport function streamKey(desc: StreamDescription): string {\n return `${desc.name}:${desc.params ? JSON.stringify(desc.params) : ''}`;\n}\n\n/**\n * Session-scoped, monotonic, transition-independent freshness (§5.2, R3/R5).\n *\n * stamp(checkpoint_n) = max(observed download start ≤ its completion,\n * completion time of checkpoint n−1,\n * sessionConnectAt when no other bound exists)\n * localFreshAt = max(previous, stamp) — a null observation never lowers it.\n */\nexport class FreshnessTracker {\n private sessionConnectAt: number | null = null;\n private localFreshAt = Number.NEGATIVE_INFINITY;\n private prevCompletionAt: number | null = null;\n private lastDownloadStart: number | null = null;\n private lastSeenSyncedAt: number | null = null;\n private lastDownloading = false;\n private lastConnected = false;\n private lastHasSynced: boolean | undefined = undefined;\n private readonly streamFreshAt = new Map<string, number>();\n private readonly streamSeenSyncedAt = new Map<string, number>();\n private readonly completions: CheckpointCompletion[] = [];\n private readonly completionListeners = new Set<(c: CheckpointCompletion) => void>();\n private readonly clearListeners = new Set<() => void>();\n private unsubscribe: (() => void) | null = null;\n\n constructor(\n private readonly driver: BridgeDriver,\n private readonly clock: Clock,\n ) {}\n\n /** Call once at engine init and again after every identity swap. */\n start(epochStartedAt: number): void {\n this.stop();\n this.reset();\n const status = this.driver.syncStatus();\n this.lastConnected = status.connected;\n this.lastDownloading = status.downloading;\n this.lastHasSynced = status.hasSynced;\n this.lastSeenSyncedAt = status.lastSyncedAt?.getTime() ?? null;\n if (status.connected) this.sessionConnectAt = epochStartedAt;\n // Boot mid-download: provisional bound for the FIRST completion, never applied to localFreshAt now.\n if (status.downloading) {\n this.prevCompletionAt = Math.max(epochStartedAt, this.lastSeenSyncedAt ?? Number.NEGATIVE_INFINITY);\n }\n for (const s of status.syncStreams) {\n if (s.lastSyncedAt) this.streamSeenSyncedAt.set(streamKey(s), s.lastSyncedAt.getTime());\n }\n this.unsubscribe = this.driver.onStatusChanged(() => this.onStatus(this.driver.syncStatus()));\n }\n\n stop(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n }\n\n private reset(): void {\n this.sessionConnectAt = null;\n this.localFreshAt = Number.NEGATIVE_INFINITY;\n this.prevCompletionAt = null;\n this.lastDownloadStart = null;\n this.lastSeenSyncedAt = null;\n this.streamFreshAt.clear();\n this.streamSeenSyncedAt.clear();\n this.completions.length = 0;\n }\n\n get sessionConnectAtMs(): number | null {\n return this.sessionConnectAt;\n }\n\n get localFreshAtMs(): number {\n return this.localFreshAt;\n }\n\n streamFreshAtMs(desc: StreamDescription): number {\n return this.streamFreshAt.get(streamKey(desc)) ?? Number.NEGATIVE_INFINITY;\n }\n\n /** Freshness for a list: the owning stream's stamp when declared, else the global stamp. */\n freshAtFor(stream: StreamDescription | undefined): number {\n if (!stream) return this.localFreshAt;\n return Math.max(this.streamFreshAtMs(stream), this.localFreshAt);\n }\n\n /** \"Answered\" = in-session freshness evidence (R5). */\n isFresh(stream: StreamDescription | undefined): boolean {\n if (this.sessionConnectAt === null) return false;\n return this.freshAtFor(stream) >= this.sessionConnectAt;\n }\n\n onCompletion(l: (c: CheckpointCompletion) => void): () => void {\n this.completionListeners.add(l);\n return () => this.completionListeners.delete(l);\n }\n\n onClear(l: () => void): () => void {\n this.clearListeners.add(l);\n return () => this.clearListeners.delete(l);\n }\n\n private onStatus(status: SyncStatusView): void {\n const now = this.clock();\n\n if (status.connected && !this.lastConnected && this.sessionConnectAt === null) {\n this.sessionConnectAt = now; // FIRST connect of the epoch — never per reconnect (R5)\n }\n this.lastConnected = status.connected;\n\n if (status.downloading && !this.lastDownloading) this.lastDownloadStart = now;\n this.lastDownloading = status.downloading;\n\n const syncedAt = status.lastSyncedAt?.getTime() ?? null;\n if (syncedAt !== null && syncedAt !== this.lastSeenSyncedAt) {\n this.recordCompletion(now, null);\n }\n if (syncedAt !== null) this.lastSeenSyncedAt = syncedAt; // null never lowers anything (R5)\n\n for (const s of status.syncStreams) {\n const key = streamKey(s);\n const at = s.lastSyncedAt?.getTime() ?? null;\n if (at !== null && at !== this.streamSeenSyncedAt.get(key)) {\n this.streamSeenSyncedAt.set(key, at);\n const stamp = this.stampFor(now);\n const prev = this.streamFreshAt.get(key) ?? Number.NEGATIVE_INFINITY;\n this.streamFreshAt.set(key, Math.max(prev, stamp));\n const c: CheckpointCompletion = { stamp: Math.max(prev, stamp), completedAt: now, stream: key };\n for (const l of this.completionListeners) l(c);\n }\n }\n\n if (this.lastHasSynced === true && status.hasSynced !== true) {\n for (const l of this.clearListeners) l();\n }\n this.lastHasSynced = status.hasSynced;\n }\n\n private stampFor(now: number): number {\n const candidates: number[] = [];\n if (this.lastDownloadStart !== null && this.lastDownloadStart <= now) candidates.push(this.lastDownloadStart);\n if (this.prevCompletionAt !== null) candidates.push(this.prevCompletionAt);\n if (candidates.length === 0 && this.sessionConnectAt !== null) candidates.push(this.sessionConnectAt);\n if (candidates.length === 0) return Number.NEGATIVE_INFINITY;\n return Math.max(...candidates);\n }\n\n private recordCompletion(now: number, stream: string | null): void {\n const stamp = this.stampFor(now);\n this.localFreshAt = Math.max(this.localFreshAt, stamp);\n this.prevCompletionAt = now;\n this.lastDownloadStart = null; // consumed; the next stamp derives from this completion\n const c: CheckpointCompletion = { stamp: this.localFreshAt, completedAt: now, stream };\n this.completions.push(c);\n for (const l of this.completionListeners) l(c);\n }\n}\n","import type { CursorOf, KeyOf, OpaqueCursor, OrderKey, SortDirection } from './types';\n\n/** `id` is always the implicit tiebreaker (§8.3): a scalar key K is treated as [K, 'id']. */\nexport function keyColumns<T>(orderKey: OrderKey<T>): [KeyOf<T>, KeyOf<T>] {\n if (typeof orderKey === 'string') return [orderKey, 'id' as KeyOf<T>];\n return [orderKey[0], orderKey[1]];\n}\n\nexport function idOf(row: unknown): string {\n const r = row as { id?: unknown };\n if (r == null || r.id === undefined || r.id === null) {\n throw new Error('powersync-bridge: every row must have an `id`');\n }\n return String(r.id);\n}\n\nfunction compareValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === null || a === undefined) return 1; // nulls last\n if (b === null || b === undefined) return -1;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n if (typeof a === 'boolean' && typeof b === 'boolean') return Number(a) - Number(b);\n const sa = String(a);\n const sb = String(b);\n return sa < sb ? -1 : sa > sb ? 1 : 0;\n}\n\n/** Comparator over rows for the declared order key + direction. Byte order for strings (SQLite BINARY). */\nexport function rowComparator<T>(orderKey: OrderKey<T>, direction: SortDirection = 'asc'): (a: T, b: T) => number {\n const [k1, k2] = keyColumns(orderKey);\n const sign = direction === 'desc' ? -1 : 1;\n return (a, b) => {\n const c1 = compareValues((a as Record<string, unknown>)[k1], (b as Record<string, unknown>)[k1]);\n if (c1 !== 0) return c1 * sign;\n return compareValues((a as Record<string, unknown>)[k2], (b as Record<string, unknown>)[k2]) * sign;\n };\n}\n\n/** The cursor a row yields, in the definition's cursor shape. */\nexport function cursorOfRow<T, K extends OrderKey<T>>(row: T, orderKey: K): CursorOf<T, K> {\n const r = row as Record<string, unknown>;\n if (typeof orderKey === 'string') return r[orderKey] as CursorOf<T, K>;\n return [r[orderKey[0]], r[orderKey[1]]] as CursorOf<T, K>;\n}\n\n/** Whether `row` sorts strictly after `cursor` in the list's order (i.e. is \"beyond\" it). */\nexport function isAfterCursor<T, K extends OrderKey<T>>(\n row: T,\n cursor: CursorOf<T, K>,\n orderKey: K,\n direction: SortDirection = 'asc',\n): boolean {\n const [k1, k2] = keyColumns(orderKey);\n const r = row as Record<string, unknown>;\n const sign = direction === 'desc' ? -1 : 1;\n const [c1, c2] = typeof orderKey === 'string' ? [cursor, undefined] : (cursor as [unknown, unknown]);\n const d1 = compareValues(r[k1], c1) * sign;\n if (d1 !== 0) return d1 > 0;\n if (c2 === undefined) return false;\n return compareValues(r[k2], c2) * sign > 0;\n}\n\nfunction toBase64(s: string): string {\n if (typeof btoa === 'function') return btoa(unescape(encodeURIComponent(s)));\n return Buffer.from(s, 'utf8').toString('base64');\n}\n\nfunction fromBase64(s: string): string {\n if (typeof atob === 'function') return decodeURIComponent(escape(atob(s)));\n return Buffer.from(s, 'base64').toString('utf8');\n}\n\nexport function encodeCursor(cursor: unknown): OpaqueCursor {\n return toBase64(JSON.stringify(cursor)) as OpaqueCursor;\n}\n\nexport function decodeCursor<C>(opaque: OpaqueCursor): C {\n return JSON.parse(fromBase64(opaque)) as C;\n}\n"]}