@evolu/common 8.12.0 → 8.13.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/dist/src/Callbacks.d.ts +12 -1
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +3 -0
- package/dist/src/Error.d.ts +10 -4
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +10 -4
- package/dist/src/Object.d.ts +65 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +142 -0
- package/dist/src/Resource.d.ts +0 -5
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +6 -13
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +7 -0
- package/dist/src/Task.d.ts +10 -8
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +41 -5
- package/dist/src/Worker.d.ts +3 -3
- package/dist/src/local-first/Db.d.ts +8 -3
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +56 -19
- package/dist/src/local-first/Evolu.d.ts +81 -37
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +31 -6
- package/dist/src/local-first/Owner.d.ts +9 -0
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +9 -0
- package/dist/src/local-first/Protocol.d.ts +6 -4
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +1 -6
- package/dist/src/local-first/Schema.d.ts +7 -1
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Shared.d.ts +523 -133
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +696 -256
- package/dist/src/local-first/Storage.d.ts +16 -12
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Callbacks.test.ts +20 -0
- package/src/Callbacks.ts +17 -1
- package/src/Error.ts +10 -4
- package/src/Object.test.ts +296 -0
- package/src/Object.ts +163 -0
- package/src/Resource.test.ts +20 -16
- package/src/Resource.ts +6 -20
- package/src/Sqlite.ts +7 -0
- package/src/Task.test.ts +233 -62
- package/src/Task.ts +47 -13
- package/src/Worker.ts +3 -3
- package/src/local-first/Db.ts +88 -18
- package/src/local-first/Evolu.test.ts +281 -1
- package/src/local-first/Evolu.ts +124 -46
- package/src/local-first/Owner.ts +9 -0
- package/src/local-first/Protocol.test.ts +42 -60
- package/src/local-first/Protocol.ts +7 -9
- package/src/local-first/Schema.ts +8 -2
- package/src/local-first/Shared.test.ts +2633 -646
- package/src/local-first/Shared.ts +1192 -364
- package/src/local-first/Storage.ts +18 -19
|
@@ -950,7 +950,7 @@ describe("E2E errors", () => {
|
|
|
950
950
|
);
|
|
951
951
|
});
|
|
952
952
|
|
|
953
|
-
it("rejected relay
|
|
953
|
+
it("reports a rejected relay write as a quota and a thrown one as a write failure", async () => {
|
|
954
954
|
const deps = testCreateDeps();
|
|
955
955
|
const initiatorMessage = createProtocolMessageFromCrdtMessages(deps)(
|
|
956
956
|
testAppOwner,
|
|
@@ -961,50 +961,48 @@ describe("E2E errors", () => {
|
|
|
961
961
|
},
|
|
962
962
|
],
|
|
963
963
|
);
|
|
964
|
-
/** Returns the relay's response and what it logged for the
|
|
965
|
-
const relayResponseFor = async (
|
|
964
|
+
/** Returns the relay's response and what it logged for the write. */
|
|
965
|
+
const relayResponseFor = async (
|
|
966
|
+
writeMessages: StorageDep["storage"]["writeMessages"],
|
|
967
|
+
) => {
|
|
966
968
|
await using run = testCreateRun({
|
|
967
969
|
storage: {
|
|
968
970
|
...shouldNotBeCalledStorageDep.storage,
|
|
969
971
|
validateWriteKey: () => true,
|
|
970
|
-
writeMessages
|
|
972
|
+
writeMessages,
|
|
971
973
|
},
|
|
972
974
|
} satisfies StorageDep);
|
|
973
975
|
const { message } = await run.orThrow(
|
|
974
976
|
applyProtocolMessageAsRelay(initiatorMessage),
|
|
975
977
|
);
|
|
976
|
-
return {
|
|
977
|
-
message,
|
|
978
|
-
logged: run.deps.console
|
|
979
|
-
.getEntriesSnapshot()
|
|
980
|
-
.map(({ method, args }) => ({ method, args })),
|
|
981
|
-
};
|
|
978
|
+
return { message, logged: run.deps.console.getEntriesSnapshot() };
|
|
982
979
|
};
|
|
983
980
|
|
|
984
981
|
await using run = testCreateRun(shouldNotBeCalledStorageDep);
|
|
985
982
|
// A quota rejection is expected, so the relay does not log it.
|
|
986
|
-
const quota = await relayResponseFor(
|
|
987
|
-
type: "StorageQuotaError",
|
|
988
|
-
|
|
989
|
-
});
|
|
983
|
+
const quota = await relayResponseFor(
|
|
984
|
+
() => () => err({ type: "StorageQuotaError", ownerId: testAppOwner.id }),
|
|
985
|
+
);
|
|
990
986
|
assertEqual(
|
|
991
987
|
await run(applyProtocolMessageAsClient(quota.message)),
|
|
992
988
|
err({ type: "ProtocolQuotaError", ownerId: testAppOwner.id }),
|
|
993
989
|
);
|
|
994
990
|
assertEqual(quota.logged, []);
|
|
995
|
-
//
|
|
996
|
-
//
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
};
|
|
1002
|
-
const other = await relayResponseFor(mismatch);
|
|
991
|
+
// A thrown write is a relay write failure, not a quota, and the relay
|
|
992
|
+
// logs it.
|
|
993
|
+
const failure = new Error("write failed");
|
|
994
|
+
const thrown = await relayResponseFor(() => {
|
|
995
|
+
throw failure;
|
|
996
|
+
});
|
|
1003
997
|
assertEqual(
|
|
1004
|
-
await run(applyProtocolMessageAsClient(
|
|
998
|
+
await run(applyProtocolMessageAsClient(thrown.message)),
|
|
1005
999
|
err({ type: "ProtocolWriteError", ownerId: testAppOwner.id }),
|
|
1006
1000
|
);
|
|
1007
|
-
assertEqual(
|
|
1001
|
+
assertEqual(
|
|
1002
|
+
thrown.logged.map(({ method }) => method),
|
|
1003
|
+
["error"],
|
|
1004
|
+
);
|
|
1005
|
+
assertSame(thrown.logged[0]?.args[0], failure);
|
|
1008
1006
|
});
|
|
1009
1007
|
});
|
|
1010
1008
|
|
|
@@ -1239,7 +1237,7 @@ describe("applyProtocolMessageAsClient results", () => {
|
|
|
1239
1237
|
assertOk(result, { type: "Readonly" });
|
|
1240
1238
|
});
|
|
1241
1239
|
|
|
1242
|
-
it("preserves
|
|
1240
|
+
it("preserves a storage write rejection", async () => {
|
|
1243
1241
|
const deps = testCreateDeps();
|
|
1244
1242
|
const input = createResponse();
|
|
1245
1243
|
input.addMessage(
|
|
@@ -1250,42 +1248,26 @@ describe("applyProtocolMessageAsClient results", () => {
|
|
|
1250
1248
|
);
|
|
1251
1249
|
const message = input.unwrap();
|
|
1252
1250
|
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
{
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
error: new Error("decoding failed"),
|
|
1262
|
-
},
|
|
1263
|
-
{
|
|
1264
|
-
type: "ProtocolTimestampMismatchError",
|
|
1265
|
-
expected: timestampBytesToTimestamp(testTimestampsAsc[0]),
|
|
1266
|
-
timestamp: timestampBytesToTimestamp(testTimestampsAsc[1]),
|
|
1251
|
+
const error: StorageWriteMessagesError = {
|
|
1252
|
+
type: "StorageQuotaError",
|
|
1253
|
+
ownerId: testAppOwner.id,
|
|
1254
|
+
};
|
|
1255
|
+
await using run = testCreateRun({
|
|
1256
|
+
storage: {
|
|
1257
|
+
...shouldNotBeCalledStorageDep.storage,
|
|
1258
|
+
writeMessages: () => () => err(error),
|
|
1267
1259
|
},
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
writeKey: testAppOwner.writeKey,
|
|
1280
|
-
});
|
|
1281
|
-
assertType<
|
|
1282
|
-
InferTaskErr<typeof task>,
|
|
1283
|
-
ProtocolError | StorageWriteMessagesError
|
|
1284
|
-
>();
|
|
1285
|
-
const result = await run(task);
|
|
1286
|
-
assertErr(result);
|
|
1287
|
-
assertSame(result.error, error);
|
|
1288
|
-
}
|
|
1260
|
+
} satisfies StorageDep);
|
|
1261
|
+
const task = applyProtocolMessageAsClient(message, {
|
|
1262
|
+
writeKey: testAppOwner.writeKey,
|
|
1263
|
+
});
|
|
1264
|
+
assertType<
|
|
1265
|
+
InferTaskErr<typeof task>,
|
|
1266
|
+
ProtocolError | StorageWriteMessagesError
|
|
1267
|
+
>();
|
|
1268
|
+
const result = await run(task);
|
|
1269
|
+
assertErr(result);
|
|
1270
|
+
assertSame(result.error, error);
|
|
1289
1271
|
});
|
|
1290
1272
|
|
|
1291
1273
|
it("reports a thrown write as failed", async () => {
|
|
@@ -465,8 +465,9 @@ export interface ProtocolWriteKeyError
|
|
|
465
465
|
extends OwnerError, Typed<"ProtocolWriteKeyError"> {}
|
|
466
466
|
|
|
467
467
|
/**
|
|
468
|
-
* Error indicating a serious relay-side write failure.
|
|
469
|
-
*
|
|
468
|
+
* Error indicating a serious relay-side write failure. Sync state shows it as
|
|
469
|
+
* the failure of that relay's route; apps show a generic sync error for the
|
|
470
|
+
* owner's `Error` status.
|
|
470
471
|
*/
|
|
471
472
|
export interface ProtocolWriteError
|
|
472
473
|
extends OwnerError, Typed<"ProtocolWriteError"> {}
|
|
@@ -489,8 +490,9 @@ export interface ProtocolQuotaError
|
|
|
489
490
|
extends OwnerError, Typed<"ProtocolQuotaError"> {}
|
|
490
491
|
|
|
491
492
|
/**
|
|
492
|
-
* Error indicating a serious relay-side synchronization failure.
|
|
493
|
-
*
|
|
493
|
+
* Error indicating a serious relay-side synchronization failure. Sync state
|
|
494
|
+
* shows it as the failure of that relay's route; apps show a generic sync error
|
|
495
|
+
* for the owner's `Error` status.
|
|
494
496
|
*/
|
|
495
497
|
export interface ProtocolSyncError
|
|
496
498
|
extends OwnerError, Typed<"ProtocolSyncError"> {}
|
|
@@ -1286,13 +1288,9 @@ export const applyProtocolMessageAsRelay =
|
|
|
1286
1288
|
);
|
|
1287
1289
|
|
|
1288
1290
|
if (!result.ok) {
|
|
1289
|
-
const isQuotaError = result.error.type === "StorageQuotaError";
|
|
1290
|
-
if (!isQuotaError) run.deps.console.error(result.error);
|
|
1291
1291
|
const message = createProtocolMessageBuffer(ownerId, {
|
|
1292
1292
|
messageType: MessageType.Response,
|
|
1293
|
-
errorCode:
|
|
1294
|
-
? ProtocolErrorCode.QuotaError
|
|
1295
|
-
: ProtocolErrorCode.WriteError,
|
|
1293
|
+
errorCode: ProtocolErrorCode.QuotaError,
|
|
1296
1294
|
}).unwrap();
|
|
1297
1295
|
return ok({ type: "Response", message });
|
|
1298
1296
|
}
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
type withDefault,
|
|
44
44
|
} from "../Type.ts";
|
|
45
45
|
import type { CompileTimeError, Simplify } from "../Types.ts";
|
|
46
|
-
import type { Evolu, maxMutationSize } from "./Evolu.ts";
|
|
46
|
+
import type { Evolu, EvoluErrorDep, maxMutationSize } from "./Evolu.ts";
|
|
47
47
|
import type { AppOwner, OwnerIdBytes } from "./Owner.ts";
|
|
48
48
|
import {
|
|
49
49
|
OwnerEncryptionKey,
|
|
@@ -514,7 +514,13 @@ export interface MutationOptions {
|
|
|
514
514
|
* Called after the mutation's changes are stored and subscribed queries
|
|
515
515
|
* reflect them. Useful for follow-up work (e.g., notifications, navigation)
|
|
516
516
|
* after insert, update, or upsert. It never runs when the database is
|
|
517
|
-
* unavailable
|
|
517
|
+
* unavailable or the mutation could not be stored, which
|
|
518
|
+
* {@link EvoluErrorDep.evoluError} reports.
|
|
519
|
+
*
|
|
520
|
+
* An Evolu instance stores its mutations in batches, each in one transaction.
|
|
521
|
+
* A batch holds the mutations made before Evolu sends it in a microtask,
|
|
522
|
+
* usually one synchronous block, and {@link Evolu.requestSync} sends it early.
|
|
523
|
+
* When one mutation cannot be stored, none of its batch is.
|
|
518
524
|
*
|
|
519
525
|
* Stored does not always mean visible. A change quarantined for
|
|
520
526
|
* {@link QuarantineReason.TimestampDrift} is stored in
|