powersync-bridge 0.0.1-alpha.0 → 0.0.1-alpha.1

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,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-KAR4QIIU.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.1",
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"]}