@tanstack/powersync-db-collection 0.0.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/package.json +71 -0
- package/src/PendingOperationStore.ts +54 -0
- package/src/PowerSyncTransactor.ts +271 -0
- package/src/definitions.ts +274 -0
- package/src/helpers.ts +112 -0
- package/src/index.ts +3 -0
- package/src/powersync.ts +479 -0
- package/src/schema.ts +100 -0
- package/src/serialization.ts +101 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ColumnType } from "@powersync/common"
|
|
2
|
+
import type { Table } from "@powersync/common"
|
|
3
|
+
import type { CustomSQLiteSerializer } from "./definitions"
|
|
4
|
+
import type {
|
|
5
|
+
ExtractedTable,
|
|
6
|
+
ExtractedTableColumns,
|
|
7
|
+
MapBaseColumnType,
|
|
8
|
+
} from "./helpers"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Serializes an object for persistence to a SQLite table, mapping its values to appropriate SQLite types.
|
|
12
|
+
*
|
|
13
|
+
* This function takes an object representing a row, a table schema, and an optional custom serializer map.
|
|
14
|
+
* It returns a new object with values transformed to be compatible with SQLite column types.
|
|
15
|
+
*
|
|
16
|
+
* ## Generics
|
|
17
|
+
* - `TOutput`: The shape of the input object, typically matching the row data.
|
|
18
|
+
* - `TTable`: The table schema, which must match the keys of `TOutput`.
|
|
19
|
+
*
|
|
20
|
+
* ## Parameters
|
|
21
|
+
* - `value`: The object to serialize (row data).
|
|
22
|
+
* - `tableSchema`: The schema describing the SQLite table columns and types.
|
|
23
|
+
* - `customSerializer`: An optional map of custom serialization functions for specific keys.
|
|
24
|
+
*
|
|
25
|
+
* ## Behavior
|
|
26
|
+
* - For each key in `value`, finds the corresponding column in `tableSchema`.
|
|
27
|
+
* - If a custom serializer is provided for a key, it is used to transform the value.
|
|
28
|
+
* - Otherwise, values are mapped according to the column type:
|
|
29
|
+
* - `TEXT`: Strings are passed through; Dates are converted to ISO strings; other types are JSON-stringified.
|
|
30
|
+
* - `INTEGER`/`REAL`: Numbers are passed through; booleans are mapped to 1/0; other types are coerced to numbers.
|
|
31
|
+
* - Throws if a column type is unknown or a value cannot be converted.
|
|
32
|
+
*
|
|
33
|
+
* ## Returns
|
|
34
|
+
* - An object with the same keys as `value`, with values transformed for SQLite compatibility.
|
|
35
|
+
*
|
|
36
|
+
* ## Errors
|
|
37
|
+
* - Throws if a key in `value` does not exist in the schema.
|
|
38
|
+
* - Throws if a value cannot be converted to the required SQLite type.
|
|
39
|
+
*/
|
|
40
|
+
export function serializeForSQLite<
|
|
41
|
+
TOutput extends Record<string, unknown>,
|
|
42
|
+
// The keys should match
|
|
43
|
+
TTable extends Table<MapBaseColumnType<TOutput>> = Table<
|
|
44
|
+
MapBaseColumnType<TOutput>
|
|
45
|
+
>,
|
|
46
|
+
>(
|
|
47
|
+
value: TOutput,
|
|
48
|
+
tableSchema: TTable,
|
|
49
|
+
customSerializer: Partial<
|
|
50
|
+
CustomSQLiteSerializer<TOutput, ExtractedTableColumns<TTable>>
|
|
51
|
+
> = {}
|
|
52
|
+
): ExtractedTable<TTable> {
|
|
53
|
+
return Object.fromEntries(
|
|
54
|
+
Object.entries(value).map(([key, value]) => {
|
|
55
|
+
// First get the output schema type
|
|
56
|
+
const outputType =
|
|
57
|
+
key == `id`
|
|
58
|
+
? ColumnType.TEXT
|
|
59
|
+
: tableSchema.columns.find((column) => column.name == key)?.type
|
|
60
|
+
if (!outputType) {
|
|
61
|
+
throw new Error(`Could not find schema for ${key} column.`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (value == null) {
|
|
65
|
+
return [key, value]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const customTransform = customSerializer[key]
|
|
69
|
+
if (customTransform) {
|
|
70
|
+
return [key, customTransform(value as TOutput[string])]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Map to the output
|
|
74
|
+
switch (outputType) {
|
|
75
|
+
case ColumnType.TEXT:
|
|
76
|
+
if (typeof value == `string`) {
|
|
77
|
+
return [key, value]
|
|
78
|
+
} else if (value instanceof Date) {
|
|
79
|
+
return [key, value.toISOString()]
|
|
80
|
+
} else {
|
|
81
|
+
return [key, JSON.stringify(value)]
|
|
82
|
+
}
|
|
83
|
+
case ColumnType.INTEGER:
|
|
84
|
+
case ColumnType.REAL:
|
|
85
|
+
if (typeof value == `number`) {
|
|
86
|
+
return [key, value]
|
|
87
|
+
} else if (typeof value == `boolean`) {
|
|
88
|
+
return [key, value ? 1 : 0]
|
|
89
|
+
} else {
|
|
90
|
+
const numberValue = Number(value)
|
|
91
|
+
if (isNaN(numberValue)) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Could not convert ${key}=${value} to a number for SQLite`
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
return [key, numberValue]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
)
|
|
101
|
+
}
|