on-zero 0.4.30 → 0.4.32
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/cjs/createZeroClient.authData.test.cjs +67 -0
- package/dist/cjs/createZeroClient.authData.test.native.js +72 -0
- package/dist/cjs/createZeroClient.authData.test.native.js.map +1 -0
- package/dist/cjs/createZeroClient.cjs +41 -26
- package/dist/cjs/createZeroClient.native.js +47 -26
- package/dist/cjs/createZeroClient.native.js.map +1 -1
- package/dist/cjs/createZeroServer.cjs +35 -33
- package/dist/cjs/createZeroServer.native.js +36 -34
- package/dist/cjs/createZeroServer.native.js.map +1 -1
- package/dist/cjs/helpers/recoverZeroClient.cjs +101 -0
- package/dist/cjs/helpers/recoverZeroClient.native.js +130 -0
- package/dist/cjs/helpers/recoverZeroClient.native.js.map +1 -0
- package/dist/cjs/helpers/recoverZeroClient.test.cjs +201 -0
- package/dist/cjs/helpers/recoverZeroClient.test.native.js +256 -0
- package/dist/cjs/helpers/recoverZeroClient.test.native.js.map +1 -0
- package/dist/cjs/index.cjs +4 -1
- package/dist/cjs/index.native.js +3 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/multiInstance.test.cjs +4 -3
- package/dist/cjs/multiInstance.test.native.js +6 -6
- package/dist/cjs/multiInstance.test.native.js.map +1 -1
- package/dist/esm/createZeroClient.authData.test.mjs +68 -0
- package/dist/esm/createZeroClient.authData.test.mjs.map +1 -0
- package/dist/esm/createZeroClient.authData.test.native.js +70 -0
- package/dist/esm/createZeroClient.authData.test.native.js.map +1 -0
- package/dist/esm/createZeroClient.mjs +42 -27
- package/dist/esm/createZeroClient.mjs.map +1 -1
- package/dist/esm/createZeroClient.native.js +48 -27
- package/dist/esm/createZeroClient.native.js.map +1 -1
- package/dist/esm/createZeroServer.mjs +35 -33
- package/dist/esm/createZeroServer.mjs.map +1 -1
- package/dist/esm/createZeroServer.native.js +36 -34
- package/dist/esm/createZeroServer.native.js.map +1 -1
- package/dist/esm/helpers/recoverZeroClient.mjs +74 -0
- package/dist/esm/helpers/recoverZeroClient.mjs.map +1 -0
- package/dist/esm/helpers/recoverZeroClient.native.js +100 -0
- package/dist/esm/helpers/recoverZeroClient.native.js.map +1 -0
- package/dist/esm/helpers/recoverZeroClient.test.mjs +202 -0
- package/dist/esm/helpers/recoverZeroClient.test.mjs.map +1 -0
- package/dist/esm/helpers/recoverZeroClient.test.native.js +254 -0
- package/dist/esm/helpers/recoverZeroClient.test.native.js.map +1 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +2 -1
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -1
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/multiInstance.test.mjs +4 -3
- package/dist/esm/multiInstance.test.mjs.map +1 -1
- package/dist/esm/multiInstance.test.native.js +6 -6
- package/dist/esm/multiInstance.test.native.js.map +1 -1
- package/package.json +2 -2
- package/src/createZeroClient.authData.test.tsx +73 -0
- package/src/createZeroClient.tsx +60 -47
- package/src/createZeroServer.ts +48 -44
- package/src/helpers/recoverZeroClient.test.ts +172 -0
- package/src/helpers/recoverZeroClient.ts +173 -0
- package/src/index.ts +5 -0
- package/src/multiInstance.test.tsx +5 -3
- package/src/multiInstanceNested.test.tsx +1 -1
- package/src/types.ts +6 -1
- package/types/createZeroClient.authData.test.d.ts +5 -0
- package/types/createZeroClient.authData.test.d.ts.map +1 -0
- package/types/createZeroClient.d.ts +3 -2
- package/types/createZeroClient.d.ts.map +1 -1
- package/types/createZeroServer.d.ts +4 -4
- package/types/createZeroServer.d.ts.map +1 -1
- package/types/helpers/recoverZeroClient.d.ts +17 -0
- package/types/helpers/recoverZeroClient.d.ts.map +1 -0
- package/types/helpers/recoverZeroClient.test.d.ts +2 -0
- package/types/helpers/recoverZeroClient.test.d.ts.map +1 -0
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/types.d.ts +6 -0
- package/types/types.d.ts.map +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const RECOVER_GUARD_MS = 6e4;
|
|
2
|
+
const LOCAL_STORE_LOST = "Expected IndexedDB not found";
|
|
3
|
+
let reloadScheduled = false;
|
|
4
|
+
const pendingDeletes = [];
|
|
5
|
+
function recoveryGuardOpen(reasonKey) {
|
|
6
|
+
try {
|
|
7
|
+
const key = `on-zero-recover-${reasonKey}`;
|
|
8
|
+
const last = Number(window.sessionStorage.getItem(key) || 0);
|
|
9
|
+
if (Date.now() - last < RECOVER_GUARD_MS) return false;
|
|
10
|
+
window.sessionStorage.setItem(key, String(Date.now()));
|
|
11
|
+
} catch {}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
function recover(deps, reasonKey, message, dropLocalState) {
|
|
15
|
+
if (typeof window === "undefined") return;
|
|
16
|
+
if (dropLocalState) {
|
|
17
|
+
pendingDeletes.push(Promise.resolve().then(deps.deleteLocalState).catch(() => {}));
|
|
18
|
+
}
|
|
19
|
+
if (reloadScheduled) return;
|
|
20
|
+
if (!recoveryGuardOpen(reasonKey)) {
|
|
21
|
+
console.error(`[on-zero] ${message} \u2014 already recovered once, not reloading`);
|
|
22
|
+
deps.zeroEvents.emit({
|
|
23
|
+
type: "fatal",
|
|
24
|
+
reason: message
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
reloadScheduled = true;
|
|
29
|
+
console.warn(`[on-zero] ${message} \u2014 recovering`);
|
|
30
|
+
deps.zeroEvents.emit({
|
|
31
|
+
type: "recovering",
|
|
32
|
+
reason: message
|
|
33
|
+
});
|
|
34
|
+
const doReload = deps.reload ?? (() => window.location.reload());
|
|
35
|
+
Promise.resolve().then(() => Promise.allSettled(pendingDeletes)).then(() => deps.beforeReload?.()).catch(() => {}).finally(doReload);
|
|
36
|
+
}
|
|
37
|
+
function makeZeroRecovery(deps) {
|
|
38
|
+
return {
|
|
39
|
+
onUpdateNeeded(reason) {
|
|
40
|
+
const dropLocalState = reason.type === "SchemaVersionNotSupported";
|
|
41
|
+
recover(deps, reason.type, `update needed (${reason.message || reason.type})`, dropLocalState);
|
|
42
|
+
},
|
|
43
|
+
onClientStateNotFound() {
|
|
44
|
+
recover(deps, "client-state-not-found", "client state not found", true);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function logToConsole(level, context, ...args) {
|
|
49
|
+
const prefix = context ? Object.entries(context).map(([key, value]) => value === void 0 ? key : `${key}=${value}`).join(" ") : "";
|
|
50
|
+
const method = level === "debug" ? "debug" : level;
|
|
51
|
+
console[method](...(prefix ? [prefix] : []), ...args);
|
|
52
|
+
}
|
|
53
|
+
function composeRecoveryLogSink(deps, consumerLogSink) {
|
|
54
|
+
const consumerFlush = consumerLogSink?.flush;
|
|
55
|
+
return {
|
|
56
|
+
log(level, context, ...args) {
|
|
57
|
+
if (consumerLogSink) consumerLogSink.log(level, context, ...args);else logToConsole(level, context, ...args);
|
|
58
|
+
if (level !== "error") return;
|
|
59
|
+
const text = args.map(arg => typeof arg === "string" ? arg : arg && typeof arg === "object" && "message" in arg ? String(arg.message) : "").join(" ");
|
|
60
|
+
if (text.includes(LOCAL_STORE_LOST)) {
|
|
61
|
+
recover(deps, "local-store", "local store lost", true);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
// call through the consumer sink so a class-based sink keeps its `this`,
|
|
65
|
+
// rather than handing Zero a detached method reference.
|
|
66
|
+
flush: consumerFlush ? () => consumerFlush.call(consumerLogSink) : void 0
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function resetRecoveryStateForTests() {
|
|
70
|
+
reloadScheduled = false;
|
|
71
|
+
pendingDeletes.length = 0;
|
|
72
|
+
}
|
|
73
|
+
export { composeRecoveryLogSink, makeZeroRecovery, resetRecoveryStateForTests };
|
|
74
|
+
//# sourceMappingURL=recoverZeroClient.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["RECOVER_GUARD_MS","LOCAL_STORE_LOST","reloadScheduled","pendingDeletes","recoveryGuardOpen","reasonKey","key","last","Number","window","sessionStorage","getItem","Date","now","setItem","String","recover","deps","message","dropLocalState","push","Promise","resolve","then","deleteLocalState","catch","console","error","zeroEvents","emit","type","reason","warn","doReload","reload","location","allSettled","beforeReload","finally","makeZeroRecovery","onUpdateNeeded","onClientStateNotFound","logToConsole","level","context","args","prefix","Object","entries","map","value","join","method","composeRecoveryLogSink","consumerLogSink","consumerFlush","flush","log","text","arg","includes","call","resetRecoveryStateForTests","length"],"sources":["../../../src/helpers/recoverZeroClient.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
function _type_of(obj) {
|
|
2
|
+
"@swc/helpers - typeof";
|
|
3
|
+
|
|
4
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
5
|
+
}
|
|
6
|
+
var RECOVER_GUARD_MS = 6e4;
|
|
7
|
+
var LOCAL_STORE_LOST = "Expected IndexedDB not found";
|
|
8
|
+
var reloadScheduled = false;
|
|
9
|
+
var pendingDeletes = [];
|
|
10
|
+
function recoveryGuardOpen(reasonKey) {
|
|
11
|
+
try {
|
|
12
|
+
var key = `on-zero-recover-${reasonKey}`;
|
|
13
|
+
var last = Number(window.sessionStorage.getItem(key) || 0);
|
|
14
|
+
if (Date.now() - last < RECOVER_GUARD_MS) return false;
|
|
15
|
+
window.sessionStorage.setItem(key, String(Date.now()));
|
|
16
|
+
} catch (e) {}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function recover(deps, reasonKey, message, dropLocalState) {
|
|
20
|
+
if (typeof window === "undefined") return;
|
|
21
|
+
if (dropLocalState) {
|
|
22
|
+
pendingDeletes.push(Promise.resolve().then(deps.deleteLocalState).catch(function () {}));
|
|
23
|
+
}
|
|
24
|
+
if (reloadScheduled) return;
|
|
25
|
+
if (!recoveryGuardOpen(reasonKey)) {
|
|
26
|
+
console.error(`[on-zero] ${message} \u2014 already recovered once, not reloading`);
|
|
27
|
+
deps.zeroEvents.emit({
|
|
28
|
+
type: "fatal",
|
|
29
|
+
reason: message
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
reloadScheduled = true;
|
|
34
|
+
console.warn(`[on-zero] ${message} \u2014 recovering`);
|
|
35
|
+
deps.zeroEvents.emit({
|
|
36
|
+
type: "recovering",
|
|
37
|
+
reason: message
|
|
38
|
+
});
|
|
39
|
+
var _deps_reload;
|
|
40
|
+
var doReload = (_deps_reload = deps.reload) !== null && _deps_reload !== void 0 ? _deps_reload : function () {
|
|
41
|
+
return window.location.reload();
|
|
42
|
+
};
|
|
43
|
+
Promise.resolve().then(function () {
|
|
44
|
+
return Promise.allSettled(pendingDeletes);
|
|
45
|
+
}).then(function () {
|
|
46
|
+
var _deps_beforeReload;
|
|
47
|
+
return (_deps_beforeReload = deps.beforeReload) === null || _deps_beforeReload === void 0 ? void 0 : _deps_beforeReload.call(deps);
|
|
48
|
+
}).catch(function () {}).finally(doReload);
|
|
49
|
+
}
|
|
50
|
+
function makeZeroRecovery(deps) {
|
|
51
|
+
return {
|
|
52
|
+
onUpdateNeeded(reason) {
|
|
53
|
+
var dropLocalState = reason.type === "SchemaVersionNotSupported";
|
|
54
|
+
recover(deps, reason.type, `update needed (${reason.message || reason.type})`, dropLocalState);
|
|
55
|
+
},
|
|
56
|
+
onClientStateNotFound() {
|
|
57
|
+
recover(deps, "client-state-not-found", "client state not found", true);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function logToConsole(level, context) {
|
|
62
|
+
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
|
|
63
|
+
args[_key - 2] = arguments[_key];
|
|
64
|
+
}
|
|
65
|
+
var prefix = context ? Object.entries(context).map(function (param) {
|
|
66
|
+
var [key, value] = param;
|
|
67
|
+
return value === void 0 ? key : `${key}=${value}`;
|
|
68
|
+
}).join(" ") : "";
|
|
69
|
+
var method = level === "debug" ? "debug" : level;
|
|
70
|
+
console[method](...(prefix ? [prefix] : []), ...args);
|
|
71
|
+
}
|
|
72
|
+
function composeRecoveryLogSink(deps, consumerLogSink) {
|
|
73
|
+
var consumerFlush = consumerLogSink === null || consumerLogSink === void 0 ? void 0 : consumerLogSink.flush;
|
|
74
|
+
return {
|
|
75
|
+
log(level, context) {
|
|
76
|
+
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
|
|
77
|
+
args[_key - 2] = arguments[_key];
|
|
78
|
+
}
|
|
79
|
+
if (consumerLogSink) consumerLogSink.log(level, context, ...args);else logToConsole(level, context, ...args);
|
|
80
|
+
if (level !== "error") return;
|
|
81
|
+
var text = args.map(function (arg) {
|
|
82
|
+
return typeof arg === "string" ? arg : arg && (typeof arg === "undefined" ? "undefined" : _type_of(arg)) === "object" && "message" in arg ? String(arg.message) : "";
|
|
83
|
+
}).join(" ");
|
|
84
|
+
if (text.includes(LOCAL_STORE_LOST)) {
|
|
85
|
+
recover(deps, "local-store", "local store lost", true);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
// call through the consumer sink so a class-based sink keeps its `this`,
|
|
89
|
+
// rather than handing Zero a detached method reference.
|
|
90
|
+
flush: consumerFlush ? function () {
|
|
91
|
+
return consumerFlush.call(consumerLogSink);
|
|
92
|
+
} : void 0
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function resetRecoveryStateForTests() {
|
|
96
|
+
reloadScheduled = false;
|
|
97
|
+
pendingDeletes.length = 0;
|
|
98
|
+
}
|
|
99
|
+
export { composeRecoveryLogSink, makeZeroRecovery, resetRecoveryStateForTests };
|
|
100
|
+
//# sourceMappingURL=recoverZeroClient.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_type_of","obj","Symbol","constructor","RECOVER_GUARD_MS","LOCAL_STORE_LOST","reloadScheduled","pendingDeletes","recoveryGuardOpen","reasonKey","key","last","Number","window","sessionStorage","getItem","Date","now","setItem","String","e","recover","deps","message","dropLocalState","push","Promise","resolve","then","deleteLocalState","catch","console","error","zeroEvents","emit","type","reason","warn","_deps_reload","doReload","reload","location","allSettled","_deps_beforeReload","beforeReload","call","finally","makeZeroRecovery","onUpdateNeeded","onClientStateNotFound","logToConsole","level","context","_len","arguments","length","args","Array","_key","prefix","Object","entries","map","param","value","join","method","composeRecoveryLogSink","consumerLogSink","consumerFlush","flush","log"],"sources":["../../../src/helpers/recoverZeroClient.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { createEmitter } from "@take-out/helpers";
|
|
2
|
+
import { UpdateNeededReasonType } from "@rocicorp/zero";
|
|
3
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
4
|
+
import { composeRecoveryLogSink, makeZeroRecovery, resetRecoveryStateForTests } from "./recoverZeroClient.mjs";
|
|
5
|
+
let emitterSeq = 0;
|
|
6
|
+
function setup() {
|
|
7
|
+
const events = [];
|
|
8
|
+
const zeroEvents = createEmitter(`test-recover-${emitterSeq++}`, null);
|
|
9
|
+
zeroEvents.listen(event => {
|
|
10
|
+
if (event) events.push(event);
|
|
11
|
+
});
|
|
12
|
+
const deleteLocalState = vi.fn(() => Promise.resolve());
|
|
13
|
+
const reload = vi.fn();
|
|
14
|
+
const deps = {
|
|
15
|
+
deleteLocalState,
|
|
16
|
+
zeroEvents,
|
|
17
|
+
reload
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
deps,
|
|
21
|
+
deleteLocalState,
|
|
22
|
+
reload,
|
|
23
|
+
events
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function flush() {
|
|
27
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
28
|
+
}
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
window.sessionStorage.clear();
|
|
31
|
+
resetRecoveryStateForTests();
|
|
32
|
+
});
|
|
33
|
+
describe("zero recovery", () => {
|
|
34
|
+
test("SchemaVersionNotSupported drops local state and reloads, emitting recovering", async () => {
|
|
35
|
+
const {
|
|
36
|
+
deps,
|
|
37
|
+
deleteLocalState,
|
|
38
|
+
reload,
|
|
39
|
+
events
|
|
40
|
+
} = setup();
|
|
41
|
+
makeZeroRecovery(deps).onUpdateNeeded({
|
|
42
|
+
type: UpdateNeededReasonType.SchemaVersionNotSupported
|
|
43
|
+
});
|
|
44
|
+
expect(events).toEqual([{
|
|
45
|
+
type: "recovering",
|
|
46
|
+
reason: expect.stringContaining("SchemaVersionNotSupported")
|
|
47
|
+
}]);
|
|
48
|
+
await flush();
|
|
49
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
50
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
51
|
+
});
|
|
52
|
+
test("NewClientGroup / VersionNotSupported reload WITHOUT deleting (sibling-tab safe)", async () => {
|
|
53
|
+
const {
|
|
54
|
+
deps,
|
|
55
|
+
deleteLocalState,
|
|
56
|
+
reload
|
|
57
|
+
} = setup();
|
|
58
|
+
const recovery = makeZeroRecovery(deps);
|
|
59
|
+
recovery.onUpdateNeeded({
|
|
60
|
+
type: UpdateNeededReasonType.NewClientGroup
|
|
61
|
+
});
|
|
62
|
+
await flush();
|
|
63
|
+
resetRecoveryStateForTests();
|
|
64
|
+
recovery.onUpdateNeeded({
|
|
65
|
+
type: UpdateNeededReasonType.VersionNotSupported
|
|
66
|
+
});
|
|
67
|
+
await flush();
|
|
68
|
+
expect(deleteLocalState).not.toHaveBeenCalled();
|
|
69
|
+
expect(reload).toHaveBeenCalledTimes(2);
|
|
70
|
+
});
|
|
71
|
+
test("onClientStateNotFound drops local state and reloads", async () => {
|
|
72
|
+
const {
|
|
73
|
+
deps,
|
|
74
|
+
deleteLocalState,
|
|
75
|
+
reload
|
|
76
|
+
} = setup();
|
|
77
|
+
makeZeroRecovery(deps).onClientStateNotFound();
|
|
78
|
+
await flush();
|
|
79
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
80
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
81
|
+
});
|
|
82
|
+
test("combined client: every instance deletes its own store, but only ONE reload", async () => {
|
|
83
|
+
const a = setup();
|
|
84
|
+
const b = setup();
|
|
85
|
+
makeZeroRecovery(a.deps).onClientStateNotFound();
|
|
86
|
+
makeZeroRecovery(b.deps).onClientStateNotFound();
|
|
87
|
+
await flush();
|
|
88
|
+
expect(a.deleteLocalState).toHaveBeenCalledTimes(1);
|
|
89
|
+
expect(b.deleteLocalState).toHaveBeenCalledTimes(1);
|
|
90
|
+
expect(a.reload.mock.calls.length + b.reload.mock.calls.length).toBe(1);
|
|
91
|
+
});
|
|
92
|
+
test("a second trigger in the same page-load adds no extra reload or fatal", async () => {
|
|
93
|
+
const {
|
|
94
|
+
deps,
|
|
95
|
+
reload,
|
|
96
|
+
events
|
|
97
|
+
} = setup();
|
|
98
|
+
const recovery = makeZeroRecovery(deps);
|
|
99
|
+
recovery.onClientStateNotFound();
|
|
100
|
+
recovery.onUpdateNeeded({
|
|
101
|
+
type: UpdateNeededReasonType.NewClientGroup
|
|
102
|
+
});
|
|
103
|
+
await flush();
|
|
104
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
105
|
+
expect(events.filter(event => event.type === "fatal")).toEqual([]);
|
|
106
|
+
});
|
|
107
|
+
test("after a reload, a re-failing reason emits fatal instead of reloading again", async () => {
|
|
108
|
+
const {
|
|
109
|
+
deps,
|
|
110
|
+
reload,
|
|
111
|
+
events
|
|
112
|
+
} = setup();
|
|
113
|
+
const recovery = makeZeroRecovery(deps);
|
|
114
|
+
recovery.onClientStateNotFound();
|
|
115
|
+
await flush();
|
|
116
|
+
resetRecoveryStateForTests();
|
|
117
|
+
recovery.onClientStateNotFound();
|
|
118
|
+
await flush();
|
|
119
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
120
|
+
expect(events.some(event => event.type === "fatal")).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
test("after a reload, a different reason still recovers", async () => {
|
|
123
|
+
const {
|
|
124
|
+
deps,
|
|
125
|
+
reload
|
|
126
|
+
} = setup();
|
|
127
|
+
const recovery = makeZeroRecovery(deps);
|
|
128
|
+
recovery.onClientStateNotFound();
|
|
129
|
+
await flush();
|
|
130
|
+
resetRecoveryStateForTests();
|
|
131
|
+
recovery.onUpdateNeeded({
|
|
132
|
+
type: UpdateNeededReasonType.SchemaVersionNotSupported
|
|
133
|
+
});
|
|
134
|
+
await flush();
|
|
135
|
+
expect(reload).toHaveBeenCalledTimes(2);
|
|
136
|
+
});
|
|
137
|
+
test("logSink recovers on local-store-lost and forwards to the consumer sink", async () => {
|
|
138
|
+
const {
|
|
139
|
+
deps,
|
|
140
|
+
deleteLocalState,
|
|
141
|
+
reload
|
|
142
|
+
} = setup();
|
|
143
|
+
const consumer = {
|
|
144
|
+
log: vi.fn()
|
|
145
|
+
};
|
|
146
|
+
const sink = composeRecoveryLogSink(deps, consumer);
|
|
147
|
+
sink.log("error", void 0, "Error during persist: Expected IndexedDB not found");
|
|
148
|
+
expect(consumer.log).toHaveBeenCalledTimes(1);
|
|
149
|
+
await flush();
|
|
150
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
151
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
152
|
+
});
|
|
153
|
+
test("logSink with no consumer preserves console output and still watches", async () => {
|
|
154
|
+
const {
|
|
155
|
+
deps,
|
|
156
|
+
deleteLocalState,
|
|
157
|
+
reload
|
|
158
|
+
} = setup();
|
|
159
|
+
const infoSpy = vi.spyOn(console, "info").mockImplementation(() => {});
|
|
160
|
+
const sink = composeRecoveryLogSink(deps);
|
|
161
|
+
sink.log("info", {
|
|
162
|
+
worker: "sync"
|
|
163
|
+
}, "connected");
|
|
164
|
+
expect(infoSpy).toHaveBeenCalledWith("worker=sync", "connected");
|
|
165
|
+
infoSpy.mockRestore();
|
|
166
|
+
sink.log("error", void 0, "Expected IndexedDB not found");
|
|
167
|
+
await flush();
|
|
168
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
169
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
170
|
+
});
|
|
171
|
+
test("logSink flush calls through the consumer sink (preserves its this)", async () => {
|
|
172
|
+
const {
|
|
173
|
+
deps
|
|
174
|
+
} = setup();
|
|
175
|
+
class ClassSink {
|
|
176
|
+
flushed = false;
|
|
177
|
+
log() {}
|
|
178
|
+
flush() {
|
|
179
|
+
this.flushed = true;
|
|
180
|
+
return Promise.resolve();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const consumer = new ClassSink();
|
|
184
|
+
const sink = composeRecoveryLogSink(deps, consumer);
|
|
185
|
+
await sink.flush?.();
|
|
186
|
+
expect(consumer.flushed).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
test("logSink ignores non-error level and non-matching messages", async () => {
|
|
189
|
+
const {
|
|
190
|
+
deps,
|
|
191
|
+
deleteLocalState,
|
|
192
|
+
reload
|
|
193
|
+
} = setup();
|
|
194
|
+
const sink = composeRecoveryLogSink(deps);
|
|
195
|
+
sink.log("info", void 0, "Expected IndexedDB not found");
|
|
196
|
+
sink.log("error", void 0, "some unrelated error");
|
|
197
|
+
await flush();
|
|
198
|
+
expect(deleteLocalState).not.toHaveBeenCalled();
|
|
199
|
+
expect(reload).not.toHaveBeenCalled();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
//# sourceMappingURL=recoverZeroClient.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createEmitter","UpdateNeededReasonType","beforeEach","describe","expect","test","vi","composeRecoveryLogSink","makeZeroRecovery","resetRecoveryStateForTests","emitterSeq","setup","events","zeroEvents","listen","event","push","deleteLocalState","fn","Promise","resolve","reload","deps","flush","setTimeout","window","sessionStorage","clear","onUpdateNeeded","type","SchemaVersionNotSupported","toEqual","reason","stringContaining","toHaveBeenCalledTimes","recovery","NewClientGroup","VersionNotSupported","not","toHaveBeenCalled","onClientStateNotFound","a","b","mock","calls","length","toBe","filter","some","consumer","log","sink","infoSpy","spyOn","console","mockImplementation","worker","toHaveBeenCalledWith","mockRestore","ClassSink","flushed"],"sources":["../../../src/helpers/recoverZeroClient.test.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { createEmitter } from "@take-out/helpers";
|
|
2
|
+
import { UpdateNeededReasonType } from "@rocicorp/zero";
|
|
3
|
+
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
4
|
+
import { composeRecoveryLogSink, makeZeroRecovery, resetRecoveryStateForTests } from "./recoverZeroClient.native.js";
|
|
5
|
+
function _class_call_check(instance, Constructor) {
|
|
6
|
+
if (!(instance instanceof Constructor)) {
|
|
7
|
+
throw new TypeError("Cannot call a class as a function");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function _defineProperties(target, props) {
|
|
11
|
+
for (var i = 0; i < props.length; i++) {
|
|
12
|
+
var descriptor = props[i];
|
|
13
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
14
|
+
descriptor.configurable = true;
|
|
15
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
16
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function _create_class(Constructor, protoProps, staticProps) {
|
|
20
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
21
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
22
|
+
return Constructor;
|
|
23
|
+
}
|
|
24
|
+
function _define_property(obj, key, value) {
|
|
25
|
+
if (key in obj) {
|
|
26
|
+
Object.defineProperty(obj, key, {
|
|
27
|
+
value,
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true
|
|
31
|
+
});
|
|
32
|
+
} else {
|
|
33
|
+
obj[key] = value;
|
|
34
|
+
}
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
var emitterSeq = 0;
|
|
38
|
+
function setup() {
|
|
39
|
+
var events = [];
|
|
40
|
+
var zeroEvents = createEmitter(`test-recover-${emitterSeq++}`, null);
|
|
41
|
+
zeroEvents.listen(function (event) {
|
|
42
|
+
if (event) events.push(event);
|
|
43
|
+
});
|
|
44
|
+
var deleteLocalState = vi.fn(function () {
|
|
45
|
+
return Promise.resolve();
|
|
46
|
+
});
|
|
47
|
+
var reload = vi.fn();
|
|
48
|
+
var deps = {
|
|
49
|
+
deleteLocalState,
|
|
50
|
+
zeroEvents,
|
|
51
|
+
reload
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
deps,
|
|
55
|
+
deleteLocalState,
|
|
56
|
+
reload,
|
|
57
|
+
events
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function flush() {
|
|
61
|
+
await new Promise(function (resolve) {
|
|
62
|
+
return setTimeout(resolve, 0);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
beforeEach(function () {
|
|
66
|
+
window.sessionStorage.clear();
|
|
67
|
+
resetRecoveryStateForTests();
|
|
68
|
+
});
|
|
69
|
+
describe("zero recovery", function () {
|
|
70
|
+
test("SchemaVersionNotSupported drops local state and reloads, emitting recovering", async function () {
|
|
71
|
+
var {
|
|
72
|
+
deps,
|
|
73
|
+
deleteLocalState,
|
|
74
|
+
reload,
|
|
75
|
+
events
|
|
76
|
+
} = setup();
|
|
77
|
+
makeZeroRecovery(deps).onUpdateNeeded({
|
|
78
|
+
type: UpdateNeededReasonType.SchemaVersionNotSupported
|
|
79
|
+
});
|
|
80
|
+
expect(events).toEqual([{
|
|
81
|
+
type: "recovering",
|
|
82
|
+
reason: expect.stringContaining("SchemaVersionNotSupported")
|
|
83
|
+
}]);
|
|
84
|
+
await flush();
|
|
85
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
86
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
87
|
+
});
|
|
88
|
+
test("NewClientGroup / VersionNotSupported reload WITHOUT deleting (sibling-tab safe)", async function () {
|
|
89
|
+
var {
|
|
90
|
+
deps,
|
|
91
|
+
deleteLocalState,
|
|
92
|
+
reload
|
|
93
|
+
} = setup();
|
|
94
|
+
var recovery = makeZeroRecovery(deps);
|
|
95
|
+
recovery.onUpdateNeeded({
|
|
96
|
+
type: UpdateNeededReasonType.NewClientGroup
|
|
97
|
+
});
|
|
98
|
+
await flush();
|
|
99
|
+
resetRecoveryStateForTests();
|
|
100
|
+
recovery.onUpdateNeeded({
|
|
101
|
+
type: UpdateNeededReasonType.VersionNotSupported
|
|
102
|
+
});
|
|
103
|
+
await flush();
|
|
104
|
+
expect(deleteLocalState).not.toHaveBeenCalled();
|
|
105
|
+
expect(reload).toHaveBeenCalledTimes(2);
|
|
106
|
+
});
|
|
107
|
+
test("onClientStateNotFound drops local state and reloads", async function () {
|
|
108
|
+
var {
|
|
109
|
+
deps,
|
|
110
|
+
deleteLocalState,
|
|
111
|
+
reload
|
|
112
|
+
} = setup();
|
|
113
|
+
makeZeroRecovery(deps).onClientStateNotFound();
|
|
114
|
+
await flush();
|
|
115
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
116
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
117
|
+
});
|
|
118
|
+
test("combined client: every instance deletes its own store, but only ONE reload", async function () {
|
|
119
|
+
var a = setup();
|
|
120
|
+
var b = setup();
|
|
121
|
+
makeZeroRecovery(a.deps).onClientStateNotFound();
|
|
122
|
+
makeZeroRecovery(b.deps).onClientStateNotFound();
|
|
123
|
+
await flush();
|
|
124
|
+
expect(a.deleteLocalState).toHaveBeenCalledTimes(1);
|
|
125
|
+
expect(b.deleteLocalState).toHaveBeenCalledTimes(1);
|
|
126
|
+
expect(a.reload.mock.calls.length + b.reload.mock.calls.length).toBe(1);
|
|
127
|
+
});
|
|
128
|
+
test("a second trigger in the same page-load adds no extra reload or fatal", async function () {
|
|
129
|
+
var {
|
|
130
|
+
deps,
|
|
131
|
+
reload,
|
|
132
|
+
events
|
|
133
|
+
} = setup();
|
|
134
|
+
var recovery = makeZeroRecovery(deps);
|
|
135
|
+
recovery.onClientStateNotFound();
|
|
136
|
+
recovery.onUpdateNeeded({
|
|
137
|
+
type: UpdateNeededReasonType.NewClientGroup
|
|
138
|
+
});
|
|
139
|
+
await flush();
|
|
140
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
141
|
+
expect(events.filter(function (event) {
|
|
142
|
+
return event.type === "fatal";
|
|
143
|
+
})).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
test("after a reload, a re-failing reason emits fatal instead of reloading again", async function () {
|
|
146
|
+
var {
|
|
147
|
+
deps,
|
|
148
|
+
reload,
|
|
149
|
+
events
|
|
150
|
+
} = setup();
|
|
151
|
+
var recovery = makeZeroRecovery(deps);
|
|
152
|
+
recovery.onClientStateNotFound();
|
|
153
|
+
await flush();
|
|
154
|
+
resetRecoveryStateForTests();
|
|
155
|
+
recovery.onClientStateNotFound();
|
|
156
|
+
await flush();
|
|
157
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
158
|
+
expect(events.some(function (event) {
|
|
159
|
+
return event.type === "fatal";
|
|
160
|
+
})).toBe(true);
|
|
161
|
+
});
|
|
162
|
+
test("after a reload, a different reason still recovers", async function () {
|
|
163
|
+
var {
|
|
164
|
+
deps,
|
|
165
|
+
reload
|
|
166
|
+
} = setup();
|
|
167
|
+
var recovery = makeZeroRecovery(deps);
|
|
168
|
+
recovery.onClientStateNotFound();
|
|
169
|
+
await flush();
|
|
170
|
+
resetRecoveryStateForTests();
|
|
171
|
+
recovery.onUpdateNeeded({
|
|
172
|
+
type: UpdateNeededReasonType.SchemaVersionNotSupported
|
|
173
|
+
});
|
|
174
|
+
await flush();
|
|
175
|
+
expect(reload).toHaveBeenCalledTimes(2);
|
|
176
|
+
});
|
|
177
|
+
test("logSink recovers on local-store-lost and forwards to the consumer sink", async function () {
|
|
178
|
+
var {
|
|
179
|
+
deps,
|
|
180
|
+
deleteLocalState,
|
|
181
|
+
reload
|
|
182
|
+
} = setup();
|
|
183
|
+
var consumer = {
|
|
184
|
+
log: vi.fn()
|
|
185
|
+
};
|
|
186
|
+
var sink = composeRecoveryLogSink(deps, consumer);
|
|
187
|
+
sink.log("error", void 0, "Error during persist: Expected IndexedDB not found");
|
|
188
|
+
expect(consumer.log).toHaveBeenCalledTimes(1);
|
|
189
|
+
await flush();
|
|
190
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
191
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
192
|
+
});
|
|
193
|
+
test("logSink with no consumer preserves console output and still watches", async function () {
|
|
194
|
+
var {
|
|
195
|
+
deps,
|
|
196
|
+
deleteLocalState,
|
|
197
|
+
reload
|
|
198
|
+
} = setup();
|
|
199
|
+
var infoSpy = vi.spyOn(console, "info").mockImplementation(function () {});
|
|
200
|
+
var sink = composeRecoveryLogSink(deps);
|
|
201
|
+
sink.log("info", {
|
|
202
|
+
worker: "sync"
|
|
203
|
+
}, "connected");
|
|
204
|
+
expect(infoSpy).toHaveBeenCalledWith("worker=sync", "connected");
|
|
205
|
+
infoSpy.mockRestore();
|
|
206
|
+
sink.log("error", void 0, "Expected IndexedDB not found");
|
|
207
|
+
await flush();
|
|
208
|
+
expect(deleteLocalState).toHaveBeenCalledTimes(1);
|
|
209
|
+
expect(reload).toHaveBeenCalledTimes(1);
|
|
210
|
+
});
|
|
211
|
+
test("logSink flush calls through the consumer sink (preserves its this)", async function () {
|
|
212
|
+
var _sink_flush;
|
|
213
|
+
var {
|
|
214
|
+
deps
|
|
215
|
+
} = setup();
|
|
216
|
+
var ClassSink = /* @__PURE__ */function () {
|
|
217
|
+
"use strict";
|
|
218
|
+
|
|
219
|
+
function ClassSink2() {
|
|
220
|
+
_class_call_check(this, ClassSink2);
|
|
221
|
+
_define_property(this, "flushed", false);
|
|
222
|
+
}
|
|
223
|
+
_create_class(ClassSink2, [{
|
|
224
|
+
key: "log",
|
|
225
|
+
value: function log() {}
|
|
226
|
+
}, {
|
|
227
|
+
key: "flush",
|
|
228
|
+
value: function flush2() {
|
|
229
|
+
this.flushed = true;
|
|
230
|
+
return Promise.resolve();
|
|
231
|
+
}
|
|
232
|
+
}]);
|
|
233
|
+
return ClassSink2;
|
|
234
|
+
}();
|
|
235
|
+
var consumer = new ClassSink();
|
|
236
|
+
var sink = composeRecoveryLogSink(deps, consumer);
|
|
237
|
+
await ((_sink_flush = sink.flush) === null || _sink_flush === void 0 ? void 0 : _sink_flush.call(sink));
|
|
238
|
+
expect(consumer.flushed).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
test("logSink ignores non-error level and non-matching messages", async function () {
|
|
241
|
+
var {
|
|
242
|
+
deps,
|
|
243
|
+
deleteLocalState,
|
|
244
|
+
reload
|
|
245
|
+
} = setup();
|
|
246
|
+
var sink = composeRecoveryLogSink(deps);
|
|
247
|
+
sink.log("info", void 0, "Expected IndexedDB not found");
|
|
248
|
+
sink.log("error", void 0, "some unrelated error");
|
|
249
|
+
await flush();
|
|
250
|
+
expect(deleteLocalState).not.toHaveBeenCalled();
|
|
251
|
+
expect(reload).not.toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
//# sourceMappingURL=recoverZeroClient.test.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["createEmitter","UpdateNeededReasonType","beforeEach","describe","expect","test","vi","composeRecoveryLogSink","makeZeroRecovery","resetRecoveryStateForTests","_class_call_check","instance","Constructor","TypeError","_defineProperties","target","props","i","length","descriptor","enumerable","configurable","writable","Object","defineProperty","key","_create_class","protoProps","staticProps","prototype","_define_property","obj","value","emitterSeq","setup","events","zeroEvents","listen","event","push","deleteLocalState","fn","Promise","resolve","reload","deps","flush","setTimeout","window","sessionStorage","clear","onUpdateNeeded","type","SchemaVersionNotSupported","toEqual","reason","stringContaining","toHaveBeenCalledTimes","recovery","NewClientGroup","VersionNotSupported","not","toHaveBeenCalled","onClientStateNotFound","a","b","mock","calls","toBe","filter","some"],"sources":["../../../src/helpers/recoverZeroClient.test.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|