@evolu/common 8.13.0 → 8.14.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.
@@ -263,6 +263,36 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
263
263
  readonly name: Name;
264
264
  /** {@link AppOwner}. */
265
265
  readonly appOwner: AppOwner;
266
+ /**
267
+ * Whether this device keeps the database, resolved once the shared worker
268
+ * serves it, or `Unknown` if this instance is disposed first.
269
+ *
270
+ * Apps tell the user when it is `NotPersisted`, because then data that has
271
+ * not synced is lost when the database closes. Apps must not tell the user
272
+ * that data is saved on the device when it is `Unknown`.
273
+ *
274
+ * ### Example
275
+ *
276
+ * ```ts
277
+ * import { assertEqual } from "@evolu/common";
278
+ * import type { DevicePersistence } from "@evolu/common/local-first";
279
+ *
280
+ * // The notice for `await evolu.devicePersistence`, or null for none.
281
+ * const persistenceNotice = (
282
+ * devicePersistence: DevicePersistence,
283
+ * ): string | null =>
284
+ * devicePersistence === "NotPersisted"
285
+ * ? "Your data isn't kept on this device. Changes that haven't synced are lost when you close this tab."
286
+ * : null;
287
+ *
288
+ * assertEqual(persistenceNotice("Unknown"), null);
289
+ * assertEqual(
290
+ * persistenceNotice("NotPersisted"),
291
+ * "Your data isn't kept on this device. Changes that haven't synced are lost when you close this tab.",
292
+ * );
293
+ * ```
294
+ */
295
+ readonly devicePersistence: Promise<DevicePersistence>;
266
296
  /**
267
297
  * Inserts a row and returns the generated {@link Id}.
268
298
  *
@@ -723,6 +753,27 @@ export interface Evolu<S extends EvoluSchema = EvoluSchema> extends AsyncDisposa
723
753
  * @group Core
724
754
  */
725
755
  export type UnuseOwner = () => void;
756
+ /**
757
+ * Whether this device keeps an {@link Evolu} database, stating only what Evolu
758
+ * knows for sure.
759
+ *
760
+ * - `Persisted`: the platform stores it in the app's own file, as React Native
761
+ * does.
762
+ * - `NotPersisted`: it is kept in memory, because {@link EvoluConfig.memoryOnly}
763
+ * asks for that or the platform offers no persistent storage, as a browser
764
+ * does in Safari's Private Browsing or a Firefox private window. Data that
765
+ * has not synced is lost when the database closes.
766
+ * - `Unknown`: it is stored, but the platform may delete it. A browser can keep
767
+ * storage only for a private session, as Chrome's incognito does without
768
+ * telling the app, delete it after a while or when disk space runs low, and
769
+ * let the user clear it.
770
+ *
771
+ * See [Will my data stay on the
772
+ * device?](https://www.evolu.dev/docs/faq#will-my-data-stay-on-the-device).
773
+ *
774
+ * @group Core
775
+ */
776
+ export type DevicePersistence = "Persisted" | "NotPersisted" | "Unknown";
726
777
  /**
727
778
  * Represents app-level errors that can occur in {@link Evolu}.
728
779
  *
@@ -936,6 +987,19 @@ export interface SyncStateDep {
936
987
  */
937
988
  readonly syncState: ReadonlyStore<SyncState | null>;
938
989
  }
990
+ /**
991
+ * Asks the platform to keep stored databases until the user deletes them.
992
+ *
993
+ * Only unsynced local changes exist nowhere else, so each {@link Evolu} instance
994
+ * calls it after its first local mutation, when its
995
+ * {@link Evolu.devicePersistence} is `Unknown`. A platform provides it when its
996
+ * storage can be deleted without the user, as a browser's can.
997
+ *
998
+ * @group Construction
999
+ */
1000
+ export interface RequestPersistentStorageDep {
1001
+ readonly requestPersistentStorage: () => void;
1002
+ }
939
1003
  /**
940
1004
  * Shared platform dependencies for creating {@link Evolu} instances.
941
1005
  *
@@ -949,11 +1013,11 @@ export type EvoluDeps = EvoluPlatformDeps & ConsoleDep & EvoluErrorDep & SyncSta
949
1013
  * Platform-specific dependencies required to create {@link EvoluDeps}.
950
1014
  *
951
1015
  * Provides worker and channel adapters plus optional platform integrations for
952
- * logging and synchronous UI flush.
1016
+ * logging, synchronous UI flush, and persistent storage requests.
953
1017
  *
954
1018
  * @group Construction
955
1019
  */
956
- export type EvoluPlatformDeps = CreateDbWorkerDep & CreateBroadcastChannelDep & CreateMessageChannelDep & LockManagerDep & ReloadAppDep & SharedWorkerDep & Partial<ConsoleDep> & Partial<FlushSyncDep>;
1020
+ export type EvoluPlatformDeps = CreateDbWorkerDep & CreateBroadcastChannelDep & CreateMessageChannelDep & LockManagerDep & ReloadAppDep & SharedWorkerDep & Partial<ConsoleDep> & Partial<FlushSyncDep> & Partial<RequestPersistentStorageDep>;
957
1021
  /**
958
1022
  * Creates shared dependencies used by all {@link createEvolu} instances on a
959
1023
  * platform.
@@ -1 +1 @@
1
- {"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/local-first/Evolu.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/local-first/Evolu.ts"],"names":[],"mappings":"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"}
@@ -210,9 +210,7 @@ export const createEvoluDeps = (deps) => {
210
210
  console.error(message.error);
211
211
  break;
212
212
  case "Waiting":
213
- case "StorageUnavailable":
214
- // Platform adapters act on these; see Builds and Storage in the Shared
215
- // module.
213
+ // Platform adapters act on it; see Builds in the Shared module.
216
214
  break;
217
215
  case "Connected": {
218
216
  assert(!syncStateBroadcastChannel, "The shared worker connects once.");
@@ -344,6 +342,12 @@ export const createEvolu = (schema, config) => async (run) => {
344
342
  exportDatabasePending?.reject({ type: "EvoluDisposedError" });
345
343
  exportDatabasePending = null;
346
344
  });
345
+ const devicePersistence = Promise.withResolvers();
346
+ // A disposed instance never hears from its tenant, so awaiters settle.
347
+ disposer.defer(() => {
348
+ devicePersistence.resolve("Unknown");
349
+ });
350
+ let isPersistentStorageRequested = false;
347
351
  let postMessage;
348
352
  // Scope worker/channel wiring and keep only postMessage outside.
349
353
  {
@@ -412,6 +416,9 @@ export const createEvolu = (schema, config) => async (run) => {
412
416
  }
413
417
  break;
414
418
  }
419
+ case "OnDevicePersistence":
420
+ devicePersistence.resolve(message.devicePersistence);
421
+ break;
415
422
  default:
416
423
  exhaustiveCheck(message);
417
424
  }
@@ -497,6 +504,17 @@ export const createEvolu = (schema, config) => async (run) => {
497
504
  const size = measureMutation(dbChange.table, dbChange.values);
498
505
  assert(size <= maxMutationSize, `The mutation of table '${dbChange.table}' is ${size} bytes, over maxMutationSize (${maxMutationSize}). Bound the Types of its columns or check it with evolu.getMutationSize.`);
499
506
  }
507
+ // Only unsynced local changes exist nowhere else, so the first one is
508
+ // when asking the platform to keep them starts to matter. Asking on
509
+ // load would show Firefox's permission prompt to every visitor.
510
+ if (!isPersistentStorageRequested) {
511
+ isPersistentStorageRequested = true;
512
+ void devicePersistence.promise.then((persistence) => {
513
+ if (persistence === "Unknown" && !disposed) {
514
+ run.deps.requestPersistentStorage?.();
515
+ }
516
+ });
517
+ }
500
518
  mutateBatch.push({
501
519
  change: { ...dbChange, ownerId: options?.ownerId ?? appOwner.id },
502
520
  onComplete: options?.onComplete,
@@ -555,6 +573,7 @@ export const createEvolu = (schema, config) => async (run) => {
555
573
  return ok(disposable({
556
574
  name,
557
575
  appOwner,
576
+ devicePersistence: devicePersistence.promise,
558
577
  insert: createMutation("insert"),
559
578
  update: createMutation("update"),
560
579
  upsert: createMutation("upsert"),
@@ -69,13 +69,20 @@
69
69
  *
70
70
  * ## Storage
71
71
  *
72
- * A platform that can lack persistent storage, as a browser does in Safari's
73
- * Private Browsing, provides {@link PersistentStorageDep}. The worker checks it
74
- * once, after it takes the build lock and before any DbWorker starts. Without
75
- * persistent storage, every DbWorker it starts keeps its database in memory,
76
- * replacements included, and each tab that connects is told with
77
- * `StorageUnavailable`. The decision holds for the worker's lifetime, so all
78
- * its tabs see one mode, and the next worker checks again.
72
+ * The platform tells the worker with {@link DevicePersistenceDep} what it can
73
+ * promise about stored databases. The worker asks once, after it takes the
74
+ * build lock and before any DbWorker starts. A browser checks whether it can
75
+ * open the origin private file system, which Safari's Private Browsing and
76
+ * Firefox's private windows refuse. Without persistent storage, every DbWorker
77
+ * the worker starts keeps its database in memory, replacements included. The
78
+ * decision holds for the worker's lifetime, so all its tabs see one mode, and
79
+ * the next worker checks again.
80
+ *
81
+ * A tenant tells each instance it adds its {@link Evolu.devicePersistence}:
82
+ * `NotPersisted` when its database is kept in memory, because of the platform
83
+ * or {@link EvoluConfig.memoryOnly}, otherwise what the platform promised.
84
+ * Instances of one name share the tenant, so a later instance gets the mode the
85
+ * first one chose.
79
86
  *
80
87
  * The check cannot tell a private session from a storage failure, but memory
81
88
  * loses nothing that refusing to start would have kept, and the persistent
@@ -262,6 +269,7 @@ import { type ExtractTyped, type Id, type InferType, type LiteralType, type Name
262
269
  import type { CreateWebSocketDep, WebSocketError } from "../WebSocket.ts";
263
270
  import type { SharedWorker as CommonSharedWorker, CreateBroadcastChannelDep, CreateMessageChannelDep, NativeMessagePort, SharedWorkerSelf, WorkerDeps } from "../Worker.ts";
264
271
  import type { DbWorkerInit, UnsupportedDbVersionError } from "./Db.ts";
272
+ import type { DevicePersistence } from "./Evolu.ts";
265
273
  import type { Owner, OwnerId, SyncOwner } from "./Owner.ts";
266
274
  import { type ApplyProtocolMessageAsClientResult, type ProtocolError, type ProtocolInvalidDataError, type ProtocolMessage, type ProtocolTimestampMismatchError } from "./Protocol.ts";
267
275
  import { type Patch, type Query, type RowsByQueryMap } from "./Query.ts";
@@ -315,13 +323,6 @@ export type SharedWorkerOutput = DbWorkerInit | {
315
323
  readonly type: "Connected";
316
324
  readonly workerId: SharedWorkerId;
317
325
  readonly syncStateChannelName: string;
318
- } | {
319
- /**
320
- * Sent to a connecting tab after `Connected` when the platform offers no
321
- * persistent storage, so the worker keeps every database in memory; see
322
- * Storage in this module's documentation.
323
- */
324
- readonly type: "StorageUnavailable";
325
326
  };
326
327
  export type ConsoleEntryOrError = {
327
328
  readonly type: "ConsoleEntry";
@@ -593,22 +594,23 @@ export interface RelaySyncState {
593
594
  * stops syncing through its relay, while a skipped change leaves out only
594
595
  * that change.
595
596
  *
596
- * Evolu saves changes on the device before they sync, so sync needs no UI while
597
- * it works: show nothing for `NoRelays`, `Syncing`, and `Synced`. An indicator
598
- * that changes with every edit distracts, and screen readers announce each
599
- * change.
597
+ * Evolu stores changes in the local database before they sync, so sync needs no
598
+ * UI while it works: show nothing for `NoRelays`, `Syncing`, and `Synced`. An
599
+ * indicator that changes with every edit distracts, and screen readers announce
600
+ * each change.
600
601
  *
601
602
  * For `Offline` and `Error`, show one quiet line that lasts as long as the
602
603
  * status, not a dialog, which interrupts, or a toast, which disappears while
603
- * the problem lasts. Say that changes are saved on this device. Evolu reports
604
- * `Offline` at once; an app may wait a few seconds before showing it, because
605
- * brief disconnections, such as waking from sleep, reconnect quickly. For
606
- * `Error`, write actionable text for the error types the app can act on, such
607
- * as a {@link ProtocolQuotaError}: the relay stores no more data for the owner,
608
- * so offer more quota, such as a plan upgrade, then call
609
- * {@link Evolu.requestSync} with the owner's ID. For any other error, show
610
- * generic text that names the error type, which helps when the user reports
611
- * it.
604
+ * the problem lasts. Do not say that changes are saved on this device unless
605
+ * {@link Evolu.devicePersistence} is `Persisted`: a browser may keep them only
606
+ * for a private session or delete them later. Evolu reports `Offline` at once;
607
+ * an app may wait a few seconds before showing it, because brief
608
+ * disconnections, such as waking from sleep, reconnect quickly. For `Error`,
609
+ * write actionable text for the error types the app can act on, such as a
610
+ * {@link ProtocolQuotaError}: the relay stores no more data for the owner, so
611
+ * offer more quota, such as a plan upgrade, then call {@link Evolu.requestSync}
612
+ * with the owner's ID. For any other error, show generic text that names the
613
+ * error type, which helps when the user reports it.
612
614
  *
613
615
  * Render the line inside one element with `role="status"` that stays mounted:
614
616
  * screen readers announce changes only in a live region that already exists,
@@ -637,18 +639,18 @@ export interface RelaySyncState {
637
639
  * case "Synced":
638
640
  * return null;
639
641
  * case "Offline":
640
- * return "Offline. Your changes are saved on this device.";
642
+ * return "Offline. Changes will sync when you're back online.";
641
643
  * case "Error":
642
644
  * return status.error.type === "ProtocolQuotaError"
643
- * ? "Sync is paused because the sync server is full. Your changes are saved on this device."
644
- * : `Sync error: ${status.error.type}. Your changes are saved on this device.`;
645
+ * ? "Sync is paused because the sync server is full."
646
+ * : `Sync error: ${status.error.type}.`;
645
647
  * }
646
648
  * };
647
649
  *
648
650
  * assertEqual(syncStatusToMessage({ type: "Synced" }), null);
649
651
  * assertEqual(
650
652
  * syncStatusToMessage({ type: "Offline" }),
651
- * "Offline. Your changes are saved on this device.",
653
+ * "Offline. Changes will sync when you're back online.",
652
654
  * );
653
655
  * assertEqual(
654
656
  * syncStatusToMessage({
@@ -659,14 +661,14 @@ export interface RelaySyncState {
659
661
  * at: Millis.orThrow(1000),
660
662
  * },
661
663
  * }),
662
- * "Sync is paused because the sync server is full. Your changes are saved on this device.",
664
+ * "Sync is paused because the sync server is full.",
663
665
  * );
664
666
  * assertEqual(
665
667
  * syncStatusToMessage({
666
668
  * type: "Error",
667
669
  * error: { type: "SyncFailed", at: Millis.orThrow(1000) },
668
670
  * }),
669
- * "Sync error: SyncFailed. Your changes are saved on this device.",
671
+ * "Sync error: SyncFailed.",
670
672
  * );
671
673
  * ```
672
674
  */
@@ -934,6 +936,10 @@ export type EvoluOutput = {
934
936
  /** The mutation with these onComplete callbacks could not be stored. */
935
937
  readonly type: "OnMutateFailed";
936
938
  readonly onCompleteIds: ReadonlyArray<Id>;
939
+ } | {
940
+ /** Sent once, when the tenant adds the instance; see Storage. */
941
+ readonly type: "OnDevicePersistence";
942
+ readonly devicePersistence: DevicePersistence;
937
943
  };
938
944
  export type DbWorkerInput = (Typed<"Request"> & {
939
945
  readonly attemptId: Id;
@@ -1022,15 +1028,16 @@ export type DbWorkerQueuedResponse = {
1022
1028
  };
1023
1029
  };
1024
1030
  /**
1025
- * Tells whether the platform can store databases persistently.
1031
+ * Tells what the platform can promise about the databases it stores.
1026
1032
  *
1027
- * Only a platform that can lack persistent storage provides it, as a browser
1028
- * does in Safari's Private Browsing; see Storage in the Shared module.
1033
+ * `NotPersisted` means the platform offers no persistent storage now, as a
1034
+ * browser does in Safari's Private Browsing or a Firefox private window, so the
1035
+ * worker keeps every database in memory; see Storage in the Shared module.
1029
1036
  */
1030
- export interface PersistentStorageDep {
1031
- readonly isPersistentStorageAvailable: () => Promise<boolean>;
1037
+ export interface DevicePersistenceDep {
1038
+ readonly getDevicePersistence: () => Promise<DevicePersistence>;
1032
1039
  }
1033
- export type SharedWorkerDeps = WorkerDeps & CreateBroadcastChannelDep & CreateMessageChannelDep & CreateWebSocketDep & LockManagerDep & Partial<PersistentStorageDep>;
1040
+ export type SharedWorkerDeps = WorkerDeps & CreateBroadcastChannelDep & CreateMessageChannelDep & CreateWebSocketDep & DevicePersistenceDep & LockManagerDep;
1034
1041
  export type EvoluInstanceId = Id & Brand<"EvoluInstance">;
1035
1042
  /**
1036
1043
  * Initializes the platform-agnostic Evolu SharedWorker.
@@ -1 +1 @@
1
- {"version":3,"file":"Shared.d.ts","sourceRoot":"","sources":["../../../src/local-first/Shared.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Shared.d.ts","sourceRoot":"","sources":["../../../src/local-first/Shared.ts"],"names":[],"mappings":"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"}
@@ -69,13 +69,20 @@
69
69
  *
70
70
  * ## Storage
71
71
  *
72
- * A platform that can lack persistent storage, as a browser does in Safari's
73
- * Private Browsing, provides {@link PersistentStorageDep}. The worker checks it
74
- * once, after it takes the build lock and before any DbWorker starts. Without
75
- * persistent storage, every DbWorker it starts keeps its database in memory,
76
- * replacements included, and each tab that connects is told with
77
- * `StorageUnavailable`. The decision holds for the worker's lifetime, so all
78
- * its tabs see one mode, and the next worker checks again.
72
+ * The platform tells the worker with {@link DevicePersistenceDep} what it can
73
+ * promise about stored databases. The worker asks once, after it takes the
74
+ * build lock and before any DbWorker starts. A browser checks whether it can
75
+ * open the origin private file system, which Safari's Private Browsing and
76
+ * Firefox's private windows refuse. Without persistent storage, every DbWorker
77
+ * the worker starts keeps its database in memory, replacements included. The
78
+ * decision holds for the worker's lifetime, so all its tabs see one mode, and
79
+ * the next worker checks again.
80
+ *
81
+ * A tenant tells each instance it adds its {@link Evolu.devicePersistence}:
82
+ * `NotPersisted` when its database is kept in memory, because of the platform
83
+ * or {@link EvoluConfig.memoryOnly}, otherwise what the platform promised.
84
+ * Instances of one name share the tenant, so a later instance gets the mode the
85
+ * first one chose.
79
86
  *
80
87
  * The check cannot tell a private session from a storage failure, but memory
81
88
  * loses nothing that refusing to start would have kept, and the persistent
@@ -806,9 +813,6 @@ export const initSharedWorker = (self) => async (run) => {
806
813
  workerId,
807
814
  syncStateChannelName,
808
815
  });
809
- if (isPersistentStorageUnavailable) {
810
- port.postMessage({ type: "StorageUnavailable" });
811
- }
812
816
  });
813
817
  };
814
818
  // Held while this worker runs. Its DbWorkers stop once they can take it,
@@ -821,11 +825,10 @@ export const initSharedWorker = (self) => async (run) => {
821
825
  // Builds.
822
826
  disposer.use(await run.ok(acquireLeaderLock("tab")));
823
827
  starting.dispose();
824
- // Checked once, before any DbWorker starts, so every DbWorker of this
825
- // worker, replacements included, keeps its database in memory; see
826
- // Storage.
827
- const isPersistentStorageUnavailable = deps.isPersistentStorageAvailable !== undefined &&
828
- !(await deps.isPersistentStorageAvailable());
828
+ // Checked once, before any DbWorker starts, so without persistent
829
+ // storage every DbWorker of this worker, replacements included, keeps its
830
+ // database in memory; see Storage.
831
+ const platformDevicePersistence = await deps.getDevicePersistence();
829
832
  disposer.defer(deps.consoleStoreOutputEntry.subscribe(() => {
830
833
  const entry = deps.consoleStoreOutputEntry.get();
831
834
  if (entry)
@@ -1193,9 +1196,11 @@ export const initSharedWorker = (self) => async (run) => {
1193
1196
  tabLeaderPortStore,
1194
1197
  transports,
1195
1198
  }));
1196
- const tenantsByName = disposer.use(await sharedWorkerRun.ok(createSharedResourceByKey((message) => createEvoluTenant(isPersistentStorageUnavailable
1197
- ? { ...message, memoryOnly: true }
1198
- : message, currentTenantsByName, workerId), {
1199
+ const tenantsByName = disposer.use(await sharedWorkerRun.ok(createSharedResourceByKey((message) => {
1200
+ const memoryOnly = message.memoryOnly ||
1201
+ platformDevicePersistence === "NotPersisted";
1202
+ return createEvoluTenant({ ...message, memoryOnly }, memoryOnly ? "NotPersisted" : platformDevicePersistence, currentTenantsByName, workerId);
1203
+ }, {
1199
1204
  idleDisposeAfter: "3s",
1200
1205
  lookup: (message) => message.name,
1201
1206
  })));
@@ -1216,7 +1221,7 @@ export const initSharedWorker = (self) => async (run) => {
1216
1221
  await result_1;
1217
1222
  }
1218
1223
  };
1219
- const createEvoluTenant = ({ name, consoleLevel, sqliteSchema, encryptionKey, memoryOnly, }, currentTenantsByName, workerId) => async (run) => {
1224
+ const createEvoluTenant = ({ name, consoleLevel, sqliteSchema, encryptionKey, memoryOnly, }, devicePersistence, currentTenantsByName, workerId) => async (run) => {
1220
1225
  const env_3 = { stack: [], error: void 0, hasError: false };
1221
1226
  try {
1222
1227
  const disposer = __addDisposableResource(env_3, new AsyncDisposableStack(), true);
@@ -2107,6 +2112,12 @@ const createEvoluTenant = ({ name, consoleLevel, sqliteSchema, encryptionKey, me
2107
2112
  disposer.defer(() => {
2108
2113
  instance.port.onMessage = null;
2109
2114
  });
2115
+ // Instances of one name share the tenant, so each learns the mode
2116
+ // the first one chose.
2117
+ instance.port.postMessage({
2118
+ type: "OnDevicePersistence",
2119
+ devicePersistence,
2120
+ });
2110
2121
  // The main-thread Evolu instance holds this per-instance leader lock
2111
2122
  // while it is alive. Acquiring the same lock here means the main
2112
2123
  // thread instance was disposed or its tab closed, so the tenant-side
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolu/common",
3
- "version": "8.13.0",
3
+ "version": "8.14.0",
4
4
  "description": "TypeScript library and local-first platform",
5
5
  "keywords": [
6
6
  "evolu",
@@ -372,16 +372,6 @@ describe("Evolu", () => {
372
372
  assertSame(setup.deps.evoluError.get(), null);
373
373
  });
374
374
 
375
- it("says nothing to a worker that reports it keeps databases in memory", async () => {
376
- using setup = await setupCreateEvoluDeps();
377
-
378
- setup.sharedWorkerPort.postMessage({ type: "StorageUnavailable" });
379
- await testWaitForWorkerMessage();
380
-
381
- assertEqual(setup.messages, []);
382
- assertSame(setup.deps.evoluError.get(), null);
383
- });
384
-
385
375
  it("announces itself as tab leader with its console level once its worker connects", async () => {
386
376
  const testConsole = testCreateConsole();
387
377
  using setup = await setupCreateEvoluDeps(testConsole);
@@ -1502,6 +1492,106 @@ describe("Evolu", () => {
1502
1492
  });
1503
1493
  });
1504
1494
 
1495
+ describe("device persistence", () => {
1496
+ const setupPersistenceRequests = async () => {
1497
+ let requestCount = 0;
1498
+ const setup = await setupRunWithEvoluDeps({
1499
+ requestPersistentStorage: () => {
1500
+ requestCount += 1;
1501
+ },
1502
+ });
1503
+ return Object.assign(setup, { getRequestCount: () => requestCount });
1504
+ };
1505
+
1506
+ it("resolves devicePersistence to what its tenant sends", async () => {
1507
+ await using setup = await setupRunWithEvoluDeps();
1508
+ const evolu = await setup.run.ok(testCreateEvolu);
1509
+
1510
+ setup.postEvoluOutput({
1511
+ type: "OnDevicePersistence",
1512
+ devicePersistence: "NotPersisted",
1513
+ });
1514
+
1515
+ assertSame(await evolu.devicePersistence, "NotPersisted");
1516
+ });
1517
+
1518
+ it("resolves devicePersistence to Unknown when disposed before its tenant sends it", async () => {
1519
+ await using setup = await setupRunWithEvoluDeps();
1520
+ const evolu = await setup.run.ok(testCreateEvolu);
1521
+
1522
+ await evolu[Symbol.asyncDispose]();
1523
+
1524
+ assertSame(await evolu.devicePersistence, "Unknown");
1525
+ });
1526
+
1527
+ it("requests persistent storage once, after the first mutation, when devicePersistence is Unknown", async () => {
1528
+ await using setup = await setupPersistenceRequests();
1529
+ const evolu = await setup.run.ok(testCreateEvolu);
1530
+ setup.postEvoluOutput({
1531
+ type: "OnDevicePersistence",
1532
+ devicePersistence: "Unknown",
1533
+ });
1534
+ await evolu.devicePersistence;
1535
+ await testWaitForWorkerMessage();
1536
+ assertSame(setup.getRequestCount(), 0);
1537
+
1538
+ evolu.insert("todo", { title: NonEmptyTrimmedString100.orThrow("A") });
1539
+ evolu.insert("todo", { title: NonEmptyTrimmedString100.orThrow("B") });
1540
+ await testWaitForWorkerMessage();
1541
+ evolu.insert("todo", { title: NonEmptyTrimmedString100.orThrow("C") });
1542
+ await testWaitForWorkerMessage();
1543
+
1544
+ assertSame(setup.getRequestCount(), 1);
1545
+ });
1546
+
1547
+ it("requests persistent storage once devicePersistence resolves after the first mutation", async () => {
1548
+ await using setup = await setupPersistenceRequests();
1549
+ const evolu = await setup.run.ok(testCreateEvolu);
1550
+
1551
+ evolu.insert("todo", { title: NonEmptyTrimmedString100.orThrow("A") });
1552
+ await testWaitForWorkerMessage();
1553
+ assertSame(setup.getRequestCount(), 0);
1554
+
1555
+ setup.postEvoluOutput({
1556
+ type: "OnDevicePersistence",
1557
+ devicePersistence: "Unknown",
1558
+ });
1559
+ await evolu.devicePersistence;
1560
+ await testWaitForWorkerMessage();
1561
+
1562
+ assertSame(setup.getRequestCount(), 1);
1563
+ });
1564
+
1565
+ for (const devicePersistence of ["Persisted", "NotPersisted"] as const) {
1566
+ it(`does not request persistent storage when devicePersistence is ${devicePersistence}`, async () => {
1567
+ await using setup = await setupPersistenceRequests();
1568
+ const evolu = await setup.run.ok(testCreateEvolu);
1569
+ setup.postEvoluOutput({
1570
+ type: "OnDevicePersistence",
1571
+ devicePersistence,
1572
+ });
1573
+
1574
+ evolu.insert("todo", { title: NonEmptyTrimmedString100.orThrow("A") });
1575
+ await evolu.devicePersistence;
1576
+ await testWaitForWorkerMessage();
1577
+
1578
+ assertSame(setup.getRequestCount(), 0);
1579
+ });
1580
+ }
1581
+
1582
+ it("does not request persistent storage after disposal", async () => {
1583
+ await using setup = await setupPersistenceRequests();
1584
+ const evolu = await setup.run.ok(testCreateEvolu);
1585
+
1586
+ evolu.insert("todo", { title: NonEmptyTrimmedString100.orThrow("A") });
1587
+ await evolu[Symbol.asyncDispose]();
1588
+ await evolu.devicePersistence;
1589
+ await testWaitForWorkerMessage();
1590
+
1591
+ assertSame(setup.getRequestCount(), 0);
1592
+ });
1593
+ });
1594
+
1505
1595
  describe("dispose evolu", () => {
1506
1596
  it("logs disposeEvolu once", async () => {
1507
1597
  const testConsole = testCreateConsole();
@@ -385,6 +385,37 @@ export interface Evolu<
385
385
  /** {@link AppOwner}. */
386
386
  readonly appOwner: AppOwner;
387
387
 
388
+ /**
389
+ * Whether this device keeps the database, resolved once the shared worker
390
+ * serves it, or `Unknown` if this instance is disposed first.
391
+ *
392
+ * Apps tell the user when it is `NotPersisted`, because then data that has
393
+ * not synced is lost when the database closes. Apps must not tell the user
394
+ * that data is saved on the device when it is `Unknown`.
395
+ *
396
+ * ### Example
397
+ *
398
+ * ```ts
399
+ * import { assertEqual } from "@evolu/common";
400
+ * import type { DevicePersistence } from "@evolu/common/local-first";
401
+ *
402
+ * // The notice for `await evolu.devicePersistence`, or null for none.
403
+ * const persistenceNotice = (
404
+ * devicePersistence: DevicePersistence,
405
+ * ): string | null =>
406
+ * devicePersistence === "NotPersisted"
407
+ * ? "Your data isn't kept on this device. Changes that haven't synced are lost when you close this tab."
408
+ * : null;
409
+ *
410
+ * assertEqual(persistenceNotice("Unknown"), null);
411
+ * assertEqual(
412
+ * persistenceNotice("NotPersisted"),
413
+ * "Your data isn't kept on this device. Changes that haven't synced are lost when you close this tab.",
414
+ * );
415
+ * ```
416
+ */
417
+ readonly devicePersistence: Promise<DevicePersistence>;
418
+
388
419
  /**
389
420
  * Inserts a row and returns the generated {@link Id}.
390
421
  *
@@ -873,6 +904,28 @@ export interface Evolu<
873
904
  */
874
905
  export type UnuseOwner = () => void;
875
906
 
907
+ /**
908
+ * Whether this device keeps an {@link Evolu} database, stating only what Evolu
909
+ * knows for sure.
910
+ *
911
+ * - `Persisted`: the platform stores it in the app's own file, as React Native
912
+ * does.
913
+ * - `NotPersisted`: it is kept in memory, because {@link EvoluConfig.memoryOnly}
914
+ * asks for that or the platform offers no persistent storage, as a browser
915
+ * does in Safari's Private Browsing or a Firefox private window. Data that
916
+ * has not synced is lost when the database closes.
917
+ * - `Unknown`: it is stored, but the platform may delete it. A browser can keep
918
+ * storage only for a private session, as Chrome's incognito does without
919
+ * telling the app, delete it after a while or when disk space runs low, and
920
+ * let the user clear it.
921
+ *
922
+ * See [Will my data stay on the
923
+ * device?](https://www.evolu.dev/docs/faq#will-my-data-stay-on-the-device).
924
+ *
925
+ * @group Core
926
+ */
927
+ export type DevicePersistence = "Persisted" | "NotPersisted" | "Unknown";
928
+
876
929
  /**
877
930
  * Represents app-level errors that can occur in {@link Evolu}.
878
931
  *
@@ -1125,6 +1178,20 @@ export interface SyncStateDep {
1125
1178
  readonly syncState: ReadonlyStore<SyncState | null>;
1126
1179
  }
1127
1180
 
1181
+ /**
1182
+ * Asks the platform to keep stored databases until the user deletes them.
1183
+ *
1184
+ * Only unsynced local changes exist nowhere else, so each {@link Evolu} instance
1185
+ * calls it after its first local mutation, when its
1186
+ * {@link Evolu.devicePersistence} is `Unknown`. A platform provides it when its
1187
+ * storage can be deleted without the user, as a browser's can.
1188
+ *
1189
+ * @group Construction
1190
+ */
1191
+ export interface RequestPersistentStorageDep {
1192
+ readonly requestPersistentStorage: () => void;
1193
+ }
1194
+
1128
1195
  /**
1129
1196
  * Shared platform dependencies for creating {@link Evolu} instances.
1130
1197
  *
@@ -1143,7 +1210,7 @@ export type EvoluDeps = EvoluPlatformDeps &
1143
1210
  * Platform-specific dependencies required to create {@link EvoluDeps}.
1144
1211
  *
1145
1212
  * Provides worker and channel adapters plus optional platform integrations for
1146
- * logging and synchronous UI flush.
1213
+ * logging, synchronous UI flush, and persistent storage requests.
1147
1214
  *
1148
1215
  * @group Construction
1149
1216
  */
@@ -1154,7 +1221,8 @@ export type EvoluPlatformDeps = CreateDbWorkerDep &
1154
1221
  ReloadAppDep &
1155
1222
  SharedWorkerDep &
1156
1223
  Partial<ConsoleDep> &
1157
- Partial<FlushSyncDep>;
1224
+ Partial<FlushSyncDep> &
1225
+ Partial<RequestPersistentStorageDep>;
1158
1226
 
1159
1227
  /**
1160
1228
  * Creates shared dependencies used by all {@link createEvolu} instances on a
@@ -1234,9 +1302,7 @@ export const createEvoluDeps = (deps: EvoluPlatformDeps): EvoluDeps => {
1234
1302
  break;
1235
1303
 
1236
1304
  case "Waiting":
1237
- case "StorageUnavailable":
1238
- // Platform adapters act on these; see Builds and Storage in the Shared
1239
- // module.
1305
+ // Platform adapters act on it; see Builds in the Shared module.
1240
1306
  break;
1241
1307
 
1242
1308
  case "Connected": {
@@ -1423,6 +1489,13 @@ export const createEvolu =
1423
1489
  exportDatabasePending = null;
1424
1490
  });
1425
1491
 
1492
+ const devicePersistence = Promise.withResolvers<DevicePersistence>();
1493
+ // A disposed instance never hears from its tenant, so awaiters settle.
1494
+ disposer.defer(() => {
1495
+ devicePersistence.resolve("Unknown");
1496
+ });
1497
+ let isPersistentStorageRequested = false;
1498
+
1426
1499
  let postMessage: (input: EvoluInput) => void;
1427
1500
 
1428
1501
  // Scope worker/channel wiring and keep only postMessage outside.
@@ -1512,6 +1585,10 @@ export const createEvolu =
1512
1585
  break;
1513
1586
  }
1514
1587
 
1588
+ case "OnDevicePersistence":
1589
+ devicePersistence.resolve(message.devicePersistence);
1590
+ break;
1591
+
1515
1592
  default:
1516
1593
  exhaustiveCheck(message);
1517
1594
  }
@@ -1639,6 +1716,18 @@ export const createEvolu =
1639
1716
  );
1640
1717
  }
1641
1718
 
1719
+ // Only unsynced local changes exist nowhere else, so the first one is
1720
+ // when asking the platform to keep them starts to matter. Asking on
1721
+ // load would show Firefox's permission prompt to every visitor.
1722
+ if (!isPersistentStorageRequested) {
1723
+ isPersistentStorageRequested = true;
1724
+ void devicePersistence.promise.then((persistence) => {
1725
+ if (persistence === "Unknown" && !disposed) {
1726
+ run.deps.requestPersistentStorage?.();
1727
+ }
1728
+ });
1729
+ }
1730
+
1642
1731
  mutateBatch.push({
1643
1732
  change: { ...dbChange, ownerId: options?.ownerId ?? appOwner.id },
1644
1733
  onComplete: options?.onComplete,
@@ -1722,6 +1811,7 @@ export const createEvolu =
1722
1811
  {
1723
1812
  name,
1724
1813
  appOwner,
1814
+ devicePersistence: devicePersistence.promise,
1725
1815
 
1726
1816
  insert: createMutation("insert"),
1727
1817
  update: createMutation("update"),
@@ -42,6 +42,7 @@ import {
42
42
  type ProtocolTimestampMismatchError,
43
43
  SubscriptionFlags,
44
44
  } from "./Protocol.ts";
45
+ import type { DevicePersistence } from "./Evolu.ts";
45
46
  import {
46
47
  createQueryBuilder,
47
48
  type EvoluSchema,
@@ -170,11 +171,11 @@ const setupSharedWorker = async ({
170
171
  createWebSocket = testCreateWebSocket({ throwOnCreate: true }),
171
172
  time,
172
173
  seed,
173
- isPersistentStorageAvailable,
174
+ getDevicePersistence = () => Promise.resolve("Unknown"),
174
175
  }: {
175
176
  createWebSocket?: CreateWebSocket;
176
177
  time?: TestTime;
177
- isPersistentStorageAvailable?: () => Promise<boolean>;
178
+ getDevicePersistence?: () => Promise<DevicePersistence>;
178
179
  /**
179
180
  * Seeds the worker's random bytes, which are otherwise the same for every
180
181
  * worker.
@@ -214,7 +215,7 @@ const setupSharedWorker = async ({
214
215
  createWebSocket,
215
216
  ...(time && { time }),
216
217
  ...(seed && { randomBytes: testCreateDeps({ seed }).randomBytes }),
217
- ...(isPersistentStorageAvailable && { isPersistentStorageAvailable }),
218
+ getDevicePersistence,
218
219
  }),
219
220
  );
220
221
 
@@ -239,8 +240,10 @@ const setupSharedWorker = async ({
239
240
  releaseDbWorkerLeaderOnDispose = true,
240
241
  autoDispose = true,
241
242
  initialClock = createTimestamp(),
243
+ memoryOnly = false,
242
244
  }: {
243
245
  tenantName?: Name;
246
+ memoryOnly?: boolean;
244
247
  evoluChannel?: TestMessageChannel<EvoluOutput, EvoluInput>;
245
248
  releaseDbWorkerLeaderOnDispose?: boolean;
246
249
  autoDispose?: boolean;
@@ -289,7 +292,7 @@ const setupSharedWorker = async ({
289
292
  consoleLevel: "debug",
290
293
  sqliteSchema: testSqliteSchema,
291
294
  encryptionKey: testAppOwner.encryptionKey,
292
- memoryOnly: false,
295
+ memoryOnly,
293
296
  evoluPort: evoluChannel.port1.native,
294
297
  };
295
298
 
@@ -339,7 +342,25 @@ const setupSharedWorker = async ({
339
342
  ) => {
340
343
  const instance = await createEvoluBeforeDbWorkerLeader(options);
341
344
  await instance.acquireDbWorkerLeader();
342
- return instance;
345
+
346
+ // Once the DbWorker starts, the tenant adds the instance and tells it its
347
+ // device persistence first, so tests see only the outputs that follow.
348
+ const firstOutputs: Array<EvoluOutput> = [];
349
+ instance.evoluChannel.port2.onMessage = (output) => {
350
+ firstOutputs.push(output);
351
+ };
352
+ await testWaitForWorkerMessage();
353
+ instance.evoluChannel.port2.onMessage = null;
354
+ assertLength(firstOutputs, 1);
355
+ const [devicePersistenceOutput] = firstOutputs;
356
+ assert(
357
+ devicePersistenceOutput.type === "OnDevicePersistence",
358
+ "Expected the device persistence first.",
359
+ );
360
+
361
+ return Object.assign(instance, {
362
+ devicePersistence: devicePersistenceOutput.devicePersistence,
363
+ });
343
364
  };
344
365
 
345
366
  return {
@@ -607,17 +628,30 @@ describe("storage", () => {
607
628
  const isMemoryOnly = (output: SharedWorkerOutput | undefined): boolean =>
608
629
  getDbWorkerInit(output).memoryOnly;
609
630
 
631
+ /** Every device persistence the tenant sent to the instance so far. */
632
+ const receiveDevicePersistences = async (
633
+ evoluChannel: TestMessageChannel<EvoluOutput, EvoluInput>,
634
+ ): Promise<ReadonlyArray<DevicePersistence>> => {
635
+ const devicePersistences: Array<DevicePersistence> = [];
636
+ evoluChannel.port2.onMessage = (output) => {
637
+ if (output.type === "OnDevicePersistence") {
638
+ devicePersistences.push(output.devicePersistence);
639
+ }
640
+ };
641
+ await testWaitForWorkerMessage();
642
+ return devicePersistences;
643
+ };
644
+
610
645
  it("keeps every database in memory, replacements included, without persistent storage", async () => {
611
- const isPersistentStorageAvailable = mock.fn(() => Promise.resolve(false));
612
- await using setup = await setupSharedWorker({
613
- isPersistentStorageAvailable,
614
- });
646
+ const getDevicePersistence = mock.fn(() =>
647
+ Promise.resolve<DevicePersistence>("NotPersisted"),
648
+ );
649
+ await using setup = await setupSharedWorker({ getDevicePersistence });
615
650
  using disposer = new DisposableStack();
616
651
 
617
- // The first tab heard it right after Connected.
618
- assertEqual(setup.sharedWorkerOutputs, [{ type: "StorageUnavailable" }]);
619
652
  const first = await setup.createEvolu();
620
- assertTrue(isMemoryOnly(setup.sharedWorkerOutputs.at(1)));
653
+ assertTrue(isMemoryOnly(setup.sharedWorkerOutputs.at(0)));
654
+ assertSame(first.devicePersistence, "NotPersisted");
621
655
 
622
656
  // A replacement DbWorker, as after its tab closed, keeps it in memory too.
623
657
  await first.releaseDbWorkerLeader();
@@ -625,8 +659,7 @@ describe("storage", () => {
625
659
  tab.port.postMessage({ type: "AnnounceTabLeader", consoleLevel: "debug" });
626
660
  await testWaitForWorkerMessage();
627
661
  await testWaitForWorkerMessage();
628
- assertSame(tab.outputs.at(0)?.type, "StorageUnavailable");
629
- const replacement = getDbWorkerInit(tab.outputs.at(1));
662
+ const replacement = getDbWorkerInit(tab.outputs.at(0));
630
663
  assertTrue(replacement.memoryOnly);
631
664
  using leaderPort = testCreateMessagePort<DbWorkerOutput, DbWorkerInput>(
632
665
  replacement.port,
@@ -638,29 +671,62 @@ describe("storage", () => {
638
671
  });
639
672
  await testWaitForWorkerMessage();
640
673
 
641
- assertSame(isPersistentStorageAvailable.mock.callCount(), 1);
674
+ assertSame(getDevicePersistence.mock.callCount(), 1);
642
675
  });
643
676
 
644
- it("keeps databases where the app configured them with persistent storage", async () => {
677
+ for (const devicePersistence of ["Persisted", "Unknown"] as const) {
678
+ it(`stores databases and tells instances ${devicePersistence} where the platform promises it`, async () => {
679
+ await using setup = await setupSharedWorker({
680
+ getDevicePersistence: () => Promise.resolve(devicePersistence),
681
+ });
682
+
683
+ const instance = await setup.createEvolu();
684
+
685
+ assertEqual(
686
+ setup.sharedWorkerOutputs.map(({ type }) => type),
687
+ ["DbWorkerInit"],
688
+ );
689
+ assertFalse(isMemoryOnly(setup.sharedWorkerOutputs.at(0)));
690
+ assertSame(instance.devicePersistence, devicePersistence);
691
+ });
692
+ }
693
+
694
+ it("tells an instance configured memoryOnly that its database is not persisted", async () => {
645
695
  await using setup = await setupSharedWorker({
646
- isPersistentStorageAvailable: () => Promise.resolve(true),
696
+ getDevicePersistence: () => Promise.resolve("Persisted"),
647
697
  });
648
698
 
649
- await setup.createEvolu();
699
+ const instance = await setup.createEvolu({ memoryOnly: true });
650
700
 
651
- assertEqual(
652
- setup.sharedWorkerOutputs.map(({ type }) => type),
653
- ["DbWorkerInit"],
654
- );
655
- assertFalse(isMemoryOnly(setup.sharedWorkerOutputs.at(0)));
701
+ assertTrue(isMemoryOnly(setup.sharedWorkerOutputs.at(0)));
702
+ assertSame(instance.devicePersistence, "NotPersisted");
656
703
  });
657
704
 
658
- it("keeps databases where the app configured them on platforms without the check", async () => {
659
- await using setup = await setupSharedWorker();
705
+ it("tells a later instance of the same name the mode the first one chose", async () => {
706
+ await using setup = await setupSharedWorker({
707
+ getDevicePersistence: () => Promise.resolve("Persisted"),
708
+ });
709
+ const { run, worker } = setup;
710
+ await setup.createEvolu({ memoryOnly: true });
660
711
 
661
- await setup.createEvolu();
712
+ const secondId: EvoluInstanceId = createId(run.deps);
713
+ await using _secondInstanceLock = await run.ok(acquireLeaderLock(secondId));
714
+ using secondChannel = testCreateMessageChannel<EvoluOutput, EvoluInput>();
715
+ worker.port.postMessage({
716
+ type: "CreateEvolu",
717
+ name: testName,
718
+ id: secondId,
719
+ consoleLevel: "debug",
720
+ sqliteSchema: testSqliteSchema,
721
+ encryptionKey: testAppOwner.encryptionKey,
722
+ memoryOnly: false,
723
+ evoluPort: secondChannel.port1.native,
724
+ });
725
+ await testWaitForWorkerMessage();
662
726
 
663
- assertFalse(isMemoryOnly(setup.sharedWorkerOutputs.at(0)));
727
+ assertEqual(await receiveDevicePersistences(secondChannel), [
728
+ "NotPersisted",
729
+ ]);
664
730
  });
665
731
  });
666
732
 
@@ -681,6 +747,7 @@ describe("builds", () => {
681
747
  lockManager,
682
748
  createMessagePort: testCreateMessagePort,
683
749
  createWebSocket: testCreateWebSocket({ throwOnCreate: true }),
750
+ getDevicePersistence: () => Promise.resolve("Unknown" as const),
684
751
  }),
685
752
  );
686
753
  const outputs: Array<SharedWorkerOutput> = [];
@@ -7265,7 +7332,14 @@ describe("with one evolu instance", () => {
7265
7332
  memoryOnly: false,
7266
7333
  evoluPort: siblingChannel.port1.native,
7267
7334
  });
7335
+ const siblingOutputs: Array<EvoluOutput> = [];
7336
+ siblingChannel.port2.onMessage = (output) => {
7337
+ siblingOutputs.push(output);
7338
+ };
7268
7339
  await testWaitForWorkerMessage();
7340
+ assertEqual(siblingOutputs, [
7341
+ { type: "OnDevicePersistence", devicePersistence: "Unknown" },
7342
+ ]);
7269
7343
  siblingChannel.port2.postMessage({
7270
7344
  type: "Mutate",
7271
7345
  changes: [
@@ -9644,6 +9718,10 @@ describe("with one evolu instance", () => {
9644
9718
  [siblingChannel.port1.native],
9645
9719
  );
9646
9720
  await testWaitForWorkerMessage();
9721
+ // The tenant keeps the first instance's persistent database.
9722
+ assertEqual(siblingOutputs.splice(0), [
9723
+ { type: "OnDevicePersistence", devicePersistence: "Unknown" },
9724
+ ]);
9647
9725
 
9648
9726
  const bytes = new Uint8Array([1, 2, 3]);
9649
9727
  const mutation: ExtractTyped<EvoluInput, "Mutate"> = {
@@ -10811,10 +10889,13 @@ describe("startup refusal", () => {
10811
10889
  });
10812
10890
  await testWaitForWorkerMessage();
10813
10891
  // The tab is told through its own connection, not the broadcast channel,
10814
- // and its instance is not answered: pending work stays pending.
10892
+ // and its instance learns only its device persistence: pending work stays
10893
+ // pending.
10815
10894
  assertEqual(firstTabOutputs(), [{ type: "Error", error: refusal }]);
10816
10895
  assertEqual(broadcasts, []);
10817
- assertEqual(evoluOutputs, []);
10896
+ assertEqual(evoluOutputs.splice(0), [
10897
+ { type: "OnDevicePersistence", devicePersistence: "Unknown" },
10898
+ ]);
10818
10899
 
10819
10900
  // Later requests are dropped without a response.
10820
10901
  instance.evoluChannel.port2.postMessage({
@@ -10852,7 +10933,9 @@ describe("startup refusal", () => {
10852
10933
  );
10853
10934
  await testWaitForWorkerMessage();
10854
10935
  assertEqual(firstTabOutputs(), [{ type: "Error", error: refusal }]);
10855
- assertEqual(secondOutputs, []);
10936
+ assertEqual(secondOutputs, [
10937
+ { type: "OnDevicePersistence", devicePersistence: "Unknown" },
10938
+ ]);
10856
10939
 
10857
10940
  // A tab that connects later is told once, through its own connection.
10858
10941
  const laterTab = setupTab(setup, disposer);
@@ -69,13 +69,20 @@
69
69
  *
70
70
  * ## Storage
71
71
  *
72
- * A platform that can lack persistent storage, as a browser does in Safari's
73
- * Private Browsing, provides {@link PersistentStorageDep}. The worker checks it
74
- * once, after it takes the build lock and before any DbWorker starts. Without
75
- * persistent storage, every DbWorker it starts keeps its database in memory,
76
- * replacements included, and each tab that connects is told with
77
- * `StorageUnavailable`. The decision holds for the worker's lifetime, so all
78
- * its tabs see one mode, and the next worker checks again.
72
+ * The platform tells the worker with {@link DevicePersistenceDep} what it can
73
+ * promise about stored databases. The worker asks once, after it takes the
74
+ * build lock and before any DbWorker starts. A browser checks whether it can
75
+ * open the origin private file system, which Safari's Private Browsing and
76
+ * Firefox's private windows refuse. Without persistent storage, every DbWorker
77
+ * the worker starts keeps its database in memory, replacements included. The
78
+ * decision holds for the worker's lifetime, so all its tabs see one mode, and
79
+ * the next worker checks again.
80
+ *
81
+ * A tenant tells each instance it adds its {@link Evolu.devicePersistence}:
82
+ * `NotPersisted` when its database is kept in memory, because of the platform
83
+ * or {@link EvoluConfig.memoryOnly}, otherwise what the platform promised.
84
+ * Instances of one name share the tenant, so a later instance gets the mode the
85
+ * first one chose.
79
86
  *
80
87
  * The check cannot tell a private session from a storage failure, but memory
81
88
  * loses nothing that refusing to start would have kept, and the persistent
@@ -333,7 +340,12 @@ import type {
333
340
  WorkerDeps,
334
341
  } from "../Worker.ts";
335
342
  import type { DbWorkerInit, UnsupportedDbVersionError } from "./Db.ts";
336
- import type { Evolu, SyncStateDep } from "./Evolu.ts";
343
+ import type {
344
+ DevicePersistence,
345
+ Evolu,
346
+ EvoluConfig,
347
+ SyncStateDep,
348
+ } from "./Evolu.ts";
337
349
  import type { Owner, OwnerId, OwnerTransport, SyncOwner } from "./Owner.ts";
338
350
  import {
339
351
  createProtocolBroadcastMessagesFromCrdtMessages,
@@ -420,14 +432,6 @@ export type SharedWorkerOutput =
420
432
  readonly type: "Connected";
421
433
  readonly workerId: SharedWorkerId;
422
434
  readonly syncStateChannelName: string;
423
- }
424
- | {
425
- /**
426
- * Sent to a connecting tab after `Connected` when the platform offers no
427
- * persistent storage, so the worker keeps every database in memory; see
428
- * Storage in this module's documentation.
429
- */
430
- readonly type: "StorageUnavailable";
431
435
  };
432
436
 
433
437
  export type ConsoleEntryOrError =
@@ -741,22 +745,23 @@ export interface RelaySyncState {
741
745
  * stops syncing through its relay, while a skipped change leaves out only
742
746
  * that change.
743
747
  *
744
- * Evolu saves changes on the device before they sync, so sync needs no UI while
745
- * it works: show nothing for `NoRelays`, `Syncing`, and `Synced`. An indicator
746
- * that changes with every edit distracts, and screen readers announce each
747
- * change.
748
+ * Evolu stores changes in the local database before they sync, so sync needs no
749
+ * UI while it works: show nothing for `NoRelays`, `Syncing`, and `Synced`. An
750
+ * indicator that changes with every edit distracts, and screen readers announce
751
+ * each change.
748
752
  *
749
753
  * For `Offline` and `Error`, show one quiet line that lasts as long as the
750
754
  * status, not a dialog, which interrupts, or a toast, which disappears while
751
- * the problem lasts. Say that changes are saved on this device. Evolu reports
752
- * `Offline` at once; an app may wait a few seconds before showing it, because
753
- * brief disconnections, such as waking from sleep, reconnect quickly. For
754
- * `Error`, write actionable text for the error types the app can act on, such
755
- * as a {@link ProtocolQuotaError}: the relay stores no more data for the owner,
756
- * so offer more quota, such as a plan upgrade, then call
757
- * {@link Evolu.requestSync} with the owner's ID. For any other error, show
758
- * generic text that names the error type, which helps when the user reports
759
- * it.
755
+ * the problem lasts. Do not say that changes are saved on this device unless
756
+ * {@link Evolu.devicePersistence} is `Persisted`: a browser may keep them only
757
+ * for a private session or delete them later. Evolu reports `Offline` at once;
758
+ * an app may wait a few seconds before showing it, because brief
759
+ * disconnections, such as waking from sleep, reconnect quickly. For `Error`,
760
+ * write actionable text for the error types the app can act on, such as a
761
+ * {@link ProtocolQuotaError}: the relay stores no more data for the owner, so
762
+ * offer more quota, such as a plan upgrade, then call {@link Evolu.requestSync}
763
+ * with the owner's ID. For any other error, show generic text that names the
764
+ * error type, which helps when the user reports it.
760
765
  *
761
766
  * Render the line inside one element with `role="status"` that stays mounted:
762
767
  * screen readers announce changes only in a live region that already exists,
@@ -785,18 +790,18 @@ export interface RelaySyncState {
785
790
  * case "Synced":
786
791
  * return null;
787
792
  * case "Offline":
788
- * return "Offline. Your changes are saved on this device.";
793
+ * return "Offline. Changes will sync when you're back online.";
789
794
  * case "Error":
790
795
  * return status.error.type === "ProtocolQuotaError"
791
- * ? "Sync is paused because the sync server is full. Your changes are saved on this device."
792
- * : `Sync error: ${status.error.type}. Your changes are saved on this device.`;
796
+ * ? "Sync is paused because the sync server is full."
797
+ * : `Sync error: ${status.error.type}.`;
793
798
  * }
794
799
  * };
795
800
  *
796
801
  * assertEqual(syncStatusToMessage({ type: "Synced" }), null);
797
802
  * assertEqual(
798
803
  * syncStatusToMessage({ type: "Offline" }),
799
- * "Offline. Your changes are saved on this device.",
804
+ * "Offline. Changes will sync when you're back online.",
800
805
  * );
801
806
  * assertEqual(
802
807
  * syncStatusToMessage({
@@ -807,14 +812,14 @@ export interface RelaySyncState {
807
812
  * at: Millis.orThrow(1000),
808
813
  * },
809
814
  * }),
810
- * "Sync is paused because the sync server is full. Your changes are saved on this device.",
815
+ * "Sync is paused because the sync server is full.",
811
816
  * );
812
817
  * assertEqual(
813
818
  * syncStatusToMessage({
814
819
  * type: "Error",
815
820
  * error: { type: "SyncFailed", at: Millis.orThrow(1000) },
816
821
  * }),
817
- * "Sync error: SyncFailed. Your changes are saved on this device.",
822
+ * "Sync error: SyncFailed.",
818
823
  * );
819
824
  * ```
820
825
  */
@@ -1210,6 +1215,11 @@ export type EvoluOutput =
1210
1215
  /** The mutation with these onComplete callbacks could not be stored. */
1211
1216
  readonly type: "OnMutateFailed";
1212
1217
  readonly onCompleteIds: ReadonlyArray<Id>;
1218
+ }
1219
+ | {
1220
+ /** Sent once, when the tenant adds the instance; see Storage. */
1221
+ readonly type: "OnDevicePersistence";
1222
+ readonly devicePersistence: DevicePersistence;
1213
1223
  };
1214
1224
 
1215
1225
  export type DbWorkerInput =
@@ -1334,21 +1344,22 @@ export type DbWorkerQueuedResponse =
1334
1344
  };
1335
1345
 
1336
1346
  /**
1337
- * Tells whether the platform can store databases persistently.
1347
+ * Tells what the platform can promise about the databases it stores.
1338
1348
  *
1339
- * Only a platform that can lack persistent storage provides it, as a browser
1340
- * does in Safari's Private Browsing; see Storage in the Shared module.
1349
+ * `NotPersisted` means the platform offers no persistent storage now, as a
1350
+ * browser does in Safari's Private Browsing or a Firefox private window, so the
1351
+ * worker keeps every database in memory; see Storage in the Shared module.
1341
1352
  */
1342
- export interface PersistentStorageDep {
1343
- readonly isPersistentStorageAvailable: () => Promise<boolean>;
1353
+ export interface DevicePersistenceDep {
1354
+ readonly getDevicePersistence: () => Promise<DevicePersistence>;
1344
1355
  }
1345
1356
 
1346
1357
  export type SharedWorkerDeps = WorkerDeps &
1347
1358
  CreateBroadcastChannelDep &
1348
1359
  CreateMessageChannelDep &
1349
1360
  CreateWebSocketDep &
1350
- LockManagerDep &
1351
- Partial<PersistentStorageDep>;
1361
+ DevicePersistenceDep &
1362
+ LockManagerDep;
1352
1363
 
1353
1364
  /**
1354
1365
  * Coordinates all instances of one named local database within a SharedWorker.
@@ -1748,9 +1759,6 @@ export const initSharedWorker =
1748
1759
  workerId,
1749
1760
  syncStateChannelName,
1750
1761
  });
1751
- if (isPersistentStorageUnavailable) {
1752
- port.postMessage({ type: "StorageUnavailable" });
1753
- }
1754
1762
  });
1755
1763
  };
1756
1764
 
@@ -1766,12 +1774,10 @@ export const initSharedWorker =
1766
1774
  disposer.use(await run.ok(acquireLeaderLock("tab")));
1767
1775
  starting.dispose();
1768
1776
 
1769
- // Checked once, before any DbWorker starts, so every DbWorker of this
1770
- // worker, replacements included, keeps its database in memory; see
1771
- // Storage.
1772
- const isPersistentStorageUnavailable =
1773
- deps.isPersistentStorageAvailable !== undefined &&
1774
- !(await deps.isPersistentStorageAvailable());
1777
+ // Checked once, before any DbWorker starts, so without persistent
1778
+ // storage every DbWorker of this worker, replacements included, keeps its
1779
+ // database in memory; see Storage.
1780
+ const platformDevicePersistence = await deps.getDevicePersistence();
1775
1781
 
1776
1782
  disposer.defer(
1777
1783
  deps.consoleStoreOutputEntry.subscribe(() => {
@@ -2233,14 +2239,17 @@ export const initSharedWorker =
2233
2239
  const tenantsByName = disposer.use(
2234
2240
  await sharedWorkerRun.ok(
2235
2241
  createSharedResourceByKey(
2236
- (message: ExtractTyped<SharedWorkerInput, "CreateEvolu">) =>
2237
- createEvoluTenant(
2238
- isPersistentStorageUnavailable
2239
- ? { ...message, memoryOnly: true }
2240
- : message,
2242
+ (message: ExtractTyped<SharedWorkerInput, "CreateEvolu">) => {
2243
+ const memoryOnly =
2244
+ message.memoryOnly ||
2245
+ platformDevicePersistence === "NotPersisted";
2246
+ return createEvoluTenant(
2247
+ { ...message, memoryOnly },
2248
+ memoryOnly ? "NotPersisted" : platformDevicePersistence,
2241
2249
  currentTenantsByName,
2242
2250
  workerId,
2243
- ),
2251
+ );
2252
+ },
2244
2253
  {
2245
2254
  idleDisposeAfter: "3s",
2246
2255
  lookup: (message) => message.name,
@@ -2269,6 +2278,7 @@ const createEvoluTenant =
2269
2278
  encryptionKey,
2270
2279
  memoryOnly,
2271
2280
  }: ExtractTyped<SharedWorkerInput, "CreateEvolu">,
2281
+ devicePersistence: DevicePersistence,
2272
2282
  currentTenantsByName: Map<Name, BorrowedResource<EvoluTenant>>,
2273
2283
  workerId: SharedWorkerId,
2274
2284
  ): Task<EvoluTenant, never, EvoluTenantDeps> =>
@@ -3404,6 +3414,12 @@ const createEvoluTenant =
3404
3414
  disposer.defer(() => {
3405
3415
  instance.port.onMessage = null;
3406
3416
  });
3417
+ // Instances of one name share the tenant, so each learns the mode
3418
+ // the first one chose.
3419
+ instance.port.postMessage({
3420
+ type: "OnDevicePersistence",
3421
+ devicePersistence,
3422
+ });
3407
3423
 
3408
3424
  // The main-thread Evolu instance holds this per-instance leader lock
3409
3425
  // while it is alive. Acquiring the same lock here means the main