@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.
- package/README.md +281 -24
- package/dist/adapters/drizzle/collection.d.ts +27 -0
- package/dist/adapters/drizzle/index.d.ts +20 -0
- package/dist/adapters/drizzle/index.js +265 -0
- package/dist/adapters/drizzle/index.js.map +14 -0
- package/dist/adapters/drizzle/predicate.d.ts +20 -0
- package/dist/adapters/drizzle/read.d.ts +31 -0
- package/dist/adapters/drizzle/topics.d.ts +41 -0
- package/dist/adapters/drizzle/write.d.ts +69 -0
- package/dist/adapters/mysql/index.d.ts +75 -0
- package/dist/adapters/mysql/index.js +171 -0
- package/dist/adapters/mysql/index.js.map +11 -0
- package/dist/adapters/postgres/index.d.ts +53 -0
- package/dist/adapters/postgres/index.js +86 -0
- package/dist/adapters/postgres/index.js.map +10 -0
- package/dist/adapters/prisma/collection.d.ts +39 -0
- package/dist/adapters/prisma/index.d.ts +23 -0
- package/dist/adapters/prisma/index.js +231 -0
- package/dist/adapters/prisma/index.js.map +14 -0
- package/dist/adapters/prisma/predicate.d.ts +20 -0
- package/dist/adapters/prisma/read.d.ts +28 -0
- package/dist/adapters/prisma/topics.d.ts +29 -0
- package/dist/adapters/prisma/write.d.ts +65 -0
- package/dist/adapters/sqlite/index.d.ts +32 -0
- package/dist/adapters/sqlite/index.js +128 -0
- package/dist/adapters/sqlite/index.js.map +11 -0
- package/dist/angular/index.d.ts +1 -0
- package/dist/angular/index.js +347 -0
- package/dist/angular/index.js.map +11 -0
- package/dist/angular/sync-collection.service.d.ts +20 -0
- package/dist/client/index.d.ts +12 -30
- package/dist/client/index.js +1099 -3
- package/dist/client/index.js.map +10 -4
- package/dist/client/liveQuery.d.ts +75 -0
- package/dist/client/presence.d.ts +37 -0
- package/dist/client/subscriber.d.ts +30 -0
- package/dist/client/syncClient.d.ts +53 -0
- package/dist/client/syncCollection.d.ts +102 -0
- package/dist/client/syncStore.d.ts +81 -0
- package/dist/engine/aggregate.d.ts +45 -0
- package/dist/engine/cluster.d.ts +41 -0
- package/dist/engine/collection.d.ts +87 -0
- package/dist/engine/connection.d.ts +103 -0
- package/dist/engine/dataflow.d.ts +109 -0
- package/dist/engine/equiJoin.d.ts +51 -0
- package/dist/engine/graph.d.ts +85 -0
- package/dist/engine/index.d.ts +40 -0
- package/dist/engine/index.js +1774 -0
- package/dist/engine/index.js.map +23 -0
- package/dist/engine/materializedView.d.ts +53 -0
- package/dist/engine/mutation.d.ts +66 -0
- package/dist/engine/pollingSource.d.ts +42 -0
- package/dist/engine/presence.d.ts +46 -0
- package/dist/engine/reactive.d.ts +67 -0
- package/dist/engine/routes.d.ts +40 -0
- package/dist/engine/socket.d.ts +67 -0
- package/dist/engine/syncEngine.d.ts +132 -0
- package/dist/engine/types.d.ts +45 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +327 -3
- package/dist/index.js.map +8 -5
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +332 -0
- package/dist/react/index.js.map +11 -0
- package/dist/react/useSyncCollection.d.ts +16 -0
- package/dist/reactiveHub.d.ts +6 -0
- package/dist/svelte/createSyncCollectionStore.d.ts +15 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +338 -0
- package/dist/svelte/index.js.map +11 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +331 -0
- package/dist/vue/index.js.map +11 -0
- package/dist/vue/useSyncCollection.d.ts +17 -0
- 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';
|