@syncular/client-react 0.0.4-26 → 0.0.6-100

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.
@@ -4,7 +4,6 @@
4
4
 
5
5
  import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
6
6
  import {
7
- ClientTableRegistry,
8
7
  enqueueOutboxCommit,
9
8
  type SyncClientDb,
10
9
  SyncEngine,
@@ -377,7 +376,7 @@ describe('SyncEngine', () => {
377
376
 
378
377
  it('should preserve first pull round commits when additional rounds run', async () => {
379
378
  const handlers = createMockHandlerRegistry();
380
- handlers.register({
379
+ handlers.push({
381
380
  table: 'sync_outbox_commits',
382
381
  applySnapshot: async () => {},
383
382
  clearAll: async () => {},
@@ -857,15 +856,16 @@ describe('SyncEngine', () => {
857
856
  async function createTestEngine(
858
857
  args: { includeProjects?: boolean } = {}
859
858
  ): Promise<SyncEngine<TestDb>> {
860
- const handlers = new ClientTableRegistry<TestDb>();
861
- handlers.register({
862
- table: 'tasks',
863
- applySnapshot: async () => {},
864
- clearAll: async () => {},
865
- applyChange: async () => {},
866
- });
859
+ const handlers: SyncEngineConfig<TestDb>['handlers'] = [
860
+ {
861
+ table: 'tasks',
862
+ applySnapshot: async () => {},
863
+ clearAll: async () => {},
864
+ applyChange: async () => {},
865
+ },
866
+ ];
867
867
  if (args.includeProjects) {
868
- handlers.register({
868
+ handlers.push({
869
869
  table: 'projects',
870
870
  applySnapshot: async () => {},
871
871
  clearAll: async () => {},
@@ -1088,13 +1088,14 @@ describe('SyncEngine', () => {
1088
1088
  });
1089
1089
  const rt = createRealtimeTransport(base);
1090
1090
 
1091
- const handlers = new ClientTableRegistry();
1092
- handlers.register({
1093
- table: 'tasks',
1094
- applySnapshot: async () => {},
1095
- clearAll: async () => {},
1096
- applyChange: async () => {},
1097
- });
1091
+ const handlers: SyncEngineConfig<SyncClientDb>['handlers'] = [
1092
+ {
1093
+ table: 'tasks',
1094
+ applySnapshot: async () => {},
1095
+ clearAll: async () => {},
1096
+ applyChange: async () => {},
1097
+ },
1098
+ ];
1098
1099
 
1099
1100
  const engine = createEngine({
1100
1101
  transport: rt,
@@ -1222,13 +1223,14 @@ describe('SyncEngine', () => {
1222
1223
  });
1223
1224
  const rt = createRealtimeTransport(base);
1224
1225
 
1225
- const handlers = new ClientTableRegistry();
1226
- handlers.register({
1227
- table: 'tasks',
1228
- applySnapshot: async () => {},
1229
- clearAll: async () => {},
1230
- applyChange: async () => {},
1231
- });
1226
+ const handlers: SyncEngineConfig<SyncClientDb>['handlers'] = [
1227
+ {
1228
+ table: 'tasks',
1229
+ applySnapshot: async () => {},
1230
+ clearAll: async () => {},
1231
+ applyChange: async () => {},
1232
+ },
1233
+ ];
1232
1234
 
1233
1235
  const engine = createEngine({
1234
1236
  transport: rt,
@@ -1289,15 +1291,16 @@ describe('SyncEngine', () => {
1289
1291
  });
1290
1292
  const rt = createRealtimeTransport(base);
1291
1293
 
1292
- const handlers = new ClientTableRegistry();
1293
- handlers.register({
1294
- table: 'tasks',
1295
- applySnapshot: async () => {},
1296
- clearAll: async () => {},
1297
- applyChange: async () => {
1298
- inlineApplyCount++;
1294
+ const handlers: SyncEngineConfig<SyncClientDb>['handlers'] = [
1295
+ {
1296
+ table: 'tasks',
1297
+ applySnapshot: async () => {},
1298
+ clearAll: async () => {},
1299
+ applyChange: async () => {
1300
+ inlineApplyCount++;
1301
+ },
1299
1302
  },
1300
- });
1303
+ ];
1301
1304
 
1302
1305
  const engine = createEngine({
1303
1306
  transport: rt,
@@ -1348,13 +1351,14 @@ describe('SyncEngine', () => {
1348
1351
  const base = createMockTransport();
1349
1352
  const rt = createRealtimeTransport(base);
1350
1353
 
1351
- const handlers = new ClientTableRegistry();
1352
- handlers.register({
1353
- table: 'tasks',
1354
- applySnapshot: async () => {},
1355
- clearAll: async () => {},
1356
- applyChange: async () => {},
1357
- });
1354
+ const handlers: SyncEngineConfig<SyncClientDb>['handlers'] = [
1355
+ {
1356
+ table: 'tasks',
1357
+ applySnapshot: async () => {},
1358
+ clearAll: async () => {},
1359
+ applyChange: async () => {},
1360
+ },
1361
+ ];
1358
1362
 
1359
1363
  const engine = createEngine({
1360
1364
  transport: rt,
@@ -22,6 +22,7 @@ import { createSyncularReact } from '../index';
22
22
  import {
23
23
  createMockDb,
24
24
  createMockHandlerRegistry,
25
+ createMockSync,
25
26
  createMockTransport,
26
27
  } from './test-utils';
27
28
 
@@ -67,16 +68,16 @@ describe('SyncProvider (StrictMode)', () => {
67
68
  function renderWithProvider(node: React.ReactNode) {
68
69
  const transport = createMockTransport();
69
70
  const handlers = createMockHandlerRegistry();
71
+ const sync = createMockSync({ handlers });
70
72
 
71
73
  return render(
72
74
  <React.StrictMode>
73
75
  <SyncProvider
74
76
  db={db}
75
77
  transport={transport}
76
- handlers={handlers}
77
- actorId="test-actor"
78
+ sync={sync}
79
+ identity={{ actorId: 'test-actor' }}
78
80
  clientId="test-client"
79
- subscriptions={[]}
80
81
  pollIntervalMs={999999}
81
82
  >
82
83
  {node}
@@ -0,0 +1,64 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { createAsyncInitRegistry } from '../async-init-registry';
3
+
4
+ describe('createAsyncInitRegistry', () => {
5
+ it('runs initializer once per key and shares the same result', async () => {
6
+ const registry = createAsyncInitRegistry<string, string>();
7
+ let runs = 0;
8
+
9
+ const first = registry.run('client-a', async () => {
10
+ runs += 1;
11
+ return 'ok';
12
+ });
13
+ const second = registry.run('client-a', async () => {
14
+ runs += 1;
15
+ return 'unexpected';
16
+ });
17
+
18
+ await expect(first).resolves.toBe('ok');
19
+ await expect(second).resolves.toBe('ok');
20
+ expect(runs).toBe(1);
21
+ });
22
+
23
+ it('evicts failed initializers so retry can succeed', async () => {
24
+ const registry = createAsyncInitRegistry<string, string>();
25
+ let runs = 0;
26
+
27
+ await expect(
28
+ registry.run('client-a', async () => {
29
+ runs += 1;
30
+ throw new Error('boom');
31
+ })
32
+ ).rejects.toThrow('boom');
33
+
34
+ await expect(
35
+ registry.run('client-a', async () => {
36
+ runs += 1;
37
+ return 'recovered';
38
+ })
39
+ ).resolves.toBe('recovered');
40
+
41
+ expect(runs).toBe(2);
42
+ });
43
+
44
+ it('supports explicit invalidation', async () => {
45
+ const registry = createAsyncInitRegistry<string, number>();
46
+ let seed = 0;
47
+
48
+ await expect(
49
+ registry.run('client-a', async () => {
50
+ seed += 1;
51
+ return seed;
52
+ })
53
+ ).resolves.toBe(1);
54
+
55
+ registry.invalidate('client-a');
56
+
57
+ await expect(
58
+ registry.run('client-a', async () => {
59
+ seed += 1;
60
+ return seed;
61
+ })
62
+ ).resolves.toBe(2);
63
+ });
64
+ });
@@ -21,6 +21,7 @@ import { createSyncularReact } from '../../index';
21
21
  import {
22
22
  createMockDb,
23
23
  createMockHandlerRegistry,
24
+ createMockSync,
24
25
  createMockTransport,
25
26
  } from '../test-utils';
26
27
 
@@ -60,15 +61,15 @@ describe('useMutation', () => {
60
61
  function createWrapper() {
61
62
  const transport = createMockTransport();
62
63
  const handlers = createMockHandlerRegistry<TestDb>();
64
+ const sync = createMockSync<TestDb>({ handlers });
63
65
 
64
66
  const Wrapper = ({ children }: { children: ReactNode }) => (
65
67
  <SyncProvider
66
68
  db={db}
67
69
  transport={transport}
68
- handlers={handlers}
69
- actorId="test-actor"
70
+ sync={sync}
71
+ identity={{ actorId: 'test-actor' }}
70
72
  clientId="test-client"
71
- subscriptions={[]}
72
73
  pollIntervalMs={999999}
73
74
  autoStart={false}
74
75
  >
@@ -14,6 +14,7 @@ import { createSyncularReact } from '../index';
14
14
  import {
15
15
  createMockDb,
16
16
  createMockHandlerRegistry,
17
+ createMockSync,
17
18
  createMockTransport,
18
19
  } from './test-utils';
19
20
 
@@ -41,15 +42,15 @@ describe('React Hooks', () => {
41
42
  function createWrapper(options?: { autoStart?: boolean }) {
42
43
  const transport = createMockTransport();
43
44
  const handlers = createMockHandlerRegistry();
45
+ const sync = createMockSync({ handlers });
44
46
 
45
47
  const Wrapper = ({ children }: { children: ReactNode }) => (
46
48
  <SyncProvider
47
49
  db={db}
48
50
  transport={transport}
49
- handlers={handlers}
50
- actorId="test-actor"
51
+ sync={sync}
52
+ identity={{ actorId: 'test-actor' }}
51
53
  clientId="test-client"
52
- subscriptions={[]}
53
54
  pollIntervalMs={999999} // Long poll interval to prevent continuous polling
54
55
  autoStart={options?.autoStart ?? false} // Disable auto-start for tests
55
56
  >
@@ -213,21 +214,21 @@ describe('React Hooks', () => {
213
214
  it('supports watchTables invalidation on matching data:change scopes', async () => {
214
215
  const transport = createMockTransport();
215
216
  const handlers = createMockHandlerRegistry();
216
- handlers.register({
217
+ handlers.push({
217
218
  table: 'tasks',
218
219
  applySnapshot: async () => {},
219
220
  clearAll: async () => {},
220
221
  applyChange: async () => {},
221
222
  });
223
+ const sync = createMockSync({ handlers });
222
224
 
223
225
  const Wrapper = ({ children }: { children: ReactNode }) => (
224
226
  <SyncProvider
225
227
  db={db}
226
228
  transport={transport}
227
- handlers={handlers}
228
- actorId="test-actor"
229
+ sync={sync}
230
+ identity={{ actorId: 'test-actor' }}
229
231
  clientId="test-client"
230
- subscriptions={[]}
231
232
  pollIntervalMs={999999}
232
233
  autoStart={false}
233
234
  >
@@ -6,12 +6,13 @@
6
6
 
7
7
  import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
8
8
  import {
9
- ClientTableRegistry,
9
+ type ClientHandlerCollection,
10
10
  ensureClientSyncSchema,
11
11
  type SyncClientDb,
12
12
  SyncEngine,
13
13
  } from '@syncular/client';
14
- import { createBunSqliteDb } from '@syncular/dialect-bun-sqlite';
14
+ import { createDatabase } from '@syncular/core';
15
+ import { createBunSqliteDialect } from '@syncular/dialect-bun-sqlite';
15
16
  import { cleanup, render } from '@testing-library/react';
16
17
  import type { Kysely } from 'kysely';
17
18
  import { createElement } from 'react';
@@ -38,20 +39,23 @@ interface ClientDb extends SyncClientDb {
38
39
 
39
40
  const { SyncProvider } = createSyncularReact<ClientDb>();
40
41
 
41
- // Create a mock ClientTableRegistry for tests
42
- function createMockClientTableRegistry(): ClientTableRegistry<ClientDb> {
43
- return new ClientTableRegistry<ClientDb>();
42
+ // Create mock handlers for tests
43
+ function createMockClientHandlers(): ClientHandlerCollection<ClientDb> {
44
+ return [];
44
45
  }
45
46
 
46
47
  describe('SyncProvider Reconfiguration', () => {
47
48
  let server: TestServer;
48
49
  let db: Kysely<ClientDb>;
49
- let mockHandlers: ClientTableRegistry<ClientDb>;
50
+ let mockHandlers: ClientHandlerCollection<ClientDb>;
50
51
 
51
52
  beforeEach(async () => {
52
53
  server = await createTestServer();
53
- db = createBunSqliteDb<ClientDb>({ path: ':memory:' });
54
- mockHandlers = createMockClientTableRegistry();
54
+ db = createDatabase<ClientDb>({
55
+ dialect: createBunSqliteDialect({ path: ':memory:' }),
56
+ family: 'sqlite',
57
+ });
58
+ mockHandlers = createMockClientHandlers();
55
59
 
56
60
  await ensureClientSyncSchema(db);
57
61
 
@@ -145,14 +149,16 @@ describe('SyncProvider Reconfiguration', () => {
145
149
  const message =
146
150
  `[SyncProvider] Critical props changed after mount: ${changedProps.join(', ')}. ` +
147
151
  'This is not supported. Use a React key prop to force remount, e.g., ' +
148
- '<SyncProvider key={actorId} ...>';
152
+ "<SyncProvider key={identity.actorId + ':' + clientId} ...>";
149
153
 
150
154
  expect(message).toContain(
151
155
  '[SyncProvider] Critical props changed after mount'
152
156
  );
153
157
  expect(message).toContain('actorId');
154
158
  expect(message).toContain('This is not supported');
155
- expect(message).toContain('<SyncProvider key={actorId} ...>');
159
+ expect(message).toContain(
160
+ "<SyncProvider key={identity.actorId + ':' + clientId} ...>"
161
+ );
156
162
  });
157
163
 
158
164
  it('engine config is immutable after creation', () => {
@@ -188,7 +194,7 @@ describe('SyncProvider Reconfiguration', () => {
188
194
 
189
195
  describe('SyncProvider React render tests', () => {
190
196
  let db: Kysely<ClientDb>;
191
- let mockHandlers: ClientTableRegistry<ClientDb>;
197
+ let mockHandlers: ClientHandlerCollection<ClientDb>;
192
198
  const mockTransport = {
193
199
  async sync() {
194
200
  return { ok: true as const };
@@ -199,8 +205,11 @@ describe('SyncProvider React render tests', () => {
199
205
  };
200
206
 
201
207
  beforeEach(async () => {
202
- db = createBunSqliteDb<ClientDb>({ path: ':memory:' });
203
- mockHandlers = new ClientTableRegistry<ClientDb>();
208
+ db = createDatabase<ClientDb>({
209
+ dialect: createBunSqliteDialect({ path: ':memory:' }),
210
+ family: 'sqlite',
211
+ });
212
+ mockHandlers = [];
204
213
  await ensureClientSyncSchema(db);
205
214
  });
206
215
 
@@ -211,14 +220,18 @@ describe('SyncProvider React render tests', () => {
211
220
 
212
221
  it('warns when critical props change after mount', () => {
213
222
  const child = createElement('div', null, 'Test Child');
223
+ const sync = {
224
+ handlers: mockHandlers,
225
+ subscriptions: () => [],
226
+ };
214
227
 
215
228
  // Render with initial props
216
229
  const { rerender } = render(
217
230
  createElement(SyncProvider, {
218
231
  db,
219
232
  transport: mockTransport,
220
- handlers: mockHandlers,
221
- actorId: 'user-1',
233
+ sync,
234
+ identity: { actorId: 'user-1' },
222
235
  clientId: 'client-1',
223
236
  autoStart: false, // Disable auto-start for faster test
224
237
  // biome-ignore lint/correctness/noChildrenProp: createElement requires children prop
@@ -232,8 +245,8 @@ describe('SyncProvider React render tests', () => {
232
245
  createElement(SyncProvider, {
233
246
  db,
234
247
  transport: mockTransport,
235
- handlers: mockHandlers,
236
- actorId: 'user-2', // Changed!
248
+ sync,
249
+ identity: { actorId: 'user-2' }, // Changed!
237
250
  clientId: 'client-1',
238
251
  autoStart: false,
239
252
  // biome-ignore lint/correctness/noChildrenProp: createElement requires children prop
@@ -245,12 +258,16 @@ describe('SyncProvider React render tests', () => {
245
258
 
246
259
  it('does not throw when non-critical props change', () => {
247
260
  const child = createElement('div', null, 'Test Child');
261
+ const sync = {
262
+ handlers: mockHandlers,
263
+ subscriptions: () => [],
264
+ };
248
265
  const { rerender } = render(
249
266
  createElement(SyncProvider, {
250
267
  db,
251
268
  transport: mockTransport,
252
- handlers: mockHandlers,
253
- actorId: 'user-1',
269
+ sync,
270
+ identity: { actorId: 'user-1' },
254
271
  clientId: 'client-1',
255
272
  autoStart: false,
256
273
  pollIntervalMs: 1000,
@@ -265,8 +282,8 @@ describe('SyncProvider React render tests', () => {
265
282
  createElement(SyncProvider, {
266
283
  db,
267
284
  transport: mockTransport,
268
- handlers: mockHandlers,
269
- actorId: 'user-1',
285
+ sync,
286
+ identity: { actorId: 'user-1' },
270
287
  clientId: 'client-1',
271
288
  autoStart: false,
272
289
  pollIntervalMs: 5000, // Changed non-critical prop
@@ -7,6 +7,7 @@
7
7
  import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
8
8
  import {
9
9
  enqueueOutboxCommit,
10
+ getClientHandlerOrThrow,
10
11
  getNextSendableOutboxCommit,
11
12
  } from '@syncular/client';
12
13
 
@@ -109,11 +110,10 @@ describe('Push Flow', () => {
109
110
 
110
111
  it('sync updates local row with server version after push', async () => {
111
112
  // First, apply the mutation locally (this is what useMutation does)
112
- const handlers = client.handlers;
113
- const handler = handlers.get('tasks');
113
+ const handler = getClientHandlerOrThrow(client.handlers, 'tasks');
114
114
 
115
115
  await client.db.transaction().execute(async (trx) => {
116
- await handler!.applyChange(
116
+ await handler.applyChange(
117
117
  { trx },
118
118
  {
119
119
  table: 'tasks',
@@ -3,13 +3,15 @@
3
3
  */
4
4
 
5
5
  import type {
6
+ ClientHandlerCollection,
7
+ ClientSyncConfig,
8
+ SyncClientDb,
6
9
  SyncPullRequest,
7
10
  SyncPullResponse,
8
11
  SyncPushRequest,
9
12
  SyncPushResponse,
10
13
  SyncTransport,
11
14
  } from '@syncular/client';
12
- import { ClientTableRegistry, type SyncClientDb } from '@syncular/client';
13
15
  import type { Kysely } from 'kysely';
14
16
 
15
17
  /**
@@ -68,8 +70,25 @@ export function createMockTransport(
68
70
  */
69
71
  export function createMockHandlerRegistry<
70
72
  DB extends SyncClientDb = SyncClientDb,
71
- >(): ClientTableRegistry<DB> {
72
- return new ClientTableRegistry<DB>();
73
+ >(): ClientHandlerCollection<DB> {
74
+ return [];
75
+ }
76
+
77
+ export function createMockSync<DB extends SyncClientDb = SyncClientDb>(args?: {
78
+ handlers?: ClientHandlerCollection<DB>;
79
+ subscriptions?: Array<{
80
+ id: string;
81
+ table: string;
82
+ scopes?: Record<string, unknown>;
83
+ }>;
84
+ }): ClientSyncConfig<DB, { actorId: string }> {
85
+ const handlers = args?.handlers ?? createMockHandlerRegistry<DB>();
86
+ const subscriptions = args?.subscriptions ?? [];
87
+
88
+ return {
89
+ handlers,
90
+ subscriptions: () => subscriptions,
91
+ };
73
92
  }
74
93
 
75
94
  /**
@@ -79,14 +98,14 @@ export async function createMockDb<
79
98
  DB extends SyncClientDb = SyncClientDb,
80
99
  >(): Promise<Kysely<DB>> {
81
100
  // Dynamic import to avoid bundling issues
82
- const { Kysely } = await import('kysely');
83
- const { BunSqliteDialect } = await import('kysely-bun-sqlite');
84
- const { Database } = await import('bun:sqlite');
85
-
86
- const db = new Kysely<DB>({
87
- dialect: new BunSqliteDialect({
88
- database: new Database(':memory:'),
89
- }),
101
+ const { createDatabase } = await import('@syncular/core');
102
+ const { createBunSqliteDialect } = await import(
103
+ '@syncular/dialect-bun-sqlite'
104
+ );
105
+
106
+ const db = createDatabase<DB>({
107
+ dialect: createBunSqliteDialect({ path: ':memory:' }),
108
+ family: 'sqlite',
90
109
  });
91
110
 
92
111
  // Create sync tables
@@ -0,0 +1,111 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { renderHook, waitFor } from '@testing-library/react';
3
+ import type { ReactNode } from 'react';
4
+ import { StrictMode } from 'react';
5
+ import {
6
+ clearCachedAsyncValues,
7
+ useCachedAsyncValue,
8
+ } from '../use-cached-async-value';
9
+
10
+ function strictModeWrapper(props: { children: ReactNode }) {
11
+ return <StrictMode>{props.children}</StrictMode>;
12
+ }
13
+
14
+ describe('useCachedAsyncValue', () => {
15
+ it('deduplicates StrictMode initialization by key', async () => {
16
+ clearCachedAsyncValues();
17
+ let runs = 0;
18
+
19
+ const { result } = renderHook(
20
+ () =>
21
+ useCachedAsyncValue(
22
+ async () => {
23
+ runs += 1;
24
+ return 'ready';
25
+ },
26
+ { key: 'strictmode-init' }
27
+ ),
28
+ { wrapper: strictModeWrapper }
29
+ );
30
+
31
+ await waitFor(() => {
32
+ expect(result.current[0]).toBe('ready');
33
+ });
34
+ expect(result.current[1]).toBeNull();
35
+ expect(runs).toBe(1);
36
+ });
37
+
38
+ it('retries after a failure when dependencies trigger rerun', async () => {
39
+ clearCachedAsyncValues();
40
+ let runs = 0;
41
+
42
+ const { result, rerender } = renderHook(
43
+ ({ attempt }: { attempt: number }) =>
44
+ useCachedAsyncValue(
45
+ async () => {
46
+ runs += 1;
47
+ if (attempt === 0) {
48
+ throw new Error('boom');
49
+ }
50
+ return 'recovered';
51
+ },
52
+ {
53
+ key: 'retryable-init',
54
+ deps: [attempt],
55
+ }
56
+ ),
57
+ {
58
+ initialProps: { attempt: 0 },
59
+ }
60
+ );
61
+
62
+ await waitFor(() => {
63
+ expect(result.current[1]?.message).toBe('boom');
64
+ });
65
+
66
+ rerender({ attempt: 1 });
67
+
68
+ await waitFor(() => {
69
+ expect(result.current[0]).toBe('recovered');
70
+ });
71
+ expect(result.current[1]).toBeNull();
72
+ expect(runs).toBe(2);
73
+ });
74
+
75
+ it('reuses cached values across component remounts', async () => {
76
+ clearCachedAsyncValues();
77
+ let runs = 0;
78
+
79
+ const first = renderHook(() =>
80
+ useCachedAsyncValue(
81
+ async () => {
82
+ runs += 1;
83
+ return 7;
84
+ },
85
+ { key: 'shared-init' }
86
+ )
87
+ );
88
+
89
+ await waitFor(() => {
90
+ expect(first.result.current[0]).toBe(7);
91
+ });
92
+ first.unmount();
93
+
94
+ const second = renderHook(() =>
95
+ useCachedAsyncValue(
96
+ async () => {
97
+ runs += 1;
98
+ return 9;
99
+ },
100
+ { key: 'shared-init' }
101
+ )
102
+ );
103
+
104
+ await waitFor(() => {
105
+ expect(second.result.current[0]).toBe(7);
106
+ });
107
+ expect(second.result.current[1]).toBeNull();
108
+ expect(runs).toBe(1);
109
+ second.unmount();
110
+ });
111
+ });
@@ -11,6 +11,7 @@ import { createSyncularReact } from '../index';
11
11
  import {
12
12
  createMockDb,
13
13
  createMockHandlerRegistry,
14
+ createMockSync,
14
15
  createMockTransport,
15
16
  } from './test-utils';
16
17
 
@@ -54,15 +55,15 @@ describe('useMutations', () => {
54
55
  function createWrapper() {
55
56
  const transport = createMockTransport();
56
57
  const handlers = createMockHandlerRegistry<TestDb>();
58
+ const sync = createMockSync<TestDb>({ handlers });
57
59
 
58
60
  const Wrapper = ({ children }: { children: ReactNode }) => (
59
61
  <SyncProvider
60
62
  db={db}
61
63
  transport={transport}
62
- handlers={handlers}
63
- actorId="test-actor"
64
+ sync={sync}
65
+ identity={{ actorId: 'test-actor' }}
64
66
  clientId="test-client"
65
- subscriptions={[]}
66
67
  pollIntervalMs={999999}
67
68
  autoStart={false}
68
69
  >