@absolutejs/sync 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +281 -24
  2. package/dist/adapters/drizzle/collection.d.ts +27 -0
  3. package/dist/adapters/drizzle/index.d.ts +20 -0
  4. package/dist/adapters/drizzle/index.js +265 -0
  5. package/dist/adapters/drizzle/index.js.map +14 -0
  6. package/dist/adapters/drizzle/predicate.d.ts +20 -0
  7. package/dist/adapters/drizzle/read.d.ts +31 -0
  8. package/dist/adapters/drizzle/topics.d.ts +41 -0
  9. package/dist/adapters/drizzle/write.d.ts +69 -0
  10. package/dist/adapters/mysql/index.d.ts +75 -0
  11. package/dist/adapters/mysql/index.js +171 -0
  12. package/dist/adapters/mysql/index.js.map +11 -0
  13. package/dist/adapters/postgres/index.d.ts +53 -0
  14. package/dist/adapters/postgres/index.js +86 -0
  15. package/dist/adapters/postgres/index.js.map +10 -0
  16. package/dist/adapters/prisma/collection.d.ts +39 -0
  17. package/dist/adapters/prisma/index.d.ts +23 -0
  18. package/dist/adapters/prisma/index.js +231 -0
  19. package/dist/adapters/prisma/index.js.map +14 -0
  20. package/dist/adapters/prisma/predicate.d.ts +20 -0
  21. package/dist/adapters/prisma/read.d.ts +28 -0
  22. package/dist/adapters/prisma/topics.d.ts +29 -0
  23. package/dist/adapters/prisma/write.d.ts +65 -0
  24. package/dist/adapters/sqlite/index.d.ts +32 -0
  25. package/dist/adapters/sqlite/index.js +128 -0
  26. package/dist/adapters/sqlite/index.js.map +11 -0
  27. package/dist/angular/index.d.ts +1 -0
  28. package/dist/angular/index.js +347 -0
  29. package/dist/angular/index.js.map +11 -0
  30. package/dist/angular/sync-collection.service.d.ts +20 -0
  31. package/dist/client/index.d.ts +12 -30
  32. package/dist/client/index.js +1099 -3
  33. package/dist/client/index.js.map +10 -4
  34. package/dist/client/liveQuery.d.ts +75 -0
  35. package/dist/client/presence.d.ts +37 -0
  36. package/dist/client/subscriber.d.ts +30 -0
  37. package/dist/client/syncClient.d.ts +53 -0
  38. package/dist/client/syncCollection.d.ts +102 -0
  39. package/dist/client/syncStore.d.ts +81 -0
  40. package/dist/engine/aggregate.d.ts +45 -0
  41. package/dist/engine/cluster.d.ts +41 -0
  42. package/dist/engine/collection.d.ts +87 -0
  43. package/dist/engine/connection.d.ts +103 -0
  44. package/dist/engine/dataflow.d.ts +109 -0
  45. package/dist/engine/equiJoin.d.ts +51 -0
  46. package/dist/engine/graph.d.ts +85 -0
  47. package/dist/engine/index.d.ts +40 -0
  48. package/dist/engine/index.js +1774 -0
  49. package/dist/engine/index.js.map +23 -0
  50. package/dist/engine/materializedView.d.ts +53 -0
  51. package/dist/engine/mutation.d.ts +66 -0
  52. package/dist/engine/pollingSource.d.ts +42 -0
  53. package/dist/engine/presence.d.ts +46 -0
  54. package/dist/engine/reactive.d.ts +67 -0
  55. package/dist/engine/routes.d.ts +40 -0
  56. package/dist/engine/socket.d.ts +67 -0
  57. package/dist/engine/syncEngine.d.ts +132 -0
  58. package/dist/engine/types.d.ts +45 -0
  59. package/dist/index.d.ts +4 -0
  60. package/dist/index.js +327 -3
  61. package/dist/index.js.map +8 -5
  62. package/dist/react/index.d.ts +1 -0
  63. package/dist/react/index.js +332 -0
  64. package/dist/react/index.js.map +11 -0
  65. package/dist/react/useSyncCollection.d.ts +16 -0
  66. package/dist/reactiveHub.d.ts +6 -0
  67. package/dist/svelte/createSyncCollectionStore.d.ts +15 -0
  68. package/dist/svelte/index.d.ts +1 -0
  69. package/dist/svelte/index.js +338 -0
  70. package/dist/svelte/index.js.map +11 -0
  71. package/dist/vue/index.d.ts +1 -0
  72. package/dist/vue/index.js +331 -0
  73. package/dist/vue/index.js.map +11 -0
  74. package/dist/vue/useSyncCollection.d.ts +17 -0
  75. package/package.json +104 -6
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Presence — ephemeral, room-scoped state shared over the live socket (who's
3
+ * online, who's typing, cursor positions). Unlike collections it is **not**
4
+ * persisted: it lives only while a member is joined, and a member's state is
5
+ * removed (and peers notified) the moment it leaves or its connection drops.
6
+ *
7
+ * A `room` is any string (a document id, a channel). Each `member` is one
8
+ * participant (typically one connection) with a `state` it owns and updates;
9
+ * everyone in the room sees the member set and its changes.
10
+ */
11
+ export type PresenceMember<S = unknown> = {
12
+ id: string;
13
+ state: S;
14
+ };
15
+ /** What changed in a room: members that joined, updated state, or left. */
16
+ export type PresenceDiff<S = unknown> = {
17
+ joined: PresenceMember<S>[];
18
+ updated: PresenceMember<S>[];
19
+ left: string[];
20
+ };
21
+ export type PresenceHandle<S> = {
22
+ /** The room's members at join time (including this one). */
23
+ members: PresenceMember<S>[];
24
+ /** Replace this member's state and notify the rest of the room. */
25
+ set: (state: S) => void;
26
+ /** Leave the room (remove this member; notify peers). */
27
+ leave: () => void;
28
+ };
29
+ export type PresenceHub = {
30
+ /**
31
+ * Join `room` as `memberId` with `state`; `onDiff` receives every later change
32
+ * to the room (not this member's own join). Returns the current members and
33
+ * handles to update/leave.
34
+ */
35
+ join: <S>(room: string, memberId: string, state: S, onDiff: (diff: PresenceDiff<S>) => void) => PresenceHandle<S>;
36
+ /** Snapshot a room's members without joining. */
37
+ members: <S = unknown>(room: string) => PresenceMember<S>[];
38
+ /** Number of members in a room (0 if none). */
39
+ count: (room: string) => number;
40
+ };
41
+ /**
42
+ * Create an in-process presence hub. Transport-agnostic (no socket import): the
43
+ * sync connection wires client `presence-*` frames to it and tears down a
44
+ * connection's memberships on close.
45
+ */
46
+ export declare const createPresenceHub: () => PresenceHub;
@@ -0,0 +1,67 @@
1
+ import type { CollectionContext } from './collection';
2
+ import type { RowKey } from './types';
3
+ /**
4
+ * Read-set tracking — the reactor. You write a plain query function that reads
5
+ * through an instrumented `ctx.db`; the engine records which tables it touched
6
+ * and re-runs it (diffing old vs new) whenever any of those tables change. No
7
+ * hand-written `match`, no operator graph, no manual change emission for reads:
8
+ * write a query, it stays live. This is the BYO-database analogue of Convex's
9
+ * automatic read-set tracking — it works on your own DB because your reads go
10
+ * through the registered {@link TableReader}s.
11
+ *
12
+ * Granularity is table-level for now (a query that read a table re-runs on any
13
+ * change to it) — the full developer experience, and always correct; key/range
14
+ * precision is a later optimization.
15
+ */
16
+ /** How to read a table for reactive queries — register with `registerReader`. */
17
+ export type TableReader<Ctx = unknown> = {
18
+ /** All rows of the table (the common case; filter in JS in your query). */
19
+ all: (ctx: Ctx) => Promise<Iterable<unknown>> | Iterable<unknown>;
20
+ /** Optional point lookup by key. */
21
+ get?: (key: RowKey, ctx: Ctx) => Promise<unknown> | unknown;
22
+ /**
23
+ * Row identity. Provide it to unlock **key-level** read tracking: a query that
24
+ * only `db.get`s specific rows re-runs solely when one of *those* rows changes
25
+ * (matched via this `key`), not on every change to the table. Omit and `get`
26
+ * falls back to a table-level dependency (coarser, still correct).
27
+ */
28
+ key?: (row: unknown) => RowKey;
29
+ };
30
+ /** The instrumented data handle passed to a reactive query — reads are tracked. */
31
+ export type ReadHandle = {
32
+ /** Read all rows of `table` (records a full-table dependency). */
33
+ all: <T = unknown>(table: string) => Promise<T[]>;
34
+ /** Read one row of `table` by key (records a row-key dependency). */
35
+ get: <T = unknown>(table: string, key: RowKey) => Promise<T | undefined>;
36
+ /**
37
+ * Read the rows of `table` matching `predicate` (records a **range**
38
+ * dependency): the query re-runs only when a change matches the predicate now
39
+ * or was in the matched set before — not on every change to the table. Needs
40
+ * the table's reader to declare a `key`; without one it falls back to a
41
+ * full-table dependency. Prefer this over `all().filter(...)` for precision.
42
+ */
43
+ where: <T = unknown>(table: string, predicate: (row: T) => boolean) => Promise<T[]>;
44
+ };
45
+ export type ReactiveQueryContext<P, Ctx> = {
46
+ /** Tracked reads — anything you read here becomes a live dependency. */
47
+ db: ReadHandle;
48
+ params: P;
49
+ ctx: Ctx;
50
+ };
51
+ export type ReactiveQueryDefinition<T, P = void, Ctx = CollectionContext> = {
52
+ name: string;
53
+ kind: 'reactive';
54
+ /** Compute the result set by reading through `ctx.db`; re-run on change. */
55
+ run: (context: ReactiveQueryContext<P, Ctx>) => Promise<T[]> | T[];
56
+ /** Result-row identity (used to diff re-runs). */
57
+ key: (row: T) => RowKey;
58
+ /** Access control; return false (or throw) to deny the subscription. */
59
+ authorize?: (params: P, ctx: Ctx) => boolean | Promise<boolean>;
60
+ };
61
+ /**
62
+ * Define a reactive query: a function that reads through `ctx.db` and is kept
63
+ * live automatically by read-set tracking. Register it with
64
+ * {@link SyncEngine.registerReactive} (and the tables it reads with
65
+ * `registerReader`).
66
+ */
67
+ export declare const defineReactiveQuery: <T, P = void, Ctx = CollectionContext>(definition: Omit<ReactiveQueryDefinition<T, P, Ctx>, "kind">) => ReactiveQueryDefinition<T, P, Ctx>;
@@ -0,0 +1,40 @@
1
+ import type { CollectionDefinition } from './collection';
2
+ import type { MutationDefinition } from './mutation';
3
+ import type { SyncEngine } from './syncEngine';
4
+ /**
5
+ * Eden-native HTTP route helpers (Tier 3). These turn a typed collection /
6
+ * mutation definition into a plain Elysia route handler — so hydrate and mutate
7
+ * are ordinary Elysia routes that Eden types end to end, with TypeBox validating
8
+ * the params/body. The live diff stream stays on the WebSocket (`syncSocket`).
9
+ *
10
+ * They import no Elysia: each returns `(context) => Promise<...>`, which you pass
11
+ * to `.get` / `.post` with a TypeBox `query` / `body` schema. The handler's
12
+ * return type carries the row / result type, so `treaty<typeof app>()` infers it.
13
+ */
14
+ /** The slice of an Elysia route context these helpers read. */
15
+ export type SyncRouteContext = {
16
+ query: unknown;
17
+ body: unknown;
18
+ [key: string]: unknown;
19
+ };
20
+ /**
21
+ * Build a GET handler that hydrates `collection` — authorize, then return its
22
+ * current rows. The handler returns `Promise<Row[]>`, so Eden infers the row
23
+ * type from the collection definition; the route's TypeBox `query` schema
24
+ * validates and types the params.
25
+ *
26
+ * @example
27
+ * .get('/sync/orders', hydrateRoute(engine, ordersCollection, (c) => ({ userId: c.userId })),
28
+ * { query: t.Object({ userId: t.Numeric() }) })
29
+ */
30
+ export declare const hydrateRoute: <Row, Params, Ctx>(engine: SyncEngine, collection: CollectionDefinition<Row, Params, Ctx>, resolveContext?: (context: SyncRouteContext) => Ctx) => (context: SyncRouteContext) => Promise<Row[]>;
31
+ /**
32
+ * Build a POST handler that runs `mutation`. The handler returns
33
+ * `Promise<Result>`, so Eden infers the result type; the route's TypeBox `body`
34
+ * schema validates and types the args.
35
+ *
36
+ * @example
37
+ * .post('/sync/createOrder', mutateRoute(engine, createOrder, (c) => ({ userId: c.userId })),
38
+ * { body: t.Object({ total: t.Number() }) })
39
+ */
40
+ export declare const mutateRoute: <Args, Ctx, Result>(engine: SyncEngine, mutation: MutationDefinition<Args, Ctx, Result>, resolveContext?: (context: SyncRouteContext) => Ctx) => (context: SyncRouteContext) => Promise<Result>;
@@ -0,0 +1,67 @@
1
+ import { Elysia } from 'elysia';
2
+ import type { PresenceHub } from './presence';
3
+ import type { SyncEngine } from './syncEngine';
4
+ export type SyncSocketOptions = {
5
+ /** The sync engine whose collections this socket serves. */
6
+ engine: SyncEngine;
7
+ /** WebSocket route. Defaults to `/sync/ws`. */
8
+ path?: string;
9
+ /** Optional presence hub; enables `presence-*` frames on this socket. */
10
+ presence?: PresenceHub;
11
+ /**
12
+ * Build the per-connection auth context from the upgrade request data
13
+ * (`ws.data`: query, headers, cookies, and anything you `derive`d/`resolve`d
14
+ * earlier in the chain). Whatever you return is the `ctx` passed to every
15
+ * collection's `authorize`/`hydrate`/`match`. Defaults to an empty object.
16
+ */
17
+ resolveContext?: (data: Record<string, unknown>) => unknown | Promise<unknown>;
18
+ };
19
+ /**
20
+ * Elysia WebSocket plugin for the Tier 3 sync engine. One socket multiplexes any
21
+ * number of collection subscriptions: the client sends `subscribe`/`unsubscribe`
22
+ * frames and receives `snapshot`/`diff`/`error` frames (see
23
+ * {@link createSyncConnection}). Mount it once and drive `engine.applyChange`
24
+ * from your mutations.
25
+ *
26
+ * Uses Elysia's first-class `.ws()` rather than a hand-rolled stream — the
27
+ * bidirectional channel carries both subscriptions and (later) mutations, and
28
+ * `ws.send` serializes frames for us.
29
+ */
30
+ export declare const syncSocket: ({ engine, path, resolveContext, presence }: SyncSocketOptions) => Elysia<"", {
31
+ decorator: {};
32
+ store: {};
33
+ derive: {};
34
+ resolve: {};
35
+ }, {
36
+ typebox: {};
37
+ error: {};
38
+ }, {
39
+ schema: {};
40
+ standaloneSchema: {};
41
+ macro: {};
42
+ macroFn: {};
43
+ parser: {};
44
+ response: {};
45
+ }, {
46
+ [x: string]: {
47
+ subscribe: {
48
+ body: {};
49
+ params: {};
50
+ query: {};
51
+ headers: {};
52
+ response: {};
53
+ };
54
+ };
55
+ }, {
56
+ derive: {};
57
+ resolve: {};
58
+ schema: {};
59
+ standaloneSchema: {};
60
+ response: {};
61
+ }, {
62
+ derive: {};
63
+ resolve: {};
64
+ schema: {};
65
+ standaloneSchema: {};
66
+ response: {};
67
+ }>;
@@ -0,0 +1,132 @@
1
+ import type { CollectionContext, CollectionDefinition, JoinCollectionDefinition } from './collection';
2
+ import type { GraphCollectionDefinition } from './graph';
3
+ import type { MutationDefinition, TableWriter, TransactionRunner } from './mutation';
4
+ import type { ReactiveQueryDefinition, TableReader } from './reactive';
5
+ import type { ClusterBus } from './cluster';
6
+ import type { ChangeSource, RowChange, ViewDiff } from './types';
7
+ /**
8
+ * Thrown when `authorize` denies a subscribe or a mutation. The message names
9
+ * the denied action; the message always starts with "Not authorized".
10
+ */
11
+ export declare class UnauthorizedError extends Error {
12
+ constructor(subject: string);
13
+ }
14
+ export type SubscribeArgs<T, P, Ctx> = {
15
+ /** Registered collection name. */
16
+ collection: string;
17
+ /** Query params (e.g. a filter value); passed to hydrate/match/authorize. */
18
+ params: P;
19
+ /** Caller context (e.g. session); passed to hydrate/match/authorize. */
20
+ ctx: Ctx;
21
+ /** Receives every non-empty diff (with its version) after the initial reply. */
22
+ onDiff: (diff: ViewDiff<T>, version: number) => void;
23
+ /**
24
+ * Resume from a version the client already applied. When the change log still
25
+ * covers `(since, now]` for a single-table collection, the engine replies with
26
+ * a catch-up diff instead of a full snapshot; otherwise it falls back to a
27
+ * snapshot.
28
+ */
29
+ since?: number;
30
+ };
31
+ export type Subscription<T> = {
32
+ /** The result set at subscribe time — a snapshot (empty when resuming). */
33
+ initial: T[];
34
+ /** Catch-up diff when resuming via `since` (instead of `initial`). */
35
+ catchup?: ViewDiff<T>;
36
+ /** The engine version this reply brings the client up to. */
37
+ version: number;
38
+ /** Stop receiving diffs and release the view. */
39
+ unsubscribe: () => void;
40
+ };
41
+ export type SyncEngine = {
42
+ /** Register a collection definition (see {@link defineCollection}). */
43
+ register: <T, P = void, Ctx = CollectionContext>(collection: CollectionDefinition<T, P, Ctx>) => void;
44
+ /** Register an incremental join collection (see {@link defineJoinCollection}). */
45
+ registerJoin: <L, R, Out, P = void, Ctx = CollectionContext>(collection: JoinCollectionDefinition<L, R, Out, P, Ctx>) => void;
46
+ /** Register an operator-graph collection (see {@link defineGraphCollection}). */
47
+ registerGraph: <Out, P = void, Ctx = CollectionContext>(collection: GraphCollectionDefinition<Out, P, Ctx>) => void;
48
+ /**
49
+ * Open a live subscription: authorize, hydrate the initial set, and stream
50
+ * diffs as changes arrive. Rejects with {@link UnauthorizedError} on deny.
51
+ */
52
+ subscribe: <T, P = void, Ctx = CollectionContext>(args: SubscribeArgs<T, P, Ctx>) => Promise<Subscription<T>>;
53
+ /**
54
+ * One-shot read: authorize and return a collection's current rows without
55
+ * subscribing. Powers an Eden-typed HTTP hydrate route (and SSR). Rejects
56
+ * with {@link UnauthorizedError} on deny.
57
+ */
58
+ hydrate: (collection: string, params: unknown, ctx: unknown) => Promise<unknown[]>;
59
+ /**
60
+ * Feed a committed change to `table` into the engine, fanning the resulting
61
+ * diff to every live subscription of every collection that reads that table.
62
+ * Call after a mutation, or wire a {@link ChangeSource} via `connectSource`.
63
+ * Single-table subscriptions diff the row; multi-table / refetch ones
64
+ * re-hydrate.
65
+ */
66
+ applyChange: <T>(table: string, change: RowChange<T>) => Promise<void>;
67
+ /**
68
+ * Connect a change source (e.g. a CDC adapter): its emitted changes flow into
69
+ * `applyChange`. Resolves to a disconnect function that stops the source.
70
+ */
71
+ connectSource: (source: ChangeSource) => Promise<() => Promise<void>>;
72
+ /**
73
+ * Join a cluster (see {@link ClusterBus}): broadcast this instance's committed
74
+ * changes to peers and apply theirs locally, so subscribers on every instance
75
+ * stay live. Resolves to a disconnect function. Run once per instance.
76
+ */
77
+ connectCluster: (bus: ClusterBus) => Promise<() => Promise<void>>;
78
+ /** Active subscription count, optionally for one collection. */
79
+ subscriptionCount: (collection?: string) => number;
80
+ /** Register a mutation definition (see {@link defineMutation}). */
81
+ registerMutation: <Args, Ctx = CollectionContext, Result = unknown>(mutation: MutationDefinition<Args, Ctx, Result>) => void;
82
+ /**
83
+ * Register how to persist a `table` (any ORM), so a mutation's
84
+ * `actions.insert/update/delete` write to your store and emit the live change
85
+ * in one step — you can't write without going live. See {@link TableWriter}.
86
+ */
87
+ registerWriter: <Row = unknown, Ctx = CollectionContext, Tx = unknown>(table: string, writer: TableWriter<Row, Ctx, Tx>) => void;
88
+ /**
89
+ * Register a read-set-tracked reactive query (see {@link defineReactiveQuery}):
90
+ * it re-runs and re-pushes whenever any table it read changes — no `match`, no
91
+ * operator graph, no manual change emission.
92
+ */
93
+ registerReactive: <T, P = void, Ctx = CollectionContext>(query: ReactiveQueryDefinition<T, P, Ctx>) => void;
94
+ /**
95
+ * Teach the engine how to read a table for reactive queries' `ctx.db` (any
96
+ * ORM). Required for every table a reactive query reads.
97
+ */
98
+ registerReader: <Ctx = CollectionContext>(table: string, reader: TableReader<Ctx>) => void;
99
+ /**
100
+ * Run a registered mutation: authorize, invoke its handler (which writes and
101
+ * emits changes via `applyChange`), and resolve with the handler's result.
102
+ * Rejects with {@link UnauthorizedError} on deny, or an error for an unknown
103
+ * mutation / a handler throw. Drive this from the transport's mutate frame.
104
+ */
105
+ runMutation: (name: string, args: unknown, ctx: unknown) => Promise<unknown>;
106
+ };
107
+ export type SyncEngineOptions = {
108
+ /**
109
+ * How many recent changes to retain for resumable reconnects. A client that
110
+ * reconnects within this window gets a catch-up diff; beyond it, a fresh
111
+ * snapshot. Defaults to 1024.
112
+ */
113
+ changeLogSize?: number;
114
+ /**
115
+ * Run every mutation inside your database's transaction (see
116
+ * {@link TransactionRunner}): the handler's writes commit all-or-nothing, and
117
+ * the engine emits the resulting diff only after the commit. Omit to run
118
+ * mutations without a transaction (each writer call is its own DB op).
119
+ */
120
+ transaction?: TransactionRunner;
121
+ };
122
+ /**
123
+ * The Tier 3 sync engine: a registry of collections plus the view syncer. It is
124
+ * transport-agnostic — `subscribe` returns the initial snapshot and an
125
+ * `onDiff` stream, which an Elysia/SSE layer wires to a connection, and
126
+ * `applyChange` is the change feed you drive from your mutations.
127
+ *
128
+ * Access control is first-class: every subscribe runs the collection's
129
+ * `authorize`, and a collection's `match`/`hydrate` scope rows to the caller, so
130
+ * a change to a row the caller can't see never reaches them.
131
+ */
132
+ export declare const createSyncEngine: (options?: SyncEngineOptions) => SyncEngine;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Core types shared across the Tier 3 sync engine (server-side). Deliberately
3
+ * ORM-agnostic: the engine operates on plain row objects, a key function, and a
4
+ * predicate — the Drizzle/Prisma adapters and the transport layer build on top.
5
+ */
6
+ /** A scalar that identifies a row within a collection. */
7
+ export type RowKey = string | number | bigint;
8
+ /** The kind of row-level change flowing through the engine's change feed. */
9
+ export type RowOp = 'insert' | 'update' | 'delete';
10
+ /** One row-level change: a row that was inserted, updated, or deleted. */
11
+ export type RowChange<T> = {
12
+ op: RowOp;
13
+ /**
14
+ * The affected row. For `insert`/`update` it is the new row value; for
15
+ * `delete` only the key field(s) need be present.
16
+ */
17
+ row: T;
18
+ };
19
+ /**
20
+ * The delta a change makes to a query's result set: rows that entered (`added`),
21
+ * left (`removed`), or stayed in the set but changed value (`changed`). The
22
+ * `removed` rows are the values the view last held for those keys.
23
+ */
24
+ export type ViewDiff<T> = {
25
+ added: T[];
26
+ removed: T[];
27
+ changed: T[];
28
+ };
29
+ /** Report a committed row change on `table` into the engine. */
30
+ export type EmitChange = (table: string, change: RowChange<unknown>) => void | Promise<void>;
31
+ /** A committed change parsed from a CDC source, ready to feed the engine. */
32
+ export type ParsedChange = {
33
+ table: string;
34
+ change: RowChange<unknown>;
35
+ };
36
+ /**
37
+ * A pluggable source of committed row changes — the seam for catching writes the
38
+ * mutation API didn't make (CDC: Postgres LISTEN/NOTIFY or logical replication,
39
+ * MySQL binlog, SQLite update hooks). `start` begins emitting via the supplied
40
+ * callback; `stop` tears it down. Wire one with {@link SyncEngine.connectSource}.
41
+ */
42
+ export type ChangeSource = {
43
+ start: (emit: EmitChange) => void | Promise<void>;
44
+ stop: () => void | Promise<void>;
45
+ };
package/dist/index.d.ts CHANGED
@@ -4,3 +4,7 @@ export { createReactiveHub } from './reactiveHub';
4
4
  export type { ReactiveEvent, ReactiveHub, ReactiveListener } from './reactiveHub';
5
5
  export { sync } from './plugin';
6
6
  export type { SyncPluginOptions, SyncRequestContext } from './plugin';
7
+ export { syncSocket } from './engine/socket';
8
+ export type { SyncSocketOptions } from './engine/socket';
9
+ export { createPresenceHub } from './engine/presence';
10
+ export type { PresenceDiff, PresenceHandle, PresenceHub, PresenceMember } from './engine/presence';