@evolu/common 1.0.17 → 2.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 +7 -7
- package/dist/src/Config.d.ts +4 -6
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Config.js +1 -1
- package/dist/src/Crdt.d.ts +15 -9
- package/dist/src/Crdt.d.ts.map +1 -1
- package/dist/src/Crdt.js +15 -14
- package/dist/src/Crypto.d.ts +7 -13
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +7 -9
- package/dist/src/Db.d.ts +36 -70
- package/dist/src/Db.d.ts.map +1 -1
- package/dist/src/Db.js +45 -51
- package/dist/src/DbWorker.d.ts +13 -14
- package/dist/src/DbWorker.d.ts.map +1 -1
- package/dist/src/DbWorker.js +56 -57
- package/dist/src/Diff.d.ts +3 -1
- package/dist/src/Diff.d.ts.map +1 -1
- package/dist/src/Diff.js +3 -7
- package/dist/src/{Errors.d.ts → ErrorStore.d.ts} +14 -7
- package/dist/src/ErrorStore.d.ts.map +1 -0
- package/dist/src/{Errors.js → ErrorStore.js} +2 -0
- package/dist/src/Evolu.d.ts +316 -58
- package/dist/src/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu.js +199 -185
- package/dist/src/Model.d.ts +47 -57
- package/dist/src/Model.d.ts.map +1 -1
- package/dist/src/Model.js +30 -35
- package/dist/src/OnCompletes.d.ts +11 -0
- package/dist/src/OnCompletes.d.ts.map +1 -0
- package/dist/src/OnCompletes.js +21 -0
- package/dist/src/Owner.d.ts +31 -0
- package/dist/src/Owner.d.ts.map +1 -0
- package/dist/src/Owner.js +20 -0
- package/dist/src/Platform.d.ts +15 -8
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +5 -5
- package/dist/src/Protobuf.d.ts +25 -73
- package/dist/src/Protobuf.d.ts.map +1 -1
- package/dist/src/Protobuf.js +4 -12
- package/dist/src/Public.d.ts +5 -3
- package/dist/src/Public.d.ts.map +1 -1
- package/dist/src/Public.js +1 -0
- package/dist/src/Sqlite.d.ts +8 -11
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +7 -7
- package/dist/src/Store.d.ts +6 -5
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +21 -16
- package/dist/src/SyncWorker.d.ts +8 -2
- package/dist/src/SyncWorker.d.ts.map +1 -1
- package/dist/src/SyncWorker.js +12 -19
- package/dist/src/index.d.ts +6 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +6 -2
- package/package.json +23 -13
- package/src/Config.ts +7 -7
- package/src/Crdt.ts +29 -26
- package/src/Crypto.ts +14 -19
- package/src/Db.ts +113 -166
- package/src/DbWorker.ts +110 -140
- package/src/Diff.ts +5 -7
- package/src/{Errors.ts → ErrorStore.ts} +17 -8
- package/src/Evolu.ts +682 -442
- package/src/Model.ts +60 -85
- package/src/OnCompletes.ts +46 -0
- package/src/Owner.ts +65 -0
- package/src/Platform.ts +24 -16
- package/src/Protobuf.ts +25 -73
- package/src/Public.ts +5 -3
- package/src/Sqlite.ts +13 -24
- package/src/Store.ts +36 -24
- package/src/SyncWorker.ts +30 -38
- package/src/index.ts +6 -2
- package/dist/src/Errors.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -28,20 +28,20 @@ Client-server architecture provides us with easy backup and synchronization, but
|
|
|
28
28
|
To start using Evolu, define tables for your database and export React Hooks.
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
|
-
import * as
|
|
31
|
+
import * as S from "@effect/schema/Schema";
|
|
32
32
|
import * as Evolu from "@evolu/react";
|
|
33
33
|
|
|
34
34
|
const TodoId = Evolu.id("Todo");
|
|
35
|
-
type TodoId =
|
|
35
|
+
type TodoId = S.Schema.To<typeof TodoId>;
|
|
36
36
|
|
|
37
|
-
const TodoTable =
|
|
37
|
+
const TodoTable = S.struct({
|
|
38
38
|
id: TodoId,
|
|
39
39
|
title: Evolu.NonEmptyString1000,
|
|
40
40
|
isCompleted: Evolu.SqliteBoolean,
|
|
41
41
|
});
|
|
42
|
-
type TodoTable =
|
|
42
|
+
type TodoTable = S.Schema.To<typeof TodoTable>;
|
|
43
43
|
|
|
44
|
-
const Database =
|
|
44
|
+
const Database = S.struct({
|
|
45
45
|
todo: TodoTable,
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -59,10 +59,10 @@ export const {
|
|
|
59
59
|
Learn more about [Schema](https://github.com/effect-ts/schema).
|
|
60
60
|
|
|
61
61
|
```ts
|
|
62
|
-
import * as
|
|
62
|
+
import * as S from "@effect/schema/Schema";
|
|
63
63
|
import * as Evolu from "@evolu/react";
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
S.parse(Evolu.String1000)(title);
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
### Mutate Data
|
package/dist/src/Config.d.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { Context, Layer } from "effect";
|
|
2
2
|
export interface Config {
|
|
3
|
-
/**
|
|
4
|
-
* Alternate URL to Evolu sync&backup server.
|
|
5
|
-
*/
|
|
3
|
+
/** Alternate URL to Evolu sync and backup server. */
|
|
6
4
|
readonly syncUrl: string;
|
|
7
5
|
/**
|
|
8
|
-
* Alternate URL to reload browser tabs after
|
|
6
|
+
* Alternate URL to reload browser tabs after {@link Owner} reset or restore.
|
|
9
7
|
* The default value is `/`.
|
|
10
8
|
*/
|
|
11
9
|
readonly reloadUrl: string;
|
|
12
10
|
/**
|
|
13
|
-
* Maximum physical clock drift allowed in ms.
|
|
14
|
-
*
|
|
11
|
+
* Maximum physical clock drift allowed in ms. The default value is 5 * 60 *
|
|
12
|
+
* 1000 (5 minutes).
|
|
15
13
|
*/
|
|
16
14
|
readonly maxDrift: number;
|
|
17
15
|
}
|
package/dist/src/Config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/Config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,WAAW,MAAM;IACrB
|
|
1
|
+
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/Config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,WAAW,MAAM;IACrB,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,MAAM,6BAAwB,CAAC;AAE5C,eAAO,MAAM,UAAU,YACZ,QAAQ,MAAM,CAAC,KACvB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAShC,CAAC"}
|
package/dist/src/Config.js
CHANGED
package/dist/src/Crdt.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as S from "@effect/schema/Schema";
|
|
2
2
|
import { Brand, Context, Effect, Layer, Option } from "effect";
|
|
3
3
|
import { Config } from "./Config.js";
|
|
4
4
|
import { NanoId, NodeId } from "./Crypto.js";
|
|
@@ -12,16 +12,17 @@ export declare const AllowedTimeRange: {
|
|
|
12
12
|
lessThan: number;
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
* Millis represents a time that is valid for usage with the Merkle tree.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* Millis represents a time that is valid for usage with the Merkle tree. It
|
|
16
|
+
* must be between Apr 13, 1997, and Nov 05, 2051, to ensure MinutesBase3 length
|
|
17
|
+
* equals 16. We can find diff for two Merkle trees only within this range. If
|
|
18
|
+
* the device clock is out of range, Evolu will not store data until it's
|
|
19
|
+
* fixed.
|
|
19
20
|
*/
|
|
20
|
-
export declare const Millis:
|
|
21
|
-
export type Millis =
|
|
21
|
+
export declare const Millis: S.BrandSchema<number, number & Brand.Brand<"Millis">>;
|
|
22
|
+
export type Millis = S.Schema.To<typeof Millis>;
|
|
22
23
|
export declare const initialMillis: number & Brand.Brand<"Millis">;
|
|
23
|
-
export declare const Counter:
|
|
24
|
-
export type Counter =
|
|
24
|
+
export declare const Counter: S.BrandSchema<number, number & Brand.Brand<"Counter">>;
|
|
25
|
+
export type Counter = S.Schema.To<typeof Counter>;
|
|
25
26
|
export type TimestampHash = number & Brand.Brand<"TimestampHash">;
|
|
26
27
|
export type TimestampString = string & Brand.Brand<"TimestampString">;
|
|
27
28
|
export declare const timestampToString: (t: Timestamp) => TimestampString;
|
|
@@ -34,6 +35,11 @@ export interface Time {
|
|
|
34
35
|
}
|
|
35
36
|
export declare const Time: Context.Tag<Time, Time>;
|
|
36
37
|
export declare const TimeLive: Layer.Layer<never, never, Time>;
|
|
38
|
+
/**
|
|
39
|
+
* The TimestampError type represents all Timestamp-related errors. If such an
|
|
40
|
+
* error happens, the device clock is skewed and should be set to the current
|
|
41
|
+
* time.
|
|
42
|
+
*/
|
|
37
43
|
export type TimestampError = TimestampDriftError | TimestampCounterOverflowError | TimestampDuplicateNodeError | TimestampTimeOutOfRangeError;
|
|
38
44
|
export interface TimestampDriftError {
|
|
39
45
|
readonly _tag: "TimestampDriftError";
|
package/dist/src/Crdt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Crdt.d.ts","sourceRoot":"","sources":["../../src/Crdt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Crdt.d.ts","sourceRoot":"","sources":["../../src/Crdt.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Crdt.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as S from "@effect/schema/Schema";
|
|
2
2
|
import { Context, Effect, Either, Layer, Number, Option, ReadonlyArray, String, pipe, } from "effect";
|
|
3
3
|
import { Config } from "./Config.js";
|
|
4
4
|
import { NanoId, NodeId } from "./Crypto.js";
|
|
@@ -8,15 +8,16 @@ export const AllowedTimeRange = {
|
|
|
8
8
|
lessThan: 2582803260000,
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
|
-
* Millis represents a time that is valid for usage with the Merkle tree.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Millis represents a time that is valid for usage with the Merkle tree. It
|
|
12
|
+
* must be between Apr 13, 1997, and Nov 05, 2051, to ensure MinutesBase3 length
|
|
13
|
+
* equals 16. We can find diff for two Merkle trees only within this range. If
|
|
14
|
+
* the device clock is out of range, Evolu will not store data until it's
|
|
15
|
+
* fixed.
|
|
15
16
|
*/
|
|
16
|
-
export const Millis =
|
|
17
|
-
export const initialMillis =
|
|
18
|
-
export const Counter =
|
|
19
|
-
const initialCounter =
|
|
17
|
+
export const Millis = S.number.pipe(S.greaterThan(AllowedTimeRange.greaterThan), S.lessThan(AllowedTimeRange.lessThan), S.brand("Millis"));
|
|
18
|
+
export const initialMillis = S.parseSync(Millis)(AllowedTimeRange.greaterThan + 1);
|
|
19
|
+
export const Counter = S.number.pipe(S.between(0, 65535), S.brand("Counter"));
|
|
20
|
+
const initialCounter = S.parseSync(Counter)(0);
|
|
20
21
|
export const timestampToString = (t) => [
|
|
21
22
|
new Date(t.millis).toISOString(),
|
|
22
23
|
t.counter.toString(16).toUpperCase().padStart(4, "0"),
|
|
@@ -31,7 +32,7 @@ export const unsafeTimestampFromString = (s) => {
|
|
|
31
32
|
};
|
|
32
33
|
};
|
|
33
34
|
export const timestampToHash = (t) => murmurhash(timestampToString(t));
|
|
34
|
-
const syncNodeId =
|
|
35
|
+
const syncNodeId = S.parseSync(NodeId)("0000000000000000");
|
|
35
36
|
export const makeSyncTimestamp = (millis = initialMillis) => ({
|
|
36
37
|
millis,
|
|
37
38
|
counter: initialCounter,
|
|
@@ -42,9 +43,9 @@ export const makeInitialTimestamp = NanoId.pipe(Effect.flatMap(({ nanoidAsNodeId
|
|
|
42
43
|
counter: initialCounter,
|
|
43
44
|
node,
|
|
44
45
|
})));
|
|
45
|
-
export const Time = Context.Tag(
|
|
46
|
+
export const Time = Context.Tag();
|
|
46
47
|
export const TimeLive = Layer.succeed(Time, Time.of({
|
|
47
|
-
now: Effect.suspend(() =>
|
|
48
|
+
now: Effect.suspend(() => S.parse(Millis)(Date.now())).pipe(Effect.catchTag("ParseError", () => Effect.fail({
|
|
48
49
|
_tag: "TimestampTimeOutOfRangeError",
|
|
49
50
|
}))),
|
|
50
51
|
}));
|
|
@@ -61,8 +62,8 @@ const getNextMillis = (millis) => Effect.gen(function* (_) {
|
|
|
61
62
|
}));
|
|
62
63
|
return next;
|
|
63
64
|
});
|
|
64
|
-
const incrementCounter = (counter) => pipe(Number.increment(counter),
|
|
65
|
-
const counterMin =
|
|
65
|
+
const incrementCounter = (counter) => pipe(Number.increment(counter), S.parseEither(Counter), Either.mapLeft(() => ({ _tag: "TimestampCounterOverflowError" })));
|
|
66
|
+
const counterMin = S.parseSync(Counter)(0);
|
|
66
67
|
export const sendTimestamp = (timestamp) => Effect.gen(function* (_) {
|
|
67
68
|
const millis = yield* _(getNextMillis([timestamp.millis]));
|
|
68
69
|
const counter = millis === timestamp.millis
|
package/dist/src/Crypto.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as S from "@effect/schema/Schema";
|
|
2
2
|
import { Brand, Context, Effect, Layer } from "effect";
|
|
3
3
|
export interface Bip39 {
|
|
4
4
|
readonly make: Effect.Effect<never, never, Mnemonic>;
|
|
@@ -12,9 +12,9 @@ export interface InvalidMnemonicError {
|
|
|
12
12
|
/**
|
|
13
13
|
* Mnemonic is a password generated by Evolu in BIP39 format.
|
|
14
14
|
*
|
|
15
|
-
* A mnemonic, also known as a "seed phrase," is a set of 12 words in a
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific
|
|
16
|
+
* order chosen from a predefined list. The purpose of the BIP39 mnemonic is to
|
|
17
|
+
* provide a human-readable way of storing a private key.
|
|
18
18
|
*/
|
|
19
19
|
export type Mnemonic = string & Brand.Brand<"Mnemonic">;
|
|
20
20
|
export interface NanoId {
|
|
@@ -22,17 +22,11 @@ export interface NanoId {
|
|
|
22
22
|
readonly nanoidAsNodeId: Effect.Effect<never, never, NodeId>;
|
|
23
23
|
}
|
|
24
24
|
export declare const NanoId: Context.Tag<NanoId, NanoId>;
|
|
25
|
-
export declare const NodeId:
|
|
26
|
-
export type NodeId =
|
|
25
|
+
export declare const NodeId: S.BrandSchema<string, string & Brand.Brand<"NodeId">>;
|
|
26
|
+
export type NodeId = S.Schema.To<typeof NodeId>;
|
|
27
27
|
export declare const NanoIdLive: Layer.Layer<never, never, NanoId>;
|
|
28
|
-
/**
|
|
29
|
-
* SLIP-21 implementation
|
|
30
|
-
* https://github.com/satoshilabs/slips/blob/master/slip-0021.md
|
|
31
|
-
*/
|
|
32
28
|
export declare const slip21Derive: (seed: Uint8Array, path: ReadonlyArray<string>) => Effect.Effect<never, never, Uint8Array>;
|
|
33
|
-
/**
|
|
34
|
-
* Alias to xsalsa20poly1305, for compatibility with libsodium / nacl
|
|
35
|
-
*/
|
|
29
|
+
/** Alias to xsalsa20poly1305, for compatibility with libsodium/nacl */
|
|
36
30
|
export interface SecretBox {
|
|
37
31
|
readonly seal: (key: Uint8Array, plaintext: Uint8Array) => Effect.Effect<never, never, Uint8Array>;
|
|
38
32
|
readonly open: (key: Uint8Array, ciphertext: Uint8Array) => Effect.Effect<never, never, Uint8Array>;
|
package/dist/src/Crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../../src/Crypto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../../src/Crypto.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,uBAAuB,CAAC;AAM3C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAIvD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAErD,QAAQ,CAAC,MAAM,EAAE,CACf,QAAQ,EAAE,QAAQ,KACf,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IAE7C,QAAQ,CAAC,KAAK,EAAE,CACd,QAAQ,EAAE,MAAM,KACb,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,oBAAoB,EAAE,QAAQ,CAAC,CAAC;CAC3D;AAED,eAAO,MAAM,KAAK,2BAAuB,CAAC;AAE1C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;CACvC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAExD,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACrD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;CAC9D;AAED,eAAO,MAAM,MAAM,6BAAwB,CAAC;AAE5C,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CACb,CAAC;AAC7D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,MAAM,CAAC,CAAC;AAIhD,eAAO,MAAM,UAAU,mCAMtB,CAAC;AAIF,eAAO,MAAM,YAAY,SACjB,UAAU,QACV,cAAc,MAAM,CAAC,KAC1B,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAWrC,CAAC;AAEL,uEAAuE;AACvE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,CACb,GAAG,EAAE,UAAU,EACf,SAAS,EAAE,UAAU,KAClB,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IAE7C,QAAQ,CAAC,IAAI,EAAE,CACb,GAAG,EAAE,UAAU,EACf,UAAU,EAAE,UAAU,KACnB,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;CAC9C;AAED,eAAO,MAAM,SAAS,mCAA2B,CAAC;AAElD,eAAO,MAAM,aAAa,sCAgBzB,CAAC"}
|
package/dist/src/Crypto.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as S from "@effect/schema/Schema";
|
|
2
2
|
import { secretbox } from "@noble/ciphers/salsa";
|
|
3
3
|
import { concatBytes } from "@noble/ciphers/utils";
|
|
4
4
|
import { hmac } from "@noble/hashes/hmac";
|
|
@@ -6,18 +6,16 @@ import { sha512 } from "@noble/hashes/sha512";
|
|
|
6
6
|
import { randomBytes } from "@noble/hashes/utils";
|
|
7
7
|
import { Context, Effect, Layer } from "effect";
|
|
8
8
|
import { customAlphabet, nanoid } from "nanoid";
|
|
9
|
-
export const Bip39 = Context.Tag(
|
|
10
|
-
export const NanoId = Context.Tag(
|
|
11
|
-
export const NodeId =
|
|
9
|
+
export const Bip39 = Context.Tag();
|
|
10
|
+
export const NanoId = Context.Tag();
|
|
11
|
+
export const NodeId = S.string.pipe(S.pattern(/^[\w-]{16}$/), S.brand("NodeId"));
|
|
12
12
|
const nanoidForNodeId = customAlphabet("0123456789abcdef", 16);
|
|
13
13
|
export const NanoIdLive = Layer.succeed(NanoId, NanoId.of({
|
|
14
14
|
nanoid: Effect.sync(() => nanoid()),
|
|
15
15
|
nanoidAsNodeId: Effect.sync(() => nanoidForNodeId()),
|
|
16
16
|
}));
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* https://github.com/satoshilabs/slips/blob/master/slip-0021.md
|
|
20
|
-
*/
|
|
17
|
+
// SLIP-21 implementation
|
|
18
|
+
// https://github.com/satoshilabs/slips/blob/master/slip-0021.md
|
|
21
19
|
export const slip21Derive = (seed, path) => Effect.sync(() => {
|
|
22
20
|
let m = hmac(sha512, "Symmetric key seed", seed);
|
|
23
21
|
for (let i = 0; i < path.length; i++) {
|
|
@@ -29,7 +27,7 @@ export const slip21Derive = (seed, path) => Effect.sync(() => {
|
|
|
29
27
|
}
|
|
30
28
|
return m.slice(32, 64);
|
|
31
29
|
});
|
|
32
|
-
export const SecretBox = Context.Tag(
|
|
30
|
+
export const SecretBox = Context.Tag();
|
|
33
31
|
export const SecretBoxLive = Layer.succeed(SecretBox, SecretBox.of({
|
|
34
32
|
seal: (key, plaintext) => Effect.sync(() => {
|
|
35
33
|
const nonce = randomBytes(24);
|
package/dist/src/Db.d.ts
CHANGED
|
@@ -1,87 +1,51 @@
|
|
|
1
1
|
import * as S from "@effect/schema/Schema";
|
|
2
|
-
import { Brand, Context, Effect, ReadonlyRecord } from "effect";
|
|
2
|
+
import { Brand, Context, Effect, Layer, ReadonlyArray, ReadonlyRecord } from "effect";
|
|
3
3
|
import * as Kysely from "kysely";
|
|
4
|
-
import { Simplify } from "kysely";
|
|
5
4
|
import { Bip39, Mnemonic, NanoId } from "./Crypto.js";
|
|
6
|
-
import { Id
|
|
7
|
-
import {
|
|
5
|
+
import { Id } from "./Model.js";
|
|
6
|
+
import { Owner } from "./Owner.js";
|
|
7
|
+
import { Sqlite, SqliteQuery, Value } from "./Sqlite.js";
|
|
8
|
+
import { Store } from "./Store.js";
|
|
9
|
+
export type Schema = ReadonlyRecord.ReadonlyRecord<TableSchema>;
|
|
8
10
|
export type TableSchema = ReadonlyRecord.ReadonlyRecord<Value> & {
|
|
9
11
|
readonly id: Id;
|
|
10
12
|
};
|
|
11
|
-
|
|
12
|
-
export type CreateQuery<S extends Schema> = (queryCallback: QueryCallback<S, Row>) => Query;
|
|
13
|
-
export type QueryCallback<S extends Schema, QueryRow> = (db: KyselyWithoutMutation<QuerySchema<S>>) => Kysely.SelectQueryBuilder<any, any, QueryRow>;
|
|
14
|
-
type KyselyWithoutMutation<DB> = Pick<Kysely.Kysely<DB>, "selectFrom" | "fn">;
|
|
15
|
-
type QuerySchema<S extends Schema> = {
|
|
16
|
-
readonly [Table in keyof S]: NullableExceptOfId<{
|
|
17
|
-
readonly [Column in keyof S[Table]]: S[Table][Column];
|
|
18
|
-
} & CommonColumns>;
|
|
19
|
-
};
|
|
20
|
-
export type NullableExceptOfId<T> = {
|
|
21
|
-
readonly [K in keyof T]: K extends "id" ? T[K] : T[K] | null;
|
|
22
|
-
};
|
|
23
|
-
export interface CommonColumns {
|
|
24
|
-
readonly createdAt: SqliteDate;
|
|
25
|
-
readonly updatedAt: SqliteDate;
|
|
26
|
-
readonly isDeleted: SqliteBoolean;
|
|
27
|
-
}
|
|
13
|
+
declare const __queryBrand: unique symbol;
|
|
28
14
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* ### Examples
|
|
33
|
-
*
|
|
34
|
-
* ```
|
|
35
|
-
* useQuery(
|
|
36
|
-
* (db) => db.selectFrom("todo").selectAll(),
|
|
37
|
-
* // Filter and map nothing.
|
|
38
|
-
* (row) => row,
|
|
39
|
-
* );
|
|
40
|
-
*
|
|
41
|
-
* useQuery(
|
|
42
|
-
* (db) => db.selectFrom("todo").selectAll(),
|
|
43
|
-
* // Filter items with title != null.
|
|
44
|
-
* // Note the title type isn't nullable anymore in rows.
|
|
45
|
-
* ({ title, ...rest }) => title != null && { title, ...rest },
|
|
46
|
-
* );
|
|
47
|
-
* ```
|
|
15
|
+
* The query is an SQL query serialized as a string with a branded type
|
|
16
|
+
* representing a row it returns.
|
|
48
17
|
*/
|
|
49
|
-
export type
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
18
|
+
export type Query<R extends Row = Row> = string & Brand.Brand<"Query"> & {
|
|
19
|
+
readonly [__queryBrand]: R;
|
|
20
|
+
};
|
|
21
|
+
export type Queries<R extends Row = Row> = ReadonlyArray<Query<R>>;
|
|
22
|
+
export declare const serializeQuery: <R extends Row>({ sql, parameters, }: SqliteQuery) => Query<R>;
|
|
23
|
+
export declare const deserializeQuery: <R extends Row>(query: Query<R>) => SqliteQuery;
|
|
24
|
+
export type Row = ReadonlyRecord.ReadonlyRecord<Value | Row | ReadonlyArray<Row>>;
|
|
25
|
+
export declare const emptyRows: <R extends Row>() => readonly R[];
|
|
26
|
+
/** An object with rows and row properties. */
|
|
27
|
+
export interface QueryResult<R extends Row = Row> {
|
|
28
|
+
/** An array containing all the rows returned by the query. */
|
|
29
|
+
readonly rows: ReadonlyArray<Readonly<Kysely.Simplify<R>>>;
|
|
55
30
|
/**
|
|
56
|
-
* The first row
|
|
31
|
+
* The first row returned by the query, or null if no rows were returned. This
|
|
32
|
+
* property is useful for queries that are expected to return a single row.
|
|
57
33
|
*/
|
|
58
|
-
readonly
|
|
34
|
+
readonly row: Readonly<Kysely.Simplify<R>> | null;
|
|
59
35
|
}
|
|
60
|
-
type
|
|
36
|
+
export type QueryResultsFromQueries<Q extends Queries> = {
|
|
37
|
+
[P in keyof Q]: Q[P] extends Query<infer R> ? QueryResult<R> : never;
|
|
38
|
+
};
|
|
39
|
+
export type QueryResultsPromisesFromQueries<Q extends Queries> = {
|
|
40
|
+
[P in keyof Q]: Q[P] extends Query<infer R> ? Promise<QueryResult<R>> : never;
|
|
41
|
+
};
|
|
42
|
+
export declare const queryResultFromRows: <R extends Row>(rows: readonly R[]) => QueryResult<R>;
|
|
43
|
+
export type Tables = ReadonlyArray<Table>;
|
|
61
44
|
export interface Table {
|
|
62
45
|
readonly name: string;
|
|
63
46
|
readonly columns: ReadonlyArray<string>;
|
|
64
47
|
}
|
|
65
|
-
export type Tables = ReadonlyArray<Table>;
|
|
66
|
-
export declare const makeCreateQuery: <S extends Schema>() => CreateQuery<S>;
|
|
67
48
|
export declare const schemaToTables: (schema: S.Schema<any, any>) => Tables;
|
|
68
|
-
/**
|
|
69
|
-
* `Owner` represents the Evolu database owner. Evolu auto-generates `Owner`
|
|
70
|
-
* on the first run. `Owner` can be reset on the current device and restored
|
|
71
|
-
* on a different one.
|
|
72
|
-
*/
|
|
73
|
-
export interface Owner {
|
|
74
|
-
/** The `Mnemonic` associated with `Owner`. */
|
|
75
|
-
readonly mnemonic: Mnemonic;
|
|
76
|
-
/** The unique identifier of `Owner` safely derived from its `Mnemonic`. */
|
|
77
|
-
readonly id: OwnerId;
|
|
78
|
-
readonly encryptionKey: Uint8Array;
|
|
79
|
-
}
|
|
80
|
-
export declare const Owner: Context.Tag<Owner, Owner>;
|
|
81
|
-
/**
|
|
82
|
-
* The unique identifier of `Owner` safely derived from its `Mnemonic`.
|
|
83
|
-
*/
|
|
84
|
-
export type OwnerId = Id & Brand.Brand<"Owner">;
|
|
85
49
|
export declare const transaction: <R, E, A>(effect: Effect.Effect<R, E, A>) => Effect.Effect<Sqlite | R, E, A>;
|
|
86
50
|
export interface NoSuchTableOrColumnError {
|
|
87
51
|
readonly _tag: "NoSuchTableOrColumnError";
|
|
@@ -93,9 +57,11 @@ export declare const SqliteNoSuchTableOrColumnError: S.Schema<{
|
|
|
93
57
|
}>;
|
|
94
58
|
export type SqliteNoSuchTableOrColumnError = S.Schema.To<typeof SqliteNoSuchTableOrColumnError>;
|
|
95
59
|
export declare const someDefectToNoSuchTableOrColumnError: <R, E, A>(self: Effect.Effect<R, E, A>) => Effect.Effect<R, NoSuchTableOrColumnError | E, A>;
|
|
96
|
-
export declare const makeOwner: (mnemonic?: Mnemonic) => Effect.Effect<Bip39, never, Owner>;
|
|
97
60
|
export declare const lazyInit: (mnemonic?: Mnemonic) => Effect.Effect<Sqlite | Bip39 | NanoId, never, Owner>;
|
|
98
61
|
export declare const ensureSchema: (tables: Tables) => Effect.Effect<Sqlite, never, void>;
|
|
99
|
-
export
|
|
62
|
+
export type RowsStore = Store<RowsStoreValue>;
|
|
63
|
+
export declare const RowsStore: Context.Tag<RowsStore, RowsStore>;
|
|
64
|
+
type RowsStoreValue = ReadonlyMap<Query, ReadonlyArray<Row>>;
|
|
65
|
+
export declare const RowsStoreLive: Layer.Layer<never, never, RowsStore>;
|
|
100
66
|
export {};
|
|
101
67
|
//# sourceMappingURL=Db.d.ts.map
|
package/dist/src/Db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../src/Db.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,uBAAuB,CAAC;AAG3C,OAAO,EACL,KAAK,EACL,OAAO,EACP,MAAM,
|
|
1
|
+
{"version":3,"file":"Db.d.ts","sourceRoot":"","sources":["../../src/Db.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Db.js
CHANGED
|
@@ -2,45 +2,64 @@ import * as AST from "@effect/schema/AST";
|
|
|
2
2
|
import * as S from "@effect/schema/Schema";
|
|
3
3
|
import { make } from "@effect/schema/Schema";
|
|
4
4
|
import { bytesToHex } from "@noble/ciphers/utils";
|
|
5
|
-
import { Context, Effect, Exit, Option, Predicate, ReadonlyArray, ReadonlyRecord, String, pipe, } from "effect";
|
|
6
|
-
import * as Kysely from "kysely";
|
|
7
|
-
import { urlAlphabet } from "nanoid";
|
|
5
|
+
import { Context, Effect, Exit, Layer, Option, Predicate, ReadonlyArray, ReadonlyRecord, String, pipe, } from "effect";
|
|
8
6
|
import { initialMerkleTree, makeInitialTimestamp, merkleTreeToString, timestampToString, } from "./Crdt.js";
|
|
9
|
-
import {
|
|
7
|
+
import { makeOwner } from "./Owner.js";
|
|
10
8
|
import { createMessageTable, createMessageTableIndex, createOwnerTable, insertOwner, } from "./Sql.js";
|
|
11
|
-
import { Sqlite,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
9
|
+
import { Sqlite, isJsonObjectOrArray, } from "./Sqlite.js";
|
|
10
|
+
import { makeStore } from "./Store.js";
|
|
11
|
+
// We use queries as keys, hence JSON.stringify.
|
|
12
|
+
export const serializeQuery = ({ sql, parameters, }) => {
|
|
13
|
+
const query = {
|
|
14
|
+
sql,
|
|
15
|
+
parameters: parameters.map((p) => {
|
|
16
|
+
return Predicate.isUint8Array(p)
|
|
17
|
+
? Array.from(p)
|
|
18
|
+
: isJsonObjectOrArray(p)
|
|
19
|
+
? { json: p }
|
|
20
|
+
: p;
|
|
21
|
+
}),
|
|
22
|
+
};
|
|
23
|
+
return JSON.stringify(query);
|
|
24
|
+
};
|
|
25
|
+
export const deserializeQuery = (query) => {
|
|
26
|
+
const serializedSqliteQuery = JSON.parse(query);
|
|
27
|
+
return {
|
|
28
|
+
...serializedSqliteQuery,
|
|
29
|
+
parameters: serializedSqliteQuery.parameters.map((p) => Array.isArray(p)
|
|
30
|
+
? new Uint8Array(p)
|
|
31
|
+
: typeof p === "object" && p != null
|
|
32
|
+
? p.json
|
|
33
|
+
: p),
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
const _emptyRows = [];
|
|
37
|
+
export const emptyRows = () => _emptyRows;
|
|
38
|
+
const queryResultCache = new WeakMap();
|
|
39
|
+
export const queryResultFromRows = (rows) => {
|
|
40
|
+
let queryResult = queryResultCache.get(rows);
|
|
41
|
+
if (queryResult == null) {
|
|
42
|
+
queryResult = { rows, row: rows[0] };
|
|
43
|
+
queryResultCache.set(rows, queryResult);
|
|
44
|
+
}
|
|
45
|
+
return queryResult;
|
|
46
|
+
};
|
|
24
47
|
// https://github.com/Effect-TS/schema/releases/tag/v0.18.0
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
26
48
|
const getPropertySignatures = (schema) => {
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
49
|
const out = {};
|
|
29
50
|
const propertySignatures = AST.getPropertySignatures(schema.ast);
|
|
30
51
|
for (let i = 0; i < propertySignatures.length; i++) {
|
|
31
52
|
const propertySignature = propertySignatures[i];
|
|
32
53
|
out[propertySignature.name] = make(propertySignature.type);
|
|
33
54
|
}
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
35
56
|
return out;
|
|
36
57
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
schema) => pipe(getPropertySignatures(schema), ReadonlyRecord.toEntries, ReadonlyArray.map(([name, schema]) => ({
|
|
58
|
+
const commonColumns = ["createdAt", "updatedAt", "isDeleted"];
|
|
59
|
+
export const schemaToTables = (schema) => pipe(getPropertySignatures(schema), ReadonlyRecord.toEntries, ReadonlyArray.map(([name, schema]) => ({
|
|
40
60
|
name,
|
|
41
61
|
columns: Object.keys(getPropertySignatures(schema)).concat(commonColumns),
|
|
42
62
|
})));
|
|
43
|
-
export const Owner = Context.Tag("evolu/Owner");
|
|
44
63
|
export const transaction = (effect) => Effect.flatMap(Sqlite, (sqlite) => Effect.acquireUseRelease(sqlite.exec("BEGIN"), () => effect, (_, exit) => Exit.isFailure(exit) ? sqlite.exec("ROLLBACK") : sqlite.exec("END")));
|
|
45
64
|
export const SqliteNoSuchTableOrColumnError = S.struct({
|
|
46
65
|
message: S.union(S.string.pipe(S.includes("no such table")), S.string.pipe(S.includes("no such column")), S.string.pipe(S.includes("has no column"))),
|
|
@@ -50,22 +69,6 @@ export const someDefectToNoSuchTableOrColumnError = Effect.catchSomeDefect((erro
|
|
|
50
69
|
_tag: "NoSuchTableOrColumnError",
|
|
51
70
|
}))
|
|
52
71
|
: Option.none());
|
|
53
|
-
export const makeOwner = (mnemonic) => Effect.gen(function* (_) {
|
|
54
|
-
const bip39 = yield* _(Bip39);
|
|
55
|
-
if (mnemonic == null)
|
|
56
|
-
mnemonic = yield* _(bip39.make);
|
|
57
|
-
const seed = yield* _(bip39.toSeed(mnemonic));
|
|
58
|
-
const id = yield* _(slip21Derive(seed, ["Evolu", "Owner Id"]).pipe(Effect.map((key) => {
|
|
59
|
-
// convert key to nanoid
|
|
60
|
-
let id = "";
|
|
61
|
-
for (let i = 0; i < 21; i++) {
|
|
62
|
-
id += urlAlphabet[key[i] & 63];
|
|
63
|
-
}
|
|
64
|
-
return id;
|
|
65
|
-
})));
|
|
66
|
-
const encryptionKey = yield* _(slip21Derive(seed, ["Evolu", "Encryption Key"]));
|
|
67
|
-
return { mnemonic, id, encryptionKey };
|
|
68
|
-
});
|
|
69
72
|
export const lazyInit = (mnemonic) => Effect.gen(function* (_) {
|
|
70
73
|
const [owner, sqlite, initialTimestampString] = yield* _(Effect.all([makeOwner(mnemonic), Sqlite, makeInitialTimestamp], {
|
|
71
74
|
concurrency: "unbounded",
|
|
@@ -110,14 +113,5 @@ const createTable = ({ name, columns, }) => Effect.flatMap(Sqlite, (sqlite) => s
|
|
|
110
113
|
export const ensureSchema = (tables) => Effect.flatMap(getTables, (existingTables) => Effect.forEach(tables, (tableDefinition) => existingTables.includes(tableDefinition.name)
|
|
111
114
|
? updateTable(tableDefinition)
|
|
112
115
|
: createTable(tableDefinition), { discard: true }));
|
|
113
|
-
export const
|
|
114
|
-
|
|
115
|
-
return (filterMap) => (row) => {
|
|
116
|
-
let cachedRow = cache.get(row);
|
|
117
|
-
if (cachedRow === undefined) {
|
|
118
|
-
cachedRow = filterMap(row);
|
|
119
|
-
cache.set(row, cachedRow);
|
|
120
|
-
}
|
|
121
|
-
return cachedRow;
|
|
122
|
-
};
|
|
123
|
-
};
|
|
116
|
+
export const RowsStore = Context.Tag();
|
|
117
|
+
export const RowsStoreLive = Layer.effect(RowsStore, makeStore(new Map()));
|