@evolu/common 6.0.1-preview.25 → 6.0.1-preview.27
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/src/Assert.d.ts +0 -13
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +0 -15
- package/dist/src/Cache.d.ts +44 -0
- package/dist/src/Cache.d.ts.map +1 -0
- package/dist/src/Cache.js +52 -0
- package/dist/src/Evolu/Db.d.ts +6 -6
- package/dist/src/Evolu/Db.d.ts.map +1 -1
- package/dist/src/Evolu/Db.js +4 -0
- package/dist/src/Evolu/LocalAuth.d.ts +4 -6
- package/dist/src/Evolu/LocalAuth.d.ts.map +1 -1
- package/dist/src/Evolu/LocalAuth.js +14 -15
- package/dist/src/Evolu/Owner.d.ts +116 -86
- package/dist/src/Evolu/Owner.d.ts.map +1 -1
- package/dist/src/Evolu/Owner.js +48 -45
- package/dist/src/Evolu/Relay.d.ts +6 -5
- package/dist/src/Evolu/Relay.d.ts.map +1 -1
- package/dist/src/Evolu/Relay.js +40 -39
- package/dist/src/Evolu/Storage.d.ts +6 -11
- package/dist/src/Evolu/Storage.d.ts.map +1 -1
- package/dist/src/Evolu/Storage.js +30 -9
- package/dist/src/Evolu/Sync.d.ts +5 -5
- package/dist/src/Evolu/Sync.d.ts.map +1 -1
- package/dist/src/Evolu/Sync.js +3 -5
- package/dist/src/Evolu/Timestamp.d.ts +24 -0
- package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
- package/dist/src/Evolu/Timestamp.js +24 -0
- package/dist/src/Identicon.d.ts +35 -0
- package/dist/src/Identicon.d.ts.map +1 -0
- package/dist/src/Identicon.js +143 -0
- package/dist/src/ManyToManyMap.d.ts +0 -3
- package/dist/src/ManyToManyMap.d.ts.map +1 -1
- package/dist/src/Platform.d.ts +20 -0
- package/dist/src/Platform.d.ts.map +1 -0
- package/dist/src/Platform.js +22 -0
- package/dist/src/Result.d.ts +13 -6
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Sqlite.d.ts +36 -1
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +56 -3
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +36 -0
- package/dist/src/Type.d.ts +5 -8
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +11 -11
- package/dist/src/Types.d.ts +1 -1
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +2 -7
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/package.json +1 -1
- package/src/Assert.ts +0 -19
- package/src/Cache.ts +85 -0
- package/src/Evolu/Db.ts +11 -7
- package/src/Evolu/LocalAuth.ts +35 -22
- package/src/Evolu/Owner.ts +140 -103
- package/src/Evolu/Relay.ts +52 -48
- package/src/Evolu/Storage.ts +47 -23
- package/src/Evolu/Sync.ts +11 -12
- package/src/Evolu/Timestamp.ts +24 -0
- package/src/Identicon.ts +197 -0
- package/src/ManyToManyMap.ts +0 -3
- package/src/Platform.ts +26 -0
- package/src/Result.ts +13 -6
- package/src/Sqlite.ts +68 -5
- package/src/Task.ts +41 -0
- package/src/Type.ts +12 -15
- package/src/Types.ts +1 -1
- package/src/WebSocket.ts +6 -10
- package/src/index.ts +3 -0
package/dist/src/Evolu/Owner.js
CHANGED
|
@@ -3,19 +3,6 @@ import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
|
3
3
|
import { createSlip21, EncryptionKey, Entropy16, Entropy32, } from "../Crypto.js";
|
|
4
4
|
import { brand, Id, IdBytes, idBytesToId, idToIdBytes, } from "../Type.js";
|
|
5
5
|
import { getOrNull } from "../Result.js";
|
|
6
|
-
/**
|
|
7
|
-
* 32 bytes of cryptographic entropy used to derive {@link Owner} keys.
|
|
8
|
-
*
|
|
9
|
-
* Can be created using {@link createOwnerSecret} or converted from a
|
|
10
|
-
* {@link Mnemonic} using {@link mnemonicToOwnerSecret}.
|
|
11
|
-
*/
|
|
12
|
-
export const OwnerSecret = brand("OwnerSecret", Entropy32);
|
|
13
|
-
/** Creates a {@link OwnerSecret}. */
|
|
14
|
-
export const createOwnerSecret = (deps) => deps.randomBytes.create(32);
|
|
15
|
-
/** Converts an {@link OwnerSecret} to a {@link Mnemonic}. */
|
|
16
|
-
export const ownerSecretToMnemonic = (secret) => bip39.entropyToMnemonic(secret, wordlist);
|
|
17
|
-
/** Converts a {@link Mnemonic} to an {@link OwnerSecret}. */
|
|
18
|
-
export const mnemonicToOwnerSecret = (mnemonic) => bip39.mnemonicToEntropy(mnemonic, wordlist);
|
|
19
6
|
/**
|
|
20
7
|
* OwnerId is a branded {@link Id} that uniquely identifies an {@link Owner}.
|
|
21
8
|
* Branded from {@link Id} to leverage existing helpers like {@link idToIdBytes}.
|
|
@@ -32,6 +19,19 @@ export const OwnerEncryptionKey = brand("OwnerEncryptionKey", EncryptionKey);
|
|
|
32
19
|
* default and can be rotated via {@link createOwnerWriteKey}.
|
|
33
20
|
*/
|
|
34
21
|
export const OwnerWriteKey = brand("OwnerWriteKey", Entropy16);
|
|
22
|
+
/**
|
|
23
|
+
* 32 bytes of cryptographic entropy used to derive {@link Owner} keys.
|
|
24
|
+
*
|
|
25
|
+
* Can be created using {@link createOwnerSecret} or converted from a
|
|
26
|
+
* {@link Mnemonic} using {@link mnemonicToOwnerSecret}.
|
|
27
|
+
*/
|
|
28
|
+
export const OwnerSecret = brand("OwnerSecret", Entropy32);
|
|
29
|
+
/** Creates a {@link OwnerSecret}. */
|
|
30
|
+
export const createOwnerSecret = (deps) => deps.randomBytes.create(32);
|
|
31
|
+
/** Converts an {@link OwnerSecret} to a {@link Mnemonic}. */
|
|
32
|
+
export const ownerSecretToMnemonic = (secret) => bip39.entropyToMnemonic(secret, wordlist);
|
|
33
|
+
/** Converts a {@link Mnemonic} to an {@link OwnerSecret}. */
|
|
34
|
+
export const mnemonicToOwnerSecret = (mnemonic) => bip39.mnemonicToEntropy(mnemonic, wordlist);
|
|
35
35
|
/** Creates a randomly generated {@link OwnerWriteKey}. */
|
|
36
36
|
export const createOwnerWriteKey = (deps) => deps.randomBytes.create(16);
|
|
37
37
|
/**
|
|
@@ -56,38 +56,6 @@ export const createAppOwner = (secret) => ({
|
|
|
56
56
|
mnemonic: ownerSecretToMnemonic(secret),
|
|
57
57
|
...createOwner(secret),
|
|
58
58
|
});
|
|
59
|
-
/**
|
|
60
|
-
* Creates a {@link WebSocketTransportConfig} for the given relay URL and
|
|
61
|
-
* {@link OwnerId}.
|
|
62
|
-
*
|
|
63
|
-
* ### Example
|
|
64
|
-
*
|
|
65
|
-
* ```ts
|
|
66
|
-
* const transport = createWebSocketTransportConfig({
|
|
67
|
-
* relayUrl: "wss://relay.evolu.dev",
|
|
68
|
-
* ownerId: owner.id,
|
|
69
|
-
* });
|
|
70
|
-
* // Result: { type: "WebSocket", url: "wss://relay.evolu.dev?ownerId=..." }
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
export const createWebSocketTransportConfig = ({ relayUrl, ownerId, }) => ({
|
|
74
|
-
type: "WebSocket",
|
|
75
|
-
url: `${relayUrl}?ownerId=${ownerId}`,
|
|
76
|
-
});
|
|
77
|
-
/**
|
|
78
|
-
* Extracts {@link OwnerId} from a URL query string.
|
|
79
|
-
*
|
|
80
|
-
* Parses the query string `?ownerId=...` and validates that the extracted value
|
|
81
|
-
* is a valid {@link OwnerId}.
|
|
82
|
-
*
|
|
83
|
-
* ### Example
|
|
84
|
-
*
|
|
85
|
-
* ```ts
|
|
86
|
-
* parseOwnerIdFromUrl("/sync?ownerId=_12345678abcdefgh");
|
|
87
|
-
* // Returns: OwnerId or null
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
export const parseOwnerIdFromUrl = (url) => getOrNull(OwnerId.fromUnknown(url?.split("=")[1]));
|
|
91
59
|
/** Creates a {@link ShardOwner} from an {@link OwnerSecret}. */
|
|
92
60
|
export const createShardOwner = (secret, transports) => {
|
|
93
61
|
return {
|
|
@@ -143,3 +111,38 @@ export const createSharedReadonlyOwner = (sharedOwner) => ({
|
|
|
143
111
|
encryptionKey: sharedOwner.encryptionKey,
|
|
144
112
|
...(sharedOwner.transports && { transports: sharedOwner.transports }),
|
|
145
113
|
});
|
|
114
|
+
/**
|
|
115
|
+
* Creates an {@link OwnerWebSocketTransport} for the given relay URL and
|
|
116
|
+
* {@link OwnerId}.
|
|
117
|
+
*
|
|
118
|
+
* ### Example
|
|
119
|
+
*
|
|
120
|
+
* ```ts
|
|
121
|
+
* const transport = createOwnerWebSocketTransport({
|
|
122
|
+
* url: "wss://relay.evolu.dev",
|
|
123
|
+
* ownerId: owner.id,
|
|
124
|
+
* });
|
|
125
|
+
* // Result: { type: "WebSocket", url: "wss://relay.evolu.dev?ownerId=..." }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export const createOwnerWebSocketTransport = (config) => ({
|
|
129
|
+
type: "WebSocket",
|
|
130
|
+
url: `${config.url}?ownerId=${config.ownerId}`,
|
|
131
|
+
});
|
|
132
|
+
/**
|
|
133
|
+
* Extracts {@link OwnerId} from an {@link OwnerWebSocketTransport} URL query
|
|
134
|
+
* string.
|
|
135
|
+
*
|
|
136
|
+
* Parses the query string `?ownerId=...` and validates that the extracted value
|
|
137
|
+
* is a valid {@link OwnerId}.
|
|
138
|
+
*
|
|
139
|
+
* ### Example
|
|
140
|
+
*
|
|
141
|
+
* ```ts
|
|
142
|
+
* parseOwnerIdFromOwnerWebSocketTransportUrl(
|
|
143
|
+
* "/sync?ownerId=_12345678abcdefgh",
|
|
144
|
+
* );
|
|
145
|
+
* // Returns: OwnerId or null
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export const parseOwnerIdFromOwnerWebSocketTransportUrl = (url) => getOrNull(OwnerId.fromUnknown(url.split("=")[1]));
|
|
@@ -2,11 +2,11 @@ import { ConsoleConfig, ConsoleDep } from "../Console.js";
|
|
|
2
2
|
import { TimingSafeEqualDep } from "../Crypto.js";
|
|
3
3
|
import { LazyValue } from "../Function.js";
|
|
4
4
|
import { Result } from "../Result.js";
|
|
5
|
-
import { SqliteError } from "../Sqlite.js";
|
|
5
|
+
import { SqliteDep, SqliteError } from "../Sqlite.js";
|
|
6
6
|
import { SimpleName } from "../Type.js";
|
|
7
7
|
import { OwnerId } from "./Owner.js";
|
|
8
8
|
import { ProtocolInvalidDataError } from "./Protocol.js";
|
|
9
|
-
import {
|
|
9
|
+
import { CreateBaseSqliteStorageOptions, SqliteStorageDeps, Storage } from "./Storage.js";
|
|
10
10
|
export interface Relay extends Disposable {
|
|
11
11
|
}
|
|
12
12
|
export interface RelayConfig extends ConsoleConfig {
|
|
@@ -34,9 +34,9 @@ export interface RelayConfig extends ConsoleConfig {
|
|
|
34
34
|
* this only controls relay access, not write permissions. Since all data is
|
|
35
35
|
* encrypted on the relay, OwnerId exposure is safe.
|
|
36
36
|
*
|
|
37
|
-
* Owners specify which relays to connect to via {@link
|
|
37
|
+
* Owners specify which relays to connect to via {@link OwnerTransport}. In
|
|
38
38
|
* WebSocket-based implementations, this check occurs before accepting the
|
|
39
|
-
* connection, with the OwnerId typically extracted from the URL
|
|
39
|
+
* connection, with the OwnerId typically extracted from the URL Path (e.g.,
|
|
40
40
|
* `ws://localhost:4000/<ownerId>`).
|
|
41
41
|
*
|
|
42
42
|
* ### Example
|
|
@@ -55,7 +55,8 @@ export interface RelayConfig extends ConsoleConfig {
|
|
|
55
55
|
*/
|
|
56
56
|
readonly authenticateOwner?: (ownerId: OwnerId) => Promise<boolean>;
|
|
57
57
|
}
|
|
58
|
-
export declare const createRelaySqliteStorage: (deps: SqliteStorageDeps & TimingSafeEqualDep) => (options:
|
|
58
|
+
export declare const createRelaySqliteStorage: (deps: SqliteStorageDeps & TimingSafeEqualDep) => (options: CreateBaseSqliteStorageOptions) => Storage;
|
|
59
|
+
export declare const createRelayStorageTables: (deps: SqliteDep) => Result<void, SqliteError>;
|
|
59
60
|
export interface RelayLogger {
|
|
60
61
|
readonly started: (enableLogging: boolean, port: number) => void;
|
|
61
62
|
readonly storageError: (error: unknown) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAW,MAAM,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAO,WAAW,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Relay.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Relay.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Evolu/Relay.js
CHANGED
|
@@ -1,37 +1,12 @@
|
|
|
1
1
|
import { isNonEmptyReadonlyArray } from "../Array.js";
|
|
2
2
|
import { err, ok } from "../Result.js";
|
|
3
3
|
import { sql } from "../Sqlite.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createBaseSqliteStorage, } from "./Storage.js";
|
|
5
5
|
import { timestampToTimestampBytes } from "./Timestamp.js";
|
|
6
6
|
export const createRelaySqliteStorage = (deps) => (options) => {
|
|
7
|
-
const sqliteStorageBase =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (const query of [
|
|
11
|
-
sql `
|
|
12
|
-
create table if not exists evolu_writeKey (
|
|
13
|
-
"ownerId" blob not null,
|
|
14
|
-
"writeKey" blob not null,
|
|
15
|
-
primary key ("ownerId")
|
|
16
|
-
)
|
|
17
|
-
strict;
|
|
18
|
-
`,
|
|
19
|
-
sql `
|
|
20
|
-
create table if not exists evolu_message (
|
|
21
|
-
"ownerId" blob not null,
|
|
22
|
-
"timestamp" blob not null,
|
|
23
|
-
"change" blob not null,
|
|
24
|
-
primary key ("ownerId", "timestamp")
|
|
25
|
-
)
|
|
26
|
-
strict;
|
|
27
|
-
`,
|
|
28
|
-
]) {
|
|
29
|
-
const result = deps.sqlite.exec(query);
|
|
30
|
-
if (!result.ok)
|
|
31
|
-
return result;
|
|
32
|
-
}
|
|
33
|
-
return ok({
|
|
34
|
-
...sqliteStorageBase.value,
|
|
7
|
+
const sqliteStorageBase = createBaseSqliteStorage(deps)(options);
|
|
8
|
+
return {
|
|
9
|
+
...sqliteStorageBase,
|
|
35
10
|
/**
|
|
36
11
|
* Lazily authorizes the initiator's {@link OwnerWriteKey} for the given
|
|
37
12
|
* {@link OwnerId}.
|
|
@@ -83,7 +58,7 @@ export const createRelaySqliteStorage = (deps) => (options) => {
|
|
|
83
58
|
writeMessages: async (ownerId, messages) => {
|
|
84
59
|
const result = deps.sqlite.transaction(() => {
|
|
85
60
|
for (const message of messages) {
|
|
86
|
-
const insertTimestampResult = sqliteStorageBase.
|
|
61
|
+
const insertTimestampResult = sqliteStorageBase.insertTimestamp(ownerId, timestampToTimestampBytes(message.timestamp));
|
|
87
62
|
if (!insertTimestampResult.ok)
|
|
88
63
|
return insertTimestampResult;
|
|
89
64
|
const insertMessage = deps.sqlite.exec(sql `
|
|
@@ -121,18 +96,18 @@ export const createRelaySqliteStorage = (deps) => (options) => {
|
|
|
121
96
|
},
|
|
122
97
|
deleteOwner: (ownerId) => {
|
|
123
98
|
const result = deps.sqlite.transaction(() => {
|
|
124
|
-
const
|
|
99
|
+
const deleteWriteKey = deps.sqlite.exec(sql `
|
|
125
100
|
delete from evolu_writeKey where ownerId = ${ownerId};
|
|
126
101
|
`);
|
|
127
|
-
if (!
|
|
128
|
-
return
|
|
129
|
-
const
|
|
102
|
+
if (!deleteWriteKey.ok)
|
|
103
|
+
return deleteWriteKey;
|
|
104
|
+
const deleteMessages = deps.sqlite.exec(sql `
|
|
130
105
|
delete from evolu_message where ownerId = ${ownerId};
|
|
131
106
|
`);
|
|
132
|
-
if (!
|
|
133
|
-
return
|
|
134
|
-
const
|
|
135
|
-
if (!
|
|
107
|
+
if (!deleteMessages.ok)
|
|
108
|
+
return deleteMessages;
|
|
109
|
+
const deleteBaseOwner = sqliteStorageBase.deleteOwner(ownerId);
|
|
110
|
+
if (!deleteBaseOwner)
|
|
136
111
|
return err(null);
|
|
137
112
|
return ok();
|
|
138
113
|
});
|
|
@@ -143,7 +118,33 @@ export const createRelaySqliteStorage = (deps) => (options) => {
|
|
|
143
118
|
}
|
|
144
119
|
return true;
|
|
145
120
|
},
|
|
146
|
-
}
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
export const createRelayStorageTables = (deps) => {
|
|
124
|
+
for (const query of [
|
|
125
|
+
sql `
|
|
126
|
+
create table evolu_writeKey (
|
|
127
|
+
"ownerId" blob not null,
|
|
128
|
+
"writeKey" blob not null,
|
|
129
|
+
primary key ("ownerId")
|
|
130
|
+
)
|
|
131
|
+
strict;
|
|
132
|
+
`,
|
|
133
|
+
sql `
|
|
134
|
+
create table evolu_message (
|
|
135
|
+
"ownerId" blob not null,
|
|
136
|
+
"timestamp" blob not null,
|
|
137
|
+
"change" blob not null,
|
|
138
|
+
primary key ("ownerId", "timestamp")
|
|
139
|
+
)
|
|
140
|
+
strict;
|
|
141
|
+
`,
|
|
142
|
+
]) {
|
|
143
|
+
const result = deps.sqlite.exec(query);
|
|
144
|
+
if (!result.ok)
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
return ok();
|
|
147
148
|
};
|
|
148
149
|
export const createRelayLogger = (deps) => ({
|
|
149
150
|
started: (enableLogging, port) => {
|
|
@@ -165,7 +165,7 @@ export type DbChange = typeof DbChange.Type;
|
|
|
165
165
|
* users, and when it goes down, nothing happens, because it will be
|
|
166
166
|
* synchronized later.
|
|
167
167
|
*/
|
|
168
|
-
export interface
|
|
168
|
+
export interface BaseSqliteStorage extends Pick<Storage, "getSize" | "fingerprint" | "fingerprintRanges" | "findLowerBound" | "iterate" | "deleteOwner"> {
|
|
169
169
|
/**
|
|
170
170
|
* Inserts a timestamp for an owner into the skiplist-based storage.
|
|
171
171
|
*
|
|
@@ -174,21 +174,16 @@ export interface SqliteStorageBase {
|
|
|
174
174
|
* messages may be received and processed multiple times.
|
|
175
175
|
*/
|
|
176
176
|
readonly insertTimestamp: (ownerId: OwnerIdBytes, timestamp: TimestampBytes) => Result<void, SqliteError>;
|
|
177
|
-
readonly getSize: Storage["getSize"];
|
|
178
|
-
readonly fingerprint: Storage["fingerprint"];
|
|
179
|
-
readonly fingerprintRanges: Storage["fingerprintRanges"];
|
|
180
|
-
readonly findLowerBound: Storage["findLowerBound"];
|
|
181
|
-
readonly iterate: Storage["iterate"];
|
|
182
|
-
readonly deleteOwner: Storage["deleteOwner"];
|
|
183
177
|
}
|
|
184
|
-
export interface
|
|
185
|
-
readonly storage:
|
|
178
|
+
export interface BaseSqliteStorageDep {
|
|
179
|
+
readonly storage: BaseSqliteStorage;
|
|
186
180
|
}
|
|
187
181
|
export type SqliteStorageDeps = RandomDep & SqliteDep;
|
|
188
|
-
export interface
|
|
182
|
+
export interface CreateBaseSqliteStorageOptions {
|
|
189
183
|
onStorageError: (error: SqliteError) => void;
|
|
190
184
|
}
|
|
191
|
-
export declare const
|
|
185
|
+
export declare const createBaseSqliteStorage: (deps: SqliteStorageDeps) => (options: CreateBaseSqliteStorageOptions) => BaseSqliteStorage;
|
|
186
|
+
export declare const createBaseSqliteStorageTables: (deps: SqliteDep) => Result<void, SqliteError>;
|
|
192
187
|
export declare const timestampBytesToFingerprint: (timestamp: TimestampBytes) => Fingerprint;
|
|
193
188
|
export declare const getTimestampByIndex: (deps: SqliteDep) => (ownerId: OwnerIdBytes, index: NonNegativeInt) => Result<TimestampBytes, SqliteError>;
|
|
194
189
|
//# sourceMappingURL=Storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,12 +24,10 @@ export const DbChange = object({
|
|
|
24
24
|
id: Id,
|
|
25
25
|
values: record(String, SqliteValue),
|
|
26
26
|
});
|
|
27
|
-
export const
|
|
28
|
-
|
|
29
|
-
if (!createTablesResult.ok)
|
|
30
|
-
return createTablesResult;
|
|
27
|
+
export const createBaseSqliteStorage = (deps) => (options) => {
|
|
28
|
+
// TODO: Use OwnerUsage table.
|
|
31
29
|
const ownerStats = new Map();
|
|
32
|
-
return
|
|
30
|
+
return {
|
|
33
31
|
insertTimestamp: (ownerId, timestamp) => {
|
|
34
32
|
const ownerIdString = ownerIdBytesToOwnerId(ownerId);
|
|
35
33
|
const level = randomSkiplistLevel(deps);
|
|
@@ -147,12 +145,12 @@ export const createSqliteStorageBase = (deps) => (options) => {
|
|
|
147
145
|
}
|
|
148
146
|
return true;
|
|
149
147
|
},
|
|
150
|
-
}
|
|
148
|
+
};
|
|
151
149
|
};
|
|
152
150
|
const assertBeginEnd = (begin, end) => {
|
|
153
151
|
assert(begin <= end, "invalid begin or end");
|
|
154
152
|
};
|
|
155
|
-
const
|
|
153
|
+
export const createBaseSqliteStorageTables = (deps) => {
|
|
156
154
|
for (const query of [
|
|
157
155
|
/**
|
|
158
156
|
* Creates the `evolu_timestamp` table for storing timestamps of multiple
|
|
@@ -177,7 +175,7 @@ const createTables = (deps) => {
|
|
|
177
175
|
* enough even without it.
|
|
178
176
|
*/
|
|
179
177
|
sql `
|
|
180
|
-
create table
|
|
178
|
+
create table evolu_timestamp (
|
|
181
179
|
"ownerId" blob not null,
|
|
182
180
|
"t" blob not null,
|
|
183
181
|
"h1" integer,
|
|
@@ -189,7 +187,7 @@ const createTables = (deps) => {
|
|
|
189
187
|
strict;
|
|
190
188
|
`,
|
|
191
189
|
sql `
|
|
192
|
-
create index
|
|
190
|
+
create index evolu_timestamp_index on evolu_timestamp (
|
|
193
191
|
"ownerId",
|
|
194
192
|
"l",
|
|
195
193
|
"t",
|
|
@@ -197,6 +195,29 @@ const createTables = (deps) => {
|
|
|
197
195
|
"h2",
|
|
198
196
|
"c"
|
|
199
197
|
);
|
|
198
|
+
`,
|
|
199
|
+
/**
|
|
200
|
+
* Creates the `evolu_usage` table for tracking data consumption per owner.
|
|
201
|
+
*
|
|
202
|
+
* Columns:
|
|
203
|
+
*
|
|
204
|
+
* - `ownerId` – OwnerIdBytes (primary key)
|
|
205
|
+
* - `storedBytes` – total bytes stored in database
|
|
206
|
+
* - `receivedBytes` – total bytes received from clients
|
|
207
|
+
* - `sentBytes` – total bytes sent to clients
|
|
208
|
+
* - `firstTimestamp` – minimum timestamp (nullable)
|
|
209
|
+
* - `lastTimestamp` – maximum timestamp (nullable)
|
|
210
|
+
*/
|
|
211
|
+
sql `
|
|
212
|
+
create table evolu_usage (
|
|
213
|
+
"ownerId" blob primary key,
|
|
214
|
+
"storedBytes" integer not null,
|
|
215
|
+
"receivedBytes" integer not null,
|
|
216
|
+
"sentBytes" integer not null,
|
|
217
|
+
"firstTimestamp" blob,
|
|
218
|
+
"lastTimestamp" blob
|
|
219
|
+
)
|
|
220
|
+
strict;
|
|
200
221
|
`,
|
|
201
222
|
]) {
|
|
202
223
|
const result = deps.sqlite.exec(query);
|
package/dist/src/Evolu/Sync.d.ts
CHANGED
|
@@ -8,10 +8,10 @@ import { SqliteDep, SqliteError } from "../Sqlite.js";
|
|
|
8
8
|
import { TimeDep } from "../Time.js";
|
|
9
9
|
import { CreateWebSocketDep } from "../WebSocket.js";
|
|
10
10
|
import type { PostMessageDep } from "./Db.js";
|
|
11
|
-
import { AppOwner, OwnerEncryptionKey, OwnerId, OwnerIdBytes,
|
|
11
|
+
import { AppOwner, OwnerEncryptionKey, OwnerId, OwnerIdBytes, OwnerTransport, OwnerWriteKey } from "./Owner.js";
|
|
12
12
|
import { ProtocolError, ProtocolInvalidDataError, ProtocolTimestampMismatchError } from "./Protocol.js";
|
|
13
13
|
import { MutationChange } from "./Schema.js";
|
|
14
|
-
import {
|
|
14
|
+
import { BaseSqliteStorage, CrdtMessage, Storage } from "./Storage.js";
|
|
15
15
|
import { Millis, Timestamp, TimestampConfigDep, TimestampCounterOverflowError, TimestampDriftError, TimestampTimeOutOfRangeError } from "./Timestamp.js";
|
|
16
16
|
export interface Sync extends Disposable {
|
|
17
17
|
/**
|
|
@@ -40,11 +40,11 @@ export interface SyncOwner {
|
|
|
40
40
|
readonly encryptionKey: OwnerEncryptionKey;
|
|
41
41
|
/** Optional for read-only owners like {@link SharedReadonlyOwner}. */
|
|
42
42
|
readonly writeKey?: OwnerWriteKey;
|
|
43
|
-
readonly transports?: ReadonlyArray<
|
|
43
|
+
readonly transports?: ReadonlyArray<OwnerTransport>;
|
|
44
44
|
}
|
|
45
45
|
export interface SyncConfig {
|
|
46
46
|
readonly appOwner: AppOwner;
|
|
47
|
-
readonly transports: ReadonlyArray<
|
|
47
|
+
readonly transports: ReadonlyArray<OwnerTransport>;
|
|
48
48
|
/**
|
|
49
49
|
* Delay in milliseconds before disposing unused WebSocket connections.
|
|
50
50
|
* Defaults to 100ms.
|
|
@@ -62,7 +62,7 @@ export interface Clock {
|
|
|
62
62
|
readonly save: (timestamp: Timestamp) => Result<void, SqliteError>;
|
|
63
63
|
}
|
|
64
64
|
export declare const createClock: (deps: RandomBytesDep & SqliteDep) => (initialTimestamp?: Timestamp) => Clock;
|
|
65
|
-
export interface ClientStorage extends
|
|
65
|
+
export interface ClientStorage extends Storage, BaseSqliteStorage {
|
|
66
66
|
}
|
|
67
67
|
export interface ClientStorageDep {
|
|
68
68
|
readonly storage: ClientStorage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sync.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EACL,cAAc,EACd,2BAA2B,EAC3B,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAA2B,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGzE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAM,MAAM,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAO,SAAS,EAAE,WAAW,EAAe,MAAM,cAAc,CAAC;AAExE,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAa,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,OAAO,EACP,YAAY,EAGZ,
|
|
1
|
+
{"version":3,"file":"Sync.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Sync.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Evolu/Sync.js
CHANGED
|
@@ -10,7 +10,7 @@ import { createMutex } from "../Task.js";
|
|
|
10
10
|
import { idBytesToId, idToIdBytes } from "../Type.js";
|
|
11
11
|
import { ownerIdBytesToOwnerId, ownerIdToOwnerIdBytes, } from "./Owner.js";
|
|
12
12
|
import { applyProtocolMessageAsClient, createProtocolMessageForSync, createProtocolMessageForUnsubscribe, createProtocolMessageFromCrdtMessages, decryptAndDecodeDbChange, encodeAndEncryptDbChange, SubscriptionFlags, } from "./Protocol.js";
|
|
13
|
-
import {
|
|
13
|
+
import { createBaseSqliteStorage, } from "./Storage.js";
|
|
14
14
|
import { createInitialTimestamp, receiveTimestamp, sendTimestamp, timestampBytesToTimestamp, timestampToTimestampBytes, timestampToTimestampString, } from "./Timestamp.js";
|
|
15
15
|
export const createSync = (deps) => (config) => {
|
|
16
16
|
let isDisposed = false;
|
|
@@ -219,14 +219,12 @@ export const createClock = (deps) => (initialTimestamp = createInitialTimestamp(
|
|
|
219
219
|
};
|
|
220
220
|
};
|
|
221
221
|
const createClientStorage = (deps) => (config) => {
|
|
222
|
-
const sqliteStorageBase =
|
|
222
|
+
const sqliteStorageBase = createBaseSqliteStorage(deps)({
|
|
223
223
|
onStorageError: config.onError,
|
|
224
224
|
});
|
|
225
|
-
if (!sqliteStorageBase.ok)
|
|
226
|
-
return sqliteStorageBase;
|
|
227
225
|
const mutex = createMutex();
|
|
228
226
|
const storage = {
|
|
229
|
-
...sqliteStorageBase
|
|
227
|
+
...sqliteStorageBase,
|
|
230
228
|
validateWriteKey: constFalse,
|
|
231
229
|
setWriteKey: constFalse,
|
|
232
230
|
writeMessages: async (ownerIdBytes, encryptedMessages) => {
|
|
@@ -79,9 +79,33 @@ export declare const maxNodeId: NodeId;
|
|
|
79
79
|
/**
|
|
80
80
|
* Hybrid Logical Clock timestamp.
|
|
81
81
|
*
|
|
82
|
+
* Timestamps serve as globally unique, causally ordered identifiers for CRDT
|
|
83
|
+
* messages in Evolu's sync protocol.
|
|
84
|
+
*
|
|
85
|
+
* ### References
|
|
86
|
+
*
|
|
82
87
|
* - https://muratbuffalo.blogspot.com/2014/07/hybrid-logical-clocks.html
|
|
83
88
|
* - https://sergeiturukin.com/2017/06/26/hybrid-logical-clocks.html
|
|
84
89
|
* - https://jaredforsyth.com/posts/hybrid-logical-clocks/
|
|
90
|
+
*
|
|
91
|
+
* ### Privacy Considerations
|
|
92
|
+
*
|
|
93
|
+
* Timestamps are metadata visible to relays and collaborators. While it can be
|
|
94
|
+
* considered a privacy leak, let us explain why it's necessary, and how to
|
|
95
|
+
* avoid it if maximum privacy is required.
|
|
96
|
+
*
|
|
97
|
+
* With real-time communication, participants always see activity (receiving
|
|
98
|
+
* bytes). We cannot trust anyone not to store that information, so explicitly
|
|
99
|
+
* exposing timestamps doesn't add additional risk.
|
|
100
|
+
*
|
|
101
|
+
* If we really want not to leak user activity, we can implement a local write
|
|
102
|
+
* queue:
|
|
103
|
+
*
|
|
104
|
+
* 1. Write changes immediately to a local-only table
|
|
105
|
+
* 2. Periodically/randomly flush messages to sync tables
|
|
106
|
+
* 3. This decouples user activity from sync timing
|
|
107
|
+
*
|
|
108
|
+
* Tradeoff: It breaks real-time collaboration.
|
|
85
109
|
*/
|
|
86
110
|
export declare const Timestamp: import("../Type.js").ObjectType<{
|
|
87
111
|
millis: import("../Type.js").BrandType<import("../Type.js").BrandType<import("../Type.js").Type<"Brand", number & Brand<"Int"> & Brand<"NonNegative">, number, import("../Type.js").NonNegativeError, number & Brand<"Int">, import("../Type.js").IntError | import("../Type.js").NumberError>, `LessThanOrEqualTo${number}`, import("../Type.js").LessThanOrEqualToError<number>, import("../Type.js").NonNegativeError | import("../Type.js").IntError | import("../Type.js").NumberError>, "Millis", import("../Type.js").BrandWithoutRefineError<"Millis", import("../Type.js").NonNegativeError | import("../Type.js").IntError | import("../Type.js").NumberError | import("../Type.js").LessThanOrEqualToError<number>>, never>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Timestamp.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Timestamp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,OAAO,EAAE,KAAK,EAAmB,MAAM,aAAa,CAAC;AACrD,OAAO,EAAW,MAAM,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAEL,SAAS,EAOV,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,6BAA6B,GAC7B,4BAA4B,CAAC;AAEjC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC;CAChD;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,IAAI,EAAE,8BAA8B,CAAC;CAC/C;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,MAAM,gsBAGlB,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAC;AAExC,eAAO,MAAM,SAAS,EAAQ,MAAM,CAAC;AACrC,eAAO,MAAM,SAAS,EAA4B,MAAM,CAAC;AAEzD,eAAO,MAAM,OAAO,4rBAGnB,CAAC;AACF,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAE1C,eAAO,MAAM,UAAU,EAAQ,OAAO,CAAC;AACvC,eAAO,MAAM,UAAU,EAAY,OAAO,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,MAAM,wPAA4C,CAAC;AAChE,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAC;AAExC,eAAO,MAAM,SAAS,EAAyB,MAAM,CAAC;AACtD,eAAO,MAAM,SAAS,EAAyB,MAAM,CAAC;AAEtD
|
|
1
|
+
{"version":3,"file":"Timestamp.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Timestamp.ts"],"names":[],"mappings":"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"}
|
|
@@ -54,9 +54,33 @@ export const maxNodeId = "ffffffffffffffff";
|
|
|
54
54
|
/**
|
|
55
55
|
* Hybrid Logical Clock timestamp.
|
|
56
56
|
*
|
|
57
|
+
* Timestamps serve as globally unique, causally ordered identifiers for CRDT
|
|
58
|
+
* messages in Evolu's sync protocol.
|
|
59
|
+
*
|
|
60
|
+
* ### References
|
|
61
|
+
*
|
|
57
62
|
* - https://muratbuffalo.blogspot.com/2014/07/hybrid-logical-clocks.html
|
|
58
63
|
* - https://sergeiturukin.com/2017/06/26/hybrid-logical-clocks.html
|
|
59
64
|
* - https://jaredforsyth.com/posts/hybrid-logical-clocks/
|
|
65
|
+
*
|
|
66
|
+
* ### Privacy Considerations
|
|
67
|
+
*
|
|
68
|
+
* Timestamps are metadata visible to relays and collaborators. While it can be
|
|
69
|
+
* considered a privacy leak, let us explain why it's necessary, and how to
|
|
70
|
+
* avoid it if maximum privacy is required.
|
|
71
|
+
*
|
|
72
|
+
* With real-time communication, participants always see activity (receiving
|
|
73
|
+
* bytes). We cannot trust anyone not to store that information, so explicitly
|
|
74
|
+
* exposing timestamps doesn't add additional risk.
|
|
75
|
+
*
|
|
76
|
+
* If we really want not to leak user activity, we can implement a local write
|
|
77
|
+
* queue:
|
|
78
|
+
*
|
|
79
|
+
* 1. Write changes immediately to a local-only table
|
|
80
|
+
* 2. Periodically/randomly flush messages to sync tables
|
|
81
|
+
* 3. This decouples user activity from sync timing
|
|
82
|
+
*
|
|
83
|
+
* Tradeoff: It breaks real-time collaboration.
|
|
60
84
|
*/
|
|
61
85
|
export const Timestamp = object({
|
|
62
86
|
millis: Millis,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Brand } from "./Brand.js";
|
|
2
|
+
import { Id } from "./Type.js";
|
|
3
|
+
/**
|
|
4
|
+
* SVG string representing a visual identicon for an {@link Id}, created with
|
|
5
|
+
* {@link createIdenticon}.
|
|
6
|
+
*/
|
|
7
|
+
export type Identicon = string & Brand<"Identicon">;
|
|
8
|
+
/** {@link Identicon} style. */
|
|
9
|
+
export type IdenticonStyle = "github" | "quadrant" | "gradient" | "sutnar";
|
|
10
|
+
/**
|
|
11
|
+
* Creates a deterministic identicon SVG from an {@link Id}.
|
|
12
|
+
*
|
|
13
|
+
* Works with any {@link Id} including branded IDs like `OwnerId`, etc.
|
|
14
|
+
*
|
|
15
|
+
* Available styles:
|
|
16
|
+
*
|
|
17
|
+
* - `"github"` (default): 5x5 grid with horizontal mirroring (GitHub-style)
|
|
18
|
+
* - `"quadrant"`: 2x2 grid with direct RGB color mapping from bytes
|
|
19
|
+
* - `"gradient"`: Diagonal stripes with smooth color gradients
|
|
20
|
+
* - `"sutnar"`: Three compositional variants with adaptive colors
|
|
21
|
+
*
|
|
22
|
+
* ### Example
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* const svg = createIdenticon(id);
|
|
26
|
+
* const quadrantStyle = createIdenticon(id, "quadrant");
|
|
27
|
+
* const gradientStyle = createIdenticon(id, "gradient");
|
|
28
|
+
* const sutnarStyle = createIdenticon(id, "sutnar");
|
|
29
|
+
*
|
|
30
|
+
* // Works with branded IDs
|
|
31
|
+
* const ownerSvg = createIdenticon(ownerId);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare const createIdenticon: (id: Id, style?: IdenticonStyle) => Identicon;
|
|
35
|
+
//# sourceMappingURL=Identicon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Identicon.d.ts","sourceRoot":"","sources":["../../src/Identicon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,EAAE,EAAe,MAAM,WAAW,CAAC;AAG5C;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;AAEpD,+BAA+B;AAC/B,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,eAAe,GAC1B,IAAI,EAAE,EACN,QAAO,cAAyB,KAC/B,SA4JF,CAAC"}
|