@taladb/react 0.10.2 → 0.11.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/index.js CHANGED
@@ -31,20 +31,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
- ReplicationProvider: () => ReplicationProvider,
35
34
  TalaDBProvider: () => TalaDBProvider,
36
35
  useAggregate: () => useAggregate,
37
36
  useCollection: () => useCollection,
38
37
  useCollectionOptions: () => useCollectionOptions,
39
- useCoverage: () => useCoverage,
40
38
  useFind: () => useFind,
41
39
  useFindOne: () => useFindOne,
42
- useHydrationProgress: () => useHydrationProgress,
43
- useMutation: () => useMutation,
44
- useQueries: () => useQueries,
45
- useQuery: () => useQuery,
46
- useReplicationConfig: () => useReplicationConfig,
47
- useTalaDB: () => useTalaDB
40
+ useTalaDB: () => useTalaDB,
41
+ useWrite: () => useWrite
48
42
  });
49
43
  module.exports = __toCommonJS(index_exports);
50
44
 
@@ -53,7 +47,8 @@ var import_react = require("react");
53
47
  var import_jsx_runtime = require("react/jsx-runtime");
54
48
  var TalaDBContext = (0, import_react.createContext)(null);
55
49
  var CollectionOptionsContext = (0, import_react.createContext)({
56
- get: () => void 0
50
+ get: () => void 0,
51
+ names: () => []
57
52
  });
58
53
  function useCollectionOptions() {
59
54
  return (0, import_react.useContext)(CollectionOptionsContext);
@@ -66,7 +61,10 @@ function CollectionOptionsProvider({
66
61
  latest.current = collections;
67
62
  const resolver = (0, import_react.useMemo)(
68
63
  () => ({
69
- get: (name) => latest.current?.[name]
64
+ get: (name) => latest.current?.[name],
65
+ // Read through the same ref as `get`, so a registry passed as an inline
66
+ // object stays current without giving the resolver a new identity.
67
+ names: () => Object.keys(latest.current ?? {})
70
68
  }),
71
69
  []
72
70
  );
@@ -207,542 +205,14 @@ function useAggregate(collection, pipeline) {
207
205
  return (0, import_react5.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
208
206
  }
209
207
 
210
- // src/replication/config.tsx
211
- var import_react7 = require("react");
212
-
213
- // src/replication/engine.ts
214
- var import_taladb = require("taladb");
215
- function replicationTarget(endpoint, collection) {
216
- return `${endpoint}::${collection}`;
217
- }
218
- async function buildAdapter(config) {
219
- const headers = config.getAuth ? await config.getAuth() : void 0;
220
- return new import_taladb.HttpSyncAdapter({
221
- endpoint: config.endpoint,
222
- headers,
223
- fetch: config.fetch,
224
- paths: config.paths
225
- });
226
- }
227
- var inflight = /* @__PURE__ */ new Map();
228
- function inflightKey(endpoint, collection, direction) {
229
- return `${endpoint}::${collection}::${direction}`;
230
- }
231
- function replicate(db, config, collection, direction) {
232
- const key = inflightKey(config.endpoint, collection, direction);
233
- const existing = inflight.get(key);
234
- if (existing) return existing;
235
- const pass = (async () => {
236
- const adapter = await buildAdapter(config);
237
- await db.sync(adapter, {
238
- collections: [collection],
239
- direction,
240
- target: replicationTarget(config.endpoint, collection)
241
- });
242
- })().finally(() => {
243
- inflight.delete(key);
244
- });
245
- inflight.set(key, pass);
246
- return pass;
247
- }
248
- var BACKOFFS_MS = [200, 400, 800];
249
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
250
- async function replicateWithRetry(db, config, collection, direction) {
251
- let lastError;
252
- for (let attempt = 0; attempt <= BACKOFFS_MS.length; attempt++) {
253
- try {
254
- await replicate(db, config, collection, direction);
255
- return;
256
- } catch (error) {
257
- lastError = error;
258
- if (attempt < BACKOFFS_MS.length) await sleep(BACKOFFS_MS[attempt]);
259
- }
260
- }
261
- throw lastError;
262
- }
263
-
264
- // src/replication/provider.tsx
208
+ // src/useWrite.ts
265
209
  var import_react6 = require("react");
266
- var import_taladb2 = require("taladb");
267
- var import_jsx_runtime2 = require("react/jsx-runtime");
268
- var ReplicationContext = (0, import_react6.createContext)(null);
269
- function whenIdle(fn) {
270
- const ric = globalThis.requestIdleCallback;
271
- if (typeof ric === "function") {
272
- const handle = ric(fn, { timeout: 2e3 });
273
- return () => {
274
- const cic = globalThis.cancelIdleCallback;
275
- cic?.(handle);
276
- };
277
- }
278
- const t = setTimeout(fn, 0);
279
- return () => clearTimeout(t);
280
- }
281
- var yieldToUi = () => new Promise((resolve) => setTimeout(resolve, 0));
282
- function ReplicationScopes({ replicate: replicate2, children }) {
283
- const db = useTalaDB();
284
- const collectionOptions = useCollectionOptions();
285
- const [coverage, setCoverage] = (0, import_react6.useState)({});
286
- const registryKey = JSON.stringify(
287
- Object.fromEntries(
288
- Object.entries(replicate2).map(([name, s]) => [
289
- name,
290
- {
291
- endpoint: s.endpoint,
292
- origin: s.origin,
293
- scope: s.scope,
294
- projectionVersion: s.projectionVersion,
295
- schemaVersion: s.schemaVersion,
296
- key: s.key,
297
- hydrate: s.hydrate,
298
- pageSize: s.pageSize,
299
- refreshMs: s.refreshMs,
300
- bridge: s.bridge,
301
- source: s.source ? {
302
- origin: s.source.origin,
303
- collection: s.source.collection,
304
- scope: s.source.scope,
305
- projectionVersion: s.source.projectionVersion,
306
- schemaVersion: s.source.schemaVersion,
307
- configVersion: s.source.configVersion
308
- } : null
309
- }
310
- ])
311
- )
312
- );
313
- const latest = (0, import_react6.useRef)(replicate2);
314
- latest.current = replicate2;
315
- const coordinators = (0, import_react6.useMemo)(() => {
316
- const map = /* @__PURE__ */ new Map();
317
- for (const [collection, scope] of Object.entries(latest.current)) {
318
- const source = scope.source ?? (0, import_taladb2.createRestSource)({ ...scope, collection });
319
- map.set(
320
- collection,
321
- new import_taladb2.ReplicationCoordinator(db, source, {
322
- pageSize: scope.pageSize,
323
- yieldFn: yieldToUi,
324
- onProgress: (state) => setCoverage((prev) => ({ ...prev, [collection]: state })),
325
- collectionOptions: collectionOptions.get(collection)
326
- })
327
- );
328
- }
329
- return map;
330
- }, [db, registryKey, collectionOptions]);
331
- (0, import_react6.useEffect)(() => {
332
- let cancelled = false;
333
- void (async () => {
334
- const seeded = {};
335
- for (const [collection, coord] of coordinators) {
336
- seeded[collection] = await coord.getCoverage();
337
- }
338
- if (!cancelled) setCoverage(seeded);
339
- })();
340
- return () => {
341
- cancelled = true;
342
- };
343
- }, [coordinators]);
344
- (0, import_react6.useEffect)(() => {
345
- const cancels = [];
346
- for (const [collection, coord] of coordinators) {
347
- const mode = latest.current[collection]?.hydrate ?? "idle";
348
- if (mode === "manual") continue;
349
- const start = () => {
350
- void coord.hydrate().catch(() => {
351
- });
352
- };
353
- if (mode === "eager") start();
354
- else cancels.push(whenIdle(start));
355
- }
356
- return () => cancels.forEach((c) => c());
357
- }, [coordinators]);
358
- (0, import_react6.useEffect)(() => {
359
- const timers = [];
360
- for (const [collection, coord] of coordinators) {
361
- const ms = latest.current[collection]?.refreshMs ?? 0;
362
- if (ms > 0) {
363
- timers.push(setInterval(() => void coord.refresh().catch(() => {
364
- }), ms));
365
- }
366
- }
367
- return () => timers.forEach(clearInterval);
368
- }, [coordinators]);
369
- const value = (0, import_react6.useMemo)(
370
- () => ({ coordinators, scopes: latest.current, coverage }),
371
- [coordinators, coverage]
372
- );
373
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ReplicationContext.Provider, { value, children });
374
- }
375
- function useReplication() {
376
- return (0, import_react6.useContext)(ReplicationContext);
377
- }
378
-
379
- // src/replication/config.tsx
380
- var import_jsx_runtime3 = require("react/jsx-runtime");
381
- var ReplicationContext2 = (0, import_react7.createContext)(null);
382
- function ReplicationProvider({
383
- children,
384
- replicate: replicate2,
385
- ...config
386
- }) {
387
- const key = `${config.endpoint ?? ""}|${config.pollMs ?? ""}|${JSON.stringify(config.paths ?? null)}|${JSON.stringify(config.prefetch ?? null)}|${config.prefetchMode ?? ""}|${config.prefetchConcurrency ?? ""}`;
388
- const value = (0, import_react7.useMemo)(
389
- () => config.endpoint ? config : null,
390
- // eslint-disable-next-line react-hooks/exhaustive-deps
391
- [key]
392
- );
393
- const inner = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(ReplicationContext2.Provider, { value, children: [
394
- value?.prefetch && value.prefetch.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PrefetchRunner, {}) : null,
395
- children
396
- ] });
397
- return replicate2 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReplicationScopes, { replicate: replicate2, children: inner }) : inner;
398
- }
399
- function resolveReplicationConfig(base, overrides) {
400
- const endpoint = overrides?.endpoint ?? base?.endpoint;
401
- const pollMs = overrides?.pollMs ?? base?.pollMs ?? 0;
402
- if (!endpoint) return { config: null, pollMs };
403
- return {
404
- config: {
405
- endpoint,
406
- getAuth: overrides?.getAuth ?? base?.getAuth,
407
- fetch: overrides?.fetch ?? base?.fetch,
408
- paths: overrides?.paths ?? base?.paths
409
- },
410
- pollMs
411
- };
412
- }
413
- function useReplicationBase() {
414
- return (0, import_react7.useContext)(ReplicationContext2);
415
- }
416
- function useReplicationConfig(overrides) {
417
- return resolveReplicationConfig((0, import_react7.useContext)(ReplicationContext2), overrides);
418
- }
419
- var CURSOR_COLLECTION = "__taladb_sync";
420
- function normalizePrefetch(entries) {
421
- return (entries ?? []).map((e) => typeof e === "string" ? { collection: e } : e);
422
- }
423
- var idleScheduler = (fn) => {
424
- const g = globalThis;
425
- if (typeof g.requestIdleCallback === "function") {
426
- const id2 = g.requestIdleCallback(fn, { timeout: 2e3 });
427
- return () => g.cancelIdleCallback?.(id2);
428
- }
429
- const id = setTimeout(fn, 0);
430
- return () => clearTimeout(id);
431
- };
432
- var schedule = idleScheduler;
433
- async function hasSynced(db, target) {
434
- try {
435
- const doc = await db.collection(CURSOR_COLLECTION).findOne({ target });
436
- return doc != null;
437
- } catch {
438
- return false;
439
- }
440
- }
441
- function PrefetchRunner() {
442
- const db = useTalaDB();
443
- const base = useReplicationBase();
444
- const slices = normalizePrefetch(base?.prefetch);
445
- const mode = base?.prefetchMode ?? "once";
446
- const concurrency = Math.max(1, base?.prefetchConcurrency ?? 2);
447
- const baseRef = (0, import_react7.useRef)(base);
448
- baseRef.current = base;
449
- const sig = JSON.stringify({ slices, mode, concurrency, endpoint: base?.endpoint ?? null });
450
- (0, import_react7.useEffect)(() => {
451
- if (slices.length === 0) return void 0;
452
- let cancelled = false;
453
- const cancelSchedule = schedule(() => {
454
- void run();
455
- });
456
- async function run() {
457
- const b = baseRef.current;
458
- const queue = normalizePrefetch(b?.prefetch);
459
- const worker = async () => {
460
- while (!cancelled) {
461
- const slice = queue.shift();
462
- if (!slice) return;
463
- const { config } = resolveReplicationConfig(b, { endpoint: slice.endpoint });
464
- if (!config) continue;
465
- const target = replicationTarget(config.endpoint, slice.collection);
466
- if (mode === "once" && await hasSynced(db, target)) continue;
467
- if (cancelled) return;
468
- try {
469
- await replicate(db, config, slice.collection, "pull");
470
- } catch {
471
- }
472
- }
473
- };
474
- const lanes = Math.min(concurrency, queue.length);
475
- await Promise.all(Array.from({ length: lanes }, () => worker()));
476
- }
477
- return () => {
478
- cancelled = true;
479
- cancelSchedule();
480
- };
481
- }, [db, sig]);
482
- return null;
483
- }
484
-
485
- // src/useCoverage.ts
486
- var import_taladb3 = require("taladb");
487
- function useCoverage(collection) {
488
- const replication = useReplication();
489
- const state = replication?.coverage[collection] ?? { status: "empty" };
490
- return {
491
- status: state.status,
492
- ready: (0, import_taladb3.isAuthoritative)(state),
493
- rows: (0, import_taladb3.rowsApplied)(state),
494
- total: "total" in state ? state.total : void 0,
495
- progress: (0, import_taladb3.progress)(state),
496
- reason: state.status === "error" ? state.error : state.status === "best-effort" || state.status === "stale" ? state.reason : void 0
497
- };
498
- }
499
- var useHydrationProgress = useCoverage;
500
-
501
- // src/useQuery.ts
502
- var import_react8 = require("react");
503
- function useQuery(options) {
504
- const { collection, filter, sort, page, limit, skip, enabled = true } = options;
210
+ function useWrite(options) {
211
+ const { collection } = options;
505
212
  const col = useCollection(collection);
506
- const db = useTalaDB();
507
- const coverage = useCoverage(collection);
508
- const replication = useReplication();
509
- const coord = replication?.coordinators.get(collection);
510
- const legacyNetworked = !coord && options.source !== "local-only";
511
- const { config: legacyConfig, pollMs } = useReplicationConfig(options);
512
- const legacyConfigRef = (0, import_react8.useRef)(legacyConfig);
513
- legacyConfigRef.current = legacyConfig;
514
- const [syncing, setSyncing] = (0, import_react8.useState)(false);
515
- const [syncError, setSyncError] = (0, import_react8.useState)(null);
516
- const [firstSyncDone, setFirstSyncDone] = (0, import_react8.useState)(false);
517
- const legacyRefetch = (0, import_react8.useCallback)(async () => {
518
- const cfg = legacyConfigRef.current;
519
- if (!legacyNetworked || !cfg) return;
520
- setSyncing(true);
521
- setSyncError(null);
522
- try {
523
- await replicate(db, cfg, collection, "pull");
524
- } catch (error) {
525
- setSyncError(error);
526
- } finally {
527
- setSyncing(false);
528
- setFirstSyncDone(true);
529
- }
530
- }, [db, collection, legacyNetworked, legacyConfig?.endpoint]);
531
- (0, import_react8.useEffect)(() => {
532
- if (!enabled || !legacyNetworked || !legacyConfig) return;
533
- void legacyRefetch();
534
- if (pollMs > 0) {
535
- const timer = setInterval(() => void legacyRefetch(), pollMs);
536
- return () => clearInterval(timer);
537
- }
538
- return void 0;
539
- }, [enabled, legacyNetworked, legacyConfig?.endpoint, pollMs, legacyRefetch]);
540
- const offset = page !== void 0 && limit !== void 0 ? (page - 1) * limit : skip ?? 0;
541
- const filterKey = JSON.stringify(filter ?? null);
542
- const sortKey = JSON.stringify(sort ?? null);
543
- const [bridgeIds, setBridgeIds] = (0, import_react8.useState)([]);
544
- const [fetchError, setFetchError] = (0, import_react8.useState)(null);
545
- const scopeValue = coord?.replicaScope;
546
- const bridgeIdKey = (bridgeIds ?? []).join("|");
547
- const pipeline = (0, import_react8.useMemo)(() => {
548
- const stages = [];
549
- const scoped = scopeValue ? { _replica_scope: scopeValue } : void 0;
550
- const bridgeOnly = !coverage.ready ? { _id: { $in: bridgeIds ?? [] } } : void 0;
551
- const matches = [scoped, bridgeOnly, filter].filter(Boolean);
552
- if (matches.length === 1) stages.push({ $match: matches[0] });
553
- else if (matches.length > 1) stages.push({ $match: { $and: matches } });
554
- if (sort) stages.push({ $sort: sort });
555
- if (coverage.ready && offset > 0) stages.push({ $skip: offset });
556
- if (limit !== void 0) stages.push({ $limit: limit });
557
- return stages;
558
- }, [filterKey, sortKey, offset, limit, coverage.ready, scopeValue, bridgeIdKey]);
559
- const read = useAggregate(col, enabled ? pipeline : [{ $limit: 0 }]);
560
- const [fetching, setFetching] = (0, import_react8.useState)(false);
561
- const bridgeKey = `${collection}|${filterKey}|${sortKey}|${offset}|${limit}`;
562
- const canBridge = replication?.scopes[collection]?.bridge !== false;
563
- (0, import_react8.useEffect)(() => {
564
- if (!enabled || coverage.ready || !canBridge) return;
565
- if (!coord) return;
566
- let cancelled = false;
567
- setFetching(true);
568
- setFetchError(null);
569
- setBridgeIds([]);
570
- void coord.bridge({
571
- filter,
572
- sort,
573
- page,
574
- limit
575
- }).then((result) => setBridgeIds(result.ids ?? [])).catch((error) => {
576
- if (!cancelled) setFetchError(error);
577
- }).finally(() => {
578
- if (!cancelled) setFetching(false);
579
- });
580
- return () => {
581
- cancelled = true;
582
- };
583
- }, [bridgeKey, coverage.ready, canBridge, enabled, coord]);
584
- const refetch = async () => {
585
- if (coord) await coord.refresh();
586
- else await legacyRefetch();
587
- };
588
- if (enabled && legacyNetworked && !legacyConfig) {
589
- throw new Error(
590
- `useQuery({ collection: '${collection}' }) needs either a coverage-first replicate scope or a legacy sync endpoint. Use source: 'local-only' for a purely local query.`
591
- );
592
- }
593
- return {
594
- data: read.data,
595
- total: coverage.total,
596
- loading: options.source === "remote-first" && legacyNetworked ? read.loading || !firstSyncDone : read.loading,
597
- error: read.error ?? fetchError,
598
- fetchError,
599
- coverage,
600
- fetching,
601
- syncing,
602
- syncError,
603
- refetch
604
- };
605
- }
606
-
607
- // src/useQueries.ts
608
- var import_react9 = require("react");
609
- function useQueries(queries) {
610
- const db = useTalaDB();
611
- const registry = useCollectionOptions();
612
- const replication = useReplication();
613
- const [results, setResults] = (0, import_react9.useState)(() => queries.map(() => ({ data: [], loading: true, error: null })));
614
- const [bridgeIds, setBridgeIds] = (0, import_react9.useState)({});
615
- const [fetchErrors, setFetchErrors] = (0, import_react9.useState)({});
616
- const signature = JSON.stringify(
617
- queries.map((q) => ({
618
- collection: q.collection,
619
- filter: q.filter ?? null,
620
- sort: q.sort ?? null,
621
- page: q.page ?? null,
622
- limit: q.limit ?? null,
623
- skip: q.skip ?? null,
624
- enabled: q.enabled ?? true
625
- }))
626
- );
627
- const latest = (0, import_react9.useRef)(queries);
628
- latest.current = queries;
629
- const bridgeManifestKey = JSON.stringify(bridgeIds);
630
- const replicationReadKey = JSON.stringify(
631
- queries.map((q) => ({
632
- scope: replication?.coordinators.get(q.collection)?.replicaScope ?? null,
633
- ready: replication?.coverage[q.collection]?.status === "complete"
634
- }))
635
- );
636
- (0, import_react9.useEffect)(() => {
637
- const current = latest.current;
638
- setResults(current.map(() => ({ data: [], loading: true, error: null })));
639
- const unsubs = current.map((q, i) => {
640
- if (q.enabled === false) return () => {
641
- };
642
- const col = db.collection(q.collection, registry.get(q.collection));
643
- const offset = q.page !== void 0 && q.limit !== void 0 ? (q.page - 1) * q.limit : q.skip ?? 0;
644
- const pipeline = [];
645
- const coord = replication?.coordinators.get(q.collection);
646
- const covered = replication?.coverage[q.collection]?.status === "complete";
647
- const matches = [
648
- coord ? { _replica_scope: coord.replicaScope } : void 0,
649
- !covered ? { _id: { $in: bridgeIds[i] ?? [] } } : void 0,
650
- q.filter
651
- ].filter(Boolean);
652
- if (matches.length === 1) pipeline.push({ $match: matches[0] });
653
- else if (matches.length > 1) pipeline.push({ $match: { $and: matches } });
654
- if (q.sort) pipeline.push({ $sort: q.sort });
655
- if (covered && offset > 0) pipeline.push({ $skip: offset });
656
- if (q.limit !== void 0) pipeline.push({ $limit: q.limit });
657
- return col.subscribeAggregate(
658
- pipeline,
659
- (docs) => setResults((prev) => {
660
- const next = [...prev];
661
- next[i] = { data: docs, loading: false, error: null };
662
- return next;
663
- }),
664
- (error) => setResults((prev) => {
665
- const next = [...prev];
666
- next[i] = { ...next[i], loading: false, error };
667
- return next;
668
- })
669
- );
670
- });
671
- return () => unsubs.forEach((u) => u());
672
- }, [db, registry, signature, replicationReadKey, bridgeManifestKey]);
673
- (0, import_react9.useEffect)(() => {
674
- for (const [i, q] of latest.current.entries()) {
675
- if (q.enabled === false) continue;
676
- const coord = replication?.coordinators.get(q.collection);
677
- if (!coord || replication?.scopes[q.collection]?.bridge === false) continue;
678
- void coord.getCoverage().then((state) => {
679
- if (state.status === "complete") return;
680
- return coord.bridge({
681
- filter: q.filter,
682
- sort: q.sort,
683
- page: q.page,
684
- limit: q.limit
685
- }).then((result) => {
686
- setBridgeIds((prev) => ({ ...prev, [i]: result.ids }));
687
- setFetchErrors((prev) => {
688
- const next = { ...prev };
689
- delete next[i];
690
- return next;
691
- });
692
- }).catch((error) => setFetchErrors((prev) => ({ ...prev, [i]: error })));
693
- });
694
- }
695
- }, [replication, signature]);
696
- return (0, import_react9.useMemo)(
697
- () => latest.current.map((q, i) => {
698
- const state = replication?.coverage[q.collection] ?? { status: "empty" };
699
- const coverage = {
700
- status: state.status,
701
- // Only `complete` licenses a local-only read — see `useCoverage`.
702
- ready: state.status === "complete",
703
- rows: "rowsApplied" in state ? state.rowsApplied ?? 0 : 0,
704
- total: "total" in state ? state.total : void 0,
705
- progress: state.status === "complete" ? 1 : void 0,
706
- reason: state.status === "error" ? state.error : state.status === "best-effort" || state.status === "stale" ? state.reason : void 0
707
- };
708
- return {
709
- data: results[i]?.data ?? [],
710
- total: coverage.total,
711
- loading: results[i]?.loading ?? true,
712
- error: results[i]?.error ?? fetchErrors[i] ?? null,
713
- fetchError: fetchErrors[i] ?? null,
714
- coverage,
715
- fetching: false,
716
- syncing: false,
717
- syncError: null,
718
- refetch: async () => {
719
- await replication?.coordinators.get(q.collection)?.refresh();
720
- }
721
- };
722
- }),
723
- // eslint-disable-next-line react-hooks/exhaustive-deps
724
- [results, signature, replication]
725
- );
726
- }
727
-
728
- // src/useMutation.ts
729
- var import_react10 = require("react");
730
- function useMutation(options) {
731
- const { collection, direction = "push", drainOnMount = true } = options;
732
- const db = useTalaDB();
733
- const col = useCollection(collection);
734
- const { config } = useReplicationConfig({
735
- endpoint: options.endpoint,
736
- getAuth: options.getAuth,
737
- fetch: options.fetch,
738
- paths: options.paths
739
- });
740
- const configRef = (0, import_react10.useRef)(config);
741
- configRef.current = config;
742
- const [pending, setPending] = (0, import_react10.useState)(false);
743
- const [error, setError] = (0, import_react10.useState)(null);
744
- const endpoint = config?.endpoint;
745
- const applyLocal = (0, import_react10.useCallback)(
213
+ const [pending, setPending] = (0, import_react6.useState)(false);
214
+ const [error, setError] = (0, import_react6.useState)(null);
215
+ const apply = (0, import_react6.useCallback)(
746
216
  async (op) => {
747
217
  switch (op.type) {
748
218
  case "insert":
@@ -758,18 +228,12 @@ function useMutation(options) {
758
228
  },
759
229
  [col]
760
230
  );
761
- const drain = (0, import_react10.useCallback)(async () => {
762
- const cfg = configRef.current;
763
- if (!cfg) return;
764
- await replicateWithRetry(db, cfg, collection, direction);
765
- }, [db, collection, direction, endpoint]);
766
- const mutateAsync = (0, import_react10.useCallback)(
231
+ const writeAsync = (0, import_react6.useCallback)(
767
232
  async (op) => {
768
233
  setPending(true);
769
234
  setError(null);
770
235
  try {
771
- await applyLocal(op);
772
- await drain();
236
+ await apply(op);
773
237
  } catch (e) {
774
238
  setError(e);
775
239
  throw e;
@@ -777,41 +241,25 @@ function useMutation(options) {
777
241
  setPending(false);
778
242
  }
779
243
  },
780
- [applyLocal, drain]
244
+ [apply]
781
245
  );
782
- const mutate = (0, import_react10.useCallback)(
246
+ const write = (0, import_react6.useCallback)(
783
247
  (op) => {
784
- void mutateAsync(op).catch(() => {
248
+ void writeAsync(op).catch(() => {
785
249
  });
786
250
  },
787
- [mutateAsync]
251
+ [writeAsync]
788
252
  );
789
- (0, import_react10.useEffect)(() => {
790
- if (!drainOnMount || !configRef.current) return;
791
- void drain().catch(() => {
792
- });
793
- }, [drain, drainOnMount]);
794
- if (!config) {
795
- throw new Error(
796
- `useMutation({ collection: '${collection}' }) needs an endpoint. Wrap the tree in <ReplicationProvider endpoint="\u2026"> or pass { endpoint }.`
797
- );
798
- }
799
- return { mutate, mutateAsync, pending, error };
253
+ return { write, writeAsync, pending, error };
800
254
  }
801
255
  // Annotate the CommonJS export names for ESM import in node:
802
256
  0 && (module.exports = {
803
- ReplicationProvider,
804
257
  TalaDBProvider,
805
258
  useAggregate,
806
259
  useCollection,
807
260
  useCollectionOptions,
808
- useCoverage,
809
261
  useFind,
810
262
  useFindOne,
811
- useHydrationProgress,
812
- useMutation,
813
- useQueries,
814
- useQuery,
815
- useReplicationConfig,
816
- useTalaDB
263
+ useTalaDB,
264
+ useWrite
817
265
  });