@evolu/common 5.4.8 → 6.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/README.md +30 -34
- package/dist/src/Array.d.ts +17 -0
- package/dist/src/Array.d.ts.map +1 -0
- package/dist/src/Array.js +12 -0
- package/dist/src/Assert.d.ts +68 -0
- package/dist/src/Assert.d.ts.map +1 -0
- package/dist/src/Assert.js +77 -0
- package/dist/src/BigInt.d.ts +20 -0
- package/dist/src/BigInt.d.ts.map +1 -0
- package/dist/src/BigInt.js +18 -0
- package/dist/src/Buffer.d.ts +92 -0
- package/dist/src/Buffer.d.ts.map +1 -0
- package/dist/src/Buffer.js +62 -0
- package/dist/src/Callbacks.d.ts +20 -0
- package/dist/src/Callbacks.d.ts.map +1 -0
- package/dist/src/Callbacks.js +18 -0
- package/dist/src/Console.d.ts +78 -0
- package/dist/src/Console.d.ts.map +1 -0
- package/dist/src/Console.js +103 -0
- package/dist/src/Crypto.d.ts +72 -39
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +89 -54
- package/dist/src/Eq.d.ts +97 -0
- package/dist/src/Eq.d.ts.map +1 -0
- package/dist/src/Eq.js +167 -0
- package/dist/src/Error.d.ts +14 -10
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +43 -11
- package/dist/src/Evolu/Config.d.ts +69 -0
- package/dist/src/Evolu/Config.d.ts.map +1 -0
- package/dist/src/Evolu/Config.js +9 -0
- package/dist/src/Evolu/Db.d.ts +126 -0
- package/dist/src/Evolu/Db.d.ts.map +1 -0
- package/dist/src/Evolu/Db.js +774 -0
- package/dist/src/Evolu/Diff.d.ts +43 -0
- package/dist/src/Evolu/Diff.d.ts.map +1 -0
- package/dist/src/Evolu/Diff.js +95 -0
- package/dist/src/Evolu/Evolu.d.ts +334 -0
- package/dist/src/Evolu/Evolu.d.ts.map +1 -0
- package/dist/src/Evolu/Evolu.js +434 -0
- package/dist/src/Evolu/Internal.d.ts +26 -0
- package/dist/src/Evolu/Internal.d.ts.map +1 -0
- package/dist/src/Evolu/Internal.js +25 -0
- package/dist/src/Evolu/Kysely.d.ts +6 -0
- package/dist/src/Evolu/Kysely.d.ts.map +1 -0
- package/dist/src/Evolu/Kysely.js +21 -0
- package/dist/src/Evolu/Owner.d.ts +155 -0
- package/dist/src/Evolu/Owner.d.ts.map +1 -0
- package/dist/src/Evolu/Owner.js +126 -0
- package/dist/src/Evolu/Platform.d.ts +23 -0
- package/dist/src/Evolu/Platform.d.ts.map +1 -0
- package/dist/src/Evolu/Platform.js +1 -0
- package/dist/src/Evolu/Protocol.d.ts +401 -0
- package/dist/src/Evolu/Protocol.d.ts.map +1 -0
- package/dist/src/Evolu/Protocol.js +1151 -0
- package/dist/src/Evolu/Public.d.ts +18 -0
- package/dist/src/Evolu/Public.d.ts.map +1 -0
- package/dist/src/Evolu/Public.js +11 -0
- package/dist/src/Evolu/PublicKysely.d.ts +148 -0
- package/dist/src/Evolu/PublicKysely.d.ts.map +1 -0
- package/dist/src/Evolu/PublicKysely.js +185 -0
- package/dist/src/Evolu/Query.d.ts +63 -0
- package/dist/src/Evolu/Query.d.ts.map +1 -0
- package/dist/src/Evolu/Query.js +61 -0
- package/dist/src/Evolu/Relay.d.ts +13 -0
- package/dist/src/Evolu/Relay.d.ts.map +1 -0
- package/dist/src/Evolu/Relay.js +109 -0
- package/dist/src/Evolu/Schema.d.ts +201 -0
- package/dist/src/Evolu/Schema.d.ts.map +1 -0
- package/dist/src/Evolu/Schema.js +150 -0
- package/dist/src/Evolu/Storage.d.ts +49 -0
- package/dist/src/Evolu/Storage.d.ts.map +1 -0
- package/dist/src/Evolu/Storage.js +1111 -0
- package/dist/src/Evolu/Sync.d.ts +59 -0
- package/dist/src/Evolu/Sync.d.ts.map +1 -0
- package/dist/src/Evolu/Sync.js +29 -0
- package/dist/src/Evolu/Timestamp.d.ts +106 -0
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -0
- package/dist/src/Evolu/Timestamp.js +179 -0
- package/dist/src/Function.d.ts +54 -0
- package/dist/src/Function.d.ts.map +1 -0
- package/dist/src/Function.js +38 -0
- package/dist/src/ManyToManyMap.d.ts +26 -0
- package/dist/src/ManyToManyMap.d.ts.map +1 -0
- package/dist/src/ManyToManyMap.js +92 -0
- package/dist/src/NanoId.d.ts +27 -0
- package/dist/src/NanoId.d.ts.map +1 -0
- package/dist/src/NanoId.js +6 -0
- package/dist/src/Number.d.ts +42 -0
- package/dist/src/Number.d.ts.map +1 -0
- package/dist/src/Number.js +55 -0
- package/dist/src/Object.d.ts +35 -0
- package/dist/src/Object.d.ts.map +1 -0
- package/dist/src/Object.js +36 -0
- package/dist/src/Order.d.ts +90 -0
- package/dist/src/Order.d.ts.map +1 -0
- package/dist/src/Order.js +85 -0
- package/dist/src/Promise.d.ts +180 -0
- package/dist/src/Promise.d.ts.map +1 -0
- package/dist/src/Promise.js +176 -0
- package/dist/src/Random.d.ts +52 -0
- package/dist/src/Random.d.ts.map +1 -0
- package/dist/src/Random.js +29 -0
- package/dist/src/Ref.d.ts +40 -0
- package/dist/src/Ref.d.ts.map +1 -0
- package/dist/src/Ref.js +13 -0
- package/dist/src/Result.d.ts +421 -0
- package/dist/src/Result.d.ts.map +1 -0
- package/dist/src/Result.js +357 -0
- package/dist/src/Skiplist.d.ts +23 -0
- package/dist/src/Skiplist.d.ts.map +1 -0
- package/dist/src/Skiplist.js +58 -0
- package/dist/src/Sqlite.d.ts +116 -52
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +183 -67
- package/dist/src/Store.d.ts +45 -8
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +33 -17
- package/dist/src/String.d.ts +2 -0
- package/dist/src/String.d.ts.map +1 -0
- package/dist/src/String.js +14 -0
- package/dist/src/Time.d.ts +20 -0
- package/dist/src/Time.d.ts.map +1 -0
- package/dist/src/Time.js +25 -0
- package/dist/src/Type.d.ts +1937 -0
- package/dist/src/Type.d.ts.map +1 -0
- package/dist/src/Type.js +2002 -0
- package/dist/src/Types.d.ts +188 -0
- package/dist/src/Types.d.ts.map +1 -0
- package/dist/src/Types.js +6 -0
- package/dist/src/WebSocket.d.ts +112 -0
- package/dist/src/WebSocket.d.ts.map +1 -0
- package/dist/src/WebSocket.js +139 -0
- package/dist/src/Worker.d.ts +44 -0
- package/dist/src/Worker.d.ts.map +1 -0
- package/dist/src/Worker.js +66 -0
- package/dist/src/index.d.ts +24 -11
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +24 -11
- package/package.json +29 -38
- package/src/Array.ts +39 -0
- package/src/Assert.ts +116 -0
- package/src/BigInt.ts +29 -0
- package/src/Buffer.ts +175 -0
- package/src/Callbacks.ts +43 -0
- package/src/Console.ts +159 -0
- package/src/Crypto.ts +169 -115
- package/src/Eq.ts +204 -0
- package/src/Error.ts +57 -20
- package/src/Evolu/Config.ts +83 -0
- package/src/Evolu/Db.ts +1275 -0
- package/src/Evolu/Diff.ts +142 -0
- package/src/Evolu/Evolu.ts +947 -0
- package/src/Evolu/Internal.ts +26 -0
- package/src/Evolu/Kysely.ts +38 -0
- package/src/Evolu/Owner.ts +296 -0
- package/src/Evolu/Platform.ts +27 -0
- package/src/Evolu/Protocol.ts +1857 -0
- package/src/Evolu/Public.ts +43 -0
- package/src/Evolu/PublicKysely.ts +240 -0
- package/src/Evolu/Query.ts +167 -0
- package/src/Evolu/Relay.ts +142 -0
- package/src/Evolu/Schema.ts +417 -0
- package/src/Evolu/Storage.ts +1281 -0
- package/src/Evolu/Sync.ts +105 -0
- package/src/Evolu/Timestamp.ts +311 -0
- package/src/Function.ts +58 -0
- package/src/ManyToManyMap.ts +140 -0
- package/src/NanoId.ts +39 -0
- package/src/Number.ts +90 -0
- package/src/Object.ts +64 -0
- package/src/Order.ts +113 -0
- package/src/Promise.ts +295 -0
- package/src/Random.ts +68 -0
- package/src/Ref.ts +63 -0
- package/src/Result.ts +453 -0
- package/src/Skiplist.ts +102 -0
- package/src/Sqlite.ts +366 -153
- package/src/Store.ts +79 -36
- package/src/String.ts +10 -0
- package/src/Time.ts +36 -0
- package/src/Type.ts +3978 -0
- package/src/Types.ts +209 -0
- package/src/WebSocket.ts +273 -0
- package/src/Worker.ts +129 -0
- package/src/index.ts +24 -11
- package/dist/src/Config.d.ts +0 -56
- package/dist/src/Config.d.ts.map +0 -1
- package/dist/src/Config.js +0 -39
- package/dist/src/Crdt.d.ts +0 -89
- package/dist/src/Crdt.d.ts.map +0 -1
- package/dist/src/Crdt.js +0 -181
- package/dist/src/Db.d.ts +0 -107
- package/dist/src/Db.d.ts.map +0 -1
- package/dist/src/Db.js +0 -443
- package/dist/src/Diff.d.ts +0 -27
- package/dist/src/Diff.d.ts.map +0 -1
- package/dist/src/Diff.js +0 -84
- package/dist/src/Evolu.d.ts +0 -426
- package/dist/src/Evolu.d.ts.map +0 -1
- package/dist/src/Evolu.js +0 -333
- package/dist/src/Model.d.ts +0 -141
- package/dist/src/Model.d.ts.map +0 -1
- package/dist/src/Model.js +0 -125
- package/dist/src/Murmurhash.d.ts +0 -2
- package/dist/src/Murmurhash.d.ts.map +0 -1
- package/dist/src/Murmurhash.js +0 -60
- package/dist/src/Owner.d.ts +0 -33
- package/dist/src/Owner.d.ts.map +0 -1
- package/dist/src/Owner.js +0 -26
- package/dist/src/Platform.d.ts +0 -37
- package/dist/src/Platform.d.ts.map +0 -1
- package/dist/src/Platform.js +0 -11
- package/dist/src/Protobuf.d.ts +0 -81
- package/dist/src/Protobuf.d.ts.map +0 -1
- package/dist/src/Protobuf.js +0 -92
- package/dist/src/Public.d.ts +0 -13
- package/dist/src/Public.d.ts.map +0 -1
- package/dist/src/Public.js +0 -6
- package/dist/src/Socket.d.ts +0 -8
- package/dist/src/Socket.d.ts.map +0 -1
- package/dist/src/Socket.js +0 -51
- package/dist/src/Sql.d.ts +0 -12
- package/dist/src/Sql.d.ts.map +0 -1
- package/dist/src/Sql.js +0 -30
- package/dist/src/Sync.d.ts +0 -70
- package/dist/src/Sync.d.ts.map +0 -1
- package/dist/src/Sync.js +0 -127
- package/src/Config.ts +0 -119
- package/src/Crdt.ts +0 -361
- package/src/Db.ts +0 -955
- package/src/Diff.ts +0 -114
- package/src/Evolu.ts +0 -1016
- package/src/Model.ts +0 -233
- package/src/Murmurhash.ts +0 -70
- package/src/Owner.ts +0 -69
- package/src/Platform.ts +0 -47
- package/src/Protobuf.ts +0 -155
- package/src/Public.ts +0 -12
- package/src/Socket.ts +0 -83
- package/src/Sql.ts +0 -41
- package/src/Sync.ts +0 -315
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 💾
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { createEvolu } from "./Evolu.js";
|
|
8
|
+
export type {
|
|
9
|
+
Evolu,
|
|
10
|
+
EvoluConfigWithInitialData,
|
|
11
|
+
EvoluDeps,
|
|
12
|
+
EvoluError,
|
|
13
|
+
} from "./Evolu.js";
|
|
14
|
+
export * from "./Owner.js";
|
|
15
|
+
export { binaryIdToId, idToBinaryId } from "./Protocol.js";
|
|
16
|
+
export type { BinaryId } from "./Protocol.js";
|
|
17
|
+
export * as kysely from "./PublicKysely.js";
|
|
18
|
+
export type { InferRow, Query, QueryRows, Row } from "./Query.js";
|
|
19
|
+
export { formatValidMutationSizeError } from "./Schema.js";
|
|
20
|
+
export type { EvoluSchema, ValidMutationSizeError } from "./Schema.js";
|
|
21
|
+
export type {
|
|
22
|
+
NetworkError,
|
|
23
|
+
PaymentRequiredError,
|
|
24
|
+
ServerError,
|
|
25
|
+
SyncState,
|
|
26
|
+
SyncStateInitial,
|
|
27
|
+
SyncStateIsNotSynced,
|
|
28
|
+
SyncStateIsSynced,
|
|
29
|
+
SyncStateIsSyncing,
|
|
30
|
+
} from "./Sync.js";
|
|
31
|
+
export {
|
|
32
|
+
binaryTimestampToTimestamp,
|
|
33
|
+
Timestamp,
|
|
34
|
+
timestampToBinaryTimestamp,
|
|
35
|
+
} from "./Timestamp.js";
|
|
36
|
+
export type {
|
|
37
|
+
BinaryTimestamp,
|
|
38
|
+
TimestampCounterOverflowError,
|
|
39
|
+
TimestampDriftError,
|
|
40
|
+
TimestampDuplicateNodeError,
|
|
41
|
+
TimestampError,
|
|
42
|
+
TimestampTimeOutOfRangeError,
|
|
43
|
+
} from "./Timestamp.js";
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AliasableExpression,
|
|
3
|
+
AliasNode,
|
|
4
|
+
ColumnNode,
|
|
5
|
+
Expression,
|
|
6
|
+
ExpressionWrapper,
|
|
7
|
+
IdentifierNode,
|
|
8
|
+
RawBuilder,
|
|
9
|
+
ReferenceNode,
|
|
10
|
+
SelectQueryNode,
|
|
11
|
+
Simplify,
|
|
12
|
+
sql,
|
|
13
|
+
TableNode,
|
|
14
|
+
ValueNode,
|
|
15
|
+
} from "kysely";
|
|
16
|
+
import { kyselyJsonIdentifier } from "./Diff.js";
|
|
17
|
+
|
|
18
|
+
export { sql } from "kysely";
|
|
19
|
+
export type { NotNull } from "kysely";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A SQLite helper for aggregating a subquery into a JSON array.
|
|
23
|
+
*
|
|
24
|
+
* ### Examples
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { kysely } from "@evolu/common";
|
|
28
|
+
*
|
|
29
|
+
* // TODO: Update for Evolu
|
|
30
|
+
* const result = await db
|
|
31
|
+
* .selectFrom("person")
|
|
32
|
+
* .select((eb) => [
|
|
33
|
+
* "id",
|
|
34
|
+
* kysely
|
|
35
|
+
* .jsonArrayFrom(
|
|
36
|
+
* eb
|
|
37
|
+
* .selectFrom("pet")
|
|
38
|
+
* .select(["pet.id as pet_id", "pet.name"])
|
|
39
|
+
* .whereRef("pet.owner_id", "=", "person.id")
|
|
40
|
+
* .orderBy("pet.name"),
|
|
41
|
+
* )
|
|
42
|
+
* .as("pets"),
|
|
43
|
+
* ])
|
|
44
|
+
* .execute();
|
|
45
|
+
*
|
|
46
|
+
* result[0]?.id;
|
|
47
|
+
* result[0]?.pets[0].pet_id;
|
|
48
|
+
* result[0]?.pets[0].name;
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* The generated SQL (SQLite):
|
|
52
|
+
*
|
|
53
|
+
* ```sql
|
|
54
|
+
* select "id", (
|
|
55
|
+
* select coalesce(json_group_array(json_object(
|
|
56
|
+
* 'pet_id', "agg"."pet_id",
|
|
57
|
+
* 'name', "agg"."name"
|
|
58
|
+
* )), '[]') from (
|
|
59
|
+
* select "pet"."id" as "pet_id", "pet"."name"
|
|
60
|
+
* from "pet"
|
|
61
|
+
* where "pet"."owner_id" = "person"."id"
|
|
62
|
+
* order by "pet"."name"
|
|
63
|
+
* ) as "agg"
|
|
64
|
+
* ) as "pets"
|
|
65
|
+
* from "person"
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
// SQL formatting breaks generated SQL for some reason.
|
|
69
|
+
// prettier-ignore
|
|
70
|
+
export function jsonArrayFrom<O>(
|
|
71
|
+
expr: SelectQueryBuilderExpression<O>,
|
|
72
|
+
): RawBuilder<Array<Simplify<O>>> {
|
|
73
|
+
return sql`(select ${sql.lit(kyselyJsonIdentifier)} || coalesce(json_group_array(json_object(${sql.join(
|
|
74
|
+
getSqliteJsonObjectArgs(expr.toOperationNode(), 'agg'),
|
|
75
|
+
)})), '[]') from ${expr} as agg)`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A SQLite helper for turning a subquery into a JSON object.
|
|
80
|
+
*
|
|
81
|
+
* The subquery must only return one row.
|
|
82
|
+
*
|
|
83
|
+
* ### Examples
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { kysely } from "@evolu/common";
|
|
87
|
+
*
|
|
88
|
+
* // TODO: Update for Evolu
|
|
89
|
+
* const result = await db
|
|
90
|
+
* .selectFrom("person")
|
|
91
|
+
* .select((eb) => [
|
|
92
|
+
* "id",
|
|
93
|
+
* jsonObjectFrom(
|
|
94
|
+
* eb
|
|
95
|
+
* .selectFrom("pet")
|
|
96
|
+
* .select(["pet.id as pet_id", "pet.name"])
|
|
97
|
+
* .whereRef("pet.owner_id", "=", "person.id")
|
|
98
|
+
* .where("pet.is_favorite", "=", true),
|
|
99
|
+
* ).as("favorite_pet"),
|
|
100
|
+
* ])
|
|
101
|
+
* .execute();
|
|
102
|
+
*
|
|
103
|
+
* result[0]?.id;
|
|
104
|
+
* result[0]?.favorite_pet?.pet_id;
|
|
105
|
+
* result[0]?.favorite_pet?.name;
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* The generated SQL (SQLite):
|
|
109
|
+
*
|
|
110
|
+
* ```sql
|
|
111
|
+
* select "id", (
|
|
112
|
+
* select json_object(
|
|
113
|
+
* 'pet_id', "obj"."pet_id",
|
|
114
|
+
* 'name', "obj"."name"
|
|
115
|
+
* ) from (
|
|
116
|
+
* select "pet"."id" as "pet_id", "pet"."name"
|
|
117
|
+
* from "pet"
|
|
118
|
+
* where "pet"."owner_id" = "person"."id"
|
|
119
|
+
* and "pet"."is_favorite" = ?
|
|
120
|
+
* ) as obj
|
|
121
|
+
* ) as "favorite_pet"
|
|
122
|
+
* from "person";
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
// SQL formatting breaks generated SQL for some reason.
|
|
126
|
+
// prettier-ignore
|
|
127
|
+
export function jsonObjectFrom<O>(
|
|
128
|
+
expr: SelectQueryBuilderExpression<O>,
|
|
129
|
+
): RawBuilder<Simplify<O> | null> {
|
|
130
|
+
return sql`(select ${sql.lit(kyselyJsonIdentifier)} || json_object(${sql.join(
|
|
131
|
+
getSqliteJsonObjectArgs(expr.toOperationNode(), 'obj'),
|
|
132
|
+
)}) from ${expr} as obj)`
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The SQLite `json_object` function.
|
|
137
|
+
*
|
|
138
|
+
* ### Examples
|
|
139
|
+
*
|
|
140
|
+
* ```ts
|
|
141
|
+
* import { kysely } from "@evolu/common";
|
|
142
|
+
*
|
|
143
|
+
* // TODO: Update for Evolu
|
|
144
|
+
* const result = await db
|
|
145
|
+
* .selectFrom("person")
|
|
146
|
+
* .select((eb) => [
|
|
147
|
+
* "id",
|
|
148
|
+
* kysely
|
|
149
|
+
* .jsonBuildObject({
|
|
150
|
+
* first: eb.ref("first_name"),
|
|
151
|
+
* last: eb.ref("last_name"),
|
|
152
|
+
* full: kysely.sql<string>`first_name || ' ' || last_name`,
|
|
153
|
+
* })
|
|
154
|
+
* .as("name"),
|
|
155
|
+
* ])
|
|
156
|
+
* .execute();
|
|
157
|
+
*
|
|
158
|
+
* result[0]?.id;
|
|
159
|
+
* result[0]?.name.first;
|
|
160
|
+
* result[0]?.name.last;
|
|
161
|
+
* result[0]?.name.full;
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* The generated SQL (SQLite):
|
|
165
|
+
*
|
|
166
|
+
* ```sql
|
|
167
|
+
* select "id", json_object(
|
|
168
|
+
* 'first', first_name,
|
|
169
|
+
* 'last', last_name,
|
|
170
|
+
* 'full', "first_name" || ' ' || "last_name"
|
|
171
|
+
* ) as "name"
|
|
172
|
+
* from "person"
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
// SQL formatting breaks generated SQL for some reason.
|
|
176
|
+
// prettier-ignore
|
|
177
|
+
export function jsonBuildObject<O extends Record<string, Expression<unknown>>>(
|
|
178
|
+
obj: O,
|
|
179
|
+
): RawBuilder<
|
|
180
|
+
Simplify<{
|
|
181
|
+
[K in keyof O]: O[K] extends Expression<infer V> ? V : never
|
|
182
|
+
}>
|
|
183
|
+
> {
|
|
184
|
+
return sql`${sql.lit(kyselyJsonIdentifier)} || json_object(${sql.join(
|
|
185
|
+
Object.keys(obj).flatMap((k) => [sql.lit(k), obj[k]]),
|
|
186
|
+
)})`
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
interface SelectQueryBuilderExpression<O> extends AliasableExpression<O> {
|
|
190
|
+
get isSelectQueryBuilder(): true;
|
|
191
|
+
toOperationNode(): SelectQueryNode;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getSqliteJsonObjectArgs(
|
|
195
|
+
node: SelectQueryNode,
|
|
196
|
+
table: string,
|
|
197
|
+
): Array<Expression<unknown> | string> {
|
|
198
|
+
try {
|
|
199
|
+
return getJsonObjectArgs(node, table);
|
|
200
|
+
} catch {
|
|
201
|
+
throw new Error(
|
|
202
|
+
"SQLite jsonArrayFrom and jsonObjectFrom functions can only handle explicit selections due to limitations of the json_object function. selectAll() is not allowed in the subquery.",
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function getJsonObjectArgs(
|
|
208
|
+
node: SelectQueryNode,
|
|
209
|
+
table: string,
|
|
210
|
+
): Array<Expression<unknown> | string> {
|
|
211
|
+
const args: Array<Expression<unknown> | string> = [];
|
|
212
|
+
args.push(kyselyJsonIdentifier, kyselyJsonIdentifier);
|
|
213
|
+
|
|
214
|
+
for (const { selection: s } of node.selections ?? []) {
|
|
215
|
+
if (ReferenceNode.is(s) && ColumnNode.is(s.column)) {
|
|
216
|
+
args.push(
|
|
217
|
+
colName(s.column.column.name),
|
|
218
|
+
colRef(table, s.column.column.name),
|
|
219
|
+
);
|
|
220
|
+
} else if (ColumnNode.is(s)) {
|
|
221
|
+
args.push(colName(s.column.name), colRef(table, s.column.name));
|
|
222
|
+
} else if (AliasNode.is(s) && IdentifierNode.is(s.alias)) {
|
|
223
|
+
args.push(colName(s.alias.name), colRef(table, s.alias.name));
|
|
224
|
+
} else {
|
|
225
|
+
throw new Error(`can't extract column names from the select query node`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return args;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function colName(col: string): Expression<unknown> {
|
|
233
|
+
return new ExpressionWrapper(ValueNode.createImmediate(col));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function colRef(table: string, col: string): Expression<unknown> {
|
|
237
|
+
return new ExpressionWrapper(
|
|
238
|
+
ReferenceNode.create(ColumnNode.create(col), TableNode.create(table)),
|
|
239
|
+
);
|
|
240
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { bytesToHex, hexToBytes } from "../Buffer.js";
|
|
2
|
+
import { objectToEntries } from "../Object.js";
|
|
3
|
+
import {
|
|
4
|
+
SafeSql,
|
|
5
|
+
SqliteQuery,
|
|
6
|
+
SqliteQueryOptions,
|
|
7
|
+
SqliteRow,
|
|
8
|
+
SqliteValue,
|
|
9
|
+
} from "../Sqlite.js";
|
|
10
|
+
import { Store, StoreSubscribe } from "../Store.js";
|
|
11
|
+
import { Brand, Simplify } from "../Types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A type-safe SQL query.
|
|
15
|
+
*
|
|
16
|
+
* ### Example
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const allTodos = evolu.createQuery((db) =>
|
|
20
|
+
* db.selectFrom("todo").selectAll(),
|
|
21
|
+
* );
|
|
22
|
+
* type AllTodosRow = typeof allTodos.Row;
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export type Query<R extends Row = Row> = string &
|
|
26
|
+
Brand<"Query"> & {
|
|
27
|
+
/**
|
|
28
|
+
* A shorthand for {@link InferRow}.
|
|
29
|
+
*
|
|
30
|
+
* ### Example
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* const allTodos = evolu.createQuery((db) =>
|
|
34
|
+
* db.selectFrom("todo").selectAll(),
|
|
35
|
+
* );
|
|
36
|
+
* type AllTodosRow = typeof allTodos.Row;
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
Row: R;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Evolu serializes {@link SqliteQuery} into a string to be easily used as a key
|
|
44
|
+
* and for comparison.
|
|
45
|
+
*/
|
|
46
|
+
export const serializeQuery = <R extends Row>(query: SqliteQuery): Query<R> => {
|
|
47
|
+
const params = query.parameters.map((v) =>
|
|
48
|
+
v instanceof Uint8Array
|
|
49
|
+
? (["b", bytesToHex(v)] as const)
|
|
50
|
+
: (["j", v] as const),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const options = query.options
|
|
54
|
+
? objectToEntries(query.options).sort(([a], [b]) => a.localeCompare(b))
|
|
55
|
+
: [];
|
|
56
|
+
|
|
57
|
+
return JSON.stringify([query.sql, params, options]) as Query<R>;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const deserializeQuery = <R extends Row>(
|
|
61
|
+
query: Query<R>,
|
|
62
|
+
): SqliteQuery => {
|
|
63
|
+
const [sql, paramsArr, optionsArr] = JSON.parse(query) as [
|
|
64
|
+
SafeSql,
|
|
65
|
+
Array<readonly ["b", string] | readonly ["j", string | number | null]>,
|
|
66
|
+
Array<Array<string | number | null>>,
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const parameters = paramsArr.map(([type, value]) =>
|
|
70
|
+
type === "b" ? hexToBytes(value) : value,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const options = optionsArr.length
|
|
74
|
+
? (Object.fromEntries(optionsArr) as SqliteQueryOptions)
|
|
75
|
+
: undefined;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
sql,
|
|
79
|
+
parameters,
|
|
80
|
+
...(options !== undefined && { options }),
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type InferRow<T extends Query> = T extends Query<infer R> ? R : never;
|
|
85
|
+
|
|
86
|
+
export interface Row {
|
|
87
|
+
readonly [key: string]:
|
|
88
|
+
| SqliteValue
|
|
89
|
+
| Row // for jsonObjectFrom from kysely/helpers/sqlite
|
|
90
|
+
| ReadonlyArray<Row>; // for jsonArrayFrom from kysely/helpers/sqlite
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// To preserve identity.
|
|
94
|
+
export const emptyRows: ReadonlyArray<Row> = [];
|
|
95
|
+
|
|
96
|
+
/** Rows returned by a query. */
|
|
97
|
+
export type QueryRows<R extends Row = Row> = ReadonlyArray<
|
|
98
|
+
Readonly<Simplify<R>>
|
|
99
|
+
>;
|
|
100
|
+
|
|
101
|
+
export type Queries<R extends Row = Row> = ReadonlyArray<Query<R>>;
|
|
102
|
+
|
|
103
|
+
export type QueriesToQueryRows<Q extends Queries> = {
|
|
104
|
+
[P in keyof Q]: Q[P] extends Query<infer R> ? QueryRows<R> : never;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type QueriesToQueryRowsPromises<Q extends Queries> = {
|
|
108
|
+
[P in keyof Q]: Q[P] extends Query<infer R> ? Promise<QueryRows<R>> : never;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export type QueryRowsMap = ReadonlyMap<Query, ReadonlyArray<Row>>;
|
|
112
|
+
|
|
113
|
+
export interface QueryRowsCache {
|
|
114
|
+
readonly set: (
|
|
115
|
+
queriesRows: ReadonlyArray<readonly [Query, ReadonlyArray<SqliteRow>]>,
|
|
116
|
+
) => void;
|
|
117
|
+
readonly get: () => QueryRowsMap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const createQueryRowsCache = (): QueryRowsCache => {
|
|
121
|
+
let queryRowsCache: QueryRowsMap = new Map();
|
|
122
|
+
|
|
123
|
+
const cache: QueryRowsCache = {
|
|
124
|
+
set: (queriesRows) => {
|
|
125
|
+
queryRowsCache = new Map([...queryRowsCache, ...queriesRows]);
|
|
126
|
+
},
|
|
127
|
+
get: () => queryRowsCache,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
return cache;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export interface SubscribedQueries {
|
|
134
|
+
subscribe: (query: Query) => StoreSubscribe;
|
|
135
|
+
|
|
136
|
+
has: (query: Query) => boolean;
|
|
137
|
+
|
|
138
|
+
get: () => ReadonlyArray<Query>;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const createSubscribedQueries = (
|
|
142
|
+
rowsStore: Store<QueryRowsMap>,
|
|
143
|
+
): SubscribedQueries => {
|
|
144
|
+
const subscribedQueryMap = new Map<Query, number>();
|
|
145
|
+
|
|
146
|
+
const subscribedQueries: SubscribedQueries = {
|
|
147
|
+
subscribe: (query) => (listener) => {
|
|
148
|
+
subscribedQueryMap.set(query, (subscribedQueryMap.get(query) ?? 0) + 1);
|
|
149
|
+
const unsubscribe = rowsStore.subscribe(listener);
|
|
150
|
+
return () => {
|
|
151
|
+
const count = subscribedQueryMap.get(query);
|
|
152
|
+
if (count != null && count > 1) {
|
|
153
|
+
subscribedQueryMap.set(query, count - 1);
|
|
154
|
+
} else {
|
|
155
|
+
subscribedQueryMap.delete(query);
|
|
156
|
+
}
|
|
157
|
+
unsubscribe();
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
get: () => [...subscribedQueryMap.keys()],
|
|
162
|
+
|
|
163
|
+
has: (query) => subscribedQueryMap.has(query),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return subscribedQueries;
|
|
167
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { isNonEmptyReadonlyArray } from "../Array.js";
|
|
2
|
+
import { ConsoleConfig } from "../Console.js";
|
|
3
|
+
import { eqArrayNumber } from "../Eq.js";
|
|
4
|
+
import { ok, Result } from "../Result.js";
|
|
5
|
+
import { sql, SqliteError } from "../Sqlite.js";
|
|
6
|
+
import { SimpleName } from "../Type.js";
|
|
7
|
+
import { OwnerId, WriteKey } from "./Owner.js";
|
|
8
|
+
import { EncryptedDbChange, Storage } from "./Protocol.js";
|
|
9
|
+
import {
|
|
10
|
+
createSqliteStorageBase,
|
|
11
|
+
CreateSqliteStorageBaseOptions,
|
|
12
|
+
SqliteStorageDeps,
|
|
13
|
+
} from "./Storage.js";
|
|
14
|
+
import { timestampToBinaryTimestamp } from "./Timestamp.js";
|
|
15
|
+
|
|
16
|
+
export interface Relay extends Disposable {}
|
|
17
|
+
|
|
18
|
+
export interface RelayConfig extends ConsoleConfig {
|
|
19
|
+
readonly name?: SimpleName;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const createRelayStorage =
|
|
23
|
+
(deps: SqliteStorageDeps) =>
|
|
24
|
+
(options: CreateSqliteStorageBaseOptions): Result<Storage, SqliteError> => {
|
|
25
|
+
const sqliteStorageBase = createSqliteStorageBase(deps)(options);
|
|
26
|
+
if (!sqliteStorageBase.ok) return sqliteStorageBase;
|
|
27
|
+
|
|
28
|
+
for (const query of [
|
|
29
|
+
sql`
|
|
30
|
+
create table if not exists evolu_writeKey (
|
|
31
|
+
"ownerId" blob not null,
|
|
32
|
+
"writeKey" blob not null,
|
|
33
|
+
primary key ("ownerId")
|
|
34
|
+
)
|
|
35
|
+
strict;
|
|
36
|
+
`,
|
|
37
|
+
|
|
38
|
+
sql`
|
|
39
|
+
create table if not exists evolu_message (
|
|
40
|
+
"ownerId" blob not null,
|
|
41
|
+
"timestamp" blob not null,
|
|
42
|
+
"change" blob not null,
|
|
43
|
+
primary key ("ownerId", "timestamp")
|
|
44
|
+
)
|
|
45
|
+
strict;
|
|
46
|
+
`,
|
|
47
|
+
]) {
|
|
48
|
+
const result = deps.sqlite.exec(query);
|
|
49
|
+
if (!result.ok) return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return ok({
|
|
53
|
+
...sqliteStorageBase.value,
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Lazily authorizes the initiator's {@link WriteKey} for the given
|
|
57
|
+
* {@link OwnerId}.
|
|
58
|
+
*
|
|
59
|
+
* - If the {@link OwnerId} does not exist, it is created and associated with
|
|
60
|
+
* the provided write key.
|
|
61
|
+
* - If the {@link OwnerId} exists, the provided write key is compared to the
|
|
62
|
+
* stored key.
|
|
63
|
+
*/
|
|
64
|
+
validateWriteKey: (ownerId, writeKey) => {
|
|
65
|
+
const selectWriteKey = deps.sqlite.exec<{ writeKey: WriteKey }>(sql`
|
|
66
|
+
select writeKey
|
|
67
|
+
from evolu_writeKey
|
|
68
|
+
where ownerId = ${ownerId};
|
|
69
|
+
`);
|
|
70
|
+
if (!selectWriteKey.ok) {
|
|
71
|
+
options.onStorageError(selectWriteKey.error);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const { rows } = selectWriteKey.value;
|
|
76
|
+
|
|
77
|
+
if (!isNonEmptyReadonlyArray(rows)) {
|
|
78
|
+
const insertWriteKey = deps.sqlite.exec(sql`
|
|
79
|
+
insert into evolu_writeKey (ownerId, writeKey)
|
|
80
|
+
values (${ownerId}, ${writeKey});
|
|
81
|
+
`);
|
|
82
|
+
if (!insertWriteKey.ok) {
|
|
83
|
+
options.onStorageError(insertWriteKey.error);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return eqArrayNumber(rows[0].writeKey, writeKey);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
writeMessages: (ownerId, messages) => {
|
|
94
|
+
const result = deps.sqlite.transaction(() => {
|
|
95
|
+
for (const message of messages) {
|
|
96
|
+
const insertTimestampResult =
|
|
97
|
+
sqliteStorageBase.value.insertTimestamp(
|
|
98
|
+
ownerId,
|
|
99
|
+
timestampToBinaryTimestamp(message.timestamp),
|
|
100
|
+
);
|
|
101
|
+
if (!insertTimestampResult.ok) return insertTimestampResult;
|
|
102
|
+
|
|
103
|
+
const insertMessage = deps.sqlite.exec(sql`
|
|
104
|
+
insert into evolu_message ("ownerId", "timestamp", "change")
|
|
105
|
+
values
|
|
106
|
+
(
|
|
107
|
+
${ownerId},
|
|
108
|
+
${timestampToBinaryTimestamp(message.timestamp)},
|
|
109
|
+
${message.change}
|
|
110
|
+
)
|
|
111
|
+
on conflict do nothing;
|
|
112
|
+
`);
|
|
113
|
+
if (!insertMessage.ok) return insertMessage;
|
|
114
|
+
}
|
|
115
|
+
return ok();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (!result.ok) {
|
|
119
|
+
options.onStorageError(result.error);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return true;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
readDbChange: (ownerId, timestamp) => {
|
|
127
|
+
const result = deps.sqlite.exec<{
|
|
128
|
+
change: EncryptedDbChange;
|
|
129
|
+
}>(sql`
|
|
130
|
+
select "change"
|
|
131
|
+
from evolu_message
|
|
132
|
+
where "ownerId" = ${ownerId} and "timestamp" = ${timestamp};
|
|
133
|
+
`);
|
|
134
|
+
if (!result.ok) {
|
|
135
|
+
options.onStorageError(result.error);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return result.value.rows[0]?.change;
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
};
|