@evolu/common 4.1.0 → 5.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 +1 -1
- package/dist/src/Config.d.ts +26 -27
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Config.js +35 -7
- package/dist/src/Crdt.d.ts +5 -4
- package/dist/src/Crdt.d.ts.map +1 -1
- package/dist/src/Crdt.js +27 -24
- package/dist/src/Crypto.d.ts +16 -10
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +24 -11
- package/dist/src/Db.d.ts +58 -78
- package/dist/src/Db.d.ts.map +1 -1
- package/dist/src/Db.js +383 -185
- package/dist/src/Diff.d.ts +9 -3
- package/dist/src/Diff.d.ts.map +1 -1
- package/dist/src/Diff.js +12 -11
- package/dist/src/Error.d.ts +15 -0
- package/dist/src/Error.d.ts.map +1 -0
- package/dist/src/Error.js +14 -0
- package/dist/src/Evolu.d.ts +139 -91
- package/dist/src/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu.js +267 -239
- package/dist/src/Model.d.ts +52 -0
- package/dist/src/Model.d.ts.map +1 -1
- package/dist/src/Model.js +47 -5
- package/dist/src/Owner.d.ts.map +1 -1
- package/dist/src/Owner.js +14 -8
- package/dist/src/Platform.d.ts +24 -34
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +9 -31
- package/dist/src/Public.d.ts +3 -5
- package/dist/src/Public.d.ts.map +1 -1
- package/dist/src/Public.js +2 -3
- package/dist/src/Sqlite.d.ts +25 -15
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +28 -7
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Sync.d.ts +70 -0
- package/dist/src/Sync.d.ts.map +1 -0
- package/dist/src/Sync.js +126 -0
- package/dist/src/index.d.ts +2 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -5
- package/package.json +12 -10
- package/src/Config.ts +82 -39
- package/src/Crdt.ts +61 -76
- package/src/Crypto.ts +83 -60
- package/src/Db.ts +802 -370
- package/src/Diff.ts +23 -21
- package/src/Error.ts +29 -0
- package/src/Evolu.ts +599 -499
- package/src/Model.ts +102 -6
- package/src/Owner.ts +24 -24
- package/src/Platform.ts +33 -81
- package/src/Public.ts +3 -5
- package/src/Sqlite.ts +81 -27
- package/src/Sync.ts +306 -0
- package/src/index.ts +2 -5
- package/dist/src/DbWorker.d.ts +0 -82
- package/dist/src/DbWorker.d.ts.map +0 -1
- package/dist/src/DbWorker.js +0 -335
- package/dist/src/ErrorStore.d.ts +0 -29
- package/dist/src/ErrorStore.d.ts.map +0 -1
- package/dist/src/ErrorStore.js +0 -13
- package/dist/src/OnCompletes.d.ts +0 -14
- package/dist/src/OnCompletes.d.ts.map +0 -1
- package/dist/src/OnCompletes.js +0 -25
- package/dist/src/SyncWorker.d.ts +0 -80
- package/dist/src/SyncWorker.d.ts.map +0 -1
- package/dist/src/SyncWorker.js +0 -150
- package/dist/src/Types.d.ts +0 -13
- package/dist/src/Types.d.ts.map +0 -1
- package/dist/src/Types.js +0 -1
- package/src/DbWorker.ts +0 -721
- package/src/ErrorStore.ts +0 -46
- package/src/OnCompletes.ts +0 -51
- package/src/SyncWorker.ts +0 -361
- package/src/Types.ts +0 -11
package/src/ErrorStore.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import { TimestampError } from "./Crdt.js";
|
|
3
|
-
import { makeStore } from "./Store.js";
|
|
4
|
-
|
|
5
|
-
export const makeErrorStore = makeStore<EvoluError | null>(null);
|
|
6
|
-
|
|
7
|
-
/** The EvoluError type is used to represent errors that can occur in Evolu. */
|
|
8
|
-
export type EvoluError = UnexpectedError | TimestampError;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The UnexpectedError interface is designed to represent errors that can occur
|
|
12
|
-
* unexpectedly anywhere, even in third-party libraries, because Evolu uses
|
|
13
|
-
* Effect to track all errors.
|
|
14
|
-
*/
|
|
15
|
-
export interface UnexpectedError {
|
|
16
|
-
readonly _tag: "UnexpectedError";
|
|
17
|
-
readonly error: TransferableError;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const makeUnexpectedError = (
|
|
21
|
-
error: unknown,
|
|
22
|
-
): Effect.Effect<never, UnexpectedError> => {
|
|
23
|
-
const isError = error instanceof Error;
|
|
24
|
-
|
|
25
|
-
return Effect.fail({
|
|
26
|
-
_tag: "UnexpectedError",
|
|
27
|
-
error: {
|
|
28
|
-
message: isError ? error.message : String(error),
|
|
29
|
-
stack: isError ? error.stack : undefined,
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* We can't use the whole error because of WebWorker postMessage DataCloneError
|
|
36
|
-
* in Safari and Firefox. TODO:
|
|
37
|
-
* https://discord.com/channels/795981131316985866/795983589644304396/1096736473396564079
|
|
38
|
-
*/
|
|
39
|
-
interface TransferableError {
|
|
40
|
-
readonly message: string;
|
|
41
|
-
readonly stack: string | undefined;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface EvoluTypeError<E extends string> {
|
|
45
|
-
readonly __evoluTypeError__: E;
|
|
46
|
-
}
|
package/src/OnCompletes.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as Brand from "effect/Brand";
|
|
2
|
-
import * as Context from "effect/Context";
|
|
3
|
-
import * as Effect from "effect/Effect";
|
|
4
|
-
import * as Layer from "effect/Layer";
|
|
5
|
-
import * as Option from "effect/Option";
|
|
6
|
-
import * as ReadonlyArray from "effect/ReadonlyArray";
|
|
7
|
-
import { NanoId, NanoIdGenerator } from "./Crypto.js";
|
|
8
|
-
|
|
9
|
-
export interface OnCompletes {
|
|
10
|
-
readonly add: (onComplete: OnComplete) => Effect.Effect<OnCompleteId>;
|
|
11
|
-
|
|
12
|
-
readonly complete: (
|
|
13
|
-
onCompleteIds: readonly OnCompleteId[],
|
|
14
|
-
) => Effect.Effect<void>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type OnComplete = () => void;
|
|
18
|
-
|
|
19
|
-
export type OnCompleteId = NanoId & Brand.Brand<"OnCompleteId">;
|
|
20
|
-
|
|
21
|
-
export const OnCompletes = Context.GenericTag<OnCompletes>(
|
|
22
|
-
"@services/OnCompletes",
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
export const OnCompletesLive = Layer.effect(
|
|
26
|
-
OnCompletes,
|
|
27
|
-
Effect.gen(function* (_) {
|
|
28
|
-
const { nanoid } = yield* _(NanoIdGenerator);
|
|
29
|
-
const map = new Map<OnCompleteId, OnComplete>();
|
|
30
|
-
|
|
31
|
-
return OnCompletes.of({
|
|
32
|
-
add: (onComplete) =>
|
|
33
|
-
nanoid.pipe(
|
|
34
|
-
Effect.map((nanoid) => {
|
|
35
|
-
const id = nanoid as OnCompleteId;
|
|
36
|
-
map.set(id, onComplete);
|
|
37
|
-
return id;
|
|
38
|
-
}),
|
|
39
|
-
),
|
|
40
|
-
|
|
41
|
-
complete: (onCompleteIds) =>
|
|
42
|
-
Effect.sync(() => {
|
|
43
|
-
ReadonlyArray.filterMap(onCompleteIds, (id) => {
|
|
44
|
-
const onComplete = map.get(id);
|
|
45
|
-
map.delete(id);
|
|
46
|
-
return Option.fromNullable(onComplete);
|
|
47
|
-
}).forEach((onComplete) => onComplete());
|
|
48
|
-
}),
|
|
49
|
-
});
|
|
50
|
-
}),
|
|
51
|
-
);
|
package/src/SyncWorker.ts
DELETED
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import * as S from "@effect/schema/Schema";
|
|
2
|
-
import { concatBytes } from "@noble/ciphers/utils";
|
|
3
|
-
import { BinaryReader, BinaryWriter } from "@protobuf-ts/runtime";
|
|
4
|
-
import * as Context from "effect/Context";
|
|
5
|
-
import * as Effect from "effect/Effect";
|
|
6
|
-
import * as Equivalence from "effect/Equivalence";
|
|
7
|
-
import * as Function from "effect/Function";
|
|
8
|
-
import { absurd, identity } from "effect/Function";
|
|
9
|
-
import * as Layer from "effect/Layer";
|
|
10
|
-
import * as Match from "effect/Match";
|
|
11
|
-
import * as Option from "effect/Option";
|
|
12
|
-
import * as Predicate from "effect/Predicate";
|
|
13
|
-
import * as ReadonlyArray from "effect/ReadonlyArray";
|
|
14
|
-
import {
|
|
15
|
-
MerkleTree,
|
|
16
|
-
Millis,
|
|
17
|
-
Timestamp,
|
|
18
|
-
TimestampString,
|
|
19
|
-
merkleTreeToString,
|
|
20
|
-
unsafeMerkleTreeFromString,
|
|
21
|
-
} from "./Crdt.js";
|
|
22
|
-
import { SecretBox } from "./Crypto.js";
|
|
23
|
-
import { UnexpectedError, makeUnexpectedError } from "./ErrorStore.js";
|
|
24
|
-
import { Id } from "./Model.js";
|
|
25
|
-
import { Owner } from "./Owner.js";
|
|
26
|
-
import { Fetch, SyncLock } from "./Platform.js";
|
|
27
|
-
import {
|
|
28
|
-
EncryptedMessage,
|
|
29
|
-
MessageContent,
|
|
30
|
-
SyncRequest,
|
|
31
|
-
SyncResponse,
|
|
32
|
-
} from "./Protobuf.js";
|
|
33
|
-
import { JsonObjectOrArray, Value } from "./Sqlite.js";
|
|
34
|
-
import { Messaging } from "./Types.js";
|
|
35
|
-
|
|
36
|
-
export interface SyncWorker
|
|
37
|
-
extends Messaging<SyncWorkerInput, SyncWorkerOutput> {}
|
|
38
|
-
export const SyncWorker = Context.GenericTag<SyncWorker>(
|
|
39
|
-
"@services/SyncWorker",
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
export type SyncWorkerPostMessage = SyncWorker["postMessage"];
|
|
43
|
-
|
|
44
|
-
export const SyncWorkerPostMessage = Context.GenericTag<SyncWorkerPostMessage>(
|
|
45
|
-
"@services/SyncWorkerPostMessage",
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
export type SyncWorkerInput =
|
|
49
|
-
| SyncWorkerInputSync
|
|
50
|
-
| SyncWorkerInputSyncCompleted;
|
|
51
|
-
|
|
52
|
-
interface SyncWorkerInputSync {
|
|
53
|
-
readonly _tag: "sync";
|
|
54
|
-
readonly syncUrl: string;
|
|
55
|
-
readonly messages: ReadonlyArray<Message>;
|
|
56
|
-
readonly merkleTree: MerkleTree;
|
|
57
|
-
readonly timestamp: Timestamp;
|
|
58
|
-
readonly owner: Owner;
|
|
59
|
-
readonly syncLoopCount: number;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface NewMessage {
|
|
63
|
-
readonly table: string;
|
|
64
|
-
readonly row: Id;
|
|
65
|
-
readonly column: string;
|
|
66
|
-
readonly value: Value;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export const NewMessageEquivalence: Equivalence.Equivalence<NewMessage> = (
|
|
70
|
-
a,
|
|
71
|
-
b,
|
|
72
|
-
) =>
|
|
73
|
-
a.table === b.table &&
|
|
74
|
-
a.row === b.row &&
|
|
75
|
-
a.column === b.column &&
|
|
76
|
-
JSON.stringify(a.value) === JSON.stringify(b.value);
|
|
77
|
-
|
|
78
|
-
export interface Message extends NewMessage {
|
|
79
|
-
readonly timestamp: TimestampString;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
interface SyncWorkerInputSyncCompleted {
|
|
83
|
-
readonly _tag: "syncCompleted";
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
type SyncWorkerOnMessage = SyncWorker["onMessage"];
|
|
87
|
-
|
|
88
|
-
const SyncWorkerOnMessage = Context.GenericTag<SyncWorkerOnMessage>(
|
|
89
|
-
"@services/SyncWorkerOnMessage",
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
export type SyncWorkerOutput =
|
|
93
|
-
| UnexpectedError
|
|
94
|
-
| SyncWorkerOutputSyncResponse
|
|
95
|
-
| SyncStateIsNotSyncedError
|
|
96
|
-
| SyncStateIsSyncing;
|
|
97
|
-
|
|
98
|
-
export type SyncWorkerOutputSyncResponse = {
|
|
99
|
-
readonly _tag: "SyncWorkerOutputSyncResponse";
|
|
100
|
-
readonly messages: ReadonlyArray<Message>;
|
|
101
|
-
readonly merkleTree: MerkleTree;
|
|
102
|
-
readonly syncLoopCount: number;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* The SyncState type represents the various states that a synchronization
|
|
107
|
-
* process can be in. Evolu apps should not bother users with an "unsynced"
|
|
108
|
-
* state. Instead, they should warn users if data have not been synced for too
|
|
109
|
-
* long (a week, for example).
|
|
110
|
-
*/
|
|
111
|
-
export type SyncState =
|
|
112
|
-
| SyncStateInitial
|
|
113
|
-
| SyncStateIsSyncing
|
|
114
|
-
| SyncStateIsSynced
|
|
115
|
-
| SyncStateIsNotSyncedError;
|
|
116
|
-
|
|
117
|
-
export interface SyncStateInitial {
|
|
118
|
-
readonly _tag: "SyncStateInitial";
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
export interface SyncStateIsSyncing {
|
|
122
|
-
readonly _tag: "SyncStateIsSyncing";
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export interface SyncStateIsSynced {
|
|
126
|
-
readonly _tag: "SyncStateIsSynced";
|
|
127
|
-
readonly time: Millis;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
export interface SyncStateIsNotSyncedError {
|
|
131
|
-
readonly _tag: "SyncStateIsNotSyncedError";
|
|
132
|
-
readonly error:
|
|
133
|
-
| SyncStateNetworkError
|
|
134
|
-
| SyncStateServerError
|
|
135
|
-
| SyncStatePaymentRequiredError;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export interface SyncStateNetworkError {
|
|
139
|
-
readonly _tag: "NetworkError";
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export interface SyncStateServerError {
|
|
143
|
-
readonly _tag: "ServerError";
|
|
144
|
-
readonly status: number;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export interface SyncStatePaymentRequiredError {
|
|
148
|
-
readonly _tag: "PaymentRequiredError";
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const version1 = new Uint8Array([0, 1]);
|
|
152
|
-
|
|
153
|
-
const valueToProtobuf = (value: Value): MessageContent["value"] => {
|
|
154
|
-
switch (typeof value) {
|
|
155
|
-
case "string":
|
|
156
|
-
return { oneofKind: "stringValue", stringValue: value };
|
|
157
|
-
case "number":
|
|
158
|
-
return {
|
|
159
|
-
oneofKind: "numberValue",
|
|
160
|
-
numberValue: S.encodeSync(S.NumberFromString)(value),
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
if (value == null) return { oneofKind: undefined };
|
|
164
|
-
if (Predicate.isUint8Array(value))
|
|
165
|
-
return { oneofKind: "bytesValue", bytesValue: value };
|
|
166
|
-
return { oneofKind: "jsonValue", jsonValue: JSON.stringify(value) };
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const valueFromProtobuf = (value: MessageContent["value"]): Value => {
|
|
170
|
-
switch (value.oneofKind) {
|
|
171
|
-
case "numberValue":
|
|
172
|
-
return S.decodeSync(S.NumberFromString)(value.numberValue);
|
|
173
|
-
case "stringValue":
|
|
174
|
-
return value.stringValue;
|
|
175
|
-
case "bytesValue":
|
|
176
|
-
return value.bytesValue;
|
|
177
|
-
case "jsonValue":
|
|
178
|
-
return JSON.parse(value.jsonValue) as JsonObjectOrArray;
|
|
179
|
-
case undefined:
|
|
180
|
-
return null;
|
|
181
|
-
default:
|
|
182
|
-
return absurd(value);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const newMessageToBinary = ({ value, ...rest }: NewMessage): Uint8Array =>
|
|
187
|
-
concatBytes(
|
|
188
|
-
version1,
|
|
189
|
-
MessageContent.toBinary(
|
|
190
|
-
{ value: valueToProtobuf(value), ...rest },
|
|
191
|
-
binaryWriteOptions,
|
|
192
|
-
),
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
const startsWithArray = (array: Uint8Array, prefix: Uint8Array): boolean => {
|
|
196
|
-
if (prefix.length > array.length) return false;
|
|
197
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
198
|
-
if (array[i] !== prefix[i]) return false;
|
|
199
|
-
}
|
|
200
|
-
return true;
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const newMessageFromBinary = (
|
|
204
|
-
binary: Uint8Array,
|
|
205
|
-
): Option.Option<NewMessage> => {
|
|
206
|
-
if (!startsWithArray(binary, version1)) return Option.none();
|
|
207
|
-
const { value, ...content } = MessageContent.fromBinary(
|
|
208
|
-
binary.slice(version1.length),
|
|
209
|
-
binaryReadOptions,
|
|
210
|
-
);
|
|
211
|
-
return Option.some({ value: valueFromProtobuf(value), ...content });
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// The 'protobuf-ts' uses TextEncoder, but polyfill fast-text-encoding
|
|
215
|
-
// doesn't support the fatal option.
|
|
216
|
-
// https://github.com/timostamm/protobuf-ts/issues/184#issuecomment-1658443836
|
|
217
|
-
const binaryWriteOptions = {
|
|
218
|
-
writerFactory: (): BinaryWriter =>
|
|
219
|
-
new BinaryWriter({
|
|
220
|
-
encode: (input: string): Uint8Array => new TextEncoder().encode(input),
|
|
221
|
-
}),
|
|
222
|
-
};
|
|
223
|
-
const binaryReadOptions = {
|
|
224
|
-
readerFactory: (bytes: Uint8Array): BinaryReader =>
|
|
225
|
-
new BinaryReader(bytes, {
|
|
226
|
-
decode: (input: Uint8Array): string => new TextDecoder().decode(input),
|
|
227
|
-
}),
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
const sync = (
|
|
231
|
-
input: SyncWorkerInputSync,
|
|
232
|
-
): Effect.Effect<
|
|
233
|
-
void,
|
|
234
|
-
never,
|
|
235
|
-
SyncLock | SyncWorkerOnMessage | Fetch | SecretBox
|
|
236
|
-
> =>
|
|
237
|
-
Effect.gen(function* (_) {
|
|
238
|
-
const syncLock = yield* _(SyncLock);
|
|
239
|
-
const syncWorkerOnMessage = yield* _(SyncWorkerOnMessage);
|
|
240
|
-
const fetch = yield* _(Fetch);
|
|
241
|
-
const secretBox = yield* _(SecretBox);
|
|
242
|
-
|
|
243
|
-
if (input.syncLoopCount === 0) {
|
|
244
|
-
if (!(yield* _(syncLock.acquire))) return;
|
|
245
|
-
syncWorkerOnMessage({ _tag: "SyncStateIsSyncing" });
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
yield* _(
|
|
249
|
-
Effect.forEach(input.messages, ({ timestamp, ...newMessage }) =>
|
|
250
|
-
secretBox
|
|
251
|
-
.seal(input.owner.encryptionKey, newMessageToBinary(newMessage))
|
|
252
|
-
.pipe(
|
|
253
|
-
Effect.map((content): EncryptedMessage => ({ timestamp, content })),
|
|
254
|
-
),
|
|
255
|
-
),
|
|
256
|
-
Effect.map((messages) =>
|
|
257
|
-
SyncRequest.toBinary(
|
|
258
|
-
{
|
|
259
|
-
messages,
|
|
260
|
-
userId: input.owner.id,
|
|
261
|
-
nodeId: input.timestamp.node,
|
|
262
|
-
merkleTree: merkleTreeToString(input.merkleTree),
|
|
263
|
-
},
|
|
264
|
-
binaryWriteOptions,
|
|
265
|
-
),
|
|
266
|
-
),
|
|
267
|
-
Effect.flatMap((body) => fetch(input.syncUrl, body)),
|
|
268
|
-
Effect.catchTag("FetchError", () =>
|
|
269
|
-
Effect.fail<SyncStateIsNotSyncedError>({
|
|
270
|
-
_tag: "SyncStateIsNotSyncedError",
|
|
271
|
-
error: { _tag: "NetworkError" },
|
|
272
|
-
}),
|
|
273
|
-
),
|
|
274
|
-
Effect.flatMap((response) => {
|
|
275
|
-
switch (response.status) {
|
|
276
|
-
case 402:
|
|
277
|
-
return Effect.fail<SyncStateIsNotSyncedError>({
|
|
278
|
-
_tag: "SyncStateIsNotSyncedError",
|
|
279
|
-
error: { _tag: "PaymentRequiredError" },
|
|
280
|
-
});
|
|
281
|
-
case 200:
|
|
282
|
-
return Effect.promise(() =>
|
|
283
|
-
response
|
|
284
|
-
.arrayBuffer()
|
|
285
|
-
.then((buffer) => new Uint8Array(buffer))
|
|
286
|
-
.then((array) =>
|
|
287
|
-
SyncResponse.fromBinary(array, binaryReadOptions),
|
|
288
|
-
),
|
|
289
|
-
);
|
|
290
|
-
default:
|
|
291
|
-
return Effect.fail<SyncStateIsNotSyncedError>({
|
|
292
|
-
_tag: "SyncStateIsNotSyncedError",
|
|
293
|
-
error: { _tag: "ServerError", status: response.status },
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
}),
|
|
297
|
-
Effect.flatMap((syncResponse) =>
|
|
298
|
-
Effect.forEach(syncResponse.messages, ({ timestamp, content }) =>
|
|
299
|
-
secretBox
|
|
300
|
-
.open(input.owner.encryptionKey, content)
|
|
301
|
-
.pipe(
|
|
302
|
-
Effect.map(newMessageFromBinary),
|
|
303
|
-
Effect.map(
|
|
304
|
-
Option.map(
|
|
305
|
-
(newMessage): Message => ({ timestamp, ...newMessage }),
|
|
306
|
-
),
|
|
307
|
-
),
|
|
308
|
-
),
|
|
309
|
-
).pipe(
|
|
310
|
-
Effect.map(
|
|
311
|
-
(messages): SyncWorkerOutputSyncResponse => ({
|
|
312
|
-
_tag: "SyncWorkerOutputSyncResponse",
|
|
313
|
-
messages: ReadonlyArray.filterMap(messages, identity),
|
|
314
|
-
merkleTree: unsafeMerkleTreeFromString(syncResponse.merkleTree),
|
|
315
|
-
syncLoopCount: input.syncLoopCount,
|
|
316
|
-
}),
|
|
317
|
-
),
|
|
318
|
-
),
|
|
319
|
-
),
|
|
320
|
-
Effect.tapError(() => syncLock.release),
|
|
321
|
-
Effect.merge,
|
|
322
|
-
Effect.map(syncWorkerOnMessage),
|
|
323
|
-
);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
export const SyncWorkerCommonLive = Layer.effect(
|
|
327
|
-
SyncWorker,
|
|
328
|
-
Effect.gen(function* (_) {
|
|
329
|
-
const syncLock = yield* _(SyncLock);
|
|
330
|
-
|
|
331
|
-
const onError = (error: UnexpectedError): Effect.Effect<void> =>
|
|
332
|
-
Effect.sync(() => {
|
|
333
|
-
syncWorker.onMessage(error);
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
const context = Context.empty().pipe(
|
|
337
|
-
Context.add(SyncLock, syncLock),
|
|
338
|
-
Context.add(Fetch, yield* _(Fetch)),
|
|
339
|
-
Context.add(SecretBox, yield* _(SecretBox)),
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
const syncWorker: SyncWorker = {
|
|
343
|
-
postMessage: (input) => {
|
|
344
|
-
Match.value(input).pipe(
|
|
345
|
-
Match.tagsExhaustive({
|
|
346
|
-
sync,
|
|
347
|
-
syncCompleted: () => syncLock.release,
|
|
348
|
-
}),
|
|
349
|
-
Effect.catchAllDefect(makeUnexpectedError),
|
|
350
|
-
Effect.catchAll(onError),
|
|
351
|
-
Effect.provide(context),
|
|
352
|
-
Effect.provideService(SyncWorkerOnMessage, syncWorker.onMessage),
|
|
353
|
-
Effect.runPromise,
|
|
354
|
-
);
|
|
355
|
-
},
|
|
356
|
-
onMessage: Function.constVoid,
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
return syncWorker;
|
|
360
|
-
}),
|
|
361
|
-
);
|
package/src/Types.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Add types for WebWorker and BroadcastChannel. Messages must have a `_tag`
|
|
3
|
-
* property.
|
|
4
|
-
*/
|
|
5
|
-
export interface Messaging<
|
|
6
|
-
Input extends { _tag: string },
|
|
7
|
-
Output extends { _tag: string },
|
|
8
|
-
> {
|
|
9
|
-
readonly postMessage: (input: Input) => void;
|
|
10
|
-
onMessage: (output: Output) => void;
|
|
11
|
-
}
|