on-zero 0.6.3 → 0.6.5
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.cjs +111 -12
- package/dist/cjs/createZeroClient.connection.test.cjs +163 -0
- package/dist/cjs/createZeroClient.connection.test.native.js +242 -0
- package/dist/cjs/createZeroClient.connection.test.native.js.map +1 -0
- package/dist/cjs/createZeroClient.native.js +145 -12
- package/dist/cjs/createZeroClient.native.js.map +1 -1
- package/dist/cjs/createZeroClient.recovery.test.cjs +52 -0
- package/dist/cjs/createZeroClient.recovery.test.native.js +52 -0
- package/dist/cjs/createZeroClient.recovery.test.native.js.map +1 -1
- package/dist/cjs/helpers/recoverZeroClient.cjs +140 -14
- package/dist/cjs/helpers/recoverZeroClient.native.js +155 -22
- package/dist/cjs/helpers/recoverZeroClient.native.js.map +1 -1
- package/dist/cjs/helpers/recoverZeroClient.test.cjs +181 -0
- package/dist/cjs/helpers/recoverZeroClient.test.native.js +209 -0
- package/dist/cjs/helpers/recoverZeroClient.test.native.js.map +1 -1
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/multi.cjs +13 -0
- package/dist/cjs/multi.native.js +35 -0
- package/dist/cjs/multi.native.js.map +1 -1
- package/dist/cjs/multiInstance.test.cjs +12 -0
- package/dist/cjs/multiInstance.test.native.js +21 -0
- package/dist/cjs/multiInstance.test.native.js.map +1 -1
- package/dist/cjs/multiPartition.test.cjs +42 -0
- package/dist/cjs/multiPartition.test.native.js +51 -0
- package/dist/cjs/multiPartition.test.native.js.map +1 -0
- package/dist/esm/createZeroClient.connection.test.mjs +164 -0
- package/dist/esm/createZeroClient.connection.test.mjs.map +1 -0
- package/dist/esm/createZeroClient.connection.test.native.js +240 -0
- package/dist/esm/createZeroClient.connection.test.native.js.map +1 -0
- package/dist/esm/createZeroClient.mjs +112 -13
- package/dist/esm/createZeroClient.mjs.map +1 -1
- package/dist/esm/createZeroClient.native.js +146 -13
- package/dist/esm/createZeroClient.native.js.map +1 -1
- package/dist/esm/createZeroClient.recovery.test.mjs +52 -0
- package/dist/esm/createZeroClient.recovery.test.mjs.map +1 -1
- package/dist/esm/createZeroClient.recovery.test.native.js +52 -0
- package/dist/esm/createZeroClient.recovery.test.native.js.map +1 -1
- package/dist/esm/helpers/recoverZeroClient.mjs +140 -15
- package/dist/esm/helpers/recoverZeroClient.mjs.map +1 -1
- package/dist/esm/helpers/recoverZeroClient.native.js +155 -23
- package/dist/esm/helpers/recoverZeroClient.native.js.map +1 -1
- package/dist/esm/helpers/recoverZeroClient.test.mjs +182 -1
- package/dist/esm/helpers/recoverZeroClient.test.mjs.map +1 -1
- package/dist/esm/helpers/recoverZeroClient.test.native.js +210 -1
- package/dist/esm/helpers/recoverZeroClient.test.native.js.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +2 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/multi.mjs +13 -1
- package/dist/esm/multi.mjs.map +1 -1
- package/dist/esm/multi.native.js +35 -1
- package/dist/esm/multi.native.js.map +1 -1
- package/dist/esm/multiInstance.test.mjs +12 -0
- package/dist/esm/multiInstance.test.mjs.map +1 -1
- package/dist/esm/multiInstance.test.native.js +21 -0
- package/dist/esm/multiInstance.test.native.js.map +1 -1
- package/dist/esm/multiPartition.test.mjs +43 -0
- package/dist/esm/multiPartition.test.mjs.map +1 -0
- package/dist/esm/multiPartition.test.native.js +49 -0
- package/dist/esm/multiPartition.test.native.js.map +1 -0
- package/lint/ssr-guards.js +164 -0
- package/package.json +2 -2
- package/readme.md +126 -0
- package/src/createZeroClient.connection.test.tsx +162 -0
- package/src/createZeroClient.recovery.test.tsx +68 -0
- package/src/createZeroClient.tsx +188 -13
- package/src/helpers/recoverZeroClient.test.ts +204 -1
- package/src/helpers/recoverZeroClient.ts +263 -42
- package/src/index.ts +3 -0
- package/src/multi.ts +33 -0
- package/src/multiInstance.test.tsx +13 -0
- package/src/multiPartition.test.ts +38 -0
- package/types/createZeroClient.connection.test.d.ts +5 -0
- package/types/createZeroClient.connection.test.d.ts.map +1 -0
- package/types/createZeroClient.d.ts +17 -0
- package/types/createZeroClient.d.ts.map +1 -1
- package/types/createZeroServer.d.ts +4 -4
- package/types/helpers/recoverZeroClient.d.ts +15 -1
- package/types/helpers/recoverZeroClient.d.ts.map +1 -1
- package/types/index.d.ts +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/multi.d.ts +1 -0
- package/types/multi.d.ts.map +1 -1
- package/types/multiPartition.test.d.ts +2 -0
- package/types/multiPartition.test.d.ts.map +1 -0
|
@@ -145,9 +145,29 @@ function createZeroClientInternal({
|
|
|
145
145
|
(0, import_run.setCustomQueries)(customQueries);
|
|
146
146
|
const DisabledContext = (0, import_react2.createContext)(false);
|
|
147
147
|
let latestZeroInstance = null;
|
|
148
|
+
function lazyMutatePath(path) {
|
|
149
|
+
const resolve = () => {
|
|
150
|
+
if (latestZeroInstance === null) {
|
|
151
|
+
throw new Error(`Zero instance not initialized. Ensure ZeroProvider is mounted before accessing 'zero'.`);
|
|
152
|
+
}
|
|
153
|
+
let target = latestZeroInstance.mutate;
|
|
154
|
+
for (const key of path) target = target[key];
|
|
155
|
+
return target;
|
|
156
|
+
};
|
|
157
|
+
return new Proxy(function lazyMutator() {}, {
|
|
158
|
+
get(_, key) {
|
|
159
|
+
if (typeof key === "symbol") return void 0;
|
|
160
|
+
return lazyMutatePath([...path, key]);
|
|
161
|
+
},
|
|
162
|
+
apply(_, __, args) {
|
|
163
|
+
return resolve()(...args);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
148
167
|
const zero = new Proxy({}, {
|
|
149
168
|
get(_, key) {
|
|
150
169
|
if (latestZeroInstance === null) {
|
|
170
|
+
if (key === "mutate") return lazyMutatePath([]);
|
|
151
171
|
throw new Error(`Zero instance not initialized. Ensure ZeroProvider is mounted before accessing 'zero'.`);
|
|
152
172
|
}
|
|
153
173
|
if (key === "delete") {
|
|
@@ -211,6 +231,14 @@ function createZeroClientInternal({
|
|
|
211
231
|
const usePermission = createUsePermission(useQuery);
|
|
212
232
|
const usePermissionDirect = createUsePermission(useQueryDirect);
|
|
213
233
|
let cachedZero = null;
|
|
234
|
+
const REMINT_GUARD_MS = 12e3;
|
|
235
|
+
const REMINT_MAX_ATTEMPTS = 5;
|
|
236
|
+
const REMINT_ATTEMPT_RESET_MS = 6e4;
|
|
237
|
+
const remintControl = {
|
|
238
|
+
bump: null
|
|
239
|
+
};
|
|
240
|
+
let lastRemintAt = 0;
|
|
241
|
+
let remintAttempts = 0;
|
|
214
242
|
function invalidateZeroInstance(instanceToInvalidate) {
|
|
215
243
|
if (!instanceToInvalidate) return;
|
|
216
244
|
if (cachedZero?.instance === instanceToInvalidate) {
|
|
@@ -233,6 +261,26 @@ function createZeroClientInternal({
|
|
|
233
261
|
invalidateZeroInstance(instanceToDelete);
|
|
234
262
|
}
|
|
235
263
|
}
|
|
264
|
+
async function remint(opts = {}) {
|
|
265
|
+
if (!remintControl.bump) return false;
|
|
266
|
+
const now = Date.now();
|
|
267
|
+
const sinceLast = now - lastRemintAt;
|
|
268
|
+
if (lastRemintAt > 0 && sinceLast < REMINT_GUARD_MS) return false;
|
|
269
|
+
if (sinceLast > REMINT_ATTEMPT_RESET_MS) remintAttempts = 0;
|
|
270
|
+
if (remintAttempts >= REMINT_MAX_ATTEMPTS) return false;
|
|
271
|
+
lastRemintAt = now;
|
|
272
|
+
remintAttempts += 1;
|
|
273
|
+
const {
|
|
274
|
+
dropLocalState = true
|
|
275
|
+
} = opts;
|
|
276
|
+
if (dropLocalState && latestZeroInstance) {
|
|
277
|
+
await deleteZeroInstance(latestZeroInstance).catch(() => {});
|
|
278
|
+
}
|
|
279
|
+
const bump = remintControl.bump;
|
|
280
|
+
if (!bump) return false;
|
|
281
|
+
bump();
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
236
284
|
const DISABLED_ZERO_STUB_CONNECTION_STATE = {
|
|
237
285
|
current: {
|
|
238
286
|
name: "closed"
|
|
@@ -266,6 +314,11 @@ function createZeroClientInternal({
|
|
|
266
314
|
transport,
|
|
267
315
|
pullIntervalMs = 3e4,
|
|
268
316
|
beforeReload,
|
|
317
|
+
scheduleReload,
|
|
318
|
+
guardStorage,
|
|
319
|
+
benignLogFilter,
|
|
320
|
+
refreshAuth,
|
|
321
|
+
connectionDataset,
|
|
269
322
|
disable,
|
|
270
323
|
...props
|
|
271
324
|
}) => {
|
|
@@ -287,10 +340,17 @@ function createZeroClientInternal({
|
|
|
287
340
|
can: permissionsHelpers.can
|
|
288
341
|
});
|
|
289
342
|
}, []);
|
|
343
|
+
const [remintGeneration, setRemintGeneration] = (0, import_react2.useState)(0);
|
|
344
|
+
(0, import_react2.useEffect)(() => {
|
|
345
|
+
remintControl.bump = () => setRemintGeneration(generation => generation + 1);
|
|
346
|
+
return () => {
|
|
347
|
+
remintControl.bump = null;
|
|
348
|
+
};
|
|
349
|
+
}, []);
|
|
290
350
|
const instanceKey = JSON.stringify([Object.entries({
|
|
291
351
|
kvStore: "mem",
|
|
292
352
|
...props
|
|
293
|
-
}).filter(([key, value]) => key !== "auth" && typeof value !== "function" && value !== void 0).sort(([a], [b]) => a < b ? -1 : 1), hasAuth, transport]);
|
|
353
|
+
}).filter(([key, value]) => key !== "auth" && typeof value !== "function" && value !== void 0).sort(([a], [b]) => a < b ? -1 : 1), hasAuth, transport, remintGeneration]);
|
|
294
354
|
const [instance2, setInstance] = (0, import_react2.useState)();
|
|
295
355
|
(0, import_react2.useEffect)(() => {
|
|
296
356
|
if (disable) {
|
|
@@ -317,7 +377,10 @@ function createZeroClientInternal({
|
|
|
317
377
|
const recoveryDeps = {
|
|
318
378
|
deleteLocalState: () => deleteZeroInstance(instanceRef.current),
|
|
319
379
|
zeroEvents,
|
|
320
|
-
beforeReload
|
|
380
|
+
beforeReload,
|
|
381
|
+
scheduleReload,
|
|
382
|
+
guardStorage,
|
|
383
|
+
benignLogFilter
|
|
321
384
|
};
|
|
322
385
|
const recovery = (0, import_recoverZeroClient.makeZeroRecovery)(recoveryDeps);
|
|
323
386
|
const createdInstance = new import_zero.Zero({
|
|
@@ -365,7 +428,10 @@ function createZeroClientInternal({
|
|
|
365
428
|
children: /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_react.ZeroContext.Provider, {
|
|
366
429
|
value: liveInstance ?? DISABLED_ZERO_STUB,
|
|
367
430
|
children: [liveInstance ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(SetZeroInstance, {}) : null, liveInstance ? /* @__PURE__ */(0, import_jsx_runtime.jsx)(ConnectionMonitor, {
|
|
368
|
-
zeroEvents
|
|
431
|
+
zeroEvents,
|
|
432
|
+
refreshAuth,
|
|
433
|
+
exposeDataset: connectionDataset,
|
|
434
|
+
datasetCacheUrl: connectionDataset ? props.cacheURL ?? props.server : void 0
|
|
369
435
|
}) : null, children]
|
|
370
436
|
})
|
|
371
437
|
})
|
|
@@ -386,23 +452,55 @@ function createZeroClientInternal({
|
|
|
386
452
|
return null;
|
|
387
453
|
};
|
|
388
454
|
const ConnectionMonitor = (0, import_react2.memo)(({
|
|
389
|
-
zeroEvents: zeroEvents2
|
|
455
|
+
zeroEvents: zeroEvents2,
|
|
456
|
+
refreshAuth,
|
|
457
|
+
exposeDataset,
|
|
458
|
+
datasetCacheUrl
|
|
390
459
|
}) => {
|
|
460
|
+
const zeroInstance = (0, import_react.useZero)();
|
|
391
461
|
const state = (0, import_react.useConnectionState)();
|
|
392
462
|
const prevState = (0, import_react2.useRef)(state.name);
|
|
463
|
+
const staleReconnectRef = (0, import_react2.useRef)(null);
|
|
464
|
+
const needsAuthRef = (0, import_react2.useRef)(false);
|
|
393
465
|
(0, import_react2.useEffect)(() => {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
466
|
+
const name = state.name;
|
|
467
|
+
const reason = "reason" in state && typeof state.reason === "string" ? state.reason : "";
|
|
468
|
+
if (exposeDataset && typeof document !== "undefined" && document.body) {
|
|
469
|
+
document.body.dataset.zeroState = name;
|
|
470
|
+
if (datasetCacheUrl) document.body.dataset.zeroCacheUrl = datasetCacheUrl;
|
|
471
|
+
if (reason) document.body.dataset.zeroReason = reason.slice(0, 200);else delete document.body.dataset.zeroReason;
|
|
472
|
+
if (name === "connected") document.body.dataset.zeroConnected = "true";else delete document.body.dataset.zeroConnected;
|
|
473
|
+
}
|
|
474
|
+
if (name === "error" && (0, import_recoverZeroClient.isRecoverableZeroStalePokeMessage)(reason)) {
|
|
475
|
+
if (staleReconnectRef.current !== reason) {
|
|
476
|
+
staleReconnectRef.current = reason;
|
|
477
|
+
void Promise.resolve(zeroInstance.connection?.connect?.()).catch(() => {});
|
|
478
|
+
}
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (name !== "error") staleReconnectRef.current = null;
|
|
482
|
+
if (name === "needs-auth") {
|
|
483
|
+
if (refreshAuth && !needsAuthRef.current) {
|
|
484
|
+
needsAuthRef.current = true;
|
|
485
|
+
void refreshAuth().then(token => {
|
|
486
|
+
if (token) zeroInstance.connection?.connect?.({
|
|
487
|
+
auth: token
|
|
488
|
+
});
|
|
489
|
+
}).catch(() => {});
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
needsAuthRef.current = false;
|
|
493
|
+
}
|
|
494
|
+
if (name !== prevState.current) {
|
|
495
|
+
prevState.current = name;
|
|
496
|
+
if (name === "error" || name === "needs-auth") {
|
|
399
497
|
zeroEvents2.emit({
|
|
400
498
|
type: "error",
|
|
401
|
-
message
|
|
499
|
+
message: reason || name
|
|
402
500
|
});
|
|
403
501
|
}
|
|
404
502
|
}
|
|
405
|
-
}, [state, zeroEvents2]);
|
|
503
|
+
}, [state, zeroEvents2, zeroInstance, refreshAuth, exposeDataset, datasetCacheUrl]);
|
|
406
504
|
return null;
|
|
407
505
|
});
|
|
408
506
|
function preload(fnArg, paramsOrOptions, optionsArg) {
|
|
@@ -445,6 +543,7 @@ function createZeroClientInternal({
|
|
|
445
543
|
usePermissionDirect,
|
|
446
544
|
zero,
|
|
447
545
|
preload,
|
|
448
|
-
getQuery
|
|
546
|
+
getQuery,
|
|
547
|
+
remint
|
|
449
548
|
};
|
|
450
549
|
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
var import_zero = require("@rocicorp/zero");
|
|
2
|
+
var import_react = require("react");
|
|
3
|
+
var import_client = require("react-dom/client");
|
|
4
|
+
var import_vitest = require("vitest");
|
|
5
|
+
var import_createZeroClient = require("./createZeroClient.cjs");
|
|
6
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
7
|
+
const fakeZero = import_vitest.vi.hoisted(() => {
|
|
8
|
+
class FakeConnectionState {
|
|
9
|
+
current;
|
|
10
|
+
listeners = /* @__PURE__ */new Set();
|
|
11
|
+
constructor(initial) {
|
|
12
|
+
this.current = initial;
|
|
13
|
+
}
|
|
14
|
+
subscribe = cb => {
|
|
15
|
+
this.listeners.add(cb);
|
|
16
|
+
return () => {
|
|
17
|
+
this.listeners.delete(cb);
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
set(next) {
|
|
21
|
+
this.current = next;
|
|
22
|
+
for (const cb of this.listeners) cb();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
class FakeZero {
|
|
26
|
+
context = {};
|
|
27
|
+
connection = {
|
|
28
|
+
state: new FakeConnectionState({
|
|
29
|
+
name: "connecting"
|
|
30
|
+
}),
|
|
31
|
+
connect: import_vitest.vi.fn(async () => {})
|
|
32
|
+
};
|
|
33
|
+
delete = import_vitest.vi.fn(async () => ({
|
|
34
|
+
errors: []
|
|
35
|
+
}));
|
|
36
|
+
close = import_vitest.vi.fn();
|
|
37
|
+
run = import_vitest.vi.fn(async () => []);
|
|
38
|
+
preload = import_vitest.vi.fn(() => ({
|
|
39
|
+
cleanup: () => {},
|
|
40
|
+
complete: Promise.resolve()
|
|
41
|
+
}));
|
|
42
|
+
constructor() {
|
|
43
|
+
instances.push(this);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const instances = [];
|
|
47
|
+
return {
|
|
48
|
+
FakeZero,
|
|
49
|
+
FakeConnectionState,
|
|
50
|
+
instances
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
import_vitest.vi.mock("@rocicorp/zero", async importOriginal => {
|
|
54
|
+
const actual = await importOriginal();
|
|
55
|
+
return {
|
|
56
|
+
...actual,
|
|
57
|
+
Zero: fakeZero.FakeZero
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
61
|
+
const noteTable = (0, import_zero.table)("note").columns({
|
|
62
|
+
id: (0, import_zero.string)(),
|
|
63
|
+
body: (0, import_zero.string)()
|
|
64
|
+
}).primaryKey("id");
|
|
65
|
+
const schema = (0, import_zero.createSchema)({
|
|
66
|
+
tables: [noteTable]
|
|
67
|
+
});
|
|
68
|
+
const client = (0, import_createZeroClient.createZeroClient)({
|
|
69
|
+
schema,
|
|
70
|
+
models: {},
|
|
71
|
+
groupedQueries: {},
|
|
72
|
+
instanceName: "connection-test"
|
|
73
|
+
});
|
|
74
|
+
let container;
|
|
75
|
+
let root;
|
|
76
|
+
(0, import_vitest.beforeEach)(() => {
|
|
77
|
+
fakeZero.instances.length = 0;
|
|
78
|
+
container = document.createElement("div");
|
|
79
|
+
root = null;
|
|
80
|
+
delete document.body.dataset.zeroState;
|
|
81
|
+
delete document.body.dataset.zeroConnected;
|
|
82
|
+
delete document.body.dataset.zeroReason;
|
|
83
|
+
});
|
|
84
|
+
(0, import_vitest.afterEach)(() => {
|
|
85
|
+
if (root) (0, import_react.act)(() => root?.unmount());
|
|
86
|
+
});
|
|
87
|
+
async function mount(extraProps = {}, userID = "conn") {
|
|
88
|
+
root = (0, import_client.createRoot)(container);
|
|
89
|
+
await (0, import_react.act)(async () => {
|
|
90
|
+
root?.render(/* @__PURE__ */(0, import_jsx_runtime.jsx)(client.ProvideZero, {
|
|
91
|
+
cacheURL: "http://127.0.0.1:7788/zero",
|
|
92
|
+
userID,
|
|
93
|
+
...extraProps,
|
|
94
|
+
children: /* @__PURE__ */(0, import_jsx_runtime.jsx)("span", {
|
|
95
|
+
children: "ok"
|
|
96
|
+
})
|
|
97
|
+
}));
|
|
98
|
+
await Promise.resolve();
|
|
99
|
+
});
|
|
100
|
+
return fakeZero.instances.at(-1);
|
|
101
|
+
}
|
|
102
|
+
(0, import_vitest.test)("refreshAuth reconnects in place on needs-auth, once per transition", async () => {
|
|
103
|
+
const refreshAuth = import_vitest.vi.fn(async () => "fresh-token");
|
|
104
|
+
const instance = await mount({
|
|
105
|
+
refreshAuth
|
|
106
|
+
}, "conn-auth");
|
|
107
|
+
await (0, import_react.act)(async () => {
|
|
108
|
+
instance.connection.state.set({
|
|
109
|
+
name: "needs-auth",
|
|
110
|
+
reason: "token expired"
|
|
111
|
+
});
|
|
112
|
+
await Promise.resolve();
|
|
113
|
+
});
|
|
114
|
+
(0, import_vitest.expect)(refreshAuth).toHaveBeenCalledTimes(1);
|
|
115
|
+
await (0, import_react.act)(async () => {
|
|
116
|
+
await Promise.resolve();
|
|
117
|
+
});
|
|
118
|
+
(0, import_vitest.expect)(instance.connection.connect).toHaveBeenCalledWith({
|
|
119
|
+
auth: "fresh-token"
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
(0, import_vitest.test)("stale-poke error reconnects instead of surfacing a fatal error", async () => {
|
|
123
|
+
const events = [];
|
|
124
|
+
const off = client.zeroEvents.listen(event => {
|
|
125
|
+
if (event) events.push(event);
|
|
126
|
+
});
|
|
127
|
+
const instance = await mount({}, "conn-stale");
|
|
128
|
+
await (0, import_react.act)(async () => {
|
|
129
|
+
instance.connection.state.set({
|
|
130
|
+
name: "error",
|
|
131
|
+
reason: "Server returned unexpected base cookie during sync"
|
|
132
|
+
});
|
|
133
|
+
await Promise.resolve();
|
|
134
|
+
});
|
|
135
|
+
(0, import_vitest.expect)(instance.connection.connect).toHaveBeenCalledTimes(1);
|
|
136
|
+
(0, import_vitest.expect)(instance.connection.connect).toHaveBeenCalledWith();
|
|
137
|
+
(0, import_vitest.expect)(events.some(event => event.type === "error")).toBe(false);
|
|
138
|
+
off();
|
|
139
|
+
});
|
|
140
|
+
(0, import_vitest.test)("connectionDataset mirrors connection state onto the body dataset", async () => {
|
|
141
|
+
const instance = await mount({
|
|
142
|
+
connectionDataset: true
|
|
143
|
+
}, "conn-dataset");
|
|
144
|
+
await (0, import_react.act)(async () => {
|
|
145
|
+
instance.connection.state.set({
|
|
146
|
+
name: "connected"
|
|
147
|
+
});
|
|
148
|
+
await Promise.resolve();
|
|
149
|
+
});
|
|
150
|
+
(0, import_vitest.expect)(document.body.dataset.zeroState).toBe("connected");
|
|
151
|
+
(0, import_vitest.expect)(document.body.dataset.zeroConnected).toBe("true");
|
|
152
|
+
(0, import_vitest.expect)(document.body.dataset.zeroCacheUrl).toBe("http://127.0.0.1:7788/zero");
|
|
153
|
+
await (0, import_react.act)(async () => {
|
|
154
|
+
instance.connection.state.set({
|
|
155
|
+
name: "error",
|
|
156
|
+
reason: "boom"
|
|
157
|
+
});
|
|
158
|
+
await Promise.resolve();
|
|
159
|
+
});
|
|
160
|
+
(0, import_vitest.expect)(document.body.dataset.zeroState).toBe("error");
|
|
161
|
+
(0, import_vitest.expect)(document.body.dataset.zeroConnected).toBeUndefined();
|
|
162
|
+
(0, import_vitest.expect)(document.body.dataset.zeroReason).toBe("boom");
|
|
163
|
+
});
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
4
|
+
var import_zero = require("@rocicorp/zero");
|
|
5
|
+
var import_react = require("react");
|
|
6
|
+
var import_client = require("react-dom/client");
|
|
7
|
+
var import_vitest = require("vitest");
|
|
8
|
+
var import_createZeroClient = require("./createZeroClient.native.js");
|
|
9
|
+
function _class_call_check(instance, Constructor) {
|
|
10
|
+
if (!(instance instanceof Constructor)) {
|
|
11
|
+
throw new TypeError("Cannot call a class as a function");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function _defineProperties(target, props) {
|
|
15
|
+
for (var i = 0; i < props.length; i++) {
|
|
16
|
+
var descriptor = props[i];
|
|
17
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
18
|
+
descriptor.configurable = true;
|
|
19
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
20
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function _create_class(Constructor, protoProps, staticProps) {
|
|
24
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
25
|
+
if (staticProps) _defineProperties(Constructor, staticProps);
|
|
26
|
+
return Constructor;
|
|
27
|
+
}
|
|
28
|
+
function _define_property(obj, key, value) {
|
|
29
|
+
if (key in obj) {
|
|
30
|
+
Object.defineProperty(obj, key, {
|
|
31
|
+
value,
|
|
32
|
+
enumerable: true,
|
|
33
|
+
configurable: true,
|
|
34
|
+
writable: true
|
|
35
|
+
});
|
|
36
|
+
} else {
|
|
37
|
+
obj[key] = value;
|
|
38
|
+
}
|
|
39
|
+
return obj;
|
|
40
|
+
}
|
|
41
|
+
var fakeZero = import_vitest.vi.hoisted(function () {
|
|
42
|
+
var FakeConnectionState = /* @__PURE__ */function () {
|
|
43
|
+
"use strict";
|
|
44
|
+
|
|
45
|
+
function FakeConnectionState2(initial) {
|
|
46
|
+
var _this = this;
|
|
47
|
+
_class_call_check(this, FakeConnectionState2);
|
|
48
|
+
_define_property(this, "current", void 0);
|
|
49
|
+
_define_property(this, "listeners", /* @__PURE__ */new Set());
|
|
50
|
+
_define_property(this, "subscribe", function (cb) {
|
|
51
|
+
_this.listeners.add(cb);
|
|
52
|
+
return function () {
|
|
53
|
+
_this.listeners.delete(cb);
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
this.current = initial;
|
|
57
|
+
}
|
|
58
|
+
_create_class(FakeConnectionState2, [{
|
|
59
|
+
key: "set",
|
|
60
|
+
value: function set(next) {
|
|
61
|
+
this.current = next;
|
|
62
|
+
var _iteratorNormalCompletion = true,
|
|
63
|
+
_didIteratorError = false,
|
|
64
|
+
_iteratorError = void 0;
|
|
65
|
+
try {
|
|
66
|
+
for (var _iterator = this.listeners[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
67
|
+
var cb = _step.value;
|
|
68
|
+
cb();
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
_didIteratorError = true;
|
|
72
|
+
_iteratorError = err;
|
|
73
|
+
} finally {
|
|
74
|
+
try {
|
|
75
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
76
|
+
_iterator.return();
|
|
77
|
+
}
|
|
78
|
+
} finally {
|
|
79
|
+
if (_didIteratorError) {
|
|
80
|
+
throw _iteratorError;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}]);
|
|
86
|
+
return FakeConnectionState2;
|
|
87
|
+
}();
|
|
88
|
+
var FakeZero = function FakeZero2() {
|
|
89
|
+
"use strict";
|
|
90
|
+
|
|
91
|
+
_class_call_check(this, FakeZero2);
|
|
92
|
+
_define_property(this, "context", {});
|
|
93
|
+
_define_property(this, "connection", {
|
|
94
|
+
state: new FakeConnectionState({
|
|
95
|
+
name: "connecting"
|
|
96
|
+
}),
|
|
97
|
+
connect: import_vitest.vi.fn(async function () {})
|
|
98
|
+
});
|
|
99
|
+
_define_property(this, "delete", import_vitest.vi.fn(async function () {
|
|
100
|
+
return {
|
|
101
|
+
errors: []
|
|
102
|
+
};
|
|
103
|
+
}));
|
|
104
|
+
_define_property(this, "close", import_vitest.vi.fn());
|
|
105
|
+
_define_property(this, "run", import_vitest.vi.fn(async function () {
|
|
106
|
+
return [];
|
|
107
|
+
}));
|
|
108
|
+
_define_property(this, "preload", import_vitest.vi.fn(function () {
|
|
109
|
+
return {
|
|
110
|
+
cleanup: function () {},
|
|
111
|
+
complete: Promise.resolve()
|
|
112
|
+
};
|
|
113
|
+
}));
|
|
114
|
+
instances.push(this);
|
|
115
|
+
};
|
|
116
|
+
var instances = [];
|
|
117
|
+
return {
|
|
118
|
+
FakeZero,
|
|
119
|
+
FakeConnectionState,
|
|
120
|
+
instances
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
import_vitest.vi.mock("@rocicorp/zero", async function (importOriginal) {
|
|
124
|
+
var actual = await importOriginal();
|
|
125
|
+
return {
|
|
126
|
+
...actual,
|
|
127
|
+
Zero: fakeZero.FakeZero
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
131
|
+
var noteTable = (0, import_zero.table)("note").columns({
|
|
132
|
+
id: (0, import_zero.string)(),
|
|
133
|
+
body: (0, import_zero.string)()
|
|
134
|
+
}).primaryKey("id");
|
|
135
|
+
var schema = (0, import_zero.createSchema)({
|
|
136
|
+
tables: [noteTable]
|
|
137
|
+
});
|
|
138
|
+
var client = (0, import_createZeroClient.createZeroClient)({
|
|
139
|
+
schema,
|
|
140
|
+
models: {},
|
|
141
|
+
groupedQueries: {},
|
|
142
|
+
instanceName: "connection-test"
|
|
143
|
+
});
|
|
144
|
+
var container;
|
|
145
|
+
var root;
|
|
146
|
+
(0, import_vitest.beforeEach)(function () {
|
|
147
|
+
fakeZero.instances.length = 0;
|
|
148
|
+
container = document.createElement("div");
|
|
149
|
+
root = null;
|
|
150
|
+
delete document.body.dataset.zeroState;
|
|
151
|
+
delete document.body.dataset.zeroConnected;
|
|
152
|
+
delete document.body.dataset.zeroReason;
|
|
153
|
+
});
|
|
154
|
+
(0, import_vitest.afterEach)(function () {
|
|
155
|
+
if (root) (0, import_react.act)(function () {
|
|
156
|
+
return root === null || root === void 0 ? void 0 : root.unmount();
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
async function mount() {
|
|
160
|
+
var extraProps = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {},
|
|
161
|
+
userID = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "conn";
|
|
162
|
+
root = (0, import_client.createRoot)(container);
|
|
163
|
+
await (0, import_react.act)(async function () {
|
|
164
|
+
root === null || root === void 0 ? void 0 : root.render(/* @__PURE__ */(0, import_jsx_runtime.jsx)(client.ProvideZero, {
|
|
165
|
+
cacheURL: "http://127.0.0.1:7788/zero",
|
|
166
|
+
userID,
|
|
167
|
+
...extraProps,
|
|
168
|
+
children: /* @__PURE__ */(0, import_jsx_runtime.jsx)("span", {
|
|
169
|
+
children: "ok"
|
|
170
|
+
})
|
|
171
|
+
}));
|
|
172
|
+
await Promise.resolve();
|
|
173
|
+
});
|
|
174
|
+
return fakeZero.instances.at(-1);
|
|
175
|
+
}
|
|
176
|
+
(0, import_vitest.test)("refreshAuth reconnects in place on needs-auth, once per transition", async function () {
|
|
177
|
+
var refreshAuth = import_vitest.vi.fn(async function () {
|
|
178
|
+
return "fresh-token";
|
|
179
|
+
});
|
|
180
|
+
var instance = await mount({
|
|
181
|
+
refreshAuth
|
|
182
|
+
}, "conn-auth");
|
|
183
|
+
await (0, import_react.act)(async function () {
|
|
184
|
+
instance.connection.state.set({
|
|
185
|
+
name: "needs-auth",
|
|
186
|
+
reason: "token expired"
|
|
187
|
+
});
|
|
188
|
+
await Promise.resolve();
|
|
189
|
+
});
|
|
190
|
+
(0, import_vitest.expect)(refreshAuth).toHaveBeenCalledTimes(1);
|
|
191
|
+
await (0, import_react.act)(async function () {
|
|
192
|
+
await Promise.resolve();
|
|
193
|
+
});
|
|
194
|
+
(0, import_vitest.expect)(instance.connection.connect).toHaveBeenCalledWith({
|
|
195
|
+
auth: "fresh-token"
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
(0, import_vitest.test)("stale-poke error reconnects instead of surfacing a fatal error", async function () {
|
|
199
|
+
var events = [];
|
|
200
|
+
var off = client.zeroEvents.listen(function (event) {
|
|
201
|
+
if (event) events.push(event);
|
|
202
|
+
});
|
|
203
|
+
var instance = await mount({}, "conn-stale");
|
|
204
|
+
await (0, import_react.act)(async function () {
|
|
205
|
+
instance.connection.state.set({
|
|
206
|
+
name: "error",
|
|
207
|
+
reason: "Server returned unexpected base cookie during sync"
|
|
208
|
+
});
|
|
209
|
+
await Promise.resolve();
|
|
210
|
+
});
|
|
211
|
+
(0, import_vitest.expect)(instance.connection.connect).toHaveBeenCalledTimes(1);
|
|
212
|
+
(0, import_vitest.expect)(instance.connection.connect).toHaveBeenCalledWith();
|
|
213
|
+
(0, import_vitest.expect)(events.some(function (event) {
|
|
214
|
+
return event.type === "error";
|
|
215
|
+
})).toBe(false);
|
|
216
|
+
off();
|
|
217
|
+
});
|
|
218
|
+
(0, import_vitest.test)("connectionDataset mirrors connection state onto the body dataset", async function () {
|
|
219
|
+
var instance = await mount({
|
|
220
|
+
connectionDataset: true
|
|
221
|
+
}, "conn-dataset");
|
|
222
|
+
await (0, import_react.act)(async function () {
|
|
223
|
+
instance.connection.state.set({
|
|
224
|
+
name: "connected"
|
|
225
|
+
});
|
|
226
|
+
await Promise.resolve();
|
|
227
|
+
});
|
|
228
|
+
(0, import_vitest.expect)(document.body.dataset.zeroState).toBe("connected");
|
|
229
|
+
(0, import_vitest.expect)(document.body.dataset.zeroConnected).toBe("true");
|
|
230
|
+
(0, import_vitest.expect)(document.body.dataset.zeroCacheUrl).toBe("http://127.0.0.1:7788/zero");
|
|
231
|
+
await (0, import_react.act)(async function () {
|
|
232
|
+
instance.connection.state.set({
|
|
233
|
+
name: "error",
|
|
234
|
+
reason: "boom"
|
|
235
|
+
});
|
|
236
|
+
await Promise.resolve();
|
|
237
|
+
});
|
|
238
|
+
(0, import_vitest.expect)(document.body.dataset.zeroState).toBe("error");
|
|
239
|
+
(0, import_vitest.expect)(document.body.dataset.zeroConnected).toBeUndefined();
|
|
240
|
+
(0, import_vitest.expect)(document.body.dataset.zeroReason).toBe("boom");
|
|
241
|
+
});
|
|
242
|
+
//# sourceMappingURL=createZeroClient.connection.test.native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["import_jsx_runtime","require","import_zero","import_react","import_client","import_vitest","import_createZeroClient","_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","fakeZero","vi","hoisted","FakeConnectionState","FakeConnectionState2","initial","_this","Set","cb","listeners","add","delete","current","set","next","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","Symbol","iterator","_step","done","err","return","FakeZero","FakeZero2","state","name","connect","fn","errors","cleanup","complete","Promise","resolve","instances","push","mock","importOriginal","actual","Zero","globalThis","IS_REACT_ACT_ENVIRONMENT","noteTable","table","columns","id","string","body","primaryKey","schema","createSchema","tables"],"sources":["../../src/createZeroClient.connection.test.tsx"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|