@rocicorp/zero 1.11.0-canary.13 → 1.11.0-canary.14
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/out/analyze-query/src/analyze-cli.js +2 -2
- package/out/analyze-query/src/analyze-cli.js.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.d.ts +8 -1
- package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
- package/out/ast-to-zql/src/ast-to-zql.js +25 -25
- package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
- package/out/otel/src/log-options.d.ts +16 -0
- package/out/otel/src/log-options.d.ts.map +1 -1
- package/out/otel/src/log-options.js +54 -1
- package/out/otel/src/log-options.js.map +1 -1
- package/out/replicache/src/get-kv-store-provider.d.ts +1 -1
- package/out/replicache/src/get-kv-store-provider.d.ts.map +1 -1
- package/out/replicache/src/get-kv-store-provider.js +4 -4
- package/out/replicache/src/get-kv-store-provider.js.map +1 -1
- package/out/replicache/src/kv/idb-store.d.ts +10 -1
- package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
- package/out/replicache/src/kv/idb-store.js +18 -2
- package/out/replicache/src/kv/idb-store.js.map +1 -1
- package/out/replicache/src/kv/mem-fallback-store.d.ts +54 -0
- package/out/replicache/src/kv/mem-fallback-store.d.ts.map +1 -0
- package/out/replicache/src/kv/mem-fallback-store.js +177 -0
- package/out/replicache/src/kv/mem-fallback-store.js.map +1 -0
- package/out/replicache/src/kv/op-sqlite/store.js +3 -3
- package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts +9 -0
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +86 -6
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/new-client-channel.js.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.d.ts +17 -3
- package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
- package/out/replicache/src/persist/collect-idb-databases.js +81 -11
- package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js +1 -1
- package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
- package/out/replicache/src/persist/persist.d.ts.map +1 -1
- package/out/replicache/src/persist/persist.js +6 -3
- package/out/replicache/src/persist/persist.js.map +1 -1
- package/out/replicache/src/replicache-impl.d.ts +43 -3
- package/out/replicache/src/replicache-impl.d.ts.map +1 -1
- package/out/replicache/src/replicache-impl.js +135 -17
- package/out/replicache/src/replicache-impl.js.map +1 -1
- package/out/replicache/src/storage-failure.d.ts +37 -0
- package/out/replicache/src/storage-failure.d.ts.map +1 -0
- package/out/replicache/src/storage-failure.js +31 -0
- package/out/replicache/src/storage-failure.js.map +1 -0
- package/out/shared/src/random-values.js +12 -1
- package/out/shared/src/random-values.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero/src/zero.js +2 -1
- package/out/zero-cache/src/config/zero-config.d.ts +26 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +36 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/observability/log-throttle.d.ts +44 -0
- package/out/zero-cache/src/observability/log-throttle.d.ts.map +1 -0
- package/out/zero-cache/src/observability/log-throttle.js +76 -0
- package/out/zero-cache/src/observability/log-throttle.js.map +1 -0
- package/out/zero-cache/src/scripts/permissions.d.ts +16 -0
- package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
- package/out/zero-cache/src/server/runner/zero-dispatcher.js +2 -0
- package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +20 -2
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js +27 -15
- package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +1 -0
- package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/common/replica-schema.js +15 -3
- package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
- package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
- package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +9 -0
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +204 -18
- package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +11 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js +11 -2
- package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +68 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +141 -0
- package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -0
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +43 -0
- package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js +10 -1
- package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +57 -0
- package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +20 -6
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js +17 -7
- package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
- package/out/zero-cache/src/services/change-source/protocol/current.js +2 -1
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +66 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js +154 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +50 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +1 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.js +268 -0
- package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -0
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +7 -6
- package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +20 -4
- package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +26 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
- package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +88 -0
- package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js +9 -1
- package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +29 -0
- package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
- package/out/zero-cache/src/services/change-streamer/subscriber.js +34 -0
- package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
- package/out/zero-cache/src/services/plannerz.d.ts +159 -0
- package/out/zero-cache/src/services/plannerz.d.ts.map +1 -0
- package/out/zero-cache/src/services/plannerz.js +263 -0
- package/out/zero-cache/src/services/plannerz.js.map +1 -0
- package/out/zero-cache/src/services/replicator/change-log-cookies.js +1 -1
- package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/change-processor.js +44 -12
- package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/incremental-sync.js +4 -3
- package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +23 -2
- package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/schema/backfilling.js +54 -2
- package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +11 -3
- package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
- package/out/zero-cache/src/services/replicator/write-worker.js +5 -1
- package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +81 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +128 -0
- package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +36 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +362 -20
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +7 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js +35 -0
- package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +25 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.js +28 -0
- package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +77 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.js +206 -0
- package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +11 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +7 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +49 -3
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/workers/connection.js +3 -3
- package/out/zero-cache/src/workers/connection.js.map +1 -1
- package/out/zero-cache/src/workers/syncer.js +2 -2
- package/out/zero-cache/src/workers/syncer.js.map +1 -1
- package/out/zero-client/src/client/options.d.ts +29 -0
- package/out/zero-client/src/client/options.d.ts.map +1 -1
- package/out/zero-client/src/client/options.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-client/src/client/zero.d.ts.map +1 -1
- package/out/zero-client/src/client/zero.js +8 -23
- package/out/zero-client/src/client/zero.js.map +1 -1
- package/out/zero-client/src/mod.d.ts +1 -0
- package/out/zero-client/src/mod.d.ts.map +1 -1
- package/out/zero-client/src/mod.js +1 -0
- package/out/zero-protocol/src/down.d.ts +2 -2
- package/out/zero-react/src/zero.js +1 -0
- package/out/zero-solid/src/zero.js +1 -0
- package/out/zql/src/builder/builder.d.ts +6 -0
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +4 -3
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts +13 -0
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +1 -1
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/planner/planner-builder.d.ts +12 -1
- package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
- package/out/zql/src/planner/planner-builder.js +8 -1
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.d.ts +43 -0
- package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
- package/out/zql/src/planner/planner-connection.js +31 -4
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-graph.d.ts +10 -0
- package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
- package/out/zql/src/planner/planner-graph.js +14 -0
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-warnings.d.ts +83 -0
- package/out/zql/src/planner/planner-warnings.d.ts.map +1 -0
- package/out/zql/src/planner/planner-warnings.js +78 -0
- package/out/zql/src/planner/planner-warnings.js.map +1 -0
- package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
- package/out/zql/src/query/measure-push-operator.js +30 -8
- package/out/zql/src/query/measure-push-operator.js.map +1 -1
- package/out/zqlite/src/pending-delta.d.ts +110 -0
- package/out/zqlite/src/pending-delta.d.ts.map +1 -0
- package/out/zqlite/src/pending-delta.js +278 -0
- package/out/zqlite/src/pending-delta.js.map +1 -0
- package/out/zqlite/src/query-delegate.d.ts.map +1 -1
- package/out/zqlite/src/query-delegate.js +5 -1
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +52 -1
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js +2 -26
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/sqlite-stat4-sample.d.ts +37 -0
- package/out/zqlite/src/sqlite-stat4-sample.d.ts.map +1 -0
- package/out/zqlite/src/sqlite-stat4-sample.js +61 -0
- package/out/zqlite/src/sqlite-stat4-sample.js.map +1 -0
- package/out/zqlite/src/table-source.d.ts +68 -0
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +206 -7
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +1 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +0 -35
- package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +0 -1
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js +0 -83
- package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts +0 -156
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts.map +0 -1
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.js +0 -15
- package/out/zero-cache/src/services/change-source/pg/backfill-resume.js.map +0 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type StorageFailureError } from '../storage-failure.ts';
|
|
2
|
+
import type { Read, Store, StoreProvider, Write } from './store.ts';
|
|
3
|
+
/** Whether an instance moved onto a memory store of this name. */
|
|
4
|
+
export declare function isFallbackStore(name: string): boolean;
|
|
5
|
+
/** Creates and drops the memory stores instances fell back to. */
|
|
6
|
+
export declare const fallbackStoreProvider: StoreProvider;
|
|
7
|
+
/**
|
|
8
|
+
* Wraps a {@link StoreProvider} so that the stores it creates move onto memory
|
|
9
|
+
* together when their storage fails while the instance is opening (see
|
|
10
|
+
* `onStorageFailure`): a store whose `create` throws a
|
|
11
|
+
* {@link StorageFailureError} starts on a {@link MemStore}, and after
|
|
12
|
+
* {@link fallBack} the other stores switch on their next `read()` or
|
|
13
|
+
* `write()`. The memory stores keep the same names, so drops reach them too.
|
|
14
|
+
*
|
|
15
|
+
* Only before {@link opened}. After that the in-memory dag holds data loaded
|
|
16
|
+
* from the real store and loads more on demand, which an empty memory store
|
|
17
|
+
* cannot serve, so a failure is left to the caller. It is still reported, the
|
|
18
|
+
* first time a transaction fails to begin or to commit on a storage failure,
|
|
19
|
+
* whichever caller ran it (persist, heartbeat, garbage collection, ...).
|
|
20
|
+
*/
|
|
21
|
+
export declare class MemFallbackStoreProvider implements StoreProvider {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(inner: StoreProvider, onFallBack: (failure: StorageFailureError) => void, onFailureAfterOpen: (failure: StorageFailureError) => void);
|
|
24
|
+
/** Set once the stores have moved onto memory. */
|
|
25
|
+
get failure(): StorageFailureError | undefined;
|
|
26
|
+
/** The open has finished; storage failures no longer move the stores. */
|
|
27
|
+
opened(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Moves the stores onto memory if `error` reports a storage failure, the
|
|
30
|
+
* open has not finished, and they are not on memory already. Returns
|
|
31
|
+
* whether it moved them.
|
|
32
|
+
*/
|
|
33
|
+
fallBack(error: unknown): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Reports `error` if it is a storage failure after the open. Before it,
|
|
36
|
+
* the open's own error handling decides whether to fall back.
|
|
37
|
+
*/
|
|
38
|
+
report(error: unknown): void;
|
|
39
|
+
create: (name: string) => MemFallbackStore;
|
|
40
|
+
drop: (name: string) => Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export declare class MemFallbackStore implements Store {
|
|
43
|
+
#private;
|
|
44
|
+
constructor(name: string, store: Store, provider: MemFallbackStoreProvider);
|
|
45
|
+
read(): Promise<Read>;
|
|
46
|
+
write(): Promise<Write>;
|
|
47
|
+
/** The store currently backing this one: the real store, or memory. */
|
|
48
|
+
get backing(): Store;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
get closed(): boolean;
|
|
51
|
+
/** `'mem'` from the moment the stores are on memory. */
|
|
52
|
+
get kind(): string | undefined;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=mem-fallback-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mem-fallback-store.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/kv/mem-fallback-store.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAC,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAC,MAAM,YAAY,CAAC;AASlE,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,EAAE,aAMnC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,qBAAa,wBAAyB,YAAW,aAAa;;IAO5D,YACE,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,EAClD,kBAAkB,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,EAK3D;IAED,kDAAkD;IAClD,IAAI,OAAO,IAAI,mBAAmB,GAAG,SAAS,CAE7C;IAED,yEAAyE;IACzE,MAAM,IAAI,IAAI,CAEb;IAED;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWhC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAO3B;IAED,MAAM,SAAU,MAAM,KAAG,gBAAgB,CAYvC;IAEF,IAAI,SAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAGG;CACxC;AAED,qBAAa,gBAAiB,YAAW,KAAK;;IAO5C,YAAY,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,wBAAwB,EAKzE;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAK1B;IAEK,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAK5B;IAOD,uEAAuE;IACvE,IAAI,OAAO,IAAI,KAAK,CAanB;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,wDAAwD;IACxD,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;CACF"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { getStorageFailure } from "../storage-failure.js";
|
|
2
|
+
import { MemStore, dropMemStore } from "./mem-store.js";
|
|
3
|
+
//#region ../replicache/src/kv/mem-fallback-store.ts
|
|
4
|
+
/**
|
|
5
|
+
* The names of the memory stores instances moved onto after a storage
|
|
6
|
+
* failure, in this process. Unlike the stores of a `kvStore: 'mem'` instance,
|
|
7
|
+
* these are cleared by drops whatever store they are asked to drop from.
|
|
8
|
+
*/
|
|
9
|
+
var fellBack = /* @__PURE__ */ new Set();
|
|
10
|
+
/** Whether an instance moved onto a memory store of this name. */
|
|
11
|
+
function isFallbackStore(name) {
|
|
12
|
+
return fellBack.has(name);
|
|
13
|
+
}
|
|
14
|
+
/** Creates and drops the memory stores instances fell back to. */
|
|
15
|
+
var fallbackStoreProvider = {
|
|
16
|
+
create: (name) => new MemStore(name),
|
|
17
|
+
drop: (name) => {
|
|
18
|
+
fellBack.delete(name);
|
|
19
|
+
return dropMemStore(name);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Wraps a {@link StoreProvider} so that the stores it creates move onto memory
|
|
24
|
+
* together when their storage fails while the instance is opening (see
|
|
25
|
+
* `onStorageFailure`): a store whose `create` throws a
|
|
26
|
+
* {@link StorageFailureError} starts on a {@link MemStore}, and after
|
|
27
|
+
* {@link fallBack} the other stores switch on their next `read()` or
|
|
28
|
+
* `write()`. The memory stores keep the same names, so drops reach them too.
|
|
29
|
+
*
|
|
30
|
+
* Only before {@link opened}. After that the in-memory dag holds data loaded
|
|
31
|
+
* from the real store and loads more on demand, which an empty memory store
|
|
32
|
+
* cannot serve, so a failure is left to the caller. It is still reported, the
|
|
33
|
+
* first time a transaction fails to begin or to commit on a storage failure,
|
|
34
|
+
* whichever caller ran it (persist, heartbeat, garbage collection, ...).
|
|
35
|
+
*/
|
|
36
|
+
var MemFallbackStoreProvider = class {
|
|
37
|
+
#inner;
|
|
38
|
+
#onFallBack;
|
|
39
|
+
#onFailureAfterOpen;
|
|
40
|
+
#failure;
|
|
41
|
+
#opened = false;
|
|
42
|
+
constructor(inner, onFallBack, onFailureAfterOpen) {
|
|
43
|
+
this.#inner = inner;
|
|
44
|
+
this.#onFallBack = onFallBack;
|
|
45
|
+
this.#onFailureAfterOpen = onFailureAfterOpen;
|
|
46
|
+
}
|
|
47
|
+
/** Set once the stores have moved onto memory. */
|
|
48
|
+
get failure() {
|
|
49
|
+
return this.#failure;
|
|
50
|
+
}
|
|
51
|
+
/** The open has finished; storage failures no longer move the stores. */
|
|
52
|
+
opened() {
|
|
53
|
+
this.#opened = true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Moves the stores onto memory if `error` reports a storage failure, the
|
|
57
|
+
* open has not finished, and they are not on memory already. Returns
|
|
58
|
+
* whether it moved them.
|
|
59
|
+
*/
|
|
60
|
+
fallBack(error) {
|
|
61
|
+
if (this.#failure !== void 0 || this.#opened) return false;
|
|
62
|
+
const failure = getStorageFailure(error);
|
|
63
|
+
if (failure === void 0) return false;
|
|
64
|
+
this.#failure = failure;
|
|
65
|
+
this.#onFallBack(failure);
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reports `error` if it is a storage failure after the open. Before it,
|
|
70
|
+
* the open's own error handling decides whether to fall back.
|
|
71
|
+
*/
|
|
72
|
+
report(error) {
|
|
73
|
+
if (this.#opened && this.#failure === void 0) {
|
|
74
|
+
const failure = getStorageFailure(error);
|
|
75
|
+
if (failure !== void 0) this.#onFailureAfterOpen(failure);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
create = (name) => {
|
|
79
|
+
if (this.#failure === void 0) try {
|
|
80
|
+
return new MemFallbackStore(name, this.#inner.create(name), this);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
if (!this.fallBack(e)) throw e;
|
|
83
|
+
}
|
|
84
|
+
fellBack.add(name);
|
|
85
|
+
return new MemFallbackStore(name, new MemStore(name), this);
|
|
86
|
+
};
|
|
87
|
+
drop = (name) => this.#failure === void 0 ? this.#inner.drop(name) : fallbackStoreProvider.drop(name);
|
|
88
|
+
};
|
|
89
|
+
var MemFallbackStore = class {
|
|
90
|
+
#name;
|
|
91
|
+
#provider;
|
|
92
|
+
#store;
|
|
93
|
+
#onMemory;
|
|
94
|
+
#closed = false;
|
|
95
|
+
constructor(name, store, provider) {
|
|
96
|
+
this.#name = name;
|
|
97
|
+
this.#store = store;
|
|
98
|
+
this.#onMemory = store instanceof MemStore;
|
|
99
|
+
this.#provider = provider;
|
|
100
|
+
}
|
|
101
|
+
async read() {
|
|
102
|
+
return new ReportingRead(await this.backing.read().catch(this.#reportAndRethrow), this.#reportAndRethrow);
|
|
103
|
+
}
|
|
104
|
+
async write() {
|
|
105
|
+
return new ReportingWrite(await this.backing.write().catch(this.#reportAndRethrow), this.#reportAndRethrow);
|
|
106
|
+
}
|
|
107
|
+
#reportAndRethrow = (e) => {
|
|
108
|
+
this.#provider.report(e);
|
|
109
|
+
throw e;
|
|
110
|
+
};
|
|
111
|
+
/** The store currently backing this one: the real store, or memory. */
|
|
112
|
+
get backing() {
|
|
113
|
+
if (!this.#onMemory && !this.#closed && this.#provider.failure !== void 0) {
|
|
114
|
+
this.#store.close().catch(() => void 0);
|
|
115
|
+
this.#store = new MemStore(this.#name);
|
|
116
|
+
this.#onMemory = true;
|
|
117
|
+
fellBack.add(this.#name);
|
|
118
|
+
}
|
|
119
|
+
return this.#store;
|
|
120
|
+
}
|
|
121
|
+
close() {
|
|
122
|
+
this.#closed = true;
|
|
123
|
+
return this.#store.close();
|
|
124
|
+
}
|
|
125
|
+
get closed() {
|
|
126
|
+
return this.#closed || this.#store.closed;
|
|
127
|
+
}
|
|
128
|
+
/** `'mem'` from the moment the stores are on memory. */
|
|
129
|
+
get kind() {
|
|
130
|
+
return this.backing.kind;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Reports a transaction that fails to end on a storage failure: its commit,
|
|
135
|
+
* or its release (the SQLite store commits a read transaction and rolls back
|
|
136
|
+
* an uncommitted write there). Not the per-key reads and writes: they are on
|
|
137
|
+
* the hot path, and the SQLite store runs a write's statements in the commit.
|
|
138
|
+
*/
|
|
139
|
+
var ReportingRead = class {
|
|
140
|
+
_tx;
|
|
141
|
+
_reportAndRethrow;
|
|
142
|
+
constructor(tx, reportAndRethrow) {
|
|
143
|
+
this._tx = tx;
|
|
144
|
+
this._reportAndRethrow = reportAndRethrow;
|
|
145
|
+
}
|
|
146
|
+
has(key) {
|
|
147
|
+
return this._tx.has(key);
|
|
148
|
+
}
|
|
149
|
+
get(key) {
|
|
150
|
+
return this._tx.get(key);
|
|
151
|
+
}
|
|
152
|
+
release() {
|
|
153
|
+
try {
|
|
154
|
+
this._tx.release();
|
|
155
|
+
} catch (e) {
|
|
156
|
+
this._reportAndRethrow(e);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
get closed() {
|
|
160
|
+
return this._tx.closed;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
var ReportingWrite = class extends ReportingRead {
|
|
164
|
+
put(key, value) {
|
|
165
|
+
return this._tx.put(key, value);
|
|
166
|
+
}
|
|
167
|
+
del(key) {
|
|
168
|
+
return this._tx.del(key);
|
|
169
|
+
}
|
|
170
|
+
commit() {
|
|
171
|
+
return this._tx.commit().catch(this._reportAndRethrow);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
//#endregion
|
|
175
|
+
export { MemFallbackStoreProvider, fallbackStoreProvider, isFallbackStore };
|
|
176
|
+
|
|
177
|
+
//# sourceMappingURL=mem-fallback-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mem-fallback-store.js","names":["#inner","#onFallBack","#onFailureAfterOpen","#failure","#opened","#name","#provider","#store","#onMemory","#reportAndRethrow","#closed"],"sources":["../../../../../replicache/src/kv/mem-fallback-store.ts"],"sourcesContent":["import type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {\n getStorageFailure,\n type StorageFailureError,\n} from '../storage-failure.ts';\nimport {dropMemStore, MemStore} from './mem-store.ts';\nimport type {Read, Store, StoreProvider, Write} from './store.ts';\n\n/**\n * The names of the memory stores instances moved onto after a storage\n * failure, in this process. Unlike the stores of a `kvStore: 'mem'` instance,\n * these are cleared by drops whatever store they are asked to drop from.\n */\nconst fellBack = new Set<string>();\n\n/** Whether an instance moved onto a memory store of this name. */\nexport function isFallbackStore(name: string): boolean {\n return fellBack.has(name);\n}\n\n/** Creates and drops the memory stores instances fell back to. */\nexport const fallbackStoreProvider: StoreProvider = {\n create: name => new MemStore(name),\n drop: name => {\n fellBack.delete(name);\n return dropMemStore(name);\n },\n};\n\n/**\n * Wraps a {@link StoreProvider} so that the stores it creates move onto memory\n * together when their storage fails while the instance is opening (see\n * `onStorageFailure`): a store whose `create` throws a\n * {@link StorageFailureError} starts on a {@link MemStore}, and after\n * {@link fallBack} the other stores switch on their next `read()` or\n * `write()`. The memory stores keep the same names, so drops reach them too.\n *\n * Only before {@link opened}. After that the in-memory dag holds data loaded\n * from the real store and loads more on demand, which an empty memory store\n * cannot serve, so a failure is left to the caller. It is still reported, the\n * first time a transaction fails to begin or to commit on a storage failure,\n * whichever caller ran it (persist, heartbeat, garbage collection, ...).\n */\nexport class MemFallbackStoreProvider implements StoreProvider {\n readonly #inner: StoreProvider;\n readonly #onFallBack: (failure: StorageFailureError) => void;\n readonly #onFailureAfterOpen: (failure: StorageFailureError) => void;\n #failure: StorageFailureError | undefined;\n #opened = false;\n\n constructor(\n inner: StoreProvider,\n onFallBack: (failure: StorageFailureError) => void,\n onFailureAfterOpen: (failure: StorageFailureError) => void,\n ) {\n this.#inner = inner;\n this.#onFallBack = onFallBack;\n this.#onFailureAfterOpen = onFailureAfterOpen;\n }\n\n /** Set once the stores have moved onto memory. */\n get failure(): StorageFailureError | undefined {\n return this.#failure;\n }\n\n /** The open has finished; storage failures no longer move the stores. */\n opened(): void {\n this.#opened = true;\n }\n\n /**\n * Moves the stores onto memory if `error` reports a storage failure, the\n * open has not finished, and they are not on memory already. Returns\n * whether it moved them.\n */\n fallBack(error: unknown): boolean {\n if (this.#failure !== undefined || this.#opened) {\n return false;\n }\n const failure = getStorageFailure(error);\n if (failure === undefined) {\n return false;\n }\n this.#failure = failure;\n this.#onFallBack(failure);\n return true;\n }\n\n /**\n * Reports `error` if it is a storage failure after the open. Before it,\n * the open's own error handling decides whether to fall back.\n */\n report(error: unknown): void {\n if (this.#opened && this.#failure === undefined) {\n const failure = getStorageFailure(error);\n if (failure !== undefined) {\n this.#onFailureAfterOpen(failure);\n }\n }\n }\n\n create = (name: string): MemFallbackStore => {\n if (this.#failure === undefined) {\n try {\n return new MemFallbackStore(name, this.#inner.create(name), this);\n } catch (e) {\n if (!this.fallBack(e)) {\n throw e;\n }\n }\n }\n fellBack.add(name);\n return new MemFallbackStore(name, new MemStore(name), this);\n };\n\n drop = (name: string): Promise<void> =>\n this.#failure === undefined\n ? this.#inner.drop(name)\n : fallbackStoreProvider.drop(name);\n}\n\nexport class MemFallbackStore implements Store {\n readonly #name: string;\n readonly #provider: MemFallbackStoreProvider;\n #store: Store;\n #onMemory: boolean;\n #closed = false;\n\n constructor(name: string, store: Store, provider: MemFallbackStoreProvider) {\n this.#name = name;\n this.#store = store;\n this.#onMemory = store instanceof MemStore;\n this.#provider = provider;\n }\n\n async read(): Promise<Read> {\n return new ReportingRead(\n await this.backing.read().catch(this.#reportAndRethrow),\n this.#reportAndRethrow,\n );\n }\n\n async write(): Promise<Write> {\n return new ReportingWrite(\n await this.backing.write().catch(this.#reportAndRethrow),\n this.#reportAndRethrow,\n );\n }\n\n readonly #reportAndRethrow = (e: unknown): never => {\n this.#provider.report(e);\n throw e;\n };\n\n /** The store currently backing this one: the real store, or memory. */\n get backing(): Store {\n if (\n !this.#onMemory &&\n !this.#closed &&\n this.#provider.failure !== undefined\n ) {\n // Not awaited: a store whose storage failed may not complete its close.\n this.#store.close().catch(() => undefined);\n this.#store = new MemStore(this.#name);\n this.#onMemory = true;\n fellBack.add(this.#name);\n }\n return this.#store;\n }\n\n close(): Promise<void> {\n this.#closed = true;\n return this.#store.close();\n }\n\n get closed(): boolean {\n return this.#closed || this.#store.closed;\n }\n\n /** `'mem'` from the moment the stores are on memory. */\n get kind(): string | undefined {\n return this.backing.kind;\n }\n}\n\n/**\n * Reports a transaction that fails to end on a storage failure: its commit,\n * or its release (the SQLite store commits a read transaction and rolls back\n * an uncommitted write there). Not the per-key reads and writes: they are on\n * the hot path, and the SQLite store runs a write's statements in the commit.\n */\nclass ReportingRead<R extends Read> implements Read {\n protected readonly _tx: R;\n protected readonly _reportAndRethrow: (e: unknown) => never;\n\n constructor(tx: R, reportAndRethrow: (e: unknown) => never) {\n this._tx = tx;\n this._reportAndRethrow = reportAndRethrow;\n }\n\n has(key: string): Promise<boolean> {\n return this._tx.has(key);\n }\n\n get(key: string): Promise<ReadonlyJSONValue | undefined> {\n return this._tx.get(key);\n }\n\n release(): void {\n try {\n this._tx.release();\n } catch (e) {\n this._reportAndRethrow(e);\n }\n }\n\n get closed(): boolean {\n return this._tx.closed;\n }\n}\n\nclass ReportingWrite extends ReportingRead<Write> implements Write {\n put(key: string, value: ReadonlyJSONValue): Promise<void> {\n return this._tx.put(key, value);\n }\n\n del(key: string): Promise<void> {\n return this._tx.del(key);\n }\n\n commit(): Promise<void> {\n return this._tx.commit().catch(this._reportAndRethrow);\n }\n}\n"],"mappings":"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"}
|
|
@@ -32,12 +32,12 @@ var OpSQLitePreparedStatement = class {
|
|
|
32
32
|
};
|
|
33
33
|
var OpSQLiteDatabase = class {
|
|
34
34
|
#db;
|
|
35
|
-
#
|
|
35
|
+
#openOpts;
|
|
36
36
|
constructor(filename, opts) {
|
|
37
|
-
this.#filename = filename;
|
|
38
37
|
const openOpts = { name: filename };
|
|
39
38
|
if (opts?.location) openOpts.location = opts.location;
|
|
40
39
|
if (opts?.encryptionKey) openOpts.encryptionKey = opts.encryptionKey;
|
|
40
|
+
this.#openOpts = openOpts;
|
|
41
41
|
this.#db = open(openOpts);
|
|
42
42
|
}
|
|
43
43
|
close() {
|
|
@@ -45,7 +45,7 @@ var OpSQLiteDatabase = class {
|
|
|
45
45
|
}
|
|
46
46
|
destroy() {
|
|
47
47
|
try {
|
|
48
|
-
const tempDb = open(
|
|
48
|
+
const tempDb = open(this.#openOpts);
|
|
49
49
|
tempDb.delete();
|
|
50
50
|
tempDb.close();
|
|
51
51
|
} catch (_error) {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","names":["#db","#sql","#
|
|
1
|
+
{"version":3,"file":"store.js","names":["#db","#sql","#openOpts"],"sources":["../../../../../../replicache/src/kv/op-sqlite/store.ts"],"sourcesContent":["import type {\n PreparedStatement,\n SQLiteDatabase,\n SQLiteStoreOptions,\n} from '../sqlite-store.ts';\nimport {dropStore, SQLiteStore} from '../sqlite-store.ts';\nimport type {StoreProvider} from '../store.ts';\nimport {open, rawResultRows, type DB} from './types.ts';\n\nexport type OpSQLiteStoreOptions = SQLiteStoreOptions & {\n // OpSQLite-specific options\n location?: 'default' | 'Library' | 'Documents' | 'Temporary';\n encryptionKey?: string;\n};\n\nfunction dropOpSQLiteStore(\n name: string,\n opts?: OpSQLiteStoreOptions,\n): Promise<void> {\n return dropStore(\n name,\n (filename, options) => new OpSQLiteDatabase(filename, options),\n opts,\n );\n}\n\n/**\n * Creates a StoreProvider for SQLite-based stores using @op-engineering/op-sqlite.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n * Uses parameterized queries for safety and performance.\n */\nexport function opSQLiteStoreProvider(\n opts?: OpSQLiteStoreOptions,\n): StoreProvider {\n return {\n create: name =>\n new SQLiteStore(\n name,\n (name, options) => new OpSQLiteDatabase(name, options),\n opts,\n 'op-sqlite',\n ),\n drop: name => dropOpSQLiteStore(name, opts),\n };\n}\n\nclass OpSQLitePreparedStatement implements PreparedStatement {\n readonly #db: DB;\n readonly #sql: string;\n\n constructor(db: DB, sql: string) {\n this.#db = db;\n this.#sql = sql;\n }\n\n async exec(params: string[]): Promise<void> {\n await this.#db.executeRaw(this.#sql, params);\n }\n\n async all(params: string[]): Promise<unknown[][]> {\n return rawResultRows(await this.#db.executeRaw(this.#sql, params));\n }\n}\n\n/** What `open()` accepts; kept so `destroy()` reopens with the same options. */\ntype OpenOptions = Parameters<typeof open>[0];\n\nclass OpSQLiteDatabase implements SQLiteDatabase {\n readonly #db: DB;\n readonly #openOpts: OpenOptions;\n\n constructor(filename: string, opts?: OpSQLiteStoreOptions) {\n const openOpts: OpenOptions = {name: filename};\n\n if (opts?.location) {\n openOpts.location = opts.location;\n }\n if (opts?.encryptionKey) {\n openOpts.encryptionKey = opts.encryptionKey;\n }\n\n this.#openOpts = openOpts;\n this.#db = open(openOpts);\n }\n\n close(): void {\n this.#db.close();\n }\n\n destroy(): void {\n // OpSQLite uses delete method on the database instance\n // We need to create a temporary connection to delete the database. It\n // must be opened with the same options as the store, or a non-default\n // `location` deletes a (freshly created, empty) file at the default path\n // and leaves the real database in place.\n try {\n const tempDb = open(this.#openOpts);\n tempDb.delete();\n tempDb.close();\n } catch (_error) {\n // Database might not exist, which is fine\n }\n }\n\n prepare(sql: string): PreparedStatement {\n return new OpSQLitePreparedStatement(this.#db, sql);\n }\n\n execSync(sql: string): void {\n this.#db.executeRawSync(sql, []);\n }\n}\n"],"mappings":";;;AAeA,SAAS,kBACP,MACA,MACe;CACf,OAAO,UACL,OACC,UAAU,YAAY,IAAI,iBAAiB,UAAU,OAAO,GAC7D,IACF;AACF;;;;;;;AAQA,SAAgB,sBACd,MACe;CACf,OAAO;EACL,SAAQ,SACN,IAAI,YACF,OACC,MAAM,YAAY,IAAI,iBAAiB,MAAM,OAAO,GACrD,MACA,WACF;EACF,OAAM,SAAQ,kBAAkB,MAAM,IAAI;CAC5C;AACF;AAEA,IAAM,4BAAN,MAA6D;CAC3D;CACA;CAEA,YAAY,IAAQ,KAAa;EAC/B,KAAKA,MAAM;EACX,KAAKC,OAAO;CACd;CAEA,MAAM,KAAK,QAAiC;EAC1C,MAAM,KAAKD,IAAI,WAAW,KAAKC,MAAM,MAAM;CAC7C;CAEA,MAAM,IAAI,QAAwC;EAChD,OAAO,cAAc,MAAM,KAAKD,IAAI,WAAW,KAAKC,MAAM,MAAM,CAAC;CACnE;AACF;AAKA,IAAM,mBAAN,MAAiD;CAC/C;CACA;CAEA,YAAY,UAAkB,MAA6B;EACzD,MAAM,WAAwB,EAAC,MAAM,SAAQ;EAE7C,IAAI,MAAM,UACR,SAAS,WAAW,KAAK;EAE3B,IAAI,MAAM,eACR,SAAS,gBAAgB,KAAK;EAGhC,KAAKC,YAAY;EACjB,KAAKF,MAAM,KAAK,QAAQ;CAC1B;CAEA,QAAc;EACZ,KAAKA,IAAI,MAAM;CACjB;CAEA,UAAgB;EAMd,IAAI;GACF,MAAM,SAAS,KAAK,KAAKE,SAAS;GAClC,OAAO,OAAO;GACd,OAAO,MAAM;EACf,SAAS,QAAQ,CAEjB;CACF;CAEA,QAAQ,KAAgC;EACtC,OAAO,IAAI,0BAA0B,KAAKF,KAAK,GAAG;CACpD;CAEA,SAAS,KAAmB;EAC1B,KAAKA,IAAI,eAAe,KAAK,CAAC,CAAC;CACjC;AACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReadonlyJSONValue } from '../../../shared/src/json.ts';
|
|
2
|
+
import { type StorageFailureKind } from '../storage-failure.ts';
|
|
2
3
|
import type { Read, Store, Write } from './store.ts';
|
|
3
4
|
import { WriteImplBase } from './write-impl-base.ts';
|
|
4
5
|
/**
|
|
@@ -88,6 +89,14 @@ export declare class SQLiteWrite extends WriteImplBase implements Write {
|
|
|
88
89
|
release(): void;
|
|
89
90
|
get closed(): boolean;
|
|
90
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* The storage failure a SQLite error reports, if any. Matched on SQLite's own
|
|
94
|
+
* `sqlite3_errmsg` text (or its symbolic code), which expo-sqlite, op-sqlite
|
|
95
|
+
* and zero-sqlite all surface inside whatever wrapper they add.
|
|
96
|
+
* `SQLITE_BUSY` is deliberately not one: it is contention, and `busy_timeout`
|
|
97
|
+
* plus a retry is the right answer to it.
|
|
98
|
+
*/
|
|
99
|
+
export declare function classifySQLiteError(error: unknown): StorageFailureKind | undefined;
|
|
91
100
|
export declare function clearAllNamedStoresForTesting(): void;
|
|
92
101
|
export declare function dropStore(name: string, createDelegate: (filename: string, opts?: SQLiteStoreOptions) => SQLiteDatabase, opts?: SQLiteStoreOptions): Promise<void>;
|
|
93
102
|
//# sourceMappingURL=sqlite-store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAGnE,OAAO,KAAK,EAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAC,MAAM,YAAY,CAAC;AAMnD,OAAO,EAAiB,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAEnE;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;IAEhB;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC;IAGxC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,kBAAkB,KACtB,cAAc,CAAC;AAEpB;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,KAAK;;IAOvC,YACE,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,oBAAoB,EAC5B,IAAI,CAAC,EAAE,kBAAkB,EACzB,IAAI,SAAW,EAKhB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAC,MAAM,YAAY,CAAC;AAMnD,OAAO,EAAiB,aAAa,EAAC,MAAM,sBAAsB,CAAC;AAEnE;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;IAEhB;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC;IAGxC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,kBAAkB,KACtB,cAAc,CAAC;AAEpB;;;;;GAKG;AACH,qBAAa,WAAY,YAAW,KAAK;;IAOvC,YACE,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,oBAAoB,EAC5B,IAAI,CAAC,EAAE,kBAAkB,EACzB,IAAI,SAAW,EAKhB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAqC1B;IAEK,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAmB5B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAe3B;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAQD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,iBAAiB,CAAC;IACvB,GAAG,EAAE,iBAAiB,CAAC;IACvB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,GAAG,EAAE,iBAAiB,CAAC;IACvB,GAAG,EAAE,iBAAiB,CAAC;IACvB,yEAAyE;IACzE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,iBAAiB,CAAC;IACvC,uEAAuE;IACvE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,iBAAiB,CAAC;CACxC,CAAC;AAiDF,MAAM,WAAW,kBAAkB;IAEjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC/B,WAAW,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAwBD;;;GAGG;AAEH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,cAAc,EACxB,IAAI,CAAC,EAAE,kBAAkB,GACxB,kBAAkB,CAqEpB;AA+FD,qBAAa,eAAgB,YAAW,IAAI;;IAU1C,YAAY,OAAO,EAAE,MAAM,IAAI,EAAE,kBAAkB,EAAE,kBAAkB,EAGtE;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASjC;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CASvD;IAmCD,OAAO,IAAI,IAAI,CAKd;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF;AAED,qBAAa,WAAY,SAAQ,aAAc,YAAW,KAAK;;IAO7D,YACE,OAAO,EAAE,MAAM,IAAI,EACnB,UAAU,EAAE,cAAc,EAC1B,kBAAkB,EAAE,kBAAkB,EAMvC;IAEK,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAoD5B;IAED,OAAO,IAAI,IAAI,CAiBd;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF;AAiED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,kBAAkB,GAAG,SAAS,CAahC;AAuED,wBAAgB,6BAA6B,IAAI,IAAI,CAKpD;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,CACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,kBAAkB,KACtB,cAAc,EACnB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deepFreeze } from "../frozen-json.js";
|
|
2
|
+
import { StorageFailureError } from "../storage-failure.js";
|
|
2
3
|
import { maybeTransactionIsClosedRejection, throwIfStoreClosed, transactionError } from "./throw-if-closed.js";
|
|
3
4
|
import { WriteImplBase, deleteSentinel } from "./write-impl-base.js";
|
|
4
5
|
import { getOrInsertComputed } from "../../../shared/src/map.js";
|
|
@@ -28,19 +29,32 @@ var SQLiteStore = class {
|
|
|
28
29
|
const entry = this.#entry;
|
|
29
30
|
const { db, lock, preparedStatements } = entry;
|
|
30
31
|
const release = await lock.read();
|
|
31
|
-
|
|
32
|
+
try {
|
|
33
|
+
if (entry.activeReaders === 0) db.execSync("BEGIN");
|
|
34
|
+
} catch (e) {
|
|
35
|
+
release();
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
32
38
|
entry.activeReaders++;
|
|
33
39
|
return new SQLiteStoreRead(() => {
|
|
34
40
|
entry.activeReaders--;
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
try {
|
|
42
|
+
if (entry.activeReaders === 0) db.execSync("COMMIT");
|
|
43
|
+
} finally {
|
|
44
|
+
release();
|
|
45
|
+
}
|
|
37
46
|
}, preparedStatements);
|
|
38
47
|
}
|
|
39
48
|
async write() {
|
|
40
49
|
throwIfStoreClosed(this);
|
|
41
50
|
const { lock, db, preparedStatements } = this.#entry;
|
|
42
51
|
const release = await lock.write();
|
|
43
|
-
|
|
52
|
+
try {
|
|
53
|
+
db.execSync("BEGIN IMMEDIATE");
|
|
54
|
+
} catch (e) {
|
|
55
|
+
release();
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
44
58
|
return new SQLiteWrite(release, db, preparedStatements);
|
|
45
59
|
}
|
|
46
60
|
async close() {
|
|
@@ -314,8 +328,16 @@ function getOrCreateEntry(filename, create, opts) {
|
|
|
314
328
|
entry.refCount++;
|
|
315
329
|
return entry;
|
|
316
330
|
}
|
|
317
|
-
const dbDelegate = create(filename, opts);
|
|
318
|
-
|
|
331
|
+
const dbDelegate = reportingStorageFailures(rethrowingStorageFailures(() => create(filename, opts)));
|
|
332
|
+
let preparedStatements;
|
|
333
|
+
try {
|
|
334
|
+
preparedStatements = setupDatabase(dbDelegate, opts);
|
|
335
|
+
} catch (e) {
|
|
336
|
+
try {
|
|
337
|
+
dbDelegate.close();
|
|
338
|
+
} catch {}
|
|
339
|
+
throw e;
|
|
340
|
+
}
|
|
319
341
|
const newEntry = {
|
|
320
342
|
lock: new RWLock(),
|
|
321
343
|
db: dbDelegate,
|
|
@@ -326,6 +348,64 @@ function getOrCreateEntry(filename, create, opts) {
|
|
|
326
348
|
stores.set(filename, newEntry);
|
|
327
349
|
return newEntry;
|
|
328
350
|
}
|
|
351
|
+
var STORAGE_FAILURE_SIGNATURES = [
|
|
352
|
+
[/database or disk is full|SQLITE_FULL/, "full"],
|
|
353
|
+
[/unable to open database file|SQLITE_CANTOPEN/, "cannot-open"],
|
|
354
|
+
[/disk I\/O error|SQLITE_IOERR/, "io-error"]
|
|
355
|
+
];
|
|
356
|
+
/**
|
|
357
|
+
* The storage failure a SQLite error reports, if any. Matched on SQLite's own
|
|
358
|
+
* `sqlite3_errmsg` text (or its symbolic code), which expo-sqlite, op-sqlite
|
|
359
|
+
* and zero-sqlite all surface inside whatever wrapper they add.
|
|
360
|
+
* `SQLITE_BUSY` is deliberately not one: it is contention, and `busy_timeout`
|
|
361
|
+
* plus a retry is the right answer to it.
|
|
362
|
+
*/
|
|
363
|
+
function classifySQLiteError(error) {
|
|
364
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : typeof error === "string" ? error : "";
|
|
365
|
+
for (const [signature, kind] of STORAGE_FAILURE_SIGNATURES) if (signature.test(message)) return kind;
|
|
366
|
+
}
|
|
367
|
+
function toStorageFailure(error) {
|
|
368
|
+
const kind = classifySQLiteError(error);
|
|
369
|
+
return kind === void 0 ? error : new StorageFailureError(kind, error instanceof Error ? error.message : String(error), { cause: error });
|
|
370
|
+
}
|
|
371
|
+
function rethrowingStorageFailures(f) {
|
|
372
|
+
try {
|
|
373
|
+
return f();
|
|
374
|
+
} catch (e) {
|
|
375
|
+
throw toStorageFailure(e);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Wraps the driver so every SQLite error that reports a storage failure
|
|
380
|
+
* reaches the store's callers as a {@link StorageFailureError}, whichever
|
|
381
|
+
* statement or transaction step it came from.
|
|
382
|
+
*/
|
|
383
|
+
function reportingStorageFailures(db) {
|
|
384
|
+
return {
|
|
385
|
+
close: () => db.close(),
|
|
386
|
+
destroy: () => db.destroy(),
|
|
387
|
+
execSync: (sql) => rethrowingStorageFailures(() => db.execSync(sql)),
|
|
388
|
+
prepare: (sql) => {
|
|
389
|
+
const statement = rethrowingStorageFailures(() => db.prepare(sql));
|
|
390
|
+
return {
|
|
391
|
+
exec: async (params) => {
|
|
392
|
+
try {
|
|
393
|
+
return await statement.exec(params);
|
|
394
|
+
} catch (e) {
|
|
395
|
+
throw toStorageFailure(e);
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
all: async (params) => {
|
|
399
|
+
try {
|
|
400
|
+
return await statement.all(params);
|
|
401
|
+
} catch (e) {
|
|
402
|
+
throw toStorageFailure(e);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
}
|
|
329
409
|
/**
|
|
330
410
|
* Decrements the reference count for a shared store and cleans up resources
|
|
331
411
|
* when the last reference is released.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.js","names":["#filename","#entry","#kind","#closed","#release","#preparedStatements","#pendingHasKeys","#pendingHasCallbacks","#scheduleLookup","#pendingGetKeys","#pendingGetCallbacks","#scheduled","#dbDelegate","#committed"],"sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport type {Read, Store, Write} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n throwIfStoreClosed,\n transactionError,\n} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\n/**\n * A SQLite prepared statement.\n *\n * `SQLiteStore` prepares one statement per SQL and shares it across all\n * concurrent readers, so implementations must make each call atomic with\n * respect to other callers of the same statement: `all()` must not let another\n * `exec()`/`all()` rebind or reset the underlying statement between executing\n * it and fetching its rows. Delegates whose native API splits execute and fetch\n * into separate round trips (e.g. expo-sqlite) must serialize per statement.\n */\nexport interface PreparedStatement {\n exec(params: string[]): Promise<void>;\n all(params: string[]): Promise<unknown[][]>;\n}\n\nexport interface SQLiteDatabase {\n /**\n * Close the database connection.\n */\n close(): void;\n\n /**\n * Destroy or delete the database (e.g. delete file).\n */\n destroy(): void;\n\n /**\n * Prepare a SQL string, returning a statement you can execute.\n * E.g. `const stmt = db.prepare(\"SELECT * FROM todos WHERE id=?\");`\n */\n prepare(sql: string): PreparedStatement;\n\n // for PRAGMA statements, schema creation and transaction control.\n execSync(sql: string): void;\n}\n\nexport type CreateSQLiteDatabase = (\n filename: string,\n opts?: SQLiteStoreOptions,\n) => SQLiteDatabase;\n\n/**\n * SQLite-based implementation of the Store interface using a configurable delegate.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n * Uses parameterized queries for safety and performance.\n */\nexport class SQLiteStore implements Store {\n readonly #filename: string;\n readonly #entry: StoreEntry;\n readonly #kind: string;\n\n #closed = false;\n\n constructor(\n name: string,\n create: CreateSQLiteDatabase,\n opts?: SQLiteStoreOptions,\n kind = 'sqlite',\n ) {\n this.#filename = resolveFilename(name, opts);\n this.#entry = getOrCreateEntry(this.#filename, create, opts);\n this.#kind = kind;\n }\n\n get kind(): string {\n return this.#kind;\n }\n\n async read(): Promise<Read> {\n throwIfStoreClosed(this);\n\n const entry = this.#entry;\n const {db, lock, preparedStatements} = entry;\n const release = await lock.read();\n\n // Start shared read transaction if this is the first reader\n // This ensures consistent reads across all concurrent readers\n if (entry.activeReaders === 0) {\n db.execSync('BEGIN');\n }\n entry.activeReaders++;\n\n return new SQLiteStoreRead(() => {\n entry.activeReaders--;\n // Commit shared read transaction when last reader finishes\n if (entry.activeReaders === 0) {\n db.execSync('COMMIT');\n }\n release();\n }, preparedStatements);\n }\n\n async write(): Promise<Write> {\n throwIfStoreClosed(this);\n\n const {lock, db, preparedStatements} = this.#entry;\n const release = await lock.write();\n\n // At this point, RWLock guarantees no active readers\n // The last reader would have already committed the shared transaction\n\n db.execSync('BEGIN IMMEDIATE');\n\n return new SQLiteWrite(release, db, preparedStatements);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n const {lock, db} = this.#entry;\n // Wait for all readers and writers to finish.\n const writeRelease = await lock.write();\n\n // Handle reference counting for shared stores - only close database\n // when this is the last store instance using it\n decrementStoreRefCount(this.#filename, db);\n\n this.#closed = true;\n writeRelease();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport function safeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9]/g, '_');\n}\n\nfunction resolveFilename(name: string, opts?: SQLiteStoreOptions): string {\n const safe = safeFilename(name);\n const dir = opts?.directory;\n return dir ? `${dir}/${safe}` : safe;\n}\n\nexport type PreparedStatements = {\n has: PreparedStatement;\n get: PreparedStatement;\n hasMany: PreparedStatement;\n getMany: PreparedStatement;\n put: PreparedStatement;\n del: PreparedStatement;\n /** Multi-row INSERT with the values bound as parameters, n rows wide. */\n putN: (n: number) => PreparedStatement;\n /** Multi-key DELETE with the keys bound as parameters, n keys wide. */\n delN: (n: number) => PreparedStatement;\n};\n\n/**\n * Widest batch we bind in one statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is\n * 32766, and a put costs two parameters per row, so this is far below the cap;\n * it exists to bound how many distinct statements we prepare and cache.\n */\nconst MAX_BATCH = 128;\n\n/** `repeatList('?', 3)` -> `'?,?,?'`. */\nfunction repeatList(item: string, n: number): string {\n return `${item},`.repeat(n).slice(0, -1);\n}\n\n/**\n * Prepares (and caches) a statement of each width on demand. Callers only ever\n * ask for powers of two, so the cache holds at most log2(MAX_BATCH)+1 entries\n * however many rows a commit turns out to have.\n */\nfunction batchStatements(\n delegate: SQLiteDatabase,\n sqlFor: (n: number) => string,\n): (n: number) => PreparedStatement {\n const cache = new Map<number, PreparedStatement>();\n return (n: number) =>\n getOrInsertComputed(cache, n, () => delegate.prepare(sqlFor(n)));\n}\n\n/**\n * Runs `items` through `getStatement` in power-of-two sized batches, so any\n * length is covered by a handful of cached statement widths.\n */\nasync function execInBatches<T>(\n items: readonly T[],\n getStatement: (n: number) => PreparedStatement,\n toParams: (item: T, out: string[]) => void,\n): Promise<void> {\n for (let i = 0; i < items.length;) {\n const remaining = Math.min(MAX_BATCH, items.length - i);\n const n = 1 << (31 - Math.clz32(remaining));\n const params: string[] = [];\n for (let j = 0; j < n; j++) {\n toParams(items[i + j], params);\n }\n await getStatement(n).exec(params);\n i += n;\n }\n}\n\nexport interface SQLiteStoreOptions {\n // Common options\n busyTimeout?: number;\n journalMode?: 'WAL' | 'DELETE';\n synchronous?: 'NORMAL' | 'FULL';\n readUncommitted?: boolean;\n /** Directory in which to create the SQLite file. Defaults to the process CWD. */\n directory?: string | undefined;\n}\n\n/**\n * Replicache's rows are B-tree chunks, which `BTreeWrite` targets at 8-16KB, so\n * most of them are larger than a page and spill into overflow pages at either\n * page size. 8192 does not avoid overflow; it halves the number of pages each\n * chunk is split across.\n *\n * Measured with `replicache-perf/rn`, 4096 vs 8192 + mmap, change in time:\n *\n * | device | persist 1024x10000 | startup read (expo / op) |\n * | ---------------------- | ------------------ | ------------------------ |\n * | iOS simulator | -6% | -12% / -14% |\n * | Android emulator, 2GB | -1% to -2% | -19% / -8% |\n * | Pixel 6 | ~0% | ~-40% / ~0% |\n *\n * Split by pragma on iOS, page_size carries the write win (~6%) and mmap most\n * of the read win. Neither regressed anything on any device.\n */\nconst PAGE_SIZE = 8192;\n\n/** 256MB, matching op-sqlite's own key-value store. */\nconst MMAP_SIZE = 268435456;\n\n/**\n * Common database setup logic shared between expo-sqlite and op-sqlite implementations.\n * Configures SQLite pragmas, creates the entry table, and prepares common statements.\n */\n\nexport function setupDatabase(\n delegate: SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): PreparedStatements {\n // Configure SQLite pragmas for optimal performance.\n //\n // page_size MUST come first. SQLite silently ignores it once the database\n // has content or a journal mode has been set — no error, no warning, the\n // pragma just does nothing and you are left on the 4096 default. Verified:\n // issuing it after `journal_mode = WAL`, or after CREATE TABLE, leaves\n // `PRAGMA page_size` reporting 4096. Do not reorder these.\n delegate.execSync(`PRAGMA page_size = ${PAGE_SIZE}`);\n delegate.execSync(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 200}`);\n delegate.execSync(`PRAGMA journal_mode = '${opts?.journalMode ?? 'WAL'}'`);\n delegate.execSync(`PRAGMA synchronous = '${opts?.synchronous ?? 'NORMAL'}'`);\n delegate.execSync(\n `PRAGMA read_uncommitted = ${Boolean(opts?.readUncommitted)}`,\n );\n // Reads served from the mmap window rather than the pager account for most\n // of the startup-read win measured in the PAGE_SIZE comment.\n delegate.execSync(`PRAGMA mmap_size = ${MMAP_SIZE}`);\n\n // Create the entry table.\n //\n // This is deliberately a rowid table, not `WITHOUT ROWID`. A `WITHOUT ROWID`\n // table stores whole rows in an index B-tree, which keeps at most ~1/4 of a\n // page inline, and SQLite recommends it only for rows under ~1/20 of a page.\n // Our rows are 8-16KB chunks. As a rowid table the key gets a small separate\n // index and the values live in the table B-tree. On a Pixel 6 (5 rounds, on\n // top of the pragmas above) that cut startup read by 24% on both expo and\n // op, expo startup scan by 10%, and persist 1024x10000 by 4-7%.\n //\n // `key` needs an explicit NOT NULL: in a rowid table a non-INTEGER primary key\n // is only a UNIQUE index, and SQLite (a bug kept for compatibility) lets it\n // hold NULLs, several of them. `WITHOUT ROWID` enforced this implicitly.\n //\n // `IF NOT EXISTS` leaves an existing database's table as it was created, so\n // stores created before this change stay `WITHOUT ROWID` until recreated.\n delegate.execSync(`\n CREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY NOT NULL,\n value TEXT NOT NULL\n )\n `);\n\n // Prepare common statements\n return {\n has: delegate.prepare(`SELECT 1 FROM entry WHERE key = ? LIMIT 1`),\n get: delegate.prepare('SELECT value FROM entry WHERE key = ?'),\n hasMany: delegate.prepare(\n `SELECT key FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n getMany: delegate.prepare(\n `SELECT key, value FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n put: delegate.prepare(\n `INSERT OR REPLACE INTO entry (key, value)\n SELECT e.value->>0, e.value->1 FROM json_each(?) e`,\n ),\n del: delegate.prepare(\n `DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n putN: batchStatements(\n delegate,\n n =>\n `INSERT OR REPLACE INTO entry (key, value) VALUES ${repeatList('(?,?)', n)}`,\n ),\n delN: batchStatements(\n delegate,\n n => `DELETE FROM entry WHERE key IN (${repeatList('?', n)})`,\n ),\n };\n}\n\n// Callbacks are stored as striped pairs: [resolve, reject, resolve, reject, ...]\nconst CB_STRIDE = 2;\nconst CB_RESOLVE = 0;\nconst CB_REJECT = 1;\n\ntype GetResolve = (v: ReadonlyJSONValue | undefined) => void;\ntype HasResolve = (v: boolean) => void;\ntype Reject = (e: unknown) => void;\n\nfunction rejectAll(callbacks: unknown[], e: unknown): void {\n for (let i = CB_REJECT; i < callbacks.length; i += CB_STRIDE) {\n (callbacks[i] as Reject)(e);\n }\n}\n\nfunction parseRawValue(raw: string | undefined): ReadonlyJSONValue | undefined {\n return raw === undefined\n ? undefined\n : deepFreeze(JSON.parse(raw) as ReadonlyJSONValue);\n}\n\nfunction resolveGet(\n resolve: GetResolve,\n reject: Reject,\n raw: string | undefined,\n): void {\n try {\n resolve(parseRawValue(raw));\n } catch (e) {\n reject(e);\n }\n}\n\nasync function flushGets(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.get.all([keys[0]])\n : await ps.getMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n resolveGet(\n callbacks[CB_RESOLVE] as GetResolve,\n callbacks[CB_REJECT] as Reject,\n rows[0]?.[0] as string | undefined,\n );\n return;\n }\n const resultMap = new Map(rows as [string, string][]);\n for (let i = 0; i < keys.length; i++) {\n resolveGet(\n callbacks[i * CB_STRIDE + CB_RESOLVE] as GetResolve,\n callbacks[i * CB_STRIDE + CB_REJECT] as Reject,\n resultMap.get(keys[i]),\n );\n }\n}\n\nasync function flushHas(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.has.all([keys[0]])\n : await ps.hasMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n (callbacks[CB_RESOLVE] as HasResolve)(rows.length > 0);\n return;\n }\n const existingKeys = new Set(rows.map(row => row[0] as string));\n for (let i = 0; i < keys.length; i++) {\n (callbacks[i * CB_STRIDE + CB_RESOLVE] as HasResolve)(\n existingKeys.has(keys[i]),\n );\n }\n}\n\nexport class SQLiteStoreRead implements Read {\n readonly #release: () => void;\n readonly #preparedStatements: PreparedStatements;\n #closed = false;\n #pendingGetKeys: string[] = [];\n #pendingGetCallbacks: unknown[] = [];\n #pendingHasKeys: string[] = [];\n #pendingHasCallbacks: unknown[] = [];\n #scheduled = false;\n\n constructor(release: () => void, preparedStatements: PreparedStatements) {\n this.#release = release;\n this.#preparedStatements = preparedStatements;\n }\n\n has(key: string): Promise<boolean> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingHasKeys.push(key);\n this.#pendingHasCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n get(key: string): Promise<ReadonlyJSONValue | undefined> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingGetKeys.push(key);\n this.#pendingGetCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n #scheduleLookup(): void {\n if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => {\n this.#scheduled = false;\n\n const ps = this.#preparedStatements;\n const getKeys = this.#pendingGetKeys;\n this.#pendingGetKeys = [];\n const getCallbacks = this.#pendingGetCallbacks;\n this.#pendingGetCallbacks = [];\n const hasKeys = this.#pendingHasKeys;\n this.#pendingHasKeys = [];\n const hasCallbacks = this.#pendingHasCallbacks;\n this.#pendingHasCallbacks = [];\n\n if (this.#closed) {\n const e = transactionError();\n rejectAll(getCallbacks, e);\n rejectAll(hasCallbacks, e);\n return;\n }\n\n if (getKeys.length > 0) {\n void flushGets(getKeys, getCallbacks, ps);\n }\n if (hasKeys.length > 0) {\n void flushHas(hasKeys, hasCallbacks, ps);\n }\n });\n }\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n this.#release();\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport class SQLiteWrite extends WriteImplBase implements Write {\n readonly #release: () => void;\n readonly #dbDelegate: SQLiteDatabase;\n readonly #preparedStatements: PreparedStatements;\n #committed = false;\n #closed = false;\n\n constructor(\n release: () => void,\n dbDelegate: SQLiteDatabase,\n preparedStatements: PreparedStatements,\n ) {\n super(new SQLiteStoreRead(() => undefined, preparedStatements));\n this.#release = release;\n this.#dbDelegate = dbDelegate;\n this.#preparedStatements = preparedStatements;\n }\n\n async commit(): Promise<void> {\n if (this.#closed) {\n throw transactionError();\n }\n\n const deleteKeys: string[] = [];\n for (const entry of this._pending) {\n if (entry[1] === deleteSentinel) {\n deleteKeys.push(entry[0]);\n this._pending.delete(entry[0]);\n }\n }\n\n // Bind real parameters rather than serializing the whole pending set into\n // one JSON document for json_each() to parse back out. Serializing it, and\n // pushing the resulting (often megabyte-scale) string across the native\n // bridge, measured as roughly a quarter of persist on device.\n //\n // Puts and deletes use different statements over disjoint keys (deletes\n // were removed from _pending above), so they overlap. The batches *within*\n // each must not: the power-of-two split reuses a width when a commit is\n // wide enough (300 rows -> 128, 128, 32, 8, 4), and running two of those\n // concurrently would have two callers on one prepared statement — the\n // rebind-during-execute hazard described in kv/expo-sqlite/store.ts, which\n // op-sqlite has no per-statement lock to absorb.\n const putP =\n this._pending.size > 0\n ? execInBatches(\n [...this._pending] as [string, ReadonlyJSONValue][],\n this.#preparedStatements.putN,\n ([key, value], out) => {\n out.push(key, JSON.stringify(value));\n },\n )\n : undefined;\n const delP =\n deleteKeys.length > 0\n ? execInBatches(\n deleteKeys,\n this.#preparedStatements.delN,\n (key, out) => {\n out.push(key);\n },\n )\n : undefined;\n\n if (putP) await putP;\n if (delP) await delP;\n\n this.#dbDelegate.execSync('COMMIT');\n this._pending.clear();\n this.#committed = true;\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n super.release();\n let rollbackError: unknown;\n if (!this.#committed) {\n try {\n this.#dbDelegate.execSync('ROLLBACK');\n } catch (e) {\n rollbackError = e;\n }\n }\n this.#release();\n if (rollbackError !== undefined) {\n throw rollbackError;\n }\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\ntype StoreEntry = {\n readonly lock: RWLock;\n readonly db: SQLiteDatabase;\n refCount: number;\n activeReaders: number;\n preparedStatements: PreparedStatements;\n};\n\n// Global map to share database connections between multiple store instances with the same name\nconst stores = new Map<string, StoreEntry>();\n\n/**\n * Gets an existing store entry or creates a new one if it doesn't exist.\n * This implements the shared connection pattern where multiple stores with the same\n * name share the same database connection, lock, and delegate.\n */\nfunction getOrCreateEntry(\n filename: string,\n create: (filename: string, opts?: SQLiteStoreOptions) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): StoreEntry {\n const entry = stores.get(filename);\n\n if (entry) {\n entry.refCount++;\n return entry;\n }\n\n const dbDelegate = create(filename, opts);\n const preparedStatements = setupDatabase(dbDelegate, opts);\n\n const lock = new RWLock();\n\n const newEntry: StoreEntry = {\n lock,\n db: dbDelegate,\n refCount: 1,\n activeReaders: 0,\n preparedStatements,\n };\n stores.set(filename, newEntry);\n return newEntry;\n}\n\n/**\n * Decrements the reference count for a shared store and cleans up resources\n * when the last reference is released.\n */\n\nfunction decrementStoreRefCount(\n filename: string,\n dbDelegate: SQLiteDatabase,\n): void {\n const entry = stores.get(filename);\n if (entry) {\n entry.refCount--;\n if (entry.refCount <= 0) {\n dbDelegate.close();\n stores.delete(filename);\n }\n }\n}\nexport function clearAllNamedStoresForTesting(): void {\n for (const entry of stores.values()) {\n entry.db.close();\n }\n stores.clear();\n}\n\nexport function dropStore(\n name: string,\n createDelegate: (\n filename: string,\n opts?: SQLiteStoreOptions,\n ) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): Promise<void> {\n const filename = resolveFilename(name, opts);\n const entry = stores.get(filename);\n if (entry) {\n try {\n entry.db.close();\n } catch {\n // Ignore close errors\n }\n stores.delete(filename);\n }\n\n // Create a temporary delegate to handle database deletion\n const tempDelegate = createDelegate(filename, opts);\n try {\n // we close the db before destroying it - this\n // caused an issue with expo-sqlite since it requires this\n tempDelegate.close();\n } catch {\n // Ignore close errors\n }\n try {\n tempDelegate.destroy();\n } catch {\n // Destroy errors shouldn't be fatal; the file may already be gone or locked\n }\n\n return Promise.resolve();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqlite-store.js","names":["#filename","#entry","#kind","#closed","#release","#preparedStatements","#pendingHasKeys","#pendingHasCallbacks","#scheduleLookup","#pendingGetKeys","#pendingGetCallbacks","#scheduled","#dbDelegate","#committed"],"sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport {\n StorageFailureError,\n type StorageFailureKind,\n} from '../storage-failure.ts';\nimport type {Read, Store, Write} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n throwIfStoreClosed,\n transactionError,\n} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\n/**\n * A SQLite prepared statement.\n *\n * `SQLiteStore` prepares one statement per SQL and shares it across all\n * concurrent readers, so implementations must make each call atomic with\n * respect to other callers of the same statement: `all()` must not let another\n * `exec()`/`all()` rebind or reset the underlying statement between executing\n * it and fetching its rows. Delegates whose native API splits execute and fetch\n * into separate round trips (e.g. expo-sqlite) must serialize per statement.\n */\nexport interface PreparedStatement {\n exec(params: string[]): Promise<void>;\n all(params: string[]): Promise<unknown[][]>;\n}\n\nexport interface SQLiteDatabase {\n /**\n * Close the database connection.\n */\n close(): void;\n\n /**\n * Destroy or delete the database (e.g. delete file).\n */\n destroy(): void;\n\n /**\n * Prepare a SQL string, returning a statement you can execute.\n * E.g. `const stmt = db.prepare(\"SELECT * FROM todos WHERE id=?\");`\n */\n prepare(sql: string): PreparedStatement;\n\n // for PRAGMA statements, schema creation and transaction control.\n execSync(sql: string): void;\n}\n\nexport type CreateSQLiteDatabase = (\n filename: string,\n opts?: SQLiteStoreOptions,\n) => SQLiteDatabase;\n\n/**\n * SQLite-based implementation of the Store interface using a configurable delegate.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n * Uses parameterized queries for safety and performance.\n */\nexport class SQLiteStore implements Store {\n readonly #filename: string;\n readonly #entry: StoreEntry;\n readonly #kind: string;\n\n #closed = false;\n\n constructor(\n name: string,\n create: CreateSQLiteDatabase,\n opts?: SQLiteStoreOptions,\n kind = 'sqlite',\n ) {\n this.#filename = resolveFilename(name, opts);\n this.#entry = getOrCreateEntry(this.#filename, create, opts);\n this.#kind = kind;\n }\n\n get kind(): string {\n return this.#kind;\n }\n\n async read(): Promise<Read> {\n throwIfStoreClosed(this);\n\n const entry = this.#entry;\n const {db, lock, preparedStatements} = entry;\n const release = await lock.read();\n\n // Start shared read transaction if this is the first reader\n // This ensures consistent reads across all concurrent readers\n try {\n if (entry.activeReaders === 0) {\n db.execSync('BEGIN');\n }\n } catch (e) {\n // The lock was acquired above; without this every later read() and\n // write() on the store waits for a release that never comes.\n release();\n throw e;\n }\n entry.activeReaders++;\n\n return new SQLiteStoreRead(() => {\n entry.activeReaders--;\n // Commit shared read transaction when last reader finishes. SQLite may\n // already have rolled the transaction back on its own (an I/O error\n // inside any reader's statement does that), in which case COMMIT\n // throws \"cannot commit - no transaction is active\". The error is\n // reported to the releasing reader, but the RWLock must be released\n // regardless or the store hangs for the rest of the process.\n try {\n if (entry.activeReaders === 0) {\n db.execSync('COMMIT');\n }\n } finally {\n release();\n }\n }, preparedStatements);\n }\n\n async write(): Promise<Write> {\n throwIfStoreClosed(this);\n\n const {lock, db, preparedStatements} = this.#entry;\n const release = await lock.write();\n\n // At this point, RWLock guarantees no active readers\n // The last reader would have already committed the shared transaction\n\n try {\n db.execSync('BEGIN IMMEDIATE');\n } catch (e) {\n // SQLITE_BUSY once busy_timeout expires, or an I/O error. The caller\n // gets the error; the write lock must not stay held.\n release();\n throw e;\n }\n\n return new SQLiteWrite(release, db, preparedStatements);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n const {lock, db} = this.#entry;\n // Wait for all readers and writers to finish.\n const writeRelease = await lock.write();\n\n // Handle reference counting for shared stores - only close database\n // when this is the last store instance using it\n decrementStoreRefCount(this.#filename, db);\n\n this.#closed = true;\n writeRelease();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport function safeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9]/g, '_');\n}\n\nfunction resolveFilename(name: string, opts?: SQLiteStoreOptions): string {\n const safe = safeFilename(name);\n const dir = opts?.directory;\n return dir ? `${dir}/${safe}` : safe;\n}\n\nexport type PreparedStatements = {\n has: PreparedStatement;\n get: PreparedStatement;\n hasMany: PreparedStatement;\n getMany: PreparedStatement;\n put: PreparedStatement;\n del: PreparedStatement;\n /** Multi-row INSERT with the values bound as parameters, n rows wide. */\n putN: (n: number) => PreparedStatement;\n /** Multi-key DELETE with the keys bound as parameters, n keys wide. */\n delN: (n: number) => PreparedStatement;\n};\n\n/**\n * Widest batch we bind in one statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is\n * 32766, and a put costs two parameters per row, so this is far below the cap;\n * it exists to bound how many distinct statements we prepare and cache.\n */\nconst MAX_BATCH = 128;\n\n/** `repeatList('?', 3)` -> `'?,?,?'`. */\nfunction repeatList(item: string, n: number): string {\n return `${item},`.repeat(n).slice(0, -1);\n}\n\n/**\n * Prepares (and caches) a statement of each width on demand. Callers only ever\n * ask for powers of two, so the cache holds at most log2(MAX_BATCH)+1 entries\n * however many rows a commit turns out to have.\n */\nfunction batchStatements(\n delegate: SQLiteDatabase,\n sqlFor: (n: number) => string,\n): (n: number) => PreparedStatement {\n const cache = new Map<number, PreparedStatement>();\n return (n: number) =>\n getOrInsertComputed(cache, n, () => delegate.prepare(sqlFor(n)));\n}\n\n/**\n * Runs `items` through `getStatement` in power-of-two sized batches, so any\n * length is covered by a handful of cached statement widths.\n */\nasync function execInBatches<T>(\n items: readonly T[],\n getStatement: (n: number) => PreparedStatement,\n toParams: (item: T, out: string[]) => void,\n): Promise<void> {\n for (let i = 0; i < items.length;) {\n const remaining = Math.min(MAX_BATCH, items.length - i);\n const n = 1 << (31 - Math.clz32(remaining));\n const params: string[] = [];\n for (let j = 0; j < n; j++) {\n toParams(items[i + j], params);\n }\n await getStatement(n).exec(params);\n i += n;\n }\n}\n\nexport interface SQLiteStoreOptions {\n // Common options\n busyTimeout?: number;\n journalMode?: 'WAL' | 'DELETE';\n synchronous?: 'NORMAL' | 'FULL';\n readUncommitted?: boolean;\n /** Directory in which to create the SQLite file. Defaults to the process CWD. */\n directory?: string | undefined;\n}\n\n/**\n * Replicache's rows are B-tree chunks, which `BTreeWrite` targets at 8-16KB, so\n * most of them are larger than a page and spill into overflow pages at either\n * page size. 8192 does not avoid overflow; it halves the number of pages each\n * chunk is split across.\n *\n * Measured with `replicache-perf/rn`, 4096 vs 8192 + mmap, change in time:\n *\n * | device | persist 1024x10000 | startup read (expo / op) |\n * | ---------------------- | ------------------ | ------------------------ |\n * | iOS simulator | -6% | -12% / -14% |\n * | Android emulator, 2GB | -1% to -2% | -19% / -8% |\n * | Pixel 6 | ~0% | ~-40% / ~0% |\n *\n * Split by pragma on iOS, page_size carries the write win (~6%) and mmap most\n * of the read win. Neither regressed anything on any device.\n */\nconst PAGE_SIZE = 8192;\n\n/** 256MB, matching op-sqlite's own key-value store. */\nconst MMAP_SIZE = 268435456;\n\n/**\n * Common database setup logic shared between expo-sqlite and op-sqlite implementations.\n * Configures SQLite pragmas, creates the entry table, and prepares common statements.\n */\n\nexport function setupDatabase(\n delegate: SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): PreparedStatements {\n // Configure SQLite pragmas for optimal performance.\n //\n // page_size MUST come first. SQLite silently ignores it once the database\n // has content or a journal mode has been set — no error, no warning, the\n // pragma just does nothing and you are left on the 4096 default. Verified:\n // issuing it after `journal_mode = WAL`, or after CREATE TABLE, leaves\n // `PRAGMA page_size` reporting 4096. Do not reorder these.\n delegate.execSync(`PRAGMA page_size = ${PAGE_SIZE}`);\n delegate.execSync(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 200}`);\n delegate.execSync(`PRAGMA journal_mode = '${opts?.journalMode ?? 'WAL'}'`);\n delegate.execSync(`PRAGMA synchronous = '${opts?.synchronous ?? 'NORMAL'}'`);\n delegate.execSync(\n `PRAGMA read_uncommitted = ${Boolean(opts?.readUncommitted)}`,\n );\n // Reads served from the mmap window rather than the pager account for most\n // of the startup-read win measured in the PAGE_SIZE comment.\n delegate.execSync(`PRAGMA mmap_size = ${MMAP_SIZE}`);\n\n // Create the entry table.\n //\n // This is deliberately a rowid table, not `WITHOUT ROWID`. A `WITHOUT ROWID`\n // table stores whole rows in an index B-tree, which keeps at most ~1/4 of a\n // page inline, and SQLite recommends it only for rows under ~1/20 of a page.\n // Our rows are 8-16KB chunks. As a rowid table the key gets a small separate\n // index and the values live in the table B-tree. On a Pixel 6 (5 rounds, on\n // top of the pragmas above) that cut startup read by 24% on both expo and\n // op, expo startup scan by 10%, and persist 1024x10000 by 4-7%.\n //\n // `key` needs an explicit NOT NULL: in a rowid table a non-INTEGER primary key\n // is only a UNIQUE index, and SQLite (a bug kept for compatibility) lets it\n // hold NULLs, several of them. `WITHOUT ROWID` enforced this implicitly.\n //\n // `IF NOT EXISTS` leaves an existing database's table as it was created, so\n // stores created before this change stay `WITHOUT ROWID` until recreated.\n delegate.execSync(`\n CREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY NOT NULL,\n value TEXT NOT NULL\n )\n `);\n\n // Prepare common statements\n return {\n has: delegate.prepare(`SELECT 1 FROM entry WHERE key = ? LIMIT 1`),\n get: delegate.prepare('SELECT value FROM entry WHERE key = ?'),\n hasMany: delegate.prepare(\n `SELECT key FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n getMany: delegate.prepare(\n `SELECT key, value FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n put: delegate.prepare(\n `INSERT OR REPLACE INTO entry (key, value)\n SELECT e.value->>0, e.value->1 FROM json_each(?) e`,\n ),\n del: delegate.prepare(\n `DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n putN: batchStatements(\n delegate,\n n =>\n `INSERT OR REPLACE INTO entry (key, value) VALUES ${repeatList('(?,?)', n)}`,\n ),\n delN: batchStatements(\n delegate,\n n => `DELETE FROM entry WHERE key IN (${repeatList('?', n)})`,\n ),\n };\n}\n\n// Callbacks are stored as striped pairs: [resolve, reject, resolve, reject, ...]\nconst CB_STRIDE = 2;\nconst CB_RESOLVE = 0;\nconst CB_REJECT = 1;\n\ntype GetResolve = (v: ReadonlyJSONValue | undefined) => void;\ntype HasResolve = (v: boolean) => void;\ntype Reject = (e: unknown) => void;\n\nfunction rejectAll(callbacks: unknown[], e: unknown): void {\n for (let i = CB_REJECT; i < callbacks.length; i += CB_STRIDE) {\n (callbacks[i] as Reject)(e);\n }\n}\n\nfunction parseRawValue(raw: string | undefined): ReadonlyJSONValue | undefined {\n return raw === undefined\n ? undefined\n : deepFreeze(JSON.parse(raw) as ReadonlyJSONValue);\n}\n\nfunction resolveGet(\n resolve: GetResolve,\n reject: Reject,\n raw: string | undefined,\n): void {\n try {\n resolve(parseRawValue(raw));\n } catch (e) {\n reject(e);\n }\n}\n\nasync function flushGets(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.get.all([keys[0]])\n : await ps.getMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n resolveGet(\n callbacks[CB_RESOLVE] as GetResolve,\n callbacks[CB_REJECT] as Reject,\n rows[0]?.[0] as string | undefined,\n );\n return;\n }\n const resultMap = new Map(rows as [string, string][]);\n for (let i = 0; i < keys.length; i++) {\n resolveGet(\n callbacks[i * CB_STRIDE + CB_RESOLVE] as GetResolve,\n callbacks[i * CB_STRIDE + CB_REJECT] as Reject,\n resultMap.get(keys[i]),\n );\n }\n}\n\nasync function flushHas(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.has.all([keys[0]])\n : await ps.hasMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n (callbacks[CB_RESOLVE] as HasResolve)(rows.length > 0);\n return;\n }\n const existingKeys = new Set(rows.map(row => row[0] as string));\n for (let i = 0; i < keys.length; i++) {\n (callbacks[i * CB_STRIDE + CB_RESOLVE] as HasResolve)(\n existingKeys.has(keys[i]),\n );\n }\n}\n\nexport class SQLiteStoreRead implements Read {\n readonly #release: () => void;\n readonly #preparedStatements: PreparedStatements;\n #closed = false;\n #pendingGetKeys: string[] = [];\n #pendingGetCallbacks: unknown[] = [];\n #pendingHasKeys: string[] = [];\n #pendingHasCallbacks: unknown[] = [];\n #scheduled = false;\n\n constructor(release: () => void, preparedStatements: PreparedStatements) {\n this.#release = release;\n this.#preparedStatements = preparedStatements;\n }\n\n has(key: string): Promise<boolean> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingHasKeys.push(key);\n this.#pendingHasCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n get(key: string): Promise<ReadonlyJSONValue | undefined> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingGetKeys.push(key);\n this.#pendingGetCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n #scheduleLookup(): void {\n if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => {\n this.#scheduled = false;\n\n const ps = this.#preparedStatements;\n const getKeys = this.#pendingGetKeys;\n this.#pendingGetKeys = [];\n const getCallbacks = this.#pendingGetCallbacks;\n this.#pendingGetCallbacks = [];\n const hasKeys = this.#pendingHasKeys;\n this.#pendingHasKeys = [];\n const hasCallbacks = this.#pendingHasCallbacks;\n this.#pendingHasCallbacks = [];\n\n if (this.#closed) {\n const e = transactionError();\n rejectAll(getCallbacks, e);\n rejectAll(hasCallbacks, e);\n return;\n }\n\n if (getKeys.length > 0) {\n void flushGets(getKeys, getCallbacks, ps);\n }\n if (hasKeys.length > 0) {\n void flushHas(hasKeys, hasCallbacks, ps);\n }\n });\n }\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n this.#release();\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport class SQLiteWrite extends WriteImplBase implements Write {\n readonly #release: () => void;\n readonly #dbDelegate: SQLiteDatabase;\n readonly #preparedStatements: PreparedStatements;\n #committed = false;\n #closed = false;\n\n constructor(\n release: () => void,\n dbDelegate: SQLiteDatabase,\n preparedStatements: PreparedStatements,\n ) {\n super(new SQLiteStoreRead(() => undefined, preparedStatements));\n this.#release = release;\n this.#dbDelegate = dbDelegate;\n this.#preparedStatements = preparedStatements;\n }\n\n async commit(): Promise<void> {\n if (this.#closed) {\n throw transactionError();\n }\n\n const deleteKeys: string[] = [];\n for (const entry of this._pending) {\n if (entry[1] === deleteSentinel) {\n deleteKeys.push(entry[0]);\n this._pending.delete(entry[0]);\n }\n }\n\n // Bind real parameters rather than serializing the whole pending set into\n // one JSON document for json_each() to parse back out. Serializing it, and\n // pushing the resulting (often megabyte-scale) string across the native\n // bridge, measured as roughly a quarter of persist on device.\n //\n // Puts and deletes use different statements over disjoint keys (deletes\n // were removed from _pending above), so they overlap. The batches *within*\n // each must not: the power-of-two split reuses a width when a commit is\n // wide enough (300 rows -> 128, 128, 32, 8, 4), and running two of those\n // concurrently would have two callers on one prepared statement — the\n // rebind-during-execute hazard described in kv/expo-sqlite/store.ts, which\n // op-sqlite has no per-statement lock to absorb.\n const putP =\n this._pending.size > 0\n ? execInBatches(\n [...this._pending] as [string, ReadonlyJSONValue][],\n this.#preparedStatements.putN,\n ([key, value], out) => {\n out.push(key, JSON.stringify(value));\n },\n )\n : undefined;\n const delP =\n deleteKeys.length > 0\n ? execInBatches(\n deleteKeys,\n this.#preparedStatements.delN,\n (key, out) => {\n out.push(key);\n },\n )\n : undefined;\n\n if (putP) await putP;\n if (delP) await delP;\n\n this.#dbDelegate.execSync('COMMIT');\n this._pending.clear();\n this.#committed = true;\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n super.release();\n let rollbackError: unknown;\n if (!this.#committed) {\n try {\n this.#dbDelegate.execSync('ROLLBACK');\n } catch (e) {\n rollbackError = e;\n }\n }\n this.#release();\n if (rollbackError !== undefined) {\n throw rollbackError;\n }\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\ntype StoreEntry = {\n readonly lock: RWLock;\n readonly db: SQLiteDatabase;\n refCount: number;\n activeReaders: number;\n preparedStatements: PreparedStatements;\n};\n\n// Global map to share database connections between multiple store instances with the same name\nconst stores = new Map<string, StoreEntry>();\n\n/**\n * Gets an existing store entry or creates a new one if it doesn't exist.\n * This implements the shared connection pattern where multiple stores with the same\n * name share the same database connection, lock, and delegate.\n */\nfunction getOrCreateEntry(\n filename: string,\n create: (filename: string, opts?: SQLiteStoreOptions) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): StoreEntry {\n const entry = stores.get(filename);\n\n if (entry) {\n entry.refCount++;\n return entry;\n }\n\n const dbDelegate = reportingStorageFailures(\n rethrowingStorageFailures(() => create(filename, opts)),\n );\n let preparedStatements: PreparedStatements;\n try {\n preparedStatements = setupDatabase(dbDelegate, opts);\n } catch (e) {\n // Not in `stores` yet, so nothing else would close this connection.\n try {\n dbDelegate.close();\n } catch {\n // The setup error is the one to report.\n }\n throw e;\n }\n\n const lock = new RWLock();\n\n const newEntry: StoreEntry = {\n lock,\n db: dbDelegate,\n refCount: 1,\n activeReaders: 0,\n preparedStatements,\n };\n stores.set(filename, newEntry);\n return newEntry;\n}\n\nconst STORAGE_FAILURE_SIGNATURES = [\n [/database or disk is full|SQLITE_FULL/, 'full'],\n [/unable to open database file|SQLITE_CANTOPEN/, 'cannot-open'],\n [/disk I\\/O error|SQLITE_IOERR/, 'io-error'],\n] as const;\n\n/**\n * The storage failure a SQLite error reports, if any. Matched on SQLite's own\n * `sqlite3_errmsg` text (or its symbolic code), which expo-sqlite, op-sqlite\n * and zero-sqlite all surface inside whatever wrapper they add.\n * `SQLITE_BUSY` is deliberately not one: it is contention, and `busy_timeout`\n * plus a retry is the right answer to it.\n */\nexport function classifySQLiteError(\n error: unknown,\n): StorageFailureKind | undefined {\n const message =\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : typeof error === 'string'\n ? error\n : '';\n for (const [signature, kind] of STORAGE_FAILURE_SIGNATURES) {\n if (signature.test(message)) {\n return kind;\n }\n }\n return undefined;\n}\n\nfunction toStorageFailure(error: unknown): unknown {\n const kind = classifySQLiteError(error);\n return kind === undefined\n ? error\n : new StorageFailureError(\n kind,\n error instanceof Error ? error.message : String(error),\n {cause: error},\n );\n}\n\nfunction rethrowingStorageFailures<T>(f: () => T): T {\n try {\n return f();\n } catch (e) {\n throw toStorageFailure(e);\n }\n}\n\n/**\n * Wraps the driver so every SQLite error that reports a storage failure\n * reaches the store's callers as a {@link StorageFailureError}, whichever\n * statement or transaction step it came from.\n */\nfunction reportingStorageFailures(db: SQLiteDatabase): SQLiteDatabase {\n return {\n close: () => db.close(),\n destroy: () => db.destroy(),\n execSync: sql => rethrowingStorageFailures(() => db.execSync(sql)),\n prepare: sql => {\n const statement = rethrowingStorageFailures(() => db.prepare(sql));\n return {\n exec: async params => {\n try {\n return await statement.exec(params);\n } catch (e) {\n throw toStorageFailure(e);\n }\n },\n all: async params => {\n try {\n return await statement.all(params);\n } catch (e) {\n throw toStorageFailure(e);\n }\n },\n };\n },\n };\n}\n\n/**\n * Decrements the reference count for a shared store and cleans up resources\n * when the last reference is released.\n */\n\nfunction decrementStoreRefCount(\n filename: string,\n dbDelegate: SQLiteDatabase,\n): void {\n const entry = stores.get(filename);\n if (entry) {\n entry.refCount--;\n if (entry.refCount <= 0) {\n dbDelegate.close();\n stores.delete(filename);\n }\n }\n}\nexport function clearAllNamedStoresForTesting(): void {\n for (const entry of stores.values()) {\n entry.db.close();\n }\n stores.clear();\n}\n\nexport function dropStore(\n name: string,\n createDelegate: (\n filename: string,\n opts?: SQLiteStoreOptions,\n ) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): Promise<void> {\n const filename = resolveFilename(name, opts);\n const entry = stores.get(filename);\n if (entry) {\n try {\n entry.db.close();\n } catch {\n // Ignore close errors\n }\n stores.delete(filename);\n }\n\n // Create a temporary delegate to handle database deletion\n const tempDelegate = createDelegate(filename, opts);\n try {\n // we close the db before destroying it - this\n // caused an issue with expo-sqlite since it requires this\n tempDelegate.close();\n } catch {\n // Ignore close errors\n }\n try {\n tempDelegate.destroy();\n } catch {\n // Destroy errors shouldn't be fatal; the file may already be gone or locked\n }\n\n return Promise.resolve();\n}\n"],"mappings":"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"}
|