{"id":45240733,"url":"https://github.com/radekdymacz/lakesync","last_synced_at":"2026-02-20T21:17:12.155Z","repository":{"id":336863666,"uuid":"1151430659","full_name":"radekdymacz/lakesync","owner":"radekdymacz","description":"Offline-first sync engine for browser apps — tracks column-level changes locally and lands them as Parquet in an Iceberg lakehouse","archived":false,"fork":false,"pushed_at":"2026-02-20T07:48:52.000Z","size":1974,"stargazers_count":0,"open_issues_count":10,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-20T08:42:21.449Z","etag":null,"topics":["bun","crdt","iceberg","lakehouse","offline-first","sync","typescript"],"latest_commit_sha":null,"homepage":"https://radekdymacz.github.io/lakesync/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/radekdymacz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-06T13:08:23.000Z","updated_at":"2026-02-20T07:48:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/radekdymacz/lakesync","commit_stats":null,"previous_names":["radekdymacz/lakesync"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/radekdymacz/lakesync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radekdymacz%2Flakesync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radekdymacz%2Flakesync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radekdymacz%2Flakesync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radekdymacz%2Flakesync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/radekdymacz","download_url":"https://codeload.github.com/radekdymacz/lakesync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radekdymacz%2Flakesync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29664893,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T19:49:36.704Z","status":"ssl_error","status_checked_at":"2026-02-20T19:44:05.372Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","crdt","iceberg","lakehouse","offline-first","sync","typescript"],"created_at":"2026-02-20T21:17:07.613Z","updated_at":"2026-02-20T21:17:12.145Z","avatar_url":"https://github.com/radekdymacz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LakeSync\n\n[![CI](https://github.com/radekdymacz/lakesync/actions/workflows/ci.yml/badge.svg)](https://github.com/radekdymacz/lakesync/actions/workflows/ci.yml)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\n**Local-first sync. Any backend.**\n\nLakeSync is an open-source sync engine for local-first TypeScript apps. Your data lives in SQLite on the device, syncs through a lightweight gateway, and flushes to the backend of your choice — **Postgres for small data, BigQuery for analytics, S3/R2 via Apache Iceberg for large data**. Source connectors pull data from external systems like Jira and Salesforce into the same pipeline. Same client code either way.\n\n**[Documentation](https://radekdymacz.github.io/lakesync)** · **[Getting Started](https://radekdymacz.github.io/lakesync/docs/getting-started)** · **[Architecture](https://radekdymacz.github.io/lakesync/docs/architecture)**\n\n## Why LakeSync?\n\nMost sync engines lock you into a single backend. LakeSync's `LakeAdapter` interface decouples sync from storage — swap backends without changing client code.\n\n| | Traditional Sync | Data Lake | LakeSync |\n|---|---|---|---|\n| Offline-first | Yes | No | **Yes** |\n| Column-level conflict resolution | Rarely | N/A | **Yes** |\n| Pluggable backend | No | No | **Yes** |\n| Small data (Postgres/MySQL) | Yes | No | **Yes** |\n| Analytics (BigQuery) | Sometimes | Sometimes | **Yes** |\n| Large data (Iceberg/S3/R2) | No | Yes | **Yes** |\n| Time-travel queries | No | Yes | **Yes** |\n| Real-time WebSocket sync | Sometimes | No | **Yes** |\n| Source connectors (Jira, Salesforce, ...) | No | No | **Yes** |\n| Self-hosted or edge (CF Workers) | Sometimes | No | **Yes** |\n\n## How It Works\n\n```mermaid\nsequenceDiagram\n    participant App as Your App\n    participant DB as Local SQLite\n    participant GW as Gateway (CF DO / Self-Hosted)\n    participant Backend as Any Backend\n\n    App-\u003e\u003eDB: INSERT / UPDATE / DELETE\n    Note over App,DB: Zero latency — local write\n    DB--\u003e\u003eGW: Push column-level deltas (HTTP or WebSocket)\n    GW--\u003e\u003eDB: ACK + pull remote changes\n    Note over GW: Deltas merge via HLC + LWW\n    GW-\u003e\u003eBackend: Batch flush\n    Note over Backend: Postgres? BigQuery? R2?\u003cbr/\u003eYou choose the adapter.\n```\n\n1. **Mutations write to local SQLite** with zero latency\n2. **Column-level deltas push** to a lightweight gateway (HTTP or WebSocket)\n3. **Gateway merges** via Hybrid Logical Clocks — concurrent edits to different columns are both preserved\n4. **Batch flush** to whatever backend you choose via the adapter interface\n5. **Source connectors poll** external systems (Jira, Salesforce, ...) and ingest data through the same gateway\n\n## Right-Size Your Backend\n\n### Small data — use what you know\n\n```\nClient SQLite → Gateway → Postgres / MySQL / RDS\n```\n\nFamiliar tooling, standard SQL queries, simple operational model. The `PostgresAdapter` or `MySQLAdapter` flushes deltas directly to your database.\n\n### Large data — scale to the lake\n\n```\nClient SQLite → Gateway → Apache Iceberg (S3/R2)\n```\n\nInfinite scale on object storage. Operational data and analytics data are the same thing. Query with Spark, DuckDB, Athena, Trino — zero ETL.\n\n### Mix both — route by table\n\n```typescript\nconst adapter = new CompositeAdapter({\n  routes: [\n    { tables: [\"users\", \"settings\"], adapter: postgresAdapter },\n    { tables: [\"events\", \"telemetry\"], adapter: icebergAdapter },\n  ],\n  defaultAdapter: postgresAdapter,\n});\n```\n\nThe `CompositeAdapter` routes deltas to different backends by table name. When your data outgrows one backend, `migrateAdapter()` moves it to another — idempotent and safe to re-run.\n\n### Replicate — fan out writes\n\n```typescript\nconst adapter = new FanOutAdapter({\n  primary: postgresAdapter,         // sync — fast operational reads/writes\n  secondaries: [bigqueryAdapter],   // async — best-effort analytics replica\n});\n```\n\nThe `FanOutAdapter` writes to a primary adapter synchronously and replicates to secondaries in the background. Secondary failures never block the write path.\n\n### Materialise — queryable destination tables\n\nAdapters that implement the `Materialisable` interface automatically create queryable destination tables after each flush. Every synced column is derived from the `TableSchema`, plus a `props JSONB` column for consumer-extensible metadata and a `synced_at` timestamp. Tombstoned rows are deleted. The `PostgresAdapter` supports this out of the box.\n\n### Tier — age-based lifecycle\n\n```typescript\nconst adapter = new LifecycleAdapter({\n  hot: { adapter: postgresAdapter, maxAgeMs: 30 * 24 * 60 * 60 * 1000 }, // 30 days\n  cold: { adapter: bigqueryAdapter },\n});\n```\n\nRecent data stays in the hot tier for fast queries. Older data is served from the cold tier. Call `migrateToTier()` on a schedule to move aged-out deltas.\n\n## Column-Level Conflict Resolution\n\nTraditional sync engines resolve conflicts at the row level — if two users edit the same row, one wins. LakeSync resolves at the **column level** using Last-Write-Wins with Hybrid Logical Clocks:\n\n```mermaid\nsequenceDiagram\n    participant A as Alice\n    participant GW as Gateway\n    participant B as Bob\n\n    Note over A,B: Both editing the same todo\n    A-\u003e\u003eA: title = \"Buy oat milk\"\n    B-\u003e\u003eB: status = \"done\"\n    A-\u003e\u003eGW: push delta (title, HLC=100)\n    B-\u003e\u003eGW: push delta (status, HLC=101)\n    Note over GW: Column-level merge\u003cbr/\u003etitle ← Alice (HLC 100)\u003cbr/\u003estatus ← Bob (HLC 101)\n    GW-\u003e\u003eA: pull → status = \"done\"\n    GW-\u003e\u003eB: pull → title = \"Buy oat milk\"\n    Note over A,B: Both changes preserved ✓\n```\n\nBoth changes are preserved because they touch different columns. The HLC timestamp determines the winner only when two clients modify the _same_ column.\n\n## Offline-First\n\nThe full dataset lives in local SQLite. Edits queue in a persistent IndexedDB outbox that survives page refreshes and browser crashes. When connectivity returns, the outbox drains automatically.\n\n```mermaid\nsequenceDiagram\n    participant App as App (offline)\n    participant DB as Local SQLite\n    participant Q as Outbox (IndexedDB)\n    participant GW as Gateway\n\n    App-\u003e\u003eDB: Edit 1\n    DB--\u003e\u003eQ: Delta queued\n    App-\u003e\u003eDB: Edit 2\n    DB--\u003e\u003eQ: Delta queued\n    App-\u003e\u003eDB: Edit 3\n    DB--\u003e\u003eQ: Delta queued\n    Note over App,Q: Fully functional offline\n    Note over Q,GW: ← Connection restored →\n    Q-\u003e\u003eGW: Push all 3 deltas\n    GW--\u003e\u003eQ: ACK\n    GW-\u003e\u003eApp: Pull remote changes\n    Note over App,GW: Caught up ✓\n```\n\n## Sync Rules\n\nDeclarative bucket-based filtering with JWT claim references. The gateway evaluates rules at pull time — clients never download data they shouldn't see. Supports operators: `eq`, `neq`, `in`, `gt`, `lt`, `gte`, `lte`.\n\n```json\n{\n  \"buckets\": [{\n    \"name\": \"user-data\",\n    \"filters\": [\n      { \"column\": \"user_id\", \"op\": \"eq\", \"value\": \"jwt:sub\" },\n      { \"column\": \"priority\", \"op\": \"gte\", \"value\": \"3\" }\n    ],\n    \"tables\": [\"todos\", \"preferences\"]\n  }]\n}\n```\n\n## Actions\n\nActions are imperative operations dispatched through the gateway to external systems. Connectors register `ActionHandler`s that declare supported action types, enabling frontend discovery. The gateway handles idempotency (via `actionId` and `idempotencyKey`), validation, and routing.\n\n```typescript\n// Discover available actions\nconst discovery = gateway.describeActions();\n// → { connectors: { \"slack\": [{ actionType: \"send_message\", ... }] } }\n\n// Execute an action\nconst result = await gateway.handleAction({\n  clientId: \"client-1\",\n  actions: [{\n    actionId: \"abc-123\",\n    clientId: \"client-1\",\n    hlc: hlc.now(),\n    connector: \"slack\",\n    actionType: \"send_message\",\n    params: { channel: \"#general\", text: \"Hello\" },\n  }],\n});\n```\n\n## React Hooks\n\n`lakesync/react` provides reactive hooks that wire directly into the sync coordinator.\n\n```tsx\nimport { LakeSyncProvider, useQuery, useMutation, useAction, useSyncStatus } from \"lakesync/react\";\n\nfunction App() {\n  return (\n    \u003cLakeSyncProvider coordinator={coordinator}\u003e\n      \u003cTodoList /\u003e\n    \u003c/LakeSyncProvider\u003e\n  );\n}\n\nfunction TodoList() {\n  const { rows } = useQuery(\"SELECT * FROM todos ORDER BY created_at DESC\");\n  const { insert, update, remove } = useMutation();\n  const { execute } = useAction();\n  const { isOnline, isSyncing } = useSyncStatus();\n\n  return (\n    \u003cul\u003e\n      {rows.map(todo =\u003e (\n        \u003cli key={todo.id} onClick={() =\u003e update(\"todos\", todo.id, { completed: 1 })}\u003e\n          {todo.title}\n        \u003c/li\u003e\n      ))}\n    \u003c/ul\u003e\n  );\n}\n```\n\nQueries re-run automatically when affected tables change — `useQuery` extracts table names from SQL and only re-renders when relevant deltas arrive, not on every sync. Actions dispatch imperative operations to connectors. `useActionDiscovery()` enables dynamic UI based on registered action handlers.\n\n## Quick Start\n\n### Install\n\n```bash\nnpm install lakesync\n```\n\n### Sync in 10 Lines\n\n```typescript\nimport { LocalDB, SyncCoordinator, HttpTransport } from \"lakesync/client\";\n\nconst db = await LocalDB.open({ name: \"my-app\", backend: \"idb\" });\n\nconst transport = new HttpTransport({\n  baseUrl: \"https://your-gateway.workers.dev\",\n  gatewayId: \"my-gateway\",\n  token: \"your-jwt-token\",\n});\n\nconst coordinator = new SyncCoordinator(db, transport);\ncoordinator.startAutoSync();\n\n// Track mutations — deltas are extracted and queued automatically\nawait coordinator.tracker.insert(\"todos\", \"row-1\", {\n  title: \"Buy milk\",\n  completed: 0,\n});\n```\n\n### Run Locally\n\n```bash\ngit clone https://github.com/radekdymacz/lakesync.git\ncd lakesync\nbun install\nbun run build\nbun run test\n```\n\n### Deploy the Gateway\n\n**Cloudflare Workers (edge):**\n\n```bash\ncd apps/gateway-worker\nwrangler r2 bucket create lakesync-data  # once\nwrangler deploy\n```\n\n**Self-hosted (Node.js / Bun):**\n\n```typescript\nimport { GatewayServer } from \"@lakesync/gateway-server\";\nimport { PostgresAdapter } from \"@lakesync/adapter\";\n\nconst adapter = new PostgresAdapter({ connectionString: \"postgres://...\" });\nconst server = new GatewayServer({\n  port: 3000,\n  gatewayId: \"my-gateway\",\n  adapter,\n  jwtSecret: \"your-secret\",\n  persistence: \"sqlite\", // survive restarts\n});\nserver.start();\n```\n\nOr use Docker:\n\n```bash\ncd packages/gateway-server\ndocker compose up\n```\n\nSee the [Todo App](apps/examples/todo-app/) for a complete working example.\n\n## Architecture\n\n```mermaid\ngraph TB\n    subgraph \"Browser\"\n        UI[Application UI]\n        SC[SyncCoordinator]\n        ST[SyncTracker]\n        DB[(LocalDB\u003cbr/\u003esql.js + IDB)]\n        Q[(IDB Queue)]\n    end\n\n    subgraph \"Gateway (pick one)\"\n        subgraph \"Edge\"\n            CF[CF Workers + DO]\n        end\n        subgraph \"Self-Hosted\"\n            SH[GatewayServer\u003cbr/\u003eNode.js / Bun]\n        end\n    end\n\n    subgraph \"Source Connectors\"\n        JIRA[Jira]\n        SF[Salesforce]\n    end\n\n    subgraph \"Backends\"\n        PG[(Postgres / MySQL)]\n        BQ[(BigQuery)]\n        R2[(R2 / S3)]\n        PQ[Parquet / Iceberg]\n    end\n\n    subgraph \"Adapters\"\n        COMP[CompositeAdapter\u003cbr/\u003eroute by table]\n        FAN[FanOutAdapter\u003cbr/\u003ereplicate writes]\n        LIFE[LifecycleAdapter\u003cbr/\u003ehot / cold tiers]\n    end\n\n    subgraph \"Analytics\"\n        DDB[DuckDB-WASM]\n    end\n\n    UI --\u003e SC\n    SC --\u003e ST\n    ST --\u003e DB\n    ST --\u003e Q\n    SC --\u003e|HTTP / WS| CF\n    SC --\u003e|HTTP / WS| SH\n    JIRA --\u003e|poll| SH\n    SF --\u003e|poll| SH\n    CF --\u003e COMP\n    SH --\u003e COMP\n    COMP --\u003e PG\n    COMP --\u003e R2\n    FAN --\u003e PG\n    FAN --\u003e BQ\n    LIFE --\u003e PG\n    LIFE --\u003e BQ\n    R2 --\u003e PQ\n    DDB --\u003e|query| PQ\n```\n\n### Key Design Decisions\n\n- **Pluggable adapters** — `LakeAdapter` (object storage) and `DatabaseAdapter` (SQL) interfaces. Swap backends at the gateway level.\n- **HLC timestamps** (branded bigints) — 48-bit wall clock + 16-bit counter, monotonic ordering across distributed clients without coordination\n- **Deterministic delta IDs** — SHA-256 hash of `(clientId, hlc, table, rowId, columns)` enables idempotent push\n- **DeltaBuffer** — atomic `BufferSnapshot` pattern (append log + row index) swapped atomically on each mutation, giving O(1) conflict checks and O(n) flush with no intermediate inconsistent state\n- **Result\\\u003cT, E\\\u003e** everywhere — no exceptions cross API boundaries; all errors are typed and composable\n- **Adapter composition** — `CompositeAdapter` (route by table), `FanOutAdapter` (replicate writes), `LifecycleAdapter` (hot/cold tiers). All implement `DatabaseAdapter` so they nest freely.\n- **Table sharding** — split a tenant's traffic across multiple Durable Objects by table name. The shard router fans out pushes and merges pull results automatically.\n- **Adapter-sourced pull** — clients can pull directly from named source adapters (e.g. a BigQuery dataset) via the gateway, with sync rules filtering applied.\n- **Real-time WebSocket sync** — `WebSocketTransport` maintains a persistent connection to the gateway server, with server-initiated broadcast of new deltas to connected clients. Binary protobuf framing with auto-reconnect and exponential backoff.\n- **Source connectors** — `BaseSourcePoller` provides a memory-managed ingestion pipeline. Connectors (Jira, Salesforce) poll external APIs and push deltas into the gateway with backpressure-aware streaming. Dynamic registration via `createPoller()` factory — import a connector package and it auto-registers.\n- **Source polling** — two strategies for change detection: cursor-based (e.g. Jira's `updated` field) and diff-based (snapshot comparison for APIs without cursor support). Both use memory-bounded accumulation with configurable chunk size and memory budget.\n- **Actions** — imperative operations dispatched through the gateway to connector-registered `ActionHandler`s. Supports idempotency, discovery, and validation. Decoupled from sync — actions go to external systems, deltas come back.\n- **Materialise** — opt-in `Materialisable` interface for adapters that can project deltas into queryable destination tables. Auto-invoked after flush. Hybrid column model: synced columns + `props JSONB` + `synced_at`.\n- **SyncEngine extraction** — pure sync operations (`push`, `pull`, `syncOnce`) are extracted into a `SyncEngine` class. `syncOnce()` is an explicit pull-then-push transaction — ordering is structural, not a convention. `SyncCoordinator` composes the engine with scheduling and lifecycle.\n- **Gateway decomposition** — `SyncGateway` is a thin facade composing `DeltaBuffer` (atomic snapshot pattern), `ActionDispatcher` (action routing + idempotency), `SchemaManager` (validation), and `FlushCoordinator` (fire-and-forget queue publish). `GatewayServer` uses a middleware pipeline with data-driven route dispatch.\n\n## Packages\n\n| Package | Description |\n|---------|-------------|\n| [`@lakesync/core`](packages/core) | HLC timestamps, delta types, LWW conflict resolution, sync rules, Result type |\n| [`@lakesync/client`](packages/client) | Client SDK: SyncEngine, SyncCoordinator, SyncTracker, LocalDB, transports, queues, initial sync |\n| [`@lakesync/gateway`](packages/gateway) | Sync gateway: SyncGateway facade, DeltaBuffer, ActionDispatcher, SchemaManager, adapter-sourced pull |\n| [`@lakesync/gateway-server`](packages/gateway-server) | Self-hosted gateway server (Node.js / Bun) with middleware pipeline, SQLite persistence, JWT auth, and WebSocket support |\n| [`@lakesync/adapter`](packages/adapter) | Storage adapters: MinIO/S3, Postgres, MySQL, BigQuery, Composite, FanOut, Lifecycle, migration tooling |\n| [`@lakesync/proto`](packages/proto) | Protobuf codec for the wire protocol |\n| [`@lakesync/parquet`](packages/parquet) | Parquet read/write via parquet-wasm |\n| [`@lakesync/catalogue`](packages/catalogue) | Iceberg REST catalogue client (Nessie-compatible) |\n| [`@lakesync/compactor`](packages/compactor) | Parquet compaction, equality deletes, checkpoint generation |\n| [`@lakesync/analyst`](packages/analyst) | Time-travel queries + analytics via DuckDB-WASM |\n| [`@lakesync/react`](packages/react) | React bindings: reactive hooks for sync, queries, and mutations |\n| [`@lakesync/connector-jira`](packages/connector-jira) | Jira Cloud source connector — polls issues, comments, and projects into the gateway |\n| [`@lakesync/connector-salesforce`](packages/connector-salesforce) | Salesforce CRM source connector — polls accounts, contacts, opportunities, and leads |\n| [`lakesync`](packages/lakesync) | Unified package with subpath exports for all of the above |\n\n| App | Description |\n|-----|-------------|\n| [`gateway-worker`](apps/gateway-worker) | Cloudflare Workers: Durable Object gateway, R2 storage, JWT auth, sync rules, table sharding |\n| [`todo-app`](apps/examples/todo-app) | Reference implementation: offline-first todo list with column-level sync |\n| [`docs`](apps/docs) | Documentation site (Fumadocs + Next.js) |\n\n## Backend Support\n\n| Backend | Adapter | Status |\n|---------|---------|--------|\n| Cloudflare R2 | `LakeAdapter` (MinIO-compatible) | Production-ready |\n| AWS S3 | `LakeAdapter` (MinIO-compatible) | Production-ready |\n| MinIO | `LakeAdapter` | Production-ready |\n| PostgreSQL | `DatabaseAdapter` + `Materialisable` | Implemented |\n| MySQL | `DatabaseAdapter` (MySQLAdapter) | Implemented |\n| BigQuery | `DatabaseAdapter` (BigQueryAdapter) | Implemented |\n| Composite (route by table) | `CompositeAdapter` | Implemented |\n| Fan-out (replicate writes) | `FanOutAdapter` | Implemented |\n| Lifecycle (hot/cold tiers) | `LifecycleAdapter` | Implemented |\n| Jira Cloud | `connector-jira` (source connector) | Implemented |\n| Salesforce CRM | `connector-salesforce` (source connector) | Implemented |\n\n## Status\n\nExperimental, but real. All planned phases are implemented and tested: core sync engine, conflict resolution, client SDK, Cloudflare Workers gateway, self-hosted gateway server with WebSocket support, React bindings, compaction, checkpoint generation, sync rules (with extended comparison operators), initial sync, database adapters (Postgres, MySQL, BigQuery), composite routing, fan-out replication, lifecycle tiering, table sharding, adapter-sourced pull, source connectors (Jira, Salesforce) with memory-managed ingestion, imperative actions with discovery and idempotency, and the materialise protocol for queryable destination tables. API is not yet stable — expect breaking changes.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and contribution guidelines.\n\n## Licence\n\nLicensed under the [Apache Licence 2.0](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradekdymacz%2Flakesync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fradekdymacz%2Flakesync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradekdymacz%2Flakesync/lists"}