@tanstack/db 0.0.4 → 0.0.5
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/dist/cjs/collection.cjs +113 -94
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +38 -11
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/proxy.cjs +87 -248
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/proxy.d.cts +5 -5
- package/dist/cjs/query/compiled-query.cjs +23 -14
- package/dist/cjs/query/compiled-query.cjs.map +1 -1
- package/dist/cjs/query/compiled-query.d.cts +3 -1
- package/dist/cjs/query/evaluators.cjs +20 -20
- package/dist/cjs/query/evaluators.cjs.map +1 -1
- package/dist/cjs/query/evaluators.d.cts +3 -2
- package/dist/cjs/query/extractors.cjs +20 -20
- package/dist/cjs/query/extractors.cjs.map +1 -1
- package/dist/cjs/query/extractors.d.cts +3 -3
- package/dist/cjs/query/group-by.cjs +12 -15
- package/dist/cjs/query/group-by.cjs.map +1 -1
- package/dist/cjs/query/group-by.d.cts +7 -7
- package/dist/cjs/query/joins.cjs +41 -55
- package/dist/cjs/query/joins.cjs.map +1 -1
- package/dist/cjs/query/joins.d.cts +3 -3
- package/dist/cjs/query/order-by.cjs +37 -84
- package/dist/cjs/query/order-by.cjs.map +1 -1
- package/dist/cjs/query/order-by.d.cts +2 -2
- package/dist/cjs/query/pipeline-compiler.cjs +13 -18
- package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
- package/dist/cjs/query/pipeline-compiler.d.cts +2 -1
- package/dist/cjs/query/query-builder.cjs +0 -12
- package/dist/cjs/query/query-builder.cjs.map +1 -1
- package/dist/cjs/query/query-builder.d.cts +4 -8
- package/dist/cjs/query/schema.d.cts +1 -6
- package/dist/cjs/query/select.cjs +35 -24
- package/dist/cjs/query/select.cjs.map +1 -1
- package/dist/cjs/query/select.d.cts +2 -2
- package/dist/cjs/query/types.d.cts +1 -0
- package/dist/cjs/transactions.cjs +17 -8
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +41 -7
- package/dist/esm/collection.d.ts +38 -11
- package/dist/esm/collection.js +113 -94
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/proxy.d.ts +5 -5
- package/dist/esm/proxy.js +87 -248
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/compiled-query.d.ts +3 -1
- package/dist/esm/query/compiled-query.js +23 -14
- package/dist/esm/query/compiled-query.js.map +1 -1
- package/dist/esm/query/evaluators.d.ts +3 -2
- package/dist/esm/query/evaluators.js +21 -21
- package/dist/esm/query/evaluators.js.map +1 -1
- package/dist/esm/query/extractors.d.ts +3 -3
- package/dist/esm/query/extractors.js +20 -20
- package/dist/esm/query/extractors.js.map +1 -1
- package/dist/esm/query/group-by.d.ts +7 -7
- package/dist/esm/query/group-by.js +14 -17
- package/dist/esm/query/group-by.js.map +1 -1
- package/dist/esm/query/joins.d.ts +3 -3
- package/dist/esm/query/joins.js +42 -56
- package/dist/esm/query/joins.js.map +1 -1
- package/dist/esm/query/order-by.d.ts +2 -2
- package/dist/esm/query/order-by.js +39 -86
- package/dist/esm/query/order-by.js.map +1 -1
- package/dist/esm/query/pipeline-compiler.d.ts +2 -1
- package/dist/esm/query/pipeline-compiler.js +14 -19
- package/dist/esm/query/pipeline-compiler.js.map +1 -1
- package/dist/esm/query/query-builder.d.ts +4 -8
- package/dist/esm/query/query-builder.js +0 -12
- package/dist/esm/query/query-builder.js.map +1 -1
- package/dist/esm/query/schema.d.ts +1 -6
- package/dist/esm/query/select.d.ts +2 -2
- package/dist/esm/query/select.js +36 -25
- package/dist/esm/query/select.js.map +1 -1
- package/dist/esm/query/types.d.ts +1 -0
- package/dist/esm/transactions.js +17 -8
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +41 -7
- package/package.json +2 -2
- package/src/collection.ts +174 -121
- package/src/proxy.ts +141 -358
- package/src/query/compiled-query.ts +30 -15
- package/src/query/evaluators.ts +22 -21
- package/src/query/extractors.ts +24 -21
- package/src/query/group-by.ts +24 -22
- package/src/query/joins.ts +88 -75
- package/src/query/order-by.ts +56 -106
- package/src/query/pipeline-compiler.ts +34 -37
- package/src/query/query-builder.ts +9 -23
- package/src/query/schema.ts +1 -10
- package/src/query/select.ts +44 -32
- package/src/query/types.ts +1 -0
- package/src/transactions.ts +22 -13
- package/src/types.ts +48 -7
- package/dist/cjs/query/key-by.cjs +0 -43
- package/dist/cjs/query/key-by.cjs.map +0 -1
- package/dist/cjs/query/key-by.d.cts +0 -3
- package/dist/esm/query/key-by.d.ts +0 -3
- package/dist/esm/query/key-by.js +0 -43
- package/dist/esm/query/key-by.js.map +0 -1
- package/src/query/key-by.ts +0 -61
|
@@ -4,18 +4,24 @@ const d2ts = require("@electric-sql/d2ts");
|
|
|
4
4
|
const extractors = require("./extractors.cjs");
|
|
5
5
|
function processSelect(pipeline, query, mainTableAlias, inputs) {
|
|
6
6
|
return pipeline.pipe(
|
|
7
|
-
d2ts.map((
|
|
7
|
+
d2ts.map(([key, namespacedRow]) => {
|
|
8
8
|
const result = {};
|
|
9
|
-
const isGroupedResult = query.groupBy && Object.keys(
|
|
10
|
-
(
|
|
9
|
+
const isGroupedResult = query.groupBy && Object.keys(namespacedRow).some(
|
|
10
|
+
(namespaceKey) => !Object.keys(inputs).includes(namespaceKey) && typeof namespacedRow[namespaceKey] !== `object`
|
|
11
11
|
);
|
|
12
|
+
if (!query.select) {
|
|
13
|
+
throw new Error(`Cannot process missing SELECT clause`);
|
|
14
|
+
}
|
|
12
15
|
for (const item of query.select) {
|
|
13
16
|
if (typeof item === `string`) {
|
|
14
17
|
if (item === `@*`) {
|
|
15
18
|
if (isGroupedResult) {
|
|
16
|
-
Object.assign(result,
|
|
19
|
+
Object.assign(result, namespacedRow);
|
|
17
20
|
} else {
|
|
18
|
-
Object.assign(
|
|
21
|
+
Object.assign(
|
|
22
|
+
result,
|
|
23
|
+
extractAllColumnsFromAllTables(namespacedRow)
|
|
24
|
+
);
|
|
19
25
|
}
|
|
20
26
|
continue;
|
|
21
27
|
}
|
|
@@ -26,7 +32,7 @@ function processSelect(pipeline, query, mainTableAlias, inputs) {
|
|
|
26
32
|
} else {
|
|
27
33
|
Object.assign(
|
|
28
34
|
result,
|
|
29
|
-
extractAllColumnsFromTable(
|
|
35
|
+
extractAllColumnsFromTable(namespacedRow, tableAlias)
|
|
30
36
|
);
|
|
31
37
|
}
|
|
32
38
|
continue;
|
|
@@ -34,11 +40,11 @@ function processSelect(pipeline, query, mainTableAlias, inputs) {
|
|
|
34
40
|
if (item.startsWith(`@`)) {
|
|
35
41
|
const columnRef = item.substring(1);
|
|
36
42
|
const alias = columnRef;
|
|
37
|
-
if (isGroupedResult && columnRef in
|
|
38
|
-
result[alias] =
|
|
43
|
+
if (isGroupedResult && columnRef in namespacedRow) {
|
|
44
|
+
result[alias] = namespacedRow[columnRef];
|
|
39
45
|
} else {
|
|
40
|
-
result[alias] = extractors.
|
|
41
|
-
|
|
46
|
+
result[alias] = extractors.extractValueFromNamespacedRow(
|
|
47
|
+
namespacedRow,
|
|
42
48
|
columnRef,
|
|
43
49
|
mainTableAlias,
|
|
44
50
|
void 0
|
|
@@ -54,22 +60,24 @@ function processSelect(pipeline, query, mainTableAlias, inputs) {
|
|
|
54
60
|
for (const [alias, expr] of Object.entries(item)) {
|
|
55
61
|
if (typeof expr === `string` && expr.startsWith(`@`)) {
|
|
56
62
|
const columnRef = expr.substring(1);
|
|
57
|
-
if (isGroupedResult && columnRef in
|
|
58
|
-
result[alias] =
|
|
63
|
+
if (isGroupedResult && columnRef in namespacedRow) {
|
|
64
|
+
result[alias] = namespacedRow[columnRef];
|
|
59
65
|
} else {
|
|
60
|
-
result[alias] = extractors.
|
|
61
|
-
|
|
66
|
+
result[alias] = extractors.extractValueFromNamespacedRow(
|
|
67
|
+
namespacedRow,
|
|
62
68
|
columnRef,
|
|
63
69
|
mainTableAlias,
|
|
64
70
|
void 0
|
|
65
71
|
);
|
|
66
72
|
}
|
|
67
73
|
} else if (typeof expr === `object`) {
|
|
68
|
-
if (isGroupedResult && alias in
|
|
69
|
-
result[alias] =
|
|
74
|
+
if (isGroupedResult && alias in namespacedRow) {
|
|
75
|
+
result[alias] = namespacedRow[alias];
|
|
76
|
+
} else if (expr.ORDER_INDEX) {
|
|
77
|
+
result[alias] = namespacedRow[mainTableAlias][alias];
|
|
70
78
|
} else {
|
|
71
|
-
result[alias] = extractors.
|
|
72
|
-
|
|
79
|
+
result[alias] = extractors.evaluateOperandOnNamespacedRow(
|
|
80
|
+
namespacedRow,
|
|
73
81
|
expr,
|
|
74
82
|
mainTableAlias,
|
|
75
83
|
void 0
|
|
@@ -79,22 +87,25 @@ function processSelect(pipeline, query, mainTableAlias, inputs) {
|
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
}
|
|
82
|
-
return result;
|
|
90
|
+
return [key, result];
|
|
83
91
|
})
|
|
84
92
|
);
|
|
85
93
|
}
|
|
86
|
-
function extractAllColumnsFromAllTables(
|
|
94
|
+
function extractAllColumnsFromAllTables(namespacedRow) {
|
|
87
95
|
const result = {};
|
|
88
|
-
for (const [tableAlias, tableData] of Object.entries(
|
|
96
|
+
for (const [tableAlias, tableData] of Object.entries(namespacedRow)) {
|
|
89
97
|
if (tableData && typeof tableData === `object`) {
|
|
90
|
-
Object.assign(
|
|
98
|
+
Object.assign(
|
|
99
|
+
result,
|
|
100
|
+
extractAllColumnsFromTable(namespacedRow, tableAlias)
|
|
101
|
+
);
|
|
91
102
|
}
|
|
92
103
|
}
|
|
93
104
|
return result;
|
|
94
105
|
}
|
|
95
|
-
function extractAllColumnsFromTable(
|
|
106
|
+
function extractAllColumnsFromTable(namespacedRow, tableAlias) {
|
|
96
107
|
const result = {};
|
|
97
|
-
const tableData =
|
|
108
|
+
const tableData = namespacedRow[tableAlias];
|
|
98
109
|
if (!tableData || typeof tableData !== `object`) {
|
|
99
110
|
return result;
|
|
100
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.cjs","sources":["../../../src/query/select.ts"],"sourcesContent":["import { map } from \"@electric-sql/d2ts\"\nimport {\n
|
|
1
|
+
{"version":3,"file":"select.cjs","sources":["../../../src/query/select.ts"],"sourcesContent":["import { map } from \"@electric-sql/d2ts\"\nimport {\n evaluateOperandOnNamespacedRow,\n extractValueFromNamespacedRow,\n} from \"./extractors\"\nimport type { ConditionOperand, Query } from \"./schema\"\nimport type { KeyedStream, NamespacedAndKeyedStream } from \"../types\"\n\nexport function processSelect(\n pipeline: NamespacedAndKeyedStream,\n query: Query,\n mainTableAlias: string,\n inputs: Record<string, KeyedStream>\n): KeyedStream {\n return pipeline.pipe(\n map(([key, namespacedRow]) => {\n const result: Record<string, unknown> = {}\n\n // Check if this is a grouped result (has no nested table structure)\n // If it's a grouped result, we need to handle it differently\n const isGroupedResult =\n query.groupBy &&\n Object.keys(namespacedRow).some(\n (namespaceKey) =>\n !Object.keys(inputs).includes(namespaceKey) &&\n typeof namespacedRow[namespaceKey] !== `object`\n )\n\n if (!query.select) {\n throw new Error(`Cannot process missing SELECT clause`)\n }\n\n for (const item of query.select) {\n if (typeof item === `string`) {\n // Handle wildcard select - all columns from all tables\n if ((item as string) === `@*`) {\n // For grouped results, just return the row as is\n if (isGroupedResult) {\n Object.assign(result, namespacedRow)\n } else {\n // Extract all columns from all tables\n Object.assign(\n result,\n extractAllColumnsFromAllTables(namespacedRow)\n )\n }\n continue\n }\n\n // Handle @table.* syntax - all columns from a specific table\n if (\n (item as string).startsWith(`@`) &&\n (item as string).endsWith(`.*`)\n ) {\n const tableAlias = (item as string).slice(1, -2) // Remove the '@' and '.*' parts\n\n // For grouped results, check if we have columns from this table\n if (isGroupedResult) {\n // In grouped results, we don't have the nested structure anymore\n // So we can't extract by table. Just continue to the next item.\n continue\n } else {\n // Extract all columns from the specified table\n Object.assign(\n result,\n extractAllColumnsFromTable(namespacedRow, tableAlias)\n )\n }\n continue\n }\n\n // Handle simple column references like \"@table.column\" or \"@column\"\n if ((item as string).startsWith(`@`)) {\n const columnRef = (item as string).substring(1)\n const alias = columnRef\n\n // For grouped results, check if the column is directly in the row first\n if (isGroupedResult && columnRef in namespacedRow) {\n result[alias] = namespacedRow[columnRef]\n } else {\n // Extract the value from the nested structure\n result[alias] = extractValueFromNamespacedRow(\n namespacedRow,\n columnRef,\n mainTableAlias,\n undefined\n )\n }\n\n // If the alias contains a dot (table.column),\n // use just the column part as the field name\n if (alias.includes(`.`)) {\n const columnName = alias.split(`.`)[1]\n result[columnName!] = result[alias]\n delete result[alias]\n }\n }\n } else {\n // Handle aliased columns like { alias: \"@column_name\" }\n for (const [alias, expr] of Object.entries(item)) {\n if (typeof expr === `string` && (expr as string).startsWith(`@`)) {\n const columnRef = (expr as string).substring(1)\n\n // For grouped results, check if the column is directly in the row first\n if (isGroupedResult && columnRef in namespacedRow) {\n result[alias] = namespacedRow[columnRef]\n } else {\n // Extract the value from the nested structure\n result[alias] = extractValueFromNamespacedRow(\n namespacedRow,\n columnRef,\n mainTableAlias,\n undefined\n )\n }\n } else if (typeof expr === `object`) {\n // For grouped results, the aggregate results are already in the row\n if (isGroupedResult && alias in namespacedRow) {\n result[alias] = namespacedRow[alias]\n } else if ((expr as { ORDER_INDEX: unknown }).ORDER_INDEX) {\n result[alias] = namespacedRow[mainTableAlias]![alias]\n } else {\n // This might be a function call\n result[alias] = evaluateOperandOnNamespacedRow(\n namespacedRow,\n expr as ConditionOperand,\n mainTableAlias,\n undefined\n )\n }\n }\n }\n }\n }\n\n return [key, result] as [string, typeof result]\n })\n )\n}\n\n// Helper function to extract all columns from all tables in a nested row\nfunction extractAllColumnsFromAllTables(\n namespacedRow: Record<string, unknown>\n): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n\n // Process each table in the nested row\n for (const [tableAlias, tableData] of Object.entries(namespacedRow)) {\n if (tableData && typeof tableData === `object`) {\n // Add all columns from this table to the result\n // If there are column name conflicts, the last table's columns will overwrite previous ones\n Object.assign(\n result,\n extractAllColumnsFromTable(namespacedRow, tableAlias)\n )\n }\n }\n\n return result\n}\n\n// Helper function to extract all columns from a table in a nested row\nfunction extractAllColumnsFromTable(\n namespacedRow: Record<string, unknown>,\n tableAlias: string\n): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n\n // Get the table data\n const tableData = namespacedRow[tableAlias] as\n | Record<string, unknown>\n | null\n | undefined\n\n if (!tableData || typeof tableData !== `object`) {\n return result\n }\n\n // Add all columns from the table to the result\n for (const [columnName, value] of Object.entries(tableData)) {\n result[columnName] = value\n }\n\n return result\n}\n"],"names":["map","extractValueFromNamespacedRow","evaluateOperandOnNamespacedRow"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { IStreamBuilder } from '@electric-sql/d2ts';
|
|
2
1
|
import { Query } from './schema.cjs';
|
|
3
|
-
|
|
2
|
+
import { KeyedStream, NamespacedAndKeyedStream } from '../types.cjs';
|
|
3
|
+
export declare function processSelect(pipeline: NamespacedAndKeyedStream, query: Query, mainTableAlias: string, inputs: Record<string, KeyedStream>): KeyedStream;
|
|
@@ -12,6 +12,7 @@ function generateUUID() {
|
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
const transactions = [];
|
|
15
|
+
let transactionStack = [];
|
|
15
16
|
function createTransaction(config) {
|
|
16
17
|
if (typeof config.mutationFn === `undefined`) {
|
|
17
18
|
throw `mutationFn is required when creating a transaction`;
|
|
@@ -24,7 +25,6 @@ function createTransaction(config) {
|
|
|
24
25
|
transactions.push(newTransaction);
|
|
25
26
|
return newTransaction;
|
|
26
27
|
}
|
|
27
|
-
let transactionStack = [];
|
|
28
28
|
function getActiveTransaction() {
|
|
29
29
|
if (transactionStack.length > 0) {
|
|
30
30
|
return transactionStack.slice(-1)[0];
|
|
@@ -38,6 +38,12 @@ function registerTransaction(tx) {
|
|
|
38
38
|
function unregisterTransaction(tx) {
|
|
39
39
|
transactionStack = transactionStack.filter((t) => t.id !== tx.id);
|
|
40
40
|
}
|
|
41
|
+
function removeFromPendingList(tx) {
|
|
42
|
+
const index = transactions.findIndex((t) => t.id === tx.id);
|
|
43
|
+
if (index !== -1) {
|
|
44
|
+
transactions.splice(index, 1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
41
47
|
class Transaction {
|
|
42
48
|
constructor(config) {
|
|
43
49
|
this.id = config.id;
|
|
@@ -51,6 +57,9 @@ class Transaction {
|
|
|
51
57
|
}
|
|
52
58
|
setState(newState) {
|
|
53
59
|
this.state = newState;
|
|
60
|
+
if (newState === `completed` || newState === `failed`) {
|
|
61
|
+
removeFromPendingList(this);
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
mutate(callback) {
|
|
56
65
|
if (this.state !== `pending`) {
|
|
@@ -87,11 +96,11 @@ class Transaction {
|
|
|
87
96
|
}
|
|
88
97
|
this.setState(`failed`);
|
|
89
98
|
if (!isSecondaryRollback) {
|
|
90
|
-
const
|
|
91
|
-
this.mutations.forEach((m) =>
|
|
92
|
-
transactions
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
const mutationIds = /* @__PURE__ */ new Set();
|
|
100
|
+
this.mutations.forEach((m) => mutationIds.add(m.key));
|
|
101
|
+
for (const t of transactions) {
|
|
102
|
+
t.state === `pending` && t.mutations.some((m) => mutationIds.has(m.key)) && t.rollback({ isSecondaryRollback: true });
|
|
103
|
+
}
|
|
95
104
|
}
|
|
96
105
|
this.isPersisted.reject((_a = this.error) == null ? void 0 : _a.error);
|
|
97
106
|
this.touchCollection();
|
|
@@ -100,13 +109,13 @@ class Transaction {
|
|
|
100
109
|
// Tell collection that something has changed with the transaction
|
|
101
110
|
touchCollection() {
|
|
102
111
|
const hasCalled = /* @__PURE__ */ new Set();
|
|
103
|
-
this.mutations
|
|
112
|
+
for (const mutation of this.mutations) {
|
|
104
113
|
if (!hasCalled.has(mutation.collection.id)) {
|
|
105
114
|
mutation.collection.transactions.setState((state) => state);
|
|
106
115
|
mutation.collection.commitPendingTransactions();
|
|
107
116
|
hasCalled.add(mutation.collection.id);
|
|
108
117
|
}
|
|
109
|
-
}
|
|
118
|
+
}
|
|
110
119
|
}
|
|
111
120
|
async commit() {
|
|
112
121
|
if (this.state !== `pending`) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transactions.cjs","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from \"./deferred\"\nimport type { Deferred } from \"./deferred\"\nimport type {\n PendingMutation,\n TransactionConfig,\n TransactionState,\n} from \"./types\"\n\nfunction generateUUID() {\n // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)\n if (\n typeof crypto !== `undefined` &&\n typeof crypto.randomUUID === `function`\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older environments\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0\n const v = c === `x` ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nconst transactions: Array<Transaction> = []\n\nexport function createTransaction(config: TransactionConfig): Transaction {\n if (typeof config.mutationFn === `undefined`) {\n throw `mutationFn is required when creating a transaction`\n }\n\n let transactionId = config.id\n if (!transactionId) {\n transactionId = generateUUID()\n }\n const newTransaction = new Transaction({ ...config, id: transactionId })\n transactions.push(newTransaction)\n\n return newTransaction\n}\n\
|
|
1
|
+
{"version":3,"file":"transactions.cjs","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from \"./deferred\"\nimport type { Deferred } from \"./deferred\"\nimport type {\n PendingMutation,\n TransactionConfig,\n TransactionState,\n} from \"./types\"\n\nfunction generateUUID() {\n // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)\n if (\n typeof crypto !== `undefined` &&\n typeof crypto.randomUUID === `function`\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older environments\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0\n const v = c === `x` ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nconst transactions: Array<Transaction> = []\nlet transactionStack: Array<Transaction> = []\n\nexport function createTransaction(config: TransactionConfig): Transaction {\n if (typeof config.mutationFn === `undefined`) {\n throw `mutationFn is required when creating a transaction`\n }\n\n let transactionId = config.id\n if (!transactionId) {\n transactionId = generateUUID()\n }\n const newTransaction = new Transaction({ ...config, id: transactionId })\n\n transactions.push(newTransaction)\n\n return newTransaction\n}\n\nexport function getActiveTransaction(): Transaction | undefined {\n if (transactionStack.length > 0) {\n return transactionStack.slice(-1)[0]\n } else {\n return undefined\n }\n}\n\nfunction registerTransaction(tx: Transaction) {\n transactionStack.push(tx)\n}\n\nfunction unregisterTransaction(tx: Transaction) {\n transactionStack = transactionStack.filter((t) => t.id !== tx.id)\n}\n\nfunction removeFromPendingList(tx: Transaction) {\n const index = transactions.findIndex((t) => t.id === tx.id)\n if (index !== -1) {\n transactions.splice(index, 1)\n }\n}\n\nexport class Transaction {\n public id: string\n public state: TransactionState\n public mutationFn\n public mutations: Array<PendingMutation<any>>\n public isPersisted: Deferred<Transaction>\n public autoCommit: boolean\n public createdAt: Date\n public metadata: Record<string, unknown>\n public error?: {\n message: string\n error: Error\n }\n\n constructor(config: TransactionConfig) {\n this.id = config.id!\n this.mutationFn = config.mutationFn\n this.state = `pending`\n this.mutations = []\n this.isPersisted = createDeferred()\n this.autoCommit = config.autoCommit ?? true\n this.createdAt = new Date()\n this.metadata = config.metadata ?? {}\n }\n\n setState(newState: TransactionState) {\n this.state = newState\n\n if (newState === `completed` || newState === `failed`) {\n removeFromPendingList(this)\n }\n }\n\n mutate(callback: () => void): Transaction {\n if (this.state !== `pending`) {\n throw `You can no longer call .mutate() as the transaction is no longer pending`\n }\n\n registerTransaction(this)\n try {\n callback()\n } finally {\n unregisterTransaction(this)\n }\n\n if (this.autoCommit) {\n this.commit()\n }\n\n return this\n }\n\n applyMutations(mutations: Array<PendingMutation<any>>): void {\n for (const newMutation of mutations) {\n const existingIndex = this.mutations.findIndex(\n (m) => m.key === newMutation.key\n )\n\n if (existingIndex >= 0) {\n // Replace existing mutation\n this.mutations[existingIndex] = newMutation\n } else {\n // Insert new mutation\n this.mutations.push(newMutation)\n }\n }\n }\n\n rollback(config?: { isSecondaryRollback?: boolean }): Transaction {\n const isSecondaryRollback = config?.isSecondaryRollback ?? false\n if (this.state === `completed`) {\n throw `You can no longer call .rollback() as the transaction is already completed`\n }\n\n this.setState(`failed`)\n\n // See if there's any other transactions w/ mutations on the same ids\n // and roll them back as well.\n if (!isSecondaryRollback) {\n const mutationIds = new Set()\n this.mutations.forEach((m) => mutationIds.add(m.key))\n for (const t of transactions) {\n t.state === `pending` &&\n t.mutations.some((m) => mutationIds.has(m.key)) &&\n t.rollback({ isSecondaryRollback: true })\n }\n }\n\n // Reject the promise\n this.isPersisted.reject(this.error?.error)\n this.touchCollection()\n\n return this\n }\n\n // Tell collection that something has changed with the transaction\n touchCollection(): void {\n const hasCalled = new Set()\n for (const mutation of this.mutations) {\n if (!hasCalled.has(mutation.collection.id)) {\n mutation.collection.transactions.setState((state) => state)\n mutation.collection.commitPendingTransactions()\n hasCalled.add(mutation.collection.id)\n }\n }\n }\n\n async commit(): Promise<Transaction> {\n if (this.state !== `pending`) {\n throw `You can no longer call .commit() as the transaction is no longer pending`\n }\n\n this.setState(`persisting`)\n\n if (this.mutations.length === 0) {\n this.setState(`completed`)\n }\n\n // Run mutationFn\n try {\n await this.mutationFn({ transaction: this })\n\n this.setState(`completed`)\n this.touchCollection()\n\n this.isPersisted.resolve(this)\n } catch (error) {\n // Update transaction with error information\n this.error = {\n message: error instanceof Error ? error.message : String(error),\n error: error instanceof Error ? error : new Error(String(error)),\n }\n\n // rollback the transaction\n return this.rollback()\n }\n\n return this\n }\n}\n"],"names":["createDeferred"],"mappings":"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"}
|
package/dist/cjs/types.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IStreamBuilder } from '@electric-sql/d2ts';
|
|
1
2
|
import { Collection } from './collection.cjs';
|
|
2
3
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
4
|
import { Transaction } from './transactions.cjs';
|
|
@@ -11,7 +12,7 @@ export interface PendingMutation<T extends object = Record<string, unknown>> {
|
|
|
11
12
|
original: Record<string, unknown>;
|
|
12
13
|
modified: Record<string, unknown>;
|
|
13
14
|
changes: Record<string, unknown>;
|
|
14
|
-
key:
|
|
15
|
+
key: any;
|
|
15
16
|
type: OperationType;
|
|
16
17
|
metadata: unknown;
|
|
17
18
|
syncMetadata: Record<string, unknown>;
|
|
@@ -36,7 +37,7 @@ export interface TransactionConfig {
|
|
|
36
37
|
}
|
|
37
38
|
export type { Transaction };
|
|
38
39
|
type Value<TExtensions = never> = string | number | boolean | bigint | null | TExtensions | Array<Value<TExtensions>> | {
|
|
39
|
-
[key: string]: Value<TExtensions>;
|
|
40
|
+
[key: string | number | symbol]: Value<TExtensions>;
|
|
40
41
|
};
|
|
41
42
|
export type Row<TExtensions = never> = Record<string, Value<TExtensions>>;
|
|
42
43
|
export type OperationType = `insert` | `update` | `delete`;
|
|
@@ -44,17 +45,17 @@ export interface SyncConfig<T extends object = Record<string, unknown>> {
|
|
|
44
45
|
sync: (params: {
|
|
45
46
|
collection: Collection<T>;
|
|
46
47
|
begin: () => void;
|
|
47
|
-
write: (message: ChangeMessage<T
|
|
48
|
+
write: (message: Omit<ChangeMessage<T>, `key`>) => void;
|
|
48
49
|
commit: () => void;
|
|
49
50
|
}) => void;
|
|
50
51
|
/**
|
|
51
52
|
* Get the sync metadata for insert operations
|
|
52
|
-
* @returns Record containing
|
|
53
|
+
* @returns Record containing relation information
|
|
53
54
|
*/
|
|
54
55
|
getSyncMetadata?: () => Record<string, unknown>;
|
|
55
56
|
}
|
|
56
57
|
export interface ChangeMessage<T extends object = Record<string, unknown>> {
|
|
57
|
-
key:
|
|
58
|
+
key: any;
|
|
58
59
|
value: T;
|
|
59
60
|
previousValue?: T;
|
|
60
61
|
type: OperationType;
|
|
@@ -83,12 +84,45 @@ export interface OperationConfig {
|
|
|
83
84
|
metadata?: Record<string, unknown>;
|
|
84
85
|
}
|
|
85
86
|
export interface InsertConfig {
|
|
86
|
-
key?: string | Array<string | undefined>;
|
|
87
87
|
metadata?: Record<string, unknown>;
|
|
88
88
|
}
|
|
89
89
|
export interface CollectionConfig<T extends object = Record<string, unknown>> {
|
|
90
|
-
id
|
|
90
|
+
id?: string;
|
|
91
91
|
sync: SyncConfig<T>;
|
|
92
92
|
schema?: StandardSchema<T>;
|
|
93
|
+
/**
|
|
94
|
+
* Function to extract the ID from an object
|
|
95
|
+
* This is required for update/delete operations which now only accept IDs
|
|
96
|
+
* @param item The item to extract the ID from
|
|
97
|
+
* @returns The ID string for the item
|
|
98
|
+
* @example
|
|
99
|
+
* // For a collection with a 'uuid' field as the primary key
|
|
100
|
+
* getId: (item) => item.uuid
|
|
101
|
+
*/
|
|
102
|
+
getId: (item: T) => any;
|
|
93
103
|
}
|
|
94
104
|
export type ChangesPayload<T extends object = Record<string, unknown>> = Array<ChangeMessage<T>>;
|
|
105
|
+
/**
|
|
106
|
+
* An input row from a collection
|
|
107
|
+
*/
|
|
108
|
+
export type InputRow = [unknown, Record<string, unknown>];
|
|
109
|
+
/**
|
|
110
|
+
* A keyed stream is a stream of rows
|
|
111
|
+
* This is used as the inputs from a collection to a query
|
|
112
|
+
*/
|
|
113
|
+
export type KeyedStream = IStreamBuilder<InputRow>;
|
|
114
|
+
/**
|
|
115
|
+
* A namespaced row is a row withing a pipeline that had each table wrapped in its alias
|
|
116
|
+
*/
|
|
117
|
+
export type NamespacedRow = Record<string, Record<string, unknown>>;
|
|
118
|
+
/**
|
|
119
|
+
* A keyed namespaced row is a row with a key and a namespaced row
|
|
120
|
+
* This is the main representation of a row in a query pipeline
|
|
121
|
+
*/
|
|
122
|
+
export type KeyedNamespacedRow = [unknown, NamespacedRow];
|
|
123
|
+
/**
|
|
124
|
+
* A namespaced and keyed stream is a stream of rows
|
|
125
|
+
* This is used throughout a query pipeline and as the output from a query without
|
|
126
|
+
* a `select` clause.
|
|
127
|
+
*/
|
|
128
|
+
export type NamespacedAndKeyedStream = IStreamBuilder<KeyedNamespacedRow>;
|
package/dist/esm/collection.d.ts
CHANGED
|
@@ -2,6 +2,14 @@ import { Derived, Store } from '@tanstack/store';
|
|
|
2
2
|
import { SortedMap } from './SortedMap.js';
|
|
3
3
|
import { ChangeMessage, CollectionConfig, InsertConfig, OperationConfig, OptimisticChangeMessage, Transaction } from './types.js';
|
|
4
4
|
export declare const collectionsStore: Store<Map<string, Collection<any>>, (cb: Map<string, Collection<any>>) => Map<string, Collection<any>>>;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a new Collection instance with the given configuration
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of items in the collection
|
|
9
|
+
* @param config - Configuration for the collection, including id and sync
|
|
10
|
+
* @returns A new Collection instance
|
|
11
|
+
*/
|
|
12
|
+
export declare function createCollection<T extends object = Record<string, unknown>>(config: CollectionConfig<T>): Collection<T>;
|
|
5
13
|
/**
|
|
6
14
|
* Preloads a collection with the given configuration
|
|
7
15
|
* Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)
|
|
@@ -55,7 +63,6 @@ export declare class Collection<T extends object = Record<string, unknown>> {
|
|
|
55
63
|
private syncedKeys;
|
|
56
64
|
config: CollectionConfig<T>;
|
|
57
65
|
private hasReceivedFirstCommit;
|
|
58
|
-
objectKeyMap: WeakMap<object, string>;
|
|
59
66
|
private onFirstCommitCallbacks;
|
|
60
67
|
/**
|
|
61
68
|
* Register a callback to be executed on the next commit
|
|
@@ -63,22 +70,23 @@ export declare class Collection<T extends object = Record<string, unknown>> {
|
|
|
63
70
|
* @param callback Function to call after the next commit
|
|
64
71
|
*/
|
|
65
72
|
onFirstCommit(callback: () => void): void;
|
|
66
|
-
id:
|
|
73
|
+
id: string;
|
|
67
74
|
/**
|
|
68
75
|
* Creates a new Collection instance
|
|
69
76
|
*
|
|
70
77
|
* @param config - Configuration object for the collection
|
|
71
78
|
* @throws Error if sync config is missing
|
|
72
79
|
*/
|
|
73
|
-
constructor(config
|
|
80
|
+
constructor(config: CollectionConfig<T>);
|
|
74
81
|
/**
|
|
75
82
|
* Attempts to commit pending synced transactions if there are no active transactions
|
|
76
83
|
* This method processes operations from pending transactions and applies them to the synced data
|
|
77
84
|
*/
|
|
78
85
|
commitPendingTransactions: () => void;
|
|
79
86
|
private ensureStandardSchema;
|
|
87
|
+
private getKeyFromId;
|
|
88
|
+
generateObjectKey(id: any, item: any): string;
|
|
80
89
|
private validateData;
|
|
81
|
-
private generateKey;
|
|
82
90
|
/**
|
|
83
91
|
* Inserts one or more items into the collection
|
|
84
92
|
* @param items - Single item or array of items to insert
|
|
@@ -118,24 +126,43 @@ export declare class Collection<T extends object = Record<string, unknown>> {
|
|
|
118
126
|
* // Update with metadata
|
|
119
127
|
* update(todo, { metadata: { reason: "user update" } }, (draft) => { draft.text = "Updated text" })
|
|
120
128
|
*/
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Updates one or more items in the collection using a callback function
|
|
131
|
+
* @param ids - Single ID or array of IDs to update
|
|
132
|
+
* @param configOrCallback - Either update configuration or update callback
|
|
133
|
+
* @param maybeCallback - Update callback if config was provided
|
|
134
|
+
* @returns A Transaction object representing the update operation(s)
|
|
135
|
+
* @throws {SchemaValidationError} If the updated data fails schema validation
|
|
136
|
+
* @example
|
|
137
|
+
* // Update a single item
|
|
138
|
+
* update("todo-1", (draft) => { draft.completed = true })
|
|
139
|
+
*
|
|
140
|
+
* // Update multiple items
|
|
141
|
+
* update(["todo-1", "todo-2"], (drafts) => {
|
|
142
|
+
* drafts.forEach(draft => { draft.completed = true })
|
|
143
|
+
* })
|
|
144
|
+
*
|
|
145
|
+
* // Update with metadata
|
|
146
|
+
* update("todo-1", { metadata: { reason: "user update" } }, (draft) => { draft.text = "Updated text" })
|
|
147
|
+
*/
|
|
148
|
+
update<TItem extends object = T>(id: unknown, configOrCallback: ((draft: TItem) => void) | OperationConfig, maybeCallback?: (draft: TItem) => void): Transaction;
|
|
149
|
+
update<TItem extends object = T>(ids: Array<unknown>, configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig, maybeCallback?: (draft: Array<TItem>) => void): Transaction;
|
|
123
150
|
/**
|
|
124
151
|
* Deletes one or more items from the collection
|
|
125
|
-
* @param
|
|
152
|
+
* @param ids - Single ID or array of IDs to delete
|
|
126
153
|
* @param config - Optional configuration including metadata
|
|
127
154
|
* @returns A Transaction object representing the delete operation(s)
|
|
128
155
|
* @example
|
|
129
156
|
* // Delete a single item
|
|
130
|
-
* delete(todo)
|
|
157
|
+
* delete("todo-1")
|
|
131
158
|
*
|
|
132
159
|
* // Delete multiple items
|
|
133
|
-
* delete([
|
|
160
|
+
* delete(["todo-1", "todo-2"])
|
|
134
161
|
*
|
|
135
162
|
* // Delete with metadata
|
|
136
|
-
* delete(todo, { metadata: { reason: "completed" } })
|
|
163
|
+
* delete("todo-1", { metadata: { reason: "completed" } })
|
|
137
164
|
*/
|
|
138
|
-
delete: (
|
|
165
|
+
delete: (ids: Array<string> | string, config?: OperationConfig) => Transaction;
|
|
139
166
|
/**
|
|
140
167
|
* Gets the current state of the collection as a Map
|
|
141
168
|
*
|