@absolutejs/sync 0.0.1 → 0.1.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 +264 -24
- package/dist/adapters/drizzle/index.d.ts +17 -0
- package/dist/adapters/drizzle/index.js +128 -0
- package/dist/adapters/drizzle/index.js.map +12 -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 +8 -30
- package/dist/client/index.js +744 -3
- package/dist/client/index.js.map +8 -4
- package/dist/client/liveQuery.d.ts +75 -0
- package/dist/client/subscriber.d.ts +30 -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/collection.d.ts +87 -0
- package/dist/engine/connection.d.ts +71 -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 +34 -0
- package/dist/engine/index.js +1269 -0
- package/dist/engine/index.js.map +20 -0
- package/dist/engine/materializedView.d.ts +53 -0
- package/dist/engine/mutation.d.ts +30 -0
- package/dist/engine/pollingSource.d.ts +42 -0
- package/dist/engine/routes.d.ts +40 -0
- package/dist/engine/socket.d.ts +64 -0
- package/dist/engine/syncEngine.d.ts +100 -0
- package/dist/engine/types.d.ts +45 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +160 -2
- package/dist/index.js.map +7 -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 +102 -6
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { AggregateGroup } from './aggregate';
|
|
2
|
+
import type { RowChange, RowKey, ViewDiff } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Composable incremental dataflow (the general operator graph, in progress).
|
|
5
|
+
*
|
|
6
|
+
* Every edge in the graph is a stream of keyed **changes** — an `upsert` (insert
|
|
7
|
+
* or update, last-write-wins per key) or a `delete`. Collapsing added/changed
|
|
8
|
+
* into one `upsert` is what makes operators compose: `filter`/`map` become
|
|
9
|
+
* stateless stream transforms, `join` reuses the equi-join operator, and a
|
|
10
|
+
* `materialize` sink turns the final stream back into a `{ added, removed,
|
|
11
|
+
* changed }` diff for the transport.
|
|
12
|
+
*/
|
|
13
|
+
export type Change<T> = {
|
|
14
|
+
op: 'upsert' | 'delete';
|
|
15
|
+
key: RowKey;
|
|
16
|
+
row: T;
|
|
17
|
+
};
|
|
18
|
+
/** A single-input incremental operator: a batch of input changes → output changes. */
|
|
19
|
+
export type Operator<In, Out> = {
|
|
20
|
+
push: (changes: Change<In>[]) => Change<Out>[];
|
|
21
|
+
};
|
|
22
|
+
/** Lift a table row change into a dataflow change (a graph source). */
|
|
23
|
+
export declare const fromRowChange: <T>(change: RowChange<T>, key: (row: T) => RowKey) => Change<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Keep only rows matching `predicate`. Stateless: a row that fails the predicate
|
|
26
|
+
* is emitted as a `delete` (downstream removes it if present, else no-op), so a
|
|
27
|
+
* row that stops matching leaves correctly.
|
|
28
|
+
*/
|
|
29
|
+
export declare const filterOp: <T>(predicate: (row: T) => boolean) => Operator<T, T>;
|
|
30
|
+
/**
|
|
31
|
+
* Transform each row. Stateless; preserves identity unless `rekey` is given (to
|
|
32
|
+
* derive the output key from the mapped row).
|
|
33
|
+
*/
|
|
34
|
+
export declare const mapOp: <In, Out>(transform: (row: In) => Out, rekey?: (row: Out) => RowKey) => Operator<In, Out>;
|
|
35
|
+
/** Compose two operators into one (`a` then `b`). Nest for longer chains. */
|
|
36
|
+
export declare const chain: <A, B, C>(a: Operator<A, B>, b: Operator<B, C>) => Operator<A, C>;
|
|
37
|
+
export type AggregateOpOptions<In> = {
|
|
38
|
+
/** Input row identity (to track each row's contribution across updates). */
|
|
39
|
+
key: (row: In) => RowKey;
|
|
40
|
+
/** Group rows by this key (omit for one `''` group). */
|
|
41
|
+
groupBy?: (row: In) => RowKey;
|
|
42
|
+
/** Numeric value for sum/avg/min/max (omit for a count-only aggregate). */
|
|
43
|
+
value?: (row: In) => number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Aggregate a change stream into a stream of group summaries — `count`/`sum`/
|
|
47
|
+
* `avg`/`min`/`max` per group, maintained incrementally (wraps
|
|
48
|
+
* {@link createAggregate}). Emits an `upsert` of the group summary for each group
|
|
49
|
+
* a batch touched, or a `delete` when a group empties. Output is keyed by group,
|
|
50
|
+
* so it composes downstream like any other operator (e.g. after a join).
|
|
51
|
+
*/
|
|
52
|
+
export declare const aggregateOp: <In>(options: AggregateOpOptions<In>) => Operator<In, AggregateGroup>;
|
|
53
|
+
export type OrderByOptions<T> = {
|
|
54
|
+
/** Row identity. */
|
|
55
|
+
key: (row: T) => RowKey;
|
|
56
|
+
/** Sort comparator (ascending: negative = a before b). */
|
|
57
|
+
compare: (a: T, b: T) => number;
|
|
58
|
+
/** Keep at most this many rows (the top-N window). */
|
|
59
|
+
limit?: number;
|
|
60
|
+
/** Skip this many rows from the front (pagination). Defaults to 0. */
|
|
61
|
+
offset?: number;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Maintain a sorted top-N window: keep only the `[offset, offset + limit)` slice
|
|
65
|
+
* by `compare`, emitting which rows entered/left the window as input changes.
|
|
66
|
+
* A single insert can both add a row and evict the one it displaced — both are
|
|
67
|
+
* emitted. Bounded output (≤ limit upserts per batch); it re-sorts its input, so
|
|
68
|
+
* place it where the input is already narrowed (e.g. after a filter/join).
|
|
69
|
+
*
|
|
70
|
+
* The window is the right *set* of rows; sort them by the same comparator on the
|
|
71
|
+
* client for display order (cheap for N rows — the diff protocol is unordered).
|
|
72
|
+
*/
|
|
73
|
+
export declare const orderByOp: <T>(options: OrderByOptions<T>) => Operator<T, T>;
|
|
74
|
+
/** A two-input incremental equi-join node. */
|
|
75
|
+
export type JoinNode<L, R, Out> = {
|
|
76
|
+
hydrate: (left: Iterable<L>, right: Iterable<R>) => void;
|
|
77
|
+
pushLeft: (changes: Change<L>[]) => Change<Out>[];
|
|
78
|
+
pushRight: (changes: Change<R>[]) => Change<Out>[];
|
|
79
|
+
rows: () => Out[];
|
|
80
|
+
};
|
|
81
|
+
export type JoinNodeOptions<L, R, Out> = {
|
|
82
|
+
leftKey: (left: L) => RowKey;
|
|
83
|
+
rightKey: (right: R) => RowKey;
|
|
84
|
+
leftOn: (left: L) => RowKey;
|
|
85
|
+
rightOn: (right: R) => RowKey;
|
|
86
|
+
select: (left: L, right: R) => Out;
|
|
87
|
+
/** Provide for a LEFT join: output for a left row with no match. */
|
|
88
|
+
selectUnmatched?: (left: L) => Out;
|
|
89
|
+
/** Output row identity (unique per emitted pair). */
|
|
90
|
+
key: (out: Out) => RowKey;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* A join as a dataflow node — reuses {@link createEquiJoin} and converts its
|
|
94
|
+
* `{ added, removed, changed }` deltas into the upsert/delete change stream.
|
|
95
|
+
*/
|
|
96
|
+
export declare const joinNode: <L, R, Out>(options: JoinNodeOptions<L, R, Out>) => JoinNode<L, R, Out>;
|
|
97
|
+
export type Materializer<T> = {
|
|
98
|
+
/** Replace the set with initial rows (no diff emitted). */
|
|
99
|
+
hydrate: (rows: Iterable<T>) => void;
|
|
100
|
+
/** Apply a change stream and return the resulting result-set diff. */
|
|
101
|
+
apply: (changes: Change<T>[]) => ViewDiff<T>;
|
|
102
|
+
rows: () => T[];
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* The graph sink: maintain a keyed result set from a change stream and emit the
|
|
106
|
+
* `{ added, removed, changed }` diff each batch produces — the boundary back to
|
|
107
|
+
* the transport / client.
|
|
108
|
+
*/
|
|
109
|
+
export declare const materialize: <T>(key: (row: T) => RowKey, equals?: (a: T, b: T) => boolean) => Materializer<T>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { RowChange, RowKey, ViewDiff } from './types';
|
|
2
|
+
export type EquiJoinOptions<L, R, Out> = {
|
|
3
|
+
/** Left row identity. */
|
|
4
|
+
leftKey: (left: L) => RowKey;
|
|
5
|
+
/** Right row identity. */
|
|
6
|
+
rightKey: (right: R) => RowKey;
|
|
7
|
+
/** Join value on the left (matched against `rightOn`). */
|
|
8
|
+
leftOn: (left: L) => RowKey;
|
|
9
|
+
/** Join value on the right (matched against `leftOn`). */
|
|
10
|
+
rightOn: (right: R) => RowKey;
|
|
11
|
+
/** Combine a matched pair into an output row. */
|
|
12
|
+
select: (left: L, right: R) => Out;
|
|
13
|
+
/**
|
|
14
|
+
* Provide to make this a LEFT join: a left row with no matching right emits
|
|
15
|
+
* `selectUnmatched(left)` instead of nothing, and is replaced by matched rows
|
|
16
|
+
* once a right appears (and reverts when the last right leaves). Omit for an
|
|
17
|
+
* inner join.
|
|
18
|
+
*/
|
|
19
|
+
selectUnmatched?: (left: L) => Out;
|
|
20
|
+
/**
|
|
21
|
+
* Equality used to detect when a matched pair's output value changed.
|
|
22
|
+
* Defaults to a shallow compare of own enumerable properties.
|
|
23
|
+
*/
|
|
24
|
+
equals?: (a: Out, b: Out) => boolean;
|
|
25
|
+
};
|
|
26
|
+
export type EquiJoin<L, R, Out> = {
|
|
27
|
+
/** Bulk-load both inputs (replaces current state). */
|
|
28
|
+
hydrate: (left: Iterable<L>, right: Iterable<R>) => void;
|
|
29
|
+
/** Apply a change to the left input; returns the output diff. */
|
|
30
|
+
applyLeft: (change: RowChange<L>) => ViewDiff<Out>;
|
|
31
|
+
/** Apply a change to the right input; returns the output diff. */
|
|
32
|
+
applyRight: (change: RowChange<R>) => ViewDiff<Out>;
|
|
33
|
+
/** Current joined rows. */
|
|
34
|
+
rows: () => Out[];
|
|
35
|
+
/** Number of joined rows. */
|
|
36
|
+
size: () => number;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* An incrementally-maintained equi-join (the differential-dataflow core, bounded
|
|
40
|
+
* to a single two-input equality join). Hydrate both sides, then feed each side's
|
|
41
|
+
* row changes through {@link EquiJoin.applyLeft} / `applyRight`: it indexes both
|
|
42
|
+
* inputs by the join value and emits only the `{ added, removed, changed }`
|
|
43
|
+
* joined rows the change affects — touching the matching pairs, not the whole
|
|
44
|
+
* result. Inner by default; pass `selectUnmatched` for a LEFT join.
|
|
45
|
+
*
|
|
46
|
+
* Output rows are keyed internally by the `(leftKey, rightKey)` pair, so it
|
|
47
|
+
* handles many-to-many. The consumer keys the emitted rows by its own key; for a
|
|
48
|
+
* many-to-many join, include both ids in the output so that key is unique
|
|
49
|
+
* (a many-to-one join can key by the left id alone).
|
|
50
|
+
*/
|
|
51
|
+
export declare const createEquiJoin: <L, R, Out>(options: EquiJoinOptions<L, R, Out>) => EquiJoin<L, R, Out>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { AggregateGroup } from './aggregate';
|
|
2
|
+
import type { CollectionContext } from './collection';
|
|
3
|
+
import type { RowChange, RowKey, ViewDiff } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Declarative incremental queries — the front door to the operator graph. Build a
|
|
6
|
+
* pipeline with {@link query} (`source → filter → map → join → groupBy`); the
|
|
7
|
+
* engine instantiates it per subscription, hydrates each source, and routes each
|
|
8
|
+
* table's changes through the wired operators, emitting result diffs.
|
|
9
|
+
*/
|
|
10
|
+
/** A table this query reads. */
|
|
11
|
+
export type GraphSource<Row, P = void, Ctx = CollectionContext> = {
|
|
12
|
+
table: string;
|
|
13
|
+
hydrate: (params: P, ctx: Ctx) => Promise<Iterable<Row>> | Iterable<Row>;
|
|
14
|
+
key: (row: Row) => RowKey;
|
|
15
|
+
/** Scope incremental changes (a row that fails it leaves). */
|
|
16
|
+
match?: (row: Row, params: P, ctx: Ctx) => boolean;
|
|
17
|
+
};
|
|
18
|
+
export type JoinOptions<Left, Right, Out> = {
|
|
19
|
+
/** Join value on the left (current) stream. */
|
|
20
|
+
on: (left: Left) => RowKey;
|
|
21
|
+
/** Join value on the right source. */
|
|
22
|
+
rightOn: (right: Right) => RowKey;
|
|
23
|
+
/** Combine a matched pair. */
|
|
24
|
+
select: (left: Left, right: Right) => Out;
|
|
25
|
+
/**
|
|
26
|
+
* Provide to make this a LEFT join: output for a left row with no matching
|
|
27
|
+
* right (e.g. a user with zero orders). Omit for an inner join.
|
|
28
|
+
*/
|
|
29
|
+
selectUnmatched?: (left: Left) => Out;
|
|
30
|
+
/** Output row identity (unique per pair). */
|
|
31
|
+
key: (out: Out) => RowKey;
|
|
32
|
+
};
|
|
33
|
+
export type GroupByOptions<Row> = {
|
|
34
|
+
/** Input row identity (to track contributions). */
|
|
35
|
+
key: (row: Row) => RowKey;
|
|
36
|
+
groupBy?: (row: Row) => RowKey;
|
|
37
|
+
value?: (row: Row) => number;
|
|
38
|
+
};
|
|
39
|
+
export type OrderByQueryOptions<Row> = {
|
|
40
|
+
/** Row identity. */
|
|
41
|
+
key: (row: Row) => RowKey;
|
|
42
|
+
/** Sort comparator (ascending). */
|
|
43
|
+
compare: (a: Row, b: Row) => number;
|
|
44
|
+
/** Keep at most this many rows (top-N). */
|
|
45
|
+
limit?: number;
|
|
46
|
+
/** Skip this many from the front (pagination). */
|
|
47
|
+
offset?: number;
|
|
48
|
+
};
|
|
49
|
+
/** A live, instantiated graph for one subscription. */
|
|
50
|
+
export type GraphInstance<Out> = {
|
|
51
|
+
tables: string[];
|
|
52
|
+
hydrate: () => Promise<Out[]>;
|
|
53
|
+
applyChange: (table: string, change: RowChange<unknown>) => ViewDiff<Out>;
|
|
54
|
+
};
|
|
55
|
+
export type Query<Row, P = void, Ctx = CollectionContext> = {
|
|
56
|
+
filter: (predicate: (row: Row, params: P, ctx: Ctx) => boolean) => Query<Row, P, Ctx>;
|
|
57
|
+
map: <Out>(transform: (row: Row) => Out, rekey?: (row: Out) => RowKey) => Query<Out, P, Ctx>;
|
|
58
|
+
join: <Right, Out>(right: GraphSource<Right, P, Ctx> | Query<Right, P, Ctx>, options: JoinOptions<Row, Right, Out>) => Query<Out, P, Ctx>;
|
|
59
|
+
/**
|
|
60
|
+
* LEFT join: like {@link join} but keeps left rows with no match, emitting
|
|
61
|
+
* `selectUnmatched(left)` for them (required, so the intent is explicit).
|
|
62
|
+
*/
|
|
63
|
+
leftJoin: <Right, Out>(right: GraphSource<Right, P, Ctx> | Query<Right, P, Ctx>, options: JoinOptions<Row, Right, Out> & {
|
|
64
|
+
selectUnmatched: (left: Row) => Out;
|
|
65
|
+
}) => Query<Out, P, Ctx>;
|
|
66
|
+
groupBy: (options: GroupByOptions<Row>) => Query<AggregateGroup, P, Ctx>;
|
|
67
|
+
orderBy: (options: OrderByQueryOptions<Row>) => Query<Row, P, Ctx>;
|
|
68
|
+
/** Source tables this query reads. */
|
|
69
|
+
tables: () => string[];
|
|
70
|
+
/** Instantiate the graph for one subscription's params/ctx. */
|
|
71
|
+
instantiate: (params: P, ctx: Ctx) => GraphInstance<Row>;
|
|
72
|
+
};
|
|
73
|
+
/** Start a query from a source table. */
|
|
74
|
+
export declare const query: <Row, P = void, Ctx = CollectionContext>(source: GraphSource<Row, P, Ctx>) => Query<Row, P, Ctx>;
|
|
75
|
+
/** A collection backed by an incremental operator graph (see {@link query}). */
|
|
76
|
+
export type GraphCollectionDefinition<Out, P = void, Ctx = CollectionContext> = {
|
|
77
|
+
name: string;
|
|
78
|
+
kind: 'graph';
|
|
79
|
+
query: Query<Out, P, Ctx>;
|
|
80
|
+
authorize?: (params: P, ctx: Ctx) => boolean | Promise<boolean>;
|
|
81
|
+
/** Output row identity (used by the engine/transport to key result rows). */
|
|
82
|
+
key: (out: Out) => RowKey;
|
|
83
|
+
};
|
|
84
|
+
/** Define a collection whose result is maintained by an operator graph. */
|
|
85
|
+
export declare const defineGraphCollection: <Out, P = void, Ctx = CollectionContext>(definition: Omit<GraphCollectionDefinition<Out, P, Ctx>, "kind">) => GraphCollectionDefinition<Out, P, Ctx>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @absolutejs/sync engine (Tier 3 — sync engine MVP, server-side).
|
|
3
|
+
*
|
|
4
|
+
* Row-level reactive query results on your own database. Hydrate a query's
|
|
5
|
+
* result set once, then maintain it incrementally as rows change and push the
|
|
6
|
+
* diffs to subscribers — instead of refetching the whole query.
|
|
7
|
+
*
|
|
8
|
+
* This entry currently exposes the predicate-matching IVM core
|
|
9
|
+
* ({@link createMaterializedView}); the collection registry, view syncer, diff
|
|
10
|
+
* transport, and client store land here as the read and write paths fill in.
|
|
11
|
+
*/
|
|
12
|
+
export { createMaterializedView, isEmptyViewDiff } from './materializedView';
|
|
13
|
+
export type { MaterializedView, MaterializedViewOptions } from './materializedView';
|
|
14
|
+
export { createAggregate } from './aggregate';
|
|
15
|
+
export type { Aggregate, AggregateGroup, AggregateOptions } from './aggregate';
|
|
16
|
+
export { createEquiJoin } from './equiJoin';
|
|
17
|
+
export type { EquiJoin, EquiJoinOptions } from './equiJoin';
|
|
18
|
+
export { aggregateOp, chain, filterOp, fromRowChange, joinNode, mapOp, materialize, orderByOp } from './dataflow';
|
|
19
|
+
export type { AggregateOpOptions, Change, JoinNode, JoinNodeOptions, Materializer, Operator, OrderByOptions } from './dataflow';
|
|
20
|
+
export type { ChangeSource, EmitChange, ParsedChange, RowChange, RowKey, RowOp, ViewDiff } from './types';
|
|
21
|
+
export { createPollingChangeSource, parseOutboxRow } from './pollingSource';
|
|
22
|
+
export type { OutboxRow, PollingChangeSourceOptions } from './pollingSource';
|
|
23
|
+
export { defineCollection, defineJoinCollection } from './collection';
|
|
24
|
+
export type { CollectionContext, CollectionDefinition, JoinCollectionDefinition, JoinSide } from './collection';
|
|
25
|
+
export { defineGraphCollection, query } from './graph';
|
|
26
|
+
export type { GraphCollectionDefinition, GraphInstance, GraphSource, GroupByOptions, JoinOptions, OrderByQueryOptions, Query } from './graph';
|
|
27
|
+
export { defineMutation } from './mutation';
|
|
28
|
+
export type { MutationActions, MutationDefinition, MutationHandler } from './mutation';
|
|
29
|
+
export { createSyncEngine, UnauthorizedError } from './syncEngine';
|
|
30
|
+
export type { SubscribeArgs, Subscription, SyncEngine } from './syncEngine';
|
|
31
|
+
export { hydrateRoute, mutateRoute } from './routes';
|
|
32
|
+
export type { SyncRouteContext } from './routes';
|
|
33
|
+
export { createSyncConnection } from './connection';
|
|
34
|
+
export type { ClientFrame, ServerFrame, SyncConnection, SyncConnectionOptions } from './connection';
|