@rocicorp/zero 1.6.0-canary.12 → 1.6.0-canary.13

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.
Files changed (551) hide show
  1. package/README.md +3 -28
  2. package/out/_virtual/{_@oxc-project_runtime@0.130.0 → _@oxc-project_runtime@0.122.0}/helpers/usingCtx.js +1 -1
  3. package/out/analyze-query/src/analyze-cli.js +3 -3
  4. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  5. package/out/analyze-query/src/bin-analyze.js +1 -6
  6. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  7. package/out/analyze-query/src/bin-transform.js.map +1 -1
  8. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  9. package/out/ast-to-zql/src/bin.js.map +1 -1
  10. package/out/ast-to-zql/src/format.js.map +1 -1
  11. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  12. package/out/otel/src/enabled.js.map +1 -1
  13. package/out/otel/src/log-options.js.map +1 -1
  14. package/out/otel/src/maybe-time.js.map +1 -1
  15. package/out/otel/src/span.js.map +1 -1
  16. package/out/replicache/src/async-iterable-to-array.js.map +1 -1
  17. package/out/replicache/src/bg-interval.js.map +1 -1
  18. package/out/replicache/src/btree/diff.js.map +1 -1
  19. package/out/replicache/src/btree/node.js.map +1 -1
  20. package/out/replicache/src/btree/read.js.map +1 -1
  21. package/out/replicache/src/btree/splice.js.map +1 -1
  22. package/out/replicache/src/btree/write.js +3 -6
  23. package/out/replicache/src/btree/write.js.map +1 -1
  24. package/out/replicache/src/call-default-fetch.js.map +1 -1
  25. package/out/replicache/src/connection-loop-delegates.js.map +1 -1
  26. package/out/replicache/src/connection-loop.js.map +1 -1
  27. package/out/replicache/src/cookies.js.map +1 -1
  28. package/out/replicache/src/dag/chunk.js.map +1 -1
  29. package/out/replicache/src/dag/gc.js.map +1 -1
  30. package/out/replicache/src/dag/key.js.map +1 -1
  31. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  32. package/out/replicache/src/dag/store-impl.js.map +1 -1
  33. package/out/replicache/src/dag/store.js.map +1 -1
  34. package/out/replicache/src/dag/visitor.js.map +1 -1
  35. package/out/replicache/src/db/commit.js.map +1 -1
  36. package/out/replicache/src/db/index.js.map +1 -1
  37. package/out/replicache/src/db/read.js.map +1 -1
  38. package/out/replicache/src/db/rebase.js.map +1 -1
  39. package/out/replicache/src/db/write.js.map +1 -1
  40. package/out/replicache/src/deleted-clients.js.map +1 -1
  41. package/out/replicache/src/error-responses.js.map +1 -1
  42. package/out/replicache/src/frozen-json.js.map +1 -1
  43. package/out/replicache/src/get-default-puller.js.map +1 -1
  44. package/out/replicache/src/get-default-pusher.js.map +1 -1
  45. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  46. package/out/replicache/src/hash.js.map +1 -1
  47. package/out/replicache/src/http-request-info.js.map +1 -1
  48. package/out/replicache/src/index-defs.js.map +1 -1
  49. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  50. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  51. package/out/replicache/src/kv/idb-store.js.map +1 -1
  52. package/out/replicache/src/kv/mem-store.js.map +1 -1
  53. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  54. package/out/replicache/src/kv/read-impl.js.map +1 -1
  55. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  56. package/out/replicache/src/kv/sqlite-store.js +1 -4
  57. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  58. package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
  59. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  60. package/out/replicache/src/kv/write-impl.js.map +1 -1
  61. package/out/replicache/src/lazy.js.map +1 -1
  62. package/out/replicache/src/log-options.js.map +1 -1
  63. package/out/replicache/src/make-idb-name.js.map +1 -1
  64. package/out/replicache/src/new-client-channel.js.map +1 -1
  65. package/out/replicache/src/on-persist-channel.js.map +1 -1
  66. package/out/replicache/src/patch-operation.js.map +1 -1
  67. package/out/replicache/src/pending-mutations.js.map +1 -1
  68. package/out/replicache/src/persist/client-gc.js.map +1 -1
  69. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  70. package/out/replicache/src/persist/client-groups.js +0 -40
  71. package/out/replicache/src/persist/client-groups.js.map +1 -1
  72. package/out/replicache/src/persist/clients.js +0 -28
  73. package/out/replicache/src/persist/clients.js.map +1 -1
  74. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  75. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  76. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  77. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  78. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  79. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  80. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  81. package/out/replicache/src/persist/persist.js.map +1 -1
  82. package/out/replicache/src/persist/refresh.js.map +1 -1
  83. package/out/replicache/src/process-scheduler.js.map +1 -1
  84. package/out/replicache/src/pusher.js.map +1 -1
  85. package/out/replicache/src/replicache-impl.js.map +1 -1
  86. package/out/replicache/src/report-error.js.map +1 -1
  87. package/out/replicache/src/request-idle.js.map +1 -1
  88. package/out/replicache/src/scan-iterator.js.map +1 -1
  89. package/out/replicache/src/scan-options.js.map +1 -1
  90. package/out/replicache/src/set-interval-with-signal.js.map +1 -1
  91. package/out/replicache/src/subscriptions.js.map +1 -1
  92. package/out/replicache/src/sync/diff.js.map +1 -1
  93. package/out/replicache/src/sync/ids.js.map +1 -1
  94. package/out/replicache/src/sync/patch.js.map +1 -1
  95. package/out/replicache/src/sync/pull-error.js.map +1 -1
  96. package/out/replicache/src/sync/pull.js.map +1 -1
  97. package/out/replicache/src/sync/push.js.map +1 -1
  98. package/out/replicache/src/sync/request-id.js.map +1 -1
  99. package/out/replicache/src/to-error.js.map +1 -1
  100. package/out/replicache/src/transaction-closed-error.js.map +1 -1
  101. package/out/replicache/src/transactions.js.map +1 -1
  102. package/out/replicache/src/with-transactions.js.map +1 -1
  103. package/out/shared/src/abort-error.js.map +1 -1
  104. package/out/shared/src/arrays.js.map +1 -1
  105. package/out/shared/src/asserts.js.map +1 -1
  106. package/out/shared/src/bigint-json.js.map +1 -1
  107. package/out/shared/src/binary-search.js.map +1 -1
  108. package/out/shared/src/broadcast-channel.js.map +1 -1
  109. package/out/shared/src/browser-env.js.map +1 -1
  110. package/out/shared/src/btree-set.js.map +1 -1
  111. package/out/shared/src/cache.js.map +1 -1
  112. package/out/shared/src/centroid.js.map +1 -1
  113. package/out/shared/src/custom-key-map.js.map +1 -1
  114. package/out/shared/src/custom-key-set.js.map +1 -1
  115. package/out/shared/src/deep-clone.js.map +1 -1
  116. package/out/shared/src/deep-merge.js.map +1 -1
  117. package/out/shared/src/document-visible.js.map +1 -1
  118. package/out/shared/src/dotenv.js.map +1 -1
  119. package/out/shared/src/error.js.map +1 -1
  120. package/out/shared/src/hash.js.map +1 -1
  121. package/out/shared/src/iterables.d.ts +0 -2
  122. package/out/shared/src/iterables.d.ts.map +1 -1
  123. package/out/shared/src/iterables.js +1 -9
  124. package/out/shared/src/iterables.js.map +1 -1
  125. package/out/shared/src/json-schema.js.map +1 -1
  126. package/out/shared/src/json.js.map +1 -1
  127. package/out/shared/src/logging-test-utils.js.map +1 -1
  128. package/out/shared/src/logging.js.map +1 -1
  129. package/out/shared/src/map.js.map +1 -1
  130. package/out/shared/src/must.js.map +1 -1
  131. package/out/shared/src/object-traversal.js.map +1 -1
  132. package/out/shared/src/objects.js.map +1 -1
  133. package/out/shared/src/options.js.map +1 -1
  134. package/out/shared/src/parse-big-int.js.map +1 -1
  135. package/out/shared/src/promise-race.js.map +1 -1
  136. package/out/shared/src/queue.d.ts.map +1 -1
  137. package/out/shared/src/queue.js +21 -15
  138. package/out/shared/src/queue.js.map +1 -1
  139. package/out/shared/src/rand.js.map +1 -1
  140. package/out/shared/src/random-uint64.js.map +1 -1
  141. package/out/shared/src/random-values.js.map +1 -1
  142. package/out/shared/src/record-proxy.js.map +1 -1
  143. package/out/shared/src/resolved-promises.js.map +1 -1
  144. package/out/shared/src/sentinels.js.map +1 -1
  145. package/out/shared/src/set-utils.js.map +1 -1
  146. package/out/shared/src/size-of-value.js.map +1 -1
  147. package/out/shared/src/sleep.js.map +1 -1
  148. package/out/shared/src/sorted-entries.js.map +1 -1
  149. package/out/shared/src/string-compare.js.map +1 -1
  150. package/out/shared/src/subscribable.js.map +1 -1
  151. package/out/shared/src/tdigest-schema.js.map +1 -1
  152. package/out/shared/src/tdigest.js.map +1 -1
  153. package/out/shared/src/valita.js.map +1 -1
  154. package/out/z2s/src/compiler.js.map +1 -1
  155. package/out/z2s/src/sql.js.map +1 -1
  156. package/out/zero/package.js +26 -34
  157. package/out/zero/package.js.map +1 -1
  158. package/out/zero/src/build-schema.js.map +1 -1
  159. package/out/zero/src/zero-cache-dev.js.map +1 -1
  160. package/out/zero/src/zero-out.js.map +1 -1
  161. package/out/zero-cache/src/auth/auth.js.map +1 -1
  162. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  163. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  164. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  165. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  166. package/out/zero-cache/src/config/network.js.map +1 -1
  167. package/out/zero-cache/src/config/normalize.js.map +1 -1
  168. package/out/zero-cache/src/config/server-context.js.map +1 -1
  169. package/out/zero-cache/src/config/zero-config.js +0 -5
  170. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  171. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  172. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  173. package/out/zero-cache/src/db/create.js.map +1 -1
  174. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  175. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  176. package/out/zero-cache/src/db/migration-lite.js +0 -19
  177. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  178. package/out/zero-cache/src/db/migration.js +0 -19
  179. package/out/zero-cache/src/db/migration.js.map +1 -1
  180. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  181. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  182. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  183. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  184. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  185. package/out/zero-cache/src/db/specs.js.map +1 -1
  186. package/out/zero-cache/src/db/statements.js.map +1 -1
  187. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  188. package/out/zero-cache/src/db/warmup.js.map +1 -1
  189. package/out/zero-cache/src/observability/events.js.map +1 -1
  190. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  191. package/out/zero-cache/src/scripts/decommission.js.map +1 -1
  192. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  193. package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
  194. package/out/zero-cache/src/scripts/permissions.js +2 -1
  195. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  196. package/out/zero-cache/src/server/anonymous-otel-start.js +7 -8
  197. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  198. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  199. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  200. package/out/zero-cache/src/server/logging.js.map +1 -1
  201. package/out/zero-cache/src/server/main.js.map +1 -1
  202. package/out/zero-cache/src/server/mutator.js.map +1 -1
  203. package/out/zero-cache/src/server/otel-diag-logger.js.map +1 -1
  204. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  205. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  206. package/out/zero-cache/src/server/priority-op.js.map +1 -1
  207. package/out/zero-cache/src/server/reaper.js.map +1 -1
  208. package/out/zero-cache/src/server/replicator.js.map +1 -1
  209. package/out/zero-cache/src/server/runner/main.js.map +1 -1
  210. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  211. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  212. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  213. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  214. package/out/zero-cache/src/server/syncer.js.map +1 -1
  215. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  216. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  217. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  218. package/out/zero-cache/src/services/analyze.js +2 -5
  219. package/out/zero-cache/src/services/analyze.js.map +1 -1
  220. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  221. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  222. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  223. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  224. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  225. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  226. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  227. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  228. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  229. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  230. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  231. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  232. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  233. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  234. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  235. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  236. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  237. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  238. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  239. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  240. package/out/zero-cache/src/services/change-source/protocol/current/data.js +0 -2
  241. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  242. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  243. package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -1
  244. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  245. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  246. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  247. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  248. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  249. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  250. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  251. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  252. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
  253. package/out/zero-cache/src/services/change-streamer/schema/init.js +25 -21
  254. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  255. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  256. package/out/zero-cache/src/services/change-streamer/snapshot.js +0 -15
  257. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  258. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  259. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  260. package/out/zero-cache/src/services/heapz.js.map +1 -1
  261. package/out/zero-cache/src/services/http-service.js.map +1 -1
  262. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  263. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  264. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  265. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  266. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  267. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  268. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  269. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  270. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  271. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  272. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  273. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  274. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  275. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  276. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  277. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  278. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  279. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  280. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  281. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  282. package/out/zero-cache/src/services/run-ast.js +0 -1
  283. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  284. package/out/zero-cache/src/services/runner.js.map +1 -1
  285. package/out/zero-cache/src/services/running-state.js.map +1 -1
  286. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  287. package/out/zero-cache/src/services/statz.js.map +1 -1
  288. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  289. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  290. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  291. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  292. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
  293. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +1 -2
  294. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  295. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  296. package/out/zero-cache/src/services/view-syncer/cvr-store.js +1 -2
  297. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  298. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  299. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  300. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +14 -0
  301. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  302. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +25 -2
  303. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  304. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  305. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  306. package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -1
  307. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  308. package/out/zero-cache/src/services/view-syncer/schema/init.js +113 -97
  309. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  310. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -103
  311. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  312. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  313. package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -1
  314. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  315. package/out/zero-cache/src/services/view-syncer/view-syncer.js +1 -4
  316. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  317. package/out/zero-cache/src/types/configuration-error.js.map +1 -1
  318. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  319. package/out/zero-cache/src/types/http.js.map +1 -1
  320. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  321. package/out/zero-cache/src/types/lite.js.map +1 -1
  322. package/out/zero-cache/src/types/names.js.map +1 -1
  323. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  324. package/out/zero-cache/src/types/pg.js.map +1 -1
  325. package/out/zero-cache/src/types/processes.js.map +1 -1
  326. package/out/zero-cache/src/types/profiler.js.map +1 -1
  327. package/out/zero-cache/src/types/row-key.js.map +1 -1
  328. package/out/zero-cache/src/types/shards.js.map +1 -1
  329. package/out/zero-cache/src/types/sql.js.map +1 -1
  330. package/out/zero-cache/src/types/state-version.js.map +1 -1
  331. package/out/zero-cache/src/types/streams.js.map +1 -1
  332. package/out/zero-cache/src/types/strings.js.map +1 -1
  333. package/out/zero-cache/src/types/subscription.js.map +1 -1
  334. package/out/zero-cache/src/types/timeout.js.map +1 -1
  335. package/out/zero-cache/src/types/url-params.js.map +1 -1
  336. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  337. package/out/zero-cache/src/types/ws.js.map +1 -1
  338. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  339. package/out/zero-cache/src/workers/connection.js.map +1 -1
  340. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  341. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  342. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  343. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  344. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  345. package/out/zero-client/src/client/connection-manager.js +1 -2
  346. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  347. package/out/zero-client/src/client/connection.js.map +1 -1
  348. package/out/zero-client/src/client/context.js.map +1 -1
  349. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  350. package/out/zero-client/src/client/crud.js.map +1 -1
  351. package/out/zero-client/src/client/custom.js +1 -2
  352. package/out/zero-client/src/client/custom.js.map +1 -1
  353. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  354. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  355. package/out/zero-client/src/client/error.js.map +1 -1
  356. package/out/zero-client/src/client/http-string.js.map +1 -1
  357. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  358. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  359. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  360. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  361. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  362. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  363. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  364. package/out/zero-client/src/client/keys.js.map +1 -1
  365. package/out/zero-client/src/client/log-options.js.map +1 -1
  366. package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
  367. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  368. package/out/zero-client/src/client/metrics.js.map +1 -1
  369. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  370. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  371. package/out/zero-client/src/client/options.js.map +1 -1
  372. package/out/zero-client/src/client/query-manager.js.map +1 -1
  373. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  374. package/out/zero-client/src/client/server-option.js.map +1 -1
  375. package/out/zero-client/src/client/version.js +1 -1
  376. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  377. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  378. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  379. package/out/zero-client/src/client/zero.js +32 -58
  380. package/out/zero-client/src/client/zero.js.map +1 -1
  381. package/out/zero-client/src/util/nanoid.js.map +1 -1
  382. package/out/zero-client/src/util/socket.d.ts +3 -0
  383. package/out/zero-client/src/util/socket.d.ts.map +1 -0
  384. package/out/zero-client/src/util/socket.js +8 -0
  385. package/out/zero-client/src/util/socket.js.map +1 -0
  386. package/out/zero-protocol/src/analyze-query-result.js +0 -3
  387. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  388. package/out/zero-protocol/src/application-error.js.map +1 -1
  389. package/out/zero-protocol/src/ast.js.map +1 -1
  390. package/out/zero-protocol/src/change-desired-queries.js +0 -1
  391. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  392. package/out/zero-protocol/src/client-schema.js.map +1 -1
  393. package/out/zero-protocol/src/close-connection.js.map +1 -1
  394. package/out/zero-protocol/src/connect.js +0 -7
  395. package/out/zero-protocol/src/connect.js.map +1 -1
  396. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  397. package/out/zero-protocol/src/data.js.map +1 -1
  398. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  399. package/out/zero-protocol/src/down.js.map +1 -1
  400. package/out/zero-protocol/src/error.js +0 -7
  401. package/out/zero-protocol/src/error.js.map +1 -1
  402. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  403. package/out/zero-protocol/src/inspect-up.js +0 -1
  404. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  405. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  406. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  407. package/out/zero-protocol/src/mutation.js.map +1 -1
  408. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  409. package/out/zero-protocol/src/ping.js.map +1 -1
  410. package/out/zero-protocol/src/poke.js +0 -4
  411. package/out/zero-protocol/src/poke.js.map +1 -1
  412. package/out/zero-protocol/src/pong.js.map +1 -1
  413. package/out/zero-protocol/src/primary-key.js.map +1 -1
  414. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  415. package/out/zero-protocol/src/pull.js.map +1 -1
  416. package/out/zero-protocol/src/push.js +0 -16
  417. package/out/zero-protocol/src/push.js.map +1 -1
  418. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  419. package/out/zero-protocol/src/query-hash.js.map +1 -1
  420. package/out/zero-protocol/src/query-server.js.map +1 -1
  421. package/out/zero-protocol/src/row-patch.js.map +1 -1
  422. package/out/zero-protocol/src/up.js.map +1 -1
  423. package/out/zero-protocol/src/update-auth.js.map +1 -1
  424. package/out/zero-protocol/src/version.js.map +1 -1
  425. package/out/zero-react/src/use-connection-state.js.map +1 -1
  426. package/out/zero-react/src/use-query.js.map +1 -1
  427. package/out/zero-react/src/use-zero-online.js.map +1 -1
  428. package/out/zero-react/src/zero-provider.js.map +1 -1
  429. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  430. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  431. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  432. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  433. package/out/zero-schema/src/name-mapper.js.map +1 -1
  434. package/out/zero-schema/src/permissions.js.map +1 -1
  435. package/out/zero-schema/src/schema-config.js.map +1 -1
  436. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  437. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  438. package/out/zero-server/src/adapters/pg.js.map +1 -1
  439. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  440. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  441. package/out/zero-server/src/custom.js +1 -2
  442. package/out/zero-server/src/custom.js.map +1 -1
  443. package/out/zero-server/src/logging.js.map +1 -1
  444. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  445. package/out/zero-server/src/process-mutations.js.map +1 -1
  446. package/out/zero-server/src/push-processor.js.map +1 -1
  447. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  448. package/out/zero-server/src/schema.js.map +1 -1
  449. package/out/zero-server/src/zql-database.js.map +1 -1
  450. package/out/zero-solid/src/solid-view.js.map +1 -1
  451. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  452. package/out/zero-solid/src/use-query.js.map +1 -1
  453. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  454. package/out/zero-solid/src/use-zero.js.map +1 -1
  455. package/out/zero-types/src/format.js.map +1 -1
  456. package/out/zero-types/src/name-mapper.js.map +1 -1
  457. package/out/zql/src/builder/builder.js.map +1 -1
  458. package/out/zql/src/builder/debug-delegate.d.ts +0 -5
  459. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  460. package/out/zql/src/builder/debug-delegate.js +1 -10
  461. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  462. package/out/zql/src/builder/filter.js.map +1 -1
  463. package/out/zql/src/builder/like.js.map +1 -1
  464. package/out/zql/src/error.js.map +1 -1
  465. package/out/zql/src/ivm/array-view.js.map +1 -1
  466. package/out/zql/src/ivm/cap.js.map +1 -1
  467. package/out/zql/src/ivm/change.js.map +1 -1
  468. package/out/zql/src/ivm/constraint.js +1 -1
  469. package/out/zql/src/ivm/constraint.js.map +1 -1
  470. package/out/zql/src/ivm/data.js.map +1 -1
  471. package/out/zql/src/ivm/exists.js.map +1 -1
  472. package/out/zql/src/ivm/fan-in.js.map +1 -1
  473. package/out/zql/src/ivm/fan-out.js.map +1 -1
  474. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  475. package/out/zql/src/ivm/filter-push.js.map +1 -1
  476. package/out/zql/src/ivm/filter.js.map +1 -1
  477. package/out/zql/src/ivm/flipped-join.d.ts +8 -4
  478. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  479. package/out/zql/src/ivm/flipped-join.js +63 -59
  480. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  481. package/out/zql/src/ivm/join-utils.js.map +1 -1
  482. package/out/zql/src/ivm/join.js.map +1 -1
  483. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  484. package/out/zql/src/ivm/memory-source.js.map +1 -1
  485. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  486. package/out/zql/src/ivm/operator.d.ts +1 -1
  487. package/out/zql/src/ivm/operator.js.map +1 -1
  488. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  489. package/out/zql/src/ivm/schema.d.ts +8 -0
  490. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  491. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  492. package/out/zql/src/ivm/skip.js.map +1 -1
  493. package/out/zql/src/ivm/source.js.map +1 -1
  494. package/out/zql/src/ivm/stream.js.map +1 -1
  495. package/out/zql/src/ivm/take.js.map +1 -1
  496. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  497. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  498. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  499. package/out/zql/src/mutate/crud.js.map +1 -1
  500. package/out/zql/src/mutate/custom.js.map +1 -1
  501. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  502. package/out/zql/src/mutate/mutator.js.map +1 -1
  503. package/out/zql/src/planner/planner-builder.js.map +1 -1
  504. package/out/zql/src/planner/planner-connection.js.map +1 -1
  505. package/out/zql/src/planner/planner-constraint.js.map +1 -1
  506. package/out/zql/src/planner/planner-debug.js.map +1 -1
  507. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  508. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  509. package/out/zql/src/planner/planner-graph.js.map +1 -1
  510. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  511. package/out/zql/src/planner/planner-join.js +1 -2
  512. package/out/zql/src/planner/planner-join.js.map +1 -1
  513. package/out/zql/src/planner/planner-node.js.map +1 -1
  514. package/out/zql/src/planner/planner-source.js.map +1 -1
  515. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  516. package/out/zql/src/query/complete-ordering.js.map +1 -1
  517. package/out/zql/src/query/create-builder.js.map +1 -1
  518. package/out/zql/src/query/error.js.map +1 -1
  519. package/out/zql/src/query/escape-like.js.map +1 -1
  520. package/out/zql/src/query/expression.js.map +1 -1
  521. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  522. package/out/zql/src/query/metrics-delegate.js.map +1 -1
  523. package/out/zql/src/query/named.js.map +1 -1
  524. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  525. package/out/zql/src/query/query-impl.js +1 -1
  526. package/out/zql/src/query/query-impl.js.map +1 -1
  527. package/out/zql/src/query/query-internals.js.map +1 -1
  528. package/out/zql/src/query/query-registry.js.map +1 -1
  529. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  530. package/out/zql/src/query/static-query.js.map +1 -1
  531. package/out/zql/src/query/ttl.js.map +1 -1
  532. package/out/zql/src/query/validate-input.js.map +1 -1
  533. package/out/zqlite/src/database-storage.js.map +1 -1
  534. package/out/zqlite/src/db.js.map +1 -1
  535. package/out/zqlite/src/explain-queries.js.map +1 -1
  536. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  537. package/out/zqlite/src/internal/sql.js.map +1 -1
  538. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  539. package/out/zqlite/src/query-builder.js.map +1 -1
  540. package/out/zqlite/src/query-delegate.js.map +1 -1
  541. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  542. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  543. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  544. package/out/zqlite/src/table-source.d.ts.map +1 -1
  545. package/out/zqlite/src/table-source.js +6 -6
  546. package/out/zqlite/src/table-source.js.map +1 -1
  547. package/package.json +26 -42
  548. package/out/shared/src/ring-buffer.d.ts +0 -32
  549. package/out/shared/src/ring-buffer.d.ts.map +0 -1
  550. package/out/shared/src/ring-buffer.js +0 -109
  551. package/out/shared/src/ring-buffer.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"key.js","names":[],"sources":["../../../../../replicache/src/dag/key.ts"],"sourcesContent":["import {type Hash, parse as parseHash} from '../hash.ts';\nimport * as KeyType from './key-type-enum.ts';\n\nexport function chunkDataKey(hash: Hash): string {\n return `c/${hash}/d`;\n}\n\nexport function chunkMetaKey(hash: Hash): string {\n return `c/${hash}/m`;\n}\n\nexport function chunkRefCountKey(hash: Hash): string {\n return `c/${hash}/r`;\n}\n\nexport function headKey(name: string): string {\n return `h/${name}`;\n}\n\nexport type Key =\n | {\n type: KeyType.ChunkData;\n hash: Hash;\n }\n | {\n type: KeyType.ChunkMeta;\n hash: Hash;\n }\n | {\n type: KeyType.ChunkRefCount;\n hash: Hash;\n }\n | {\n type: KeyType.Head;\n name: string;\n };\n\nexport function parse(key: string): Key {\n const invalidKey = () => new Error(`Invalid key. Got \"${key}\"`);\n const hash = () => parseHash(key.substring(2, key.length - 2));\n\n // '/'\n if (key.charCodeAt(1) === 47) {\n switch (key.charCodeAt(0)) {\n // c\n case 99: {\n if (key.length < 4 || key.charCodeAt(key.length - 2) !== 47) {\n throw invalidKey();\n }\n switch (key.charCodeAt(key.length - 1)) {\n case 100: // d\n return {\n type: KeyType.ChunkData,\n hash: hash(),\n };\n case 109: // m\n return {\n type: KeyType.ChunkMeta,\n hash: hash(),\n };\n case 114: // r\n return {\n type: KeyType.ChunkRefCount,\n hash: hash(),\n };\n }\n break;\n }\n case 104: // h\n return {\n type: KeyType.Head,\n name: key.substring(2),\n };\n }\n }\n throw invalidKey();\n}\n"],"mappings":";;AAGA,SAAgB,aAAa,MAAoB;CAC/C,OAAO,KAAK,KAAK;AACnB;AAEA,SAAgB,aAAa,MAAoB;CAC/C,OAAO,KAAK,KAAK;AACnB;AAEA,SAAgB,iBAAiB,MAAoB;CACnD,OAAO,KAAK,KAAK;AACnB;AAEA,SAAgB,QAAQ,MAAsB;CAC5C,OAAO,KAAK;AACd"}
1
+ {"version":3,"file":"key.js","names":[],"sources":["../../../../../replicache/src/dag/key.ts"],"sourcesContent":["import {type Hash, parse as parseHash} from '../hash.ts';\nimport * as KeyType from './key-type-enum.ts';\n\nexport function chunkDataKey(hash: Hash): string {\n return `c/${hash}/d`;\n}\n\nexport function chunkMetaKey(hash: Hash): string {\n return `c/${hash}/m`;\n}\n\nexport function chunkRefCountKey(hash: Hash): string {\n return `c/${hash}/r`;\n}\n\nexport function headKey(name: string): string {\n return `h/${name}`;\n}\n\nexport type Key =\n | {\n type: KeyType.ChunkData;\n hash: Hash;\n }\n | {\n type: KeyType.ChunkMeta;\n hash: Hash;\n }\n | {\n type: KeyType.ChunkRefCount;\n hash: Hash;\n }\n | {\n type: KeyType.Head;\n name: string;\n };\n\nexport function parse(key: string): Key {\n const invalidKey = () => new Error(`Invalid key. Got \"${key}\"`);\n const hash = () => parseHash(key.substring(2, key.length - 2));\n\n // '/'\n if (key.charCodeAt(1) === 47) {\n switch (key.charCodeAt(0)) {\n // c\n case 99: {\n if (key.length < 4 || key.charCodeAt(key.length - 2) !== 47) {\n throw invalidKey();\n }\n switch (key.charCodeAt(key.length - 1)) {\n case 100: // d\n return {\n type: KeyType.ChunkData,\n hash: hash(),\n };\n case 109: // m\n return {\n type: KeyType.ChunkMeta,\n hash: hash(),\n };\n case 114: // r\n return {\n type: KeyType.ChunkRefCount,\n hash: hash(),\n };\n }\n break;\n }\n case 104: // h\n return {\n type: KeyType.Head,\n name: key.substring(2),\n };\n }\n }\n throw invalidKey();\n}\n"],"mappings":";;AAGA,SAAgB,aAAa,MAAoB;AAC/C,QAAO,KAAK,KAAK;;AAGnB,SAAgB,aAAa,MAAoB;AAC/C,QAAO,KAAK,KAAK;;AAGnB,SAAgB,iBAAiB,MAAoB;AACnD,QAAO,KAAK,KAAK;;AAGnB,SAAgB,QAAQ,MAAsB;AAC5C,QAAO,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-store.js","names":["#rwLock","#heads","#sourceStore","#chunkHasher","#assertValidHash","#release","#sourceReadOwnedByCaller","#sourceRead","#closed","#refCounts","#refs","#createdChunks","#setHead","#cacheSizeLimit","#getSizeOfChunk","#suspendedDeletes","#cacheChunk","#ensureCacheSizeLimit","#evictsAndDeletesSuspended","#size","#evict","#deleteEntryByHash"],"sources":["../../../../../replicache/src/dag/lazy-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport {joinIterables} from '../../../shared/src/iterables.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {getSizeOfValue} from '../../../shared/src/size-of-value.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport type {Hash} from '../hash.ts';\nimport type {Chunk} from './chunk.ts';\nimport {type ChunkHasher, type Refs, createChunk} from './chunk.ts';\nimport {\n type HeadChange,\n type RefCountUpdatesDelegate,\n computeRefCountUpdates,\n} from './gc.ts';\nimport {type Read, type Store, type Write, mustGetChunk} from './store.ts';\n\n/**\n * Dag Store which lazily loads values from a source store and then caches\n * them in an LRU cache. The memory cache for chunks from the source store\n * size is limited to `sourceCacheSizeLimit` bytes, and values are evicted in an\n * LRU fashion. The purpose of this store is to avoid holding the entire client\n * view (i.e. the source store's content) in each tab's JavaScript heap.\n *\n * This store's heads are independent from the heads of source store, and are\n * only stored in memory.\n *\n * Chunks which are created via this store's {@link Write} transaction's\n * {@link createChunk} method are assumed to not be persisted to the source\n * store and thus are cached separately from the source store chunks. These\n * memory-only chunks will not be evicted, and their sizes are not counted\n * towards the source chunk cache size. A memory-only chunk will be deleted if\n * it is no longer reachable from one of this store's heads.\n *\n * Writes only manipulate the in memory state of this store and do not alter the\n * source store. Thus values must be written to the source store through a\n * separate process (see {@link persist}).\n *\n * Intended use:\n * 1. source store is the 'perdag', a slower persistent store (i.e.\n * dag.StoreImpl using a kv.IDBStore)\n * 2. this store's 'main' head is initialized to the hash of a chunk containing\n * a commit in the source store\n * 3. reads lazily read chunks from the source store and cache them\n * 3. writes are initially made to this store with memory-only chunks\n * 4. writes are asynchronously persisted to the source store through a separate\n * process (see {@link persist}}. This process gathers memory-only chunks\n * from this store and then writes them to the source store. It then informs\n * this store that these chunks are no longer memory-only by calling\n * {@link chunksPersisted}, which move these chunks\n * to this store's LRU cache of source chunks (making them eligible for\n * eviction).\n *\n * @param sourceStore Store to lazy load and cache values from.\n * @param sourceCacheSizeLimit Size limit in bytes for cache of chunks loaded\n * from `sourceStore`. This size of a value is determined using\n * `getSizeOfValue`. Keys do not count towards cache size. Memory-only chunks\n * do not count towards cache size.\n * @param getSizeOfValue Function for measuring the size in bytes of a value.\n */\nexport class LazyStore implements Store {\n /**\n * This lock is used to ensure correct isolation of Reads and Writes.\n * Multiple Reads are allowed in parallel but only a single Write. Reads and\n * Writes see an isolated view of the store (corresponding to the Serializable\n * level of transaction isolation defined in the SQL standard).\n *\n * To ensure these semantics the read lock must be acquired when a Read is\n * created and held til it is closed, and a Write lock must be acquired when a\n * Write is created and held til it is committed or closed.\n *\n * Code must have a read or write lock to\n * - read `_heads`\n * - read `_memOnlyChunks`\n * - read `_sourceStore`\n * - read and write `_sourceChunksCache`\n * - read and write `_refCounts`\n * - read and write `_refs`\n * and must have a write lock to\n * - write `_heads`\n * - write `_memOnlyChunks`\n */\n readonly #rwLock = new RWLock();\n readonly #heads = new Map<string, Hash>();\n readonly #sourceStore: Store;\n readonly #chunkHasher: ChunkHasher;\n readonly #assertValidHash: (hash: Hash) => void;\n\n /** The following are protected so testing subclass can access. */\n protected readonly _memOnlyChunks = new Map<Hash, Chunk>();\n protected readonly _sourceChunksCache: ChunksCache;\n /**\n * Ref counts are maintained so that chunks which are unreachable\n * from this stores heads can be eagerly and deterministically deleted from\n * `this._memOnlyChunks` and `this._sourceChunksCache`.\n *\n * These ref counts are independent from `this._sourceStore`'s ref counts.\n * These ref counts are based on reachability from `this._heads`.\n * A chunk is deleted from `this._memOnlyChunks` or\n * `this._sourceChunksCache` (which ever it is in) when its ref count becomes\n * zero.\n * These ref counts count the refs in `this._heads` and `this._refs`.\n *\n * Not all reachable chunk's refs are included in `this._refs`, because this\n * would require loading all chunks reachable in the source store in a\n * non-lazy manner. `this._refs` contains the refs of all currently reachable\n * chunks that were ever in `this._memOnlyChunks` or\n * `this._sourceChunksCache` (even if they have been evicted). A\n * chunk's ref information is lazily discovered and stored in `this._refs` and\n * counted in `this._refCounts`. A chunk's entries in `this._refs` and\n * `this._refCounts` are only deleted when a chunk is deleted due to it\n * becoming unreachable (it is not deleted if the chunk is evicted from the\n * source-store cache).\n *\n * The major implication of this lazy discovery of source store refs, is that\n * a reachable source store chunk may not be cached when loaded, because it is\n * not known to be reachable because some of the pertinent refs have not been\n * discovered. However, in practice chunks are read by traversing the graph\n * starting from a head, and all pertinent refs are discovered as part of the\n * traversal.\n *\n * These ref counts can be changed in two ways:\n * 1. A LazyRead has a cache miss and loads a chunk from the source store that\n * is reachable from this._heads. If this chunk's refs are not currently\n * counted, it will not have an entry in `this._refs`. In this case, the\n * chunks refs will be put in `this._refs` and `this._refCounts` will be\n * updated to count them.\n * 2. A LazyWrite commit updates a head (which can result in increasing or\n * decreasing ref count) or puts a reachable chunk (either a `memory-only` or\n * `source` chunk) that references this hash (increasing ref count). The\n * computation of these ref count changes is delegated to the\n * `computeRefCountUpdates` shared with dag.StoreImpl. In order to\n * delegate determining reachability to `computeRefCountUpdates` and defer\n * this determination until commit time, LazyWrite treats cache misses\n * as a 'put' of the lazily-loaded chunk.\n *\n * A chunk's hash may have an entry in `this._refCounts` without that\n * chunk have ever been in `this._memOnlyChunks` or `this._sourceChunksCache`.\n * This is the case when a head or a reachable chunk that was ever in\n * `this._memOnlyChunks` or `this._sourceChunksCache` references a chunk\n * which is not currently cached (either because it has not been read, or\n * because it has been evicted).\n */\n protected readonly _refCounts = new Map<Hash, number>();\n protected readonly _refs = new Map<Hash, readonly Hash[]>();\n\n constructor(\n sourceStore: Store,\n sourceCacheSizeLimit: number,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n getSizeOfChunk: (chunk: Chunk) => number = getSizeOfValue,\n ) {\n this._sourceChunksCache = new ChunksCache(\n sourceCacheSizeLimit,\n getSizeOfChunk,\n this._refCounts,\n this._refs,\n );\n this.#sourceStore = sourceStore;\n this.#chunkHasher = chunkHasher;\n this.#assertValidHash = assertValidHash;\n }\n\n async read(sourceRead?: Read): Promise<LazyRead> {\n const release = await this.#rwLock.read();\n return new LazyRead(\n this.#heads,\n this._memOnlyChunks,\n this._sourceChunksCache,\n this.#sourceStore,\n release,\n this.#assertValidHash,\n sourceRead,\n );\n }\n\n async write(): Promise<LazyWrite> {\n const release = await this.#rwLock.write();\n return new LazyWrite(\n this.#heads,\n this._memOnlyChunks,\n this._sourceChunksCache,\n this.#sourceStore,\n this._refCounts,\n this._refs,\n release,\n this.#chunkHasher,\n this.#assertValidHash,\n );\n }\n\n close(): Promise<void> {\n return promiseVoid;\n }\n\n /**\n * Does not acquire any lock on the store.\n */\n isCached(chunkHash: Hash): boolean {\n return (\n this._sourceChunksCache.getWithoutUpdatingLRU(chunkHash) !== undefined\n );\n }\n\n withSuspendedSourceCacheEvictsAndDeletes<T>(\n fn: () => MaybePromise<T>,\n ): Promise<T> {\n return this._sourceChunksCache.withSuspendedEvictsAndDeletes(fn);\n }\n}\n\nexport class LazyRead implements Read {\n protected readonly _heads: Map<string, Hash>;\n protected readonly _memOnlyChunks: Map<Hash, Chunk>;\n protected readonly _sourceChunksCache: ChunksCache;\n protected readonly _sourceStore: Store;\n #sourceRead: Promise<Read> | undefined = undefined;\n readonly #release: () => void;\n #closed = false;\n readonly assertValidHash: (hash: Hash) => void;\n readonly #sourceReadOwnedByCaller: boolean;\n\n constructor(\n heads: Map<string, Hash>,\n memOnlyChunks: Map<Hash, Chunk>,\n sourceChunksCache: ChunksCache,\n sourceStore: Store,\n release: () => void,\n assertValidHash: (hash: Hash) => void,\n // If the lazyRead is being run in the context of an existing\n // read, or write, from the perdag then we must use _that_\n // transaction's read. Trying to open our own `sourceRead` will\n // cause the outer transaction to auto-commit.\n sourceRead?: Read,\n ) {\n this._heads = heads;\n this._memOnlyChunks = memOnlyChunks;\n this._sourceChunksCache = sourceChunksCache;\n this._sourceStore = sourceStore;\n this.#release = release;\n this.assertValidHash = assertValidHash;\n this.#sourceRead =\n sourceRead !== undefined ? Promise.resolve(sourceRead) : undefined;\n this.#sourceReadOwnedByCaller = sourceRead !== undefined;\n }\n\n isMemOnlyChunkHash(hash: Hash): boolean {\n return this._memOnlyChunks.has(hash);\n }\n\n async hasChunk(hash: Hash): Promise<boolean> {\n return (await this.getChunk(hash)) !== undefined;\n }\n\n async getChunk(hash: Hash): Promise<Chunk | undefined> {\n const memOnlyChunk = this._memOnlyChunks.get(hash);\n if (memOnlyChunk !== undefined) {\n return memOnlyChunk;\n }\n let chunk = this._sourceChunksCache.get(hash);\n if (chunk === undefined) {\n chunk = await (await this._getSourceRead()).getChunk(hash);\n if (chunk !== undefined) {\n this._sourceChunksCache.put(chunk);\n }\n }\n return chunk;\n }\n\n mustGetChunk(hash: Hash): Promise<Chunk> {\n return mustGetChunk(this, hash);\n }\n\n getHead(name: string): Promise<Hash | undefined> {\n return Promise.resolve(this._heads.get(name));\n }\n\n release(): void {\n if (!this.#closed) {\n this.#release();\n if (!this.#sourceReadOwnedByCaller) {\n this.#sourceRead\n ?.then(read => read.release())\n // If creation of the read failed there is nothing to release.\n // Catch to avoid `Uncaught (in promise)` errors being reported.\n .catch(_ => {});\n }\n this.#closed = true;\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n protected _getSourceRead(): Promise<Read> {\n if (!this.#sourceRead) {\n this.#sourceRead = this._sourceStore.read();\n }\n return this.#sourceRead;\n }\n}\n\nexport class LazyWrite\n extends LazyRead\n implements Write, RefCountUpdatesDelegate\n{\n readonly #refCounts: Map<Hash, number>;\n readonly #refs: Map<Hash, readonly Hash[]>;\n readonly #chunkHasher: ChunkHasher;\n protected readonly _pendingHeadChanges = new Map<string, HeadChange>();\n protected readonly _pendingMemOnlyChunks = new Map<Hash, Chunk>();\n protected readonly _pendingCachedChunks = new Map<\n Hash,\n {chunk: Chunk; size: number}\n >();\n readonly #createdChunks = new Set<Hash>();\n\n constructor(\n heads: Map<string, Hash>,\n memOnlyChunks: Map<Hash, Chunk>,\n sourceChunksCache: ChunksCache,\n sourceStore: Store,\n refCounts: Map<Hash, number>,\n refs: Map<Hash, readonly Hash[]>,\n release: () => void,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n ) {\n super(\n heads,\n memOnlyChunks,\n sourceChunksCache,\n sourceStore,\n release,\n assertValidHash,\n );\n this.#refCounts = refCounts;\n this.#refs = refs;\n this.#chunkHasher = chunkHasher;\n }\n\n createChunk = <V>(data: V, refs: Refs): Chunk<V> => {\n const chunk = createChunk(data, refs, this.#chunkHasher);\n this.#createdChunks.add(chunk.hash);\n return chunk;\n };\n\n putChunk<V>(c: Chunk<V>, size?: number): Promise<void> {\n const {hash, meta} = c;\n this.assertValidHash(hash);\n if (meta.length > 0) {\n for (const h of meta) {\n this.assertValidHash(h);\n }\n }\n if (this.#createdChunks.has(hash) || this.isMemOnlyChunkHash(hash)) {\n this._pendingMemOnlyChunks.set(hash, c);\n } else {\n this._pendingCachedChunks.set(hash, {chunk: c, size: size ?? -1});\n }\n return promiseVoid;\n }\n\n async setHead(name: string, hash: Hash): Promise<void> {\n await this.#setHead(name, hash);\n }\n\n async removeHead(name: string): Promise<void> {\n await this.#setHead(name, undefined);\n }\n\n async #setHead(name: string, hash: Hash | undefined): Promise<void> {\n const oldHash = await this.getHead(name);\n const v = this._pendingHeadChanges.get(name);\n if (v === undefined) {\n this._pendingHeadChanges.set(name, {new: hash, old: oldHash});\n } else {\n // Keep old if existing\n v.new = hash;\n }\n }\n\n override isMemOnlyChunkHash(hash: Hash): boolean {\n return (\n this._pendingMemOnlyChunks.has(hash) || super.isMemOnlyChunkHash(hash)\n );\n }\n\n override async getChunk(hash: Hash): Promise<Chunk | undefined> {\n const pendingMemOnlyChunk = this._pendingMemOnlyChunks.get(hash);\n if (pendingMemOnlyChunk !== undefined) {\n return pendingMemOnlyChunk;\n }\n const memOnlyChunk = this._memOnlyChunks.get(hash);\n if (memOnlyChunk !== undefined) {\n return memOnlyChunk;\n }\n // In order to delegate determining reachability to `computeRefCountUpdates`\n // and defer this determination until commit time, treat cache misses\n // as a 'put' of the lazily-loaded chunk.\n const pendingCachedChunk = this._pendingCachedChunks.get(hash);\n if (pendingCachedChunk !== undefined) {\n return pendingCachedChunk.chunk;\n }\n let chunk = this._sourceChunksCache.get(hash);\n if (chunk === undefined) {\n chunk = await (await this._getSourceRead()).getChunk(hash);\n if (chunk !== undefined) {\n this._pendingCachedChunks.set(chunk.hash, {chunk, size: -1});\n }\n }\n return chunk;\n }\n\n override getHead(name: string): Promise<Hash | undefined> {\n const headChange = this._pendingHeadChanges.get(name);\n if (headChange) {\n return Promise.resolve(headChange.new);\n }\n return super.getHead(name);\n }\n\n async commit(): Promise<void> {\n const pendingChunks = new Set(\n joinIterables(\n this._pendingMemOnlyChunks.keys(),\n this._pendingCachedChunks.keys(),\n ),\n );\n const refCountUpdates = await computeRefCountUpdates(\n this._pendingHeadChanges.values(),\n pendingChunks,\n this,\n );\n\n for (const [hash, count] of refCountUpdates) {\n if (this.isMemOnlyChunkHash(hash)) {\n if (count === 0) {\n this.#refCounts.delete(hash);\n this._memOnlyChunks.delete(hash);\n this.#refs.delete(hash);\n } else {\n this.#refCounts.set(hash, count);\n const chunk = this._pendingMemOnlyChunks.get(hash);\n if (chunk) {\n this.#refs.set(hash, chunk.meta);\n this._memOnlyChunks.set(hash, chunk);\n }\n }\n refCountUpdates.delete(hash);\n }\n }\n\n this._sourceChunksCache.updateForCommit(\n this._pendingCachedChunks,\n refCountUpdates,\n );\n\n for (const [name, headChange] of this._pendingHeadChanges) {\n if (headChange.new) {\n this._heads.set(name, headChange.new);\n } else {\n this._heads.delete(name);\n }\n }\n\n this._pendingMemOnlyChunks.clear();\n this._pendingCachedChunks.clear();\n this._pendingHeadChanges.clear();\n this.release();\n }\n\n getRefCount(hash: Hash): number | undefined {\n return this.#refCounts.get(hash);\n }\n\n getRefs(hash: Hash): readonly Hash[] | undefined {\n const pendingMemOnlyChunk = this._pendingMemOnlyChunks.get(hash);\n if (pendingMemOnlyChunk) {\n return pendingMemOnlyChunk.meta;\n }\n const memOnlyChunk = this._memOnlyChunks.get(hash);\n if (memOnlyChunk) {\n return memOnlyChunk.meta;\n }\n const pendingCachedChunk = this._pendingCachedChunks.get(hash);\n if (pendingCachedChunk !== undefined) {\n return pendingCachedChunk.chunk.meta;\n }\n return this.#refs.get(hash);\n }\n\n areRefsCounted(hash: Hash): boolean {\n return this.#refs.has(hash);\n }\n\n chunksPersisted(chunkHashes: readonly Hash[]): void {\n const chunksToCache = [];\n for (const chunkHash of chunkHashes) {\n const chunk = this._memOnlyChunks.get(chunkHash);\n if (chunk) {\n this._memOnlyChunks.delete(chunkHash);\n chunksToCache.push(chunk);\n }\n }\n this._sourceChunksCache.persisted(chunksToCache);\n }\n}\n\ntype CacheEntry = {\n chunk: Chunk;\n size: number;\n};\n\nclass ChunksCache {\n readonly #cacheSizeLimit: number;\n readonly #getSizeOfChunk: (chunk: Chunk) => number;\n readonly #refCounts: Map<Hash, number>;\n readonly #refs: Map<Hash, readonly Hash[]>;\n #size = 0;\n #evictsAndDeletesSuspended = false;\n readonly #suspendedDeletes: Hash[] = [];\n\n /**\n * Iteration order is from least to most recently used.\n *\n * Public so that testing subclass can access.\n */\n readonly cacheEntries = new Map<Hash, CacheEntry>();\n\n constructor(\n cacheSizeLimit: number,\n getSizeOfChunk: (v: Chunk) => number,\n refCounts: Map<Hash, number>,\n refs: Map<Hash, readonly Hash[]>,\n ) {\n this.#cacheSizeLimit = cacheSizeLimit;\n this.#getSizeOfChunk = getSizeOfChunk;\n this.#refCounts = refCounts;\n this.#refs = refs;\n }\n\n get(hash: Hash): Chunk | undefined {\n const cacheEntry = this.cacheEntries.get(hash);\n if (cacheEntry) {\n // Update order in map for LRU tracking.\n this.cacheEntries.delete(hash);\n this.cacheEntries.set(hash, cacheEntry);\n }\n return cacheEntry?.chunk;\n }\n\n getWithoutUpdatingLRU(hash: Hash): Chunk | undefined {\n return this.cacheEntries.get(hash)?.chunk;\n }\n\n put(chunk: Chunk): void {\n const {hash} = chunk;\n // If there is an existing cache entry then the cached value must be\n // equivalent. Update order in map for LRU tracking and early return.\n const oldCacheEntry = this.cacheEntries.get(hash);\n if (oldCacheEntry) {\n this.cacheEntries.delete(hash);\n this.cacheEntries.set(hash, oldCacheEntry);\n return;\n }\n\n // Only cache if there is a ref from a head to this chunk\n const refCount = this.#refCounts.get(hash);\n if (refCount === undefined || refCount < 1) {\n return;\n }\n if (!this.#cacheChunk(chunk)) {\n return;\n }\n if (!this.#refs.has(hash)) {\n for (const refHash of chunk.meta) {\n this.#refCounts.set(refHash, (this.#refCounts.get(refHash) || 0) + 1);\n }\n this.#refs.set(hash, chunk.meta);\n }\n\n this.#ensureCacheSizeLimit();\n }\n\n #ensureCacheSizeLimit() {\n if (this.#evictsAndDeletesSuspended) {\n return;\n }\n for (const entry of this.cacheEntries.values()) {\n if (this.#size <= this.#cacheSizeLimit) {\n break;\n }\n this.#evict(entry);\n }\n }\n\n #cacheChunk(chunk: Chunk, size?: number): boolean {\n const chunkSize = size ?? this.#getSizeOfChunk(chunk);\n if (chunkSize > this.#cacheSizeLimit) {\n // This value cannot be cached due to its size exceeding the\n // cache size limit, don't evict other entries to try to make\n // room for it.\n return false;\n }\n this.#size += chunkSize;\n this.cacheEntries.set(chunk.hash, {chunk, size: chunkSize});\n return true;\n }\n\n #evict(cacheEntry: CacheEntry): void {\n const {hash} = cacheEntry.chunk;\n this.#size -= cacheEntry.size;\n this.cacheEntries.delete(hash);\n }\n\n #deleteEntryByHash(hash: Hash): void {\n this.#refCounts.delete(hash);\n this.#refs.delete(hash);\n const cacheEntry = this.cacheEntries.get(hash);\n if (cacheEntry) {\n this.#size -= cacheEntry.size;\n this.cacheEntries.delete(hash);\n }\n }\n\n updateForCommit(\n chunksToPut: Map<Hash, {chunk: Chunk; size: number}>,\n refCountUpdates: Map<Hash, number>,\n ): void {\n for (const [hash, count] of refCountUpdates) {\n if (count === 0) {\n if (!this.#evictsAndDeletesSuspended) {\n this.#deleteEntryByHash(hash);\n } else {\n this.#refCounts.set(hash, 0);\n this.#suspendedDeletes.push(hash);\n }\n } else {\n this.#refCounts.set(hash, count);\n const chunkAndSize = chunksToPut.get(hash);\n if (chunkAndSize) {\n const {chunk, size} = chunkAndSize;\n const oldCacheEntry = this.cacheEntries.get(hash);\n if (oldCacheEntry) {\n // If there is an existing cache entry then the cached value must be\n // equivalent. Update order in map for LRU tracking but avoid\n // recomputing size and creating a new cache entry.\n this.cacheEntries.delete(hash);\n this.cacheEntries.set(hash, oldCacheEntry);\n } else {\n this.#cacheChunk(chunk, size !== -1 ? size : undefined);\n this.#refs.set(hash, chunk.meta);\n }\n }\n }\n }\n this.#ensureCacheSizeLimit();\n }\n\n persisted(chunks: Iterable<Chunk>) {\n for (const chunk of chunks) {\n this.#cacheChunk(chunk);\n }\n this.#ensureCacheSizeLimit();\n }\n\n async withSuspendedEvictsAndDeletes<T>(\n fn: () => MaybePromise<T>,\n ): Promise<T> {\n this.#evictsAndDeletesSuspended = true;\n try {\n return await fn();\n } finally {\n this.#evictsAndDeletesSuspended = false;\n for (const hash of this.#suspendedDeletes) {\n if (this.#refCounts.get(hash) === 0) {\n this.#deleteEntryByHash(hash);\n }\n }\n this.#ensureCacheSizeLimit();\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lazy-store.js","names":["#rwLock","#heads","#sourceStore","#chunkHasher","#assertValidHash","#release","#sourceReadOwnedByCaller","#sourceRead","#closed","#refCounts","#refs","#createdChunks","#setHead","#cacheSizeLimit","#getSizeOfChunk","#suspendedDeletes","#cacheChunk","#ensureCacheSizeLimit","#evictsAndDeletesSuspended","#size","#evict","#deleteEntryByHash"],"sources":["../../../../../replicache/src/dag/lazy-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport {joinIterables} from '../../../shared/src/iterables.ts';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {getSizeOfValue} from '../../../shared/src/size-of-value.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport type {Hash} from '../hash.ts';\nimport type {Chunk} from './chunk.ts';\nimport {type ChunkHasher, type Refs, createChunk} from './chunk.ts';\nimport {\n type HeadChange,\n type RefCountUpdatesDelegate,\n computeRefCountUpdates,\n} from './gc.ts';\nimport {type Read, type Store, type Write, mustGetChunk} from './store.ts';\n\n/**\n * Dag Store which lazily loads values from a source store and then caches\n * them in an LRU cache. The memory cache for chunks from the source store\n * size is limited to `sourceCacheSizeLimit` bytes, and values are evicted in an\n * LRU fashion. The purpose of this store is to avoid holding the entire client\n * view (i.e. the source store's content) in each tab's JavaScript heap.\n *\n * This store's heads are independent from the heads of source store, and are\n * only stored in memory.\n *\n * Chunks which are created via this store's {@link Write} transaction's\n * {@link createChunk} method are assumed to not be persisted to the source\n * store and thus are cached separately from the source store chunks. These\n * memory-only chunks will not be evicted, and their sizes are not counted\n * towards the source chunk cache size. A memory-only chunk will be deleted if\n * it is no longer reachable from one of this store's heads.\n *\n * Writes only manipulate the in memory state of this store and do not alter the\n * source store. Thus values must be written to the source store through a\n * separate process (see {@link persist}).\n *\n * Intended use:\n * 1. source store is the 'perdag', a slower persistent store (i.e.\n * dag.StoreImpl using a kv.IDBStore)\n * 2. this store's 'main' head is initialized to the hash of a chunk containing\n * a commit in the source store\n * 3. reads lazily read chunks from the source store and cache them\n * 3. writes are initially made to this store with memory-only chunks\n * 4. writes are asynchronously persisted to the source store through a separate\n * process (see {@link persist}}. This process gathers memory-only chunks\n * from this store and then writes them to the source store. It then informs\n * this store that these chunks are no longer memory-only by calling\n * {@link chunksPersisted}, which move these chunks\n * to this store's LRU cache of source chunks (making them eligible for\n * eviction).\n *\n * @param sourceStore Store to lazy load and cache values from.\n * @param sourceCacheSizeLimit Size limit in bytes for cache of chunks loaded\n * from `sourceStore`. This size of a value is determined using\n * `getSizeOfValue`. Keys do not count towards cache size. Memory-only chunks\n * do not count towards cache size.\n * @param getSizeOfValue Function for measuring the size in bytes of a value.\n */\nexport class LazyStore implements Store {\n /**\n * This lock is used to ensure correct isolation of Reads and Writes.\n * Multiple Reads are allowed in parallel but only a single Write. Reads and\n * Writes see an isolated view of the store (corresponding to the Serializable\n * level of transaction isolation defined in the SQL standard).\n *\n * To ensure these semantics the read lock must be acquired when a Read is\n * created and held til it is closed, and a Write lock must be acquired when a\n * Write is created and held til it is committed or closed.\n *\n * Code must have a read or write lock to\n * - read `_heads`\n * - read `_memOnlyChunks`\n * - read `_sourceStore`\n * - read and write `_sourceChunksCache`\n * - read and write `_refCounts`\n * - read and write `_refs`\n * and must have a write lock to\n * - write `_heads`\n * - write `_memOnlyChunks`\n */\n readonly #rwLock = new RWLock();\n readonly #heads = new Map<string, Hash>();\n readonly #sourceStore: Store;\n readonly #chunkHasher: ChunkHasher;\n readonly #assertValidHash: (hash: Hash) => void;\n\n /** The following are protected so testing subclass can access. */\n protected readonly _memOnlyChunks = new Map<Hash, Chunk>();\n protected readonly _sourceChunksCache: ChunksCache;\n /**\n * Ref counts are maintained so that chunks which are unreachable\n * from this stores heads can be eagerly and deterministically deleted from\n * `this._memOnlyChunks` and `this._sourceChunksCache`.\n *\n * These ref counts are independent from `this._sourceStore`'s ref counts.\n * These ref counts are based on reachability from `this._heads`.\n * A chunk is deleted from `this._memOnlyChunks` or\n * `this._sourceChunksCache` (which ever it is in) when its ref count becomes\n * zero.\n * These ref counts count the refs in `this._heads` and `this._refs`.\n *\n * Not all reachable chunk's refs are included in `this._refs`, because this\n * would require loading all chunks reachable in the source store in a\n * non-lazy manner. `this._refs` contains the refs of all currently reachable\n * chunks that were ever in `this._memOnlyChunks` or\n * `this._sourceChunksCache` (even if they have been evicted). A\n * chunk's ref information is lazily discovered and stored in `this._refs` and\n * counted in `this._refCounts`. A chunk's entries in `this._refs` and\n * `this._refCounts` are only deleted when a chunk is deleted due to it\n * becoming unreachable (it is not deleted if the chunk is evicted from the\n * source-store cache).\n *\n * The major implication of this lazy discovery of source store refs, is that\n * a reachable source store chunk may not be cached when loaded, because it is\n * not known to be reachable because some of the pertinent refs have not been\n * discovered. However, in practice chunks are read by traversing the graph\n * starting from a head, and all pertinent refs are discovered as part of the\n * traversal.\n *\n * These ref counts can be changed in two ways:\n * 1. A LazyRead has a cache miss and loads a chunk from the source store that\n * is reachable from this._heads. If this chunk's refs are not currently\n * counted, it will not have an entry in `this._refs`. In this case, the\n * chunks refs will be put in `this._refs` and `this._refCounts` will be\n * updated to count them.\n * 2. A LazyWrite commit updates a head (which can result in increasing or\n * decreasing ref count) or puts a reachable chunk (either a `memory-only` or\n * `source` chunk) that references this hash (increasing ref count). The\n * computation of these ref count changes is delegated to the\n * `computeRefCountUpdates` shared with dag.StoreImpl. In order to\n * delegate determining reachability to `computeRefCountUpdates` and defer\n * this determination until commit time, LazyWrite treats cache misses\n * as a 'put' of the lazily-loaded chunk.\n *\n * A chunk's hash may have an entry in `this._refCounts` without that\n * chunk have ever been in `this._memOnlyChunks` or `this._sourceChunksCache`.\n * This is the case when a head or a reachable chunk that was ever in\n * `this._memOnlyChunks` or `this._sourceChunksCache` references a chunk\n * which is not currently cached (either because it has not been read, or\n * because it has been evicted).\n */\n protected readonly _refCounts = new Map<Hash, number>();\n protected readonly _refs = new Map<Hash, readonly Hash[]>();\n\n constructor(\n sourceStore: Store,\n sourceCacheSizeLimit: number,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n getSizeOfChunk: (chunk: Chunk) => number = getSizeOfValue,\n ) {\n this._sourceChunksCache = new ChunksCache(\n sourceCacheSizeLimit,\n getSizeOfChunk,\n this._refCounts,\n this._refs,\n );\n this.#sourceStore = sourceStore;\n this.#chunkHasher = chunkHasher;\n this.#assertValidHash = assertValidHash;\n }\n\n async read(sourceRead?: Read): Promise<LazyRead> {\n const release = await this.#rwLock.read();\n return new LazyRead(\n this.#heads,\n this._memOnlyChunks,\n this._sourceChunksCache,\n this.#sourceStore,\n release,\n this.#assertValidHash,\n sourceRead,\n );\n }\n\n async write(): Promise<LazyWrite> {\n const release = await this.#rwLock.write();\n return new LazyWrite(\n this.#heads,\n this._memOnlyChunks,\n this._sourceChunksCache,\n this.#sourceStore,\n this._refCounts,\n this._refs,\n release,\n this.#chunkHasher,\n this.#assertValidHash,\n );\n }\n\n close(): Promise<void> {\n return promiseVoid;\n }\n\n /**\n * Does not acquire any lock on the store.\n */\n isCached(chunkHash: Hash): boolean {\n return (\n this._sourceChunksCache.getWithoutUpdatingLRU(chunkHash) !== undefined\n );\n }\n\n withSuspendedSourceCacheEvictsAndDeletes<T>(\n fn: () => MaybePromise<T>,\n ): Promise<T> {\n return this._sourceChunksCache.withSuspendedEvictsAndDeletes(fn);\n }\n}\n\nexport class LazyRead implements Read {\n protected readonly _heads: Map<string, Hash>;\n protected readonly _memOnlyChunks: Map<Hash, Chunk>;\n protected readonly _sourceChunksCache: ChunksCache;\n protected readonly _sourceStore: Store;\n #sourceRead: Promise<Read> | undefined = undefined;\n readonly #release: () => void;\n #closed = false;\n readonly assertValidHash: (hash: Hash) => void;\n readonly #sourceReadOwnedByCaller: boolean;\n\n constructor(\n heads: Map<string, Hash>,\n memOnlyChunks: Map<Hash, Chunk>,\n sourceChunksCache: ChunksCache,\n sourceStore: Store,\n release: () => void,\n assertValidHash: (hash: Hash) => void,\n // If the lazyRead is being run in the context of an existing\n // read, or write, from the perdag then we must use _that_\n // transaction's read. Trying to open our own `sourceRead` will\n // cause the outer transaction to auto-commit.\n sourceRead?: Read,\n ) {\n this._heads = heads;\n this._memOnlyChunks = memOnlyChunks;\n this._sourceChunksCache = sourceChunksCache;\n this._sourceStore = sourceStore;\n this.#release = release;\n this.assertValidHash = assertValidHash;\n this.#sourceRead =\n sourceRead !== undefined ? Promise.resolve(sourceRead) : undefined;\n this.#sourceReadOwnedByCaller = sourceRead !== undefined;\n }\n\n isMemOnlyChunkHash(hash: Hash): boolean {\n return this._memOnlyChunks.has(hash);\n }\n\n async hasChunk(hash: Hash): Promise<boolean> {\n return (await this.getChunk(hash)) !== undefined;\n }\n\n async getChunk(hash: Hash): Promise<Chunk | undefined> {\n const memOnlyChunk = this._memOnlyChunks.get(hash);\n if (memOnlyChunk !== undefined) {\n return memOnlyChunk;\n }\n let chunk = this._sourceChunksCache.get(hash);\n if (chunk === undefined) {\n chunk = await (await this._getSourceRead()).getChunk(hash);\n if (chunk !== undefined) {\n this._sourceChunksCache.put(chunk);\n }\n }\n return chunk;\n }\n\n mustGetChunk(hash: Hash): Promise<Chunk> {\n return mustGetChunk(this, hash);\n }\n\n getHead(name: string): Promise<Hash | undefined> {\n return Promise.resolve(this._heads.get(name));\n }\n\n release(): void {\n if (!this.#closed) {\n this.#release();\n if (!this.#sourceReadOwnedByCaller) {\n this.#sourceRead\n ?.then(read => read.release())\n // If creation of the read failed there is nothing to release.\n // Catch to avoid `Uncaught (in promise)` errors being reported.\n .catch(_ => {});\n }\n this.#closed = true;\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n protected _getSourceRead(): Promise<Read> {\n if (!this.#sourceRead) {\n this.#sourceRead = this._sourceStore.read();\n }\n return this.#sourceRead;\n }\n}\n\nexport class LazyWrite\n extends LazyRead\n implements Write, RefCountUpdatesDelegate\n{\n readonly #refCounts: Map<Hash, number>;\n readonly #refs: Map<Hash, readonly Hash[]>;\n readonly #chunkHasher: ChunkHasher;\n protected readonly _pendingHeadChanges = new Map<string, HeadChange>();\n protected readonly _pendingMemOnlyChunks = new Map<Hash, Chunk>();\n protected readonly _pendingCachedChunks = new Map<\n Hash,\n {chunk: Chunk; size: number}\n >();\n readonly #createdChunks = new Set<Hash>();\n\n constructor(\n heads: Map<string, Hash>,\n memOnlyChunks: Map<Hash, Chunk>,\n sourceChunksCache: ChunksCache,\n sourceStore: Store,\n refCounts: Map<Hash, number>,\n refs: Map<Hash, readonly Hash[]>,\n release: () => void,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n ) {\n super(\n heads,\n memOnlyChunks,\n sourceChunksCache,\n sourceStore,\n release,\n assertValidHash,\n );\n this.#refCounts = refCounts;\n this.#refs = refs;\n this.#chunkHasher = chunkHasher;\n }\n\n createChunk = <V>(data: V, refs: Refs): Chunk<V> => {\n const chunk = createChunk(data, refs, this.#chunkHasher);\n this.#createdChunks.add(chunk.hash);\n return chunk;\n };\n\n putChunk<V>(c: Chunk<V>, size?: number): Promise<void> {\n const {hash, meta} = c;\n this.assertValidHash(hash);\n if (meta.length > 0) {\n for (const h of meta) {\n this.assertValidHash(h);\n }\n }\n if (this.#createdChunks.has(hash) || this.isMemOnlyChunkHash(hash)) {\n this._pendingMemOnlyChunks.set(hash, c);\n } else {\n this._pendingCachedChunks.set(hash, {chunk: c, size: size ?? -1});\n }\n return promiseVoid;\n }\n\n async setHead(name: string, hash: Hash): Promise<void> {\n await this.#setHead(name, hash);\n }\n\n async removeHead(name: string): Promise<void> {\n await this.#setHead(name, undefined);\n }\n\n async #setHead(name: string, hash: Hash | undefined): Promise<void> {\n const oldHash = await this.getHead(name);\n const v = this._pendingHeadChanges.get(name);\n if (v === undefined) {\n this._pendingHeadChanges.set(name, {new: hash, old: oldHash});\n } else {\n // Keep old if existing\n v.new = hash;\n }\n }\n\n override isMemOnlyChunkHash(hash: Hash): boolean {\n return (\n this._pendingMemOnlyChunks.has(hash) || super.isMemOnlyChunkHash(hash)\n );\n }\n\n override async getChunk(hash: Hash): Promise<Chunk | undefined> {\n const pendingMemOnlyChunk = this._pendingMemOnlyChunks.get(hash);\n if (pendingMemOnlyChunk !== undefined) {\n return pendingMemOnlyChunk;\n }\n const memOnlyChunk = this._memOnlyChunks.get(hash);\n if (memOnlyChunk !== undefined) {\n return memOnlyChunk;\n }\n // In order to delegate determining reachability to `computeRefCountUpdates`\n // and defer this determination until commit time, treat cache misses\n // as a 'put' of the lazily-loaded chunk.\n const pendingCachedChunk = this._pendingCachedChunks.get(hash);\n if (pendingCachedChunk !== undefined) {\n return pendingCachedChunk.chunk;\n }\n let chunk = this._sourceChunksCache.get(hash);\n if (chunk === undefined) {\n chunk = await (await this._getSourceRead()).getChunk(hash);\n if (chunk !== undefined) {\n this._pendingCachedChunks.set(chunk.hash, {chunk, size: -1});\n }\n }\n return chunk;\n }\n\n override getHead(name: string): Promise<Hash | undefined> {\n const headChange = this._pendingHeadChanges.get(name);\n if (headChange) {\n return Promise.resolve(headChange.new);\n }\n return super.getHead(name);\n }\n\n async commit(): Promise<void> {\n const pendingChunks = new Set(\n joinIterables(\n this._pendingMemOnlyChunks.keys(),\n this._pendingCachedChunks.keys(),\n ),\n );\n const refCountUpdates = await computeRefCountUpdates(\n this._pendingHeadChanges.values(),\n pendingChunks,\n this,\n );\n\n for (const [hash, count] of refCountUpdates) {\n if (this.isMemOnlyChunkHash(hash)) {\n if (count === 0) {\n this.#refCounts.delete(hash);\n this._memOnlyChunks.delete(hash);\n this.#refs.delete(hash);\n } else {\n this.#refCounts.set(hash, count);\n const chunk = this._pendingMemOnlyChunks.get(hash);\n if (chunk) {\n this.#refs.set(hash, chunk.meta);\n this._memOnlyChunks.set(hash, chunk);\n }\n }\n refCountUpdates.delete(hash);\n }\n }\n\n this._sourceChunksCache.updateForCommit(\n this._pendingCachedChunks,\n refCountUpdates,\n );\n\n for (const [name, headChange] of this._pendingHeadChanges) {\n if (headChange.new) {\n this._heads.set(name, headChange.new);\n } else {\n this._heads.delete(name);\n }\n }\n\n this._pendingMemOnlyChunks.clear();\n this._pendingCachedChunks.clear();\n this._pendingHeadChanges.clear();\n this.release();\n }\n\n getRefCount(hash: Hash): number | undefined {\n return this.#refCounts.get(hash);\n }\n\n getRefs(hash: Hash): readonly Hash[] | undefined {\n const pendingMemOnlyChunk = this._pendingMemOnlyChunks.get(hash);\n if (pendingMemOnlyChunk) {\n return pendingMemOnlyChunk.meta;\n }\n const memOnlyChunk = this._memOnlyChunks.get(hash);\n if (memOnlyChunk) {\n return memOnlyChunk.meta;\n }\n const pendingCachedChunk = this._pendingCachedChunks.get(hash);\n if (pendingCachedChunk !== undefined) {\n return pendingCachedChunk.chunk.meta;\n }\n return this.#refs.get(hash);\n }\n\n areRefsCounted(hash: Hash): boolean {\n return this.#refs.has(hash);\n }\n\n chunksPersisted(chunkHashes: readonly Hash[]): void {\n const chunksToCache = [];\n for (const chunkHash of chunkHashes) {\n const chunk = this._memOnlyChunks.get(chunkHash);\n if (chunk) {\n this._memOnlyChunks.delete(chunkHash);\n chunksToCache.push(chunk);\n }\n }\n this._sourceChunksCache.persisted(chunksToCache);\n }\n}\n\ntype CacheEntry = {\n chunk: Chunk;\n size: number;\n};\n\nclass ChunksCache {\n readonly #cacheSizeLimit: number;\n readonly #getSizeOfChunk: (chunk: Chunk) => number;\n readonly #refCounts: Map<Hash, number>;\n readonly #refs: Map<Hash, readonly Hash[]>;\n #size = 0;\n #evictsAndDeletesSuspended = false;\n readonly #suspendedDeletes: Hash[] = [];\n\n /**\n * Iteration order is from least to most recently used.\n *\n * Public so that testing subclass can access.\n */\n readonly cacheEntries = new Map<Hash, CacheEntry>();\n\n constructor(\n cacheSizeLimit: number,\n getSizeOfChunk: (v: Chunk) => number,\n refCounts: Map<Hash, number>,\n refs: Map<Hash, readonly Hash[]>,\n ) {\n this.#cacheSizeLimit = cacheSizeLimit;\n this.#getSizeOfChunk = getSizeOfChunk;\n this.#refCounts = refCounts;\n this.#refs = refs;\n }\n\n get(hash: Hash): Chunk | undefined {\n const cacheEntry = this.cacheEntries.get(hash);\n if (cacheEntry) {\n // Update order in map for LRU tracking.\n this.cacheEntries.delete(hash);\n this.cacheEntries.set(hash, cacheEntry);\n }\n return cacheEntry?.chunk;\n }\n\n getWithoutUpdatingLRU(hash: Hash): Chunk | undefined {\n return this.cacheEntries.get(hash)?.chunk;\n }\n\n put(chunk: Chunk): void {\n const {hash} = chunk;\n // If there is an existing cache entry then the cached value must be\n // equivalent. Update order in map for LRU tracking and early return.\n const oldCacheEntry = this.cacheEntries.get(hash);\n if (oldCacheEntry) {\n this.cacheEntries.delete(hash);\n this.cacheEntries.set(hash, oldCacheEntry);\n return;\n }\n\n // Only cache if there is a ref from a head to this chunk\n const refCount = this.#refCounts.get(hash);\n if (refCount === undefined || refCount < 1) {\n return;\n }\n if (!this.#cacheChunk(chunk)) {\n return;\n }\n if (!this.#refs.has(hash)) {\n for (const refHash of chunk.meta) {\n this.#refCounts.set(refHash, (this.#refCounts.get(refHash) || 0) + 1);\n }\n this.#refs.set(hash, chunk.meta);\n }\n\n this.#ensureCacheSizeLimit();\n }\n\n #ensureCacheSizeLimit() {\n if (this.#evictsAndDeletesSuspended) {\n return;\n }\n for (const entry of this.cacheEntries.values()) {\n if (this.#size <= this.#cacheSizeLimit) {\n break;\n }\n this.#evict(entry);\n }\n }\n\n #cacheChunk(chunk: Chunk, size?: number): boolean {\n const chunkSize = size ?? this.#getSizeOfChunk(chunk);\n if (chunkSize > this.#cacheSizeLimit) {\n // This value cannot be cached due to its size exceeding the\n // cache size limit, don't evict other entries to try to make\n // room for it.\n return false;\n }\n this.#size += chunkSize;\n this.cacheEntries.set(chunk.hash, {chunk, size: chunkSize});\n return true;\n }\n\n #evict(cacheEntry: CacheEntry): void {\n const {hash} = cacheEntry.chunk;\n this.#size -= cacheEntry.size;\n this.cacheEntries.delete(hash);\n }\n\n #deleteEntryByHash(hash: Hash): void {\n this.#refCounts.delete(hash);\n this.#refs.delete(hash);\n const cacheEntry = this.cacheEntries.get(hash);\n if (cacheEntry) {\n this.#size -= cacheEntry.size;\n this.cacheEntries.delete(hash);\n }\n }\n\n updateForCommit(\n chunksToPut: Map<Hash, {chunk: Chunk; size: number}>,\n refCountUpdates: Map<Hash, number>,\n ): void {\n for (const [hash, count] of refCountUpdates) {\n if (count === 0) {\n if (!this.#evictsAndDeletesSuspended) {\n this.#deleteEntryByHash(hash);\n } else {\n this.#refCounts.set(hash, 0);\n this.#suspendedDeletes.push(hash);\n }\n } else {\n this.#refCounts.set(hash, count);\n const chunkAndSize = chunksToPut.get(hash);\n if (chunkAndSize) {\n const {chunk, size} = chunkAndSize;\n const oldCacheEntry = this.cacheEntries.get(hash);\n if (oldCacheEntry) {\n // If there is an existing cache entry then the cached value must be\n // equivalent. Update order in map for LRU tracking but avoid\n // recomputing size and creating a new cache entry.\n this.cacheEntries.delete(hash);\n this.cacheEntries.set(hash, oldCacheEntry);\n } else {\n this.#cacheChunk(chunk, size !== -1 ? size : undefined);\n this.#refs.set(hash, chunk.meta);\n }\n }\n }\n }\n this.#ensureCacheSizeLimit();\n }\n\n persisted(chunks: Iterable<Chunk>) {\n for (const chunk of chunks) {\n this.#cacheChunk(chunk);\n }\n this.#ensureCacheSizeLimit();\n }\n\n async withSuspendedEvictsAndDeletes<T>(\n fn: () => MaybePromise<T>,\n ): Promise<T> {\n this.#evictsAndDeletesSuspended = true;\n try {\n return await fn();\n } finally {\n this.#evictsAndDeletesSuspended = false;\n for (const hash of this.#suspendedDeletes) {\n if (this.#refCounts.get(hash) === 0) {\n this.#deleteEntryByHash(hash);\n }\n }\n this.#ensureCacheSizeLimit();\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"store-impl.js","names":["#kv","#chunkHasher","#assertValidHash","#putChunks","#changedHeads","#setHead","#applyRefCountUpdates","#removeAllRelatedKeys"],"sources":["../../../../../replicache/src/dag/store-impl.ts"],"sourcesContent":["import {assertNumber} from '../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {type Hash, assertHash} from '../hash.ts';\nimport type {\n Read as KVRead,\n Store as KVStore,\n Write as KVWrite,\n} from '../kv/store.ts';\nimport {\n Chunk,\n type ChunkHasher,\n type Refs,\n assertRefs,\n createChunk,\n} from './chunk.ts';\nimport {type RefCountUpdatesDelegate, computeRefCountUpdates} from './gc.ts';\nimport {chunkDataKey, chunkMetaKey, chunkRefCountKey, headKey} from './key.ts';\nimport {type Read, type Store, type Write, mustGetChunk} from './store.ts';\n\nexport class StoreImpl implements Store {\n readonly #kv: KVStore;\n readonly #chunkHasher: ChunkHasher;\n readonly #assertValidHash: (hash: Hash) => void;\n\n constructor(\n kv: KVStore,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n ) {\n this.#kv = kv;\n this.#chunkHasher = chunkHasher;\n this.#assertValidHash = assertValidHash;\n }\n\n async read(): Promise<Read> {\n return new ReadImpl(await this.#kv.read(), this.#assertValidHash);\n }\n\n async write(): Promise<Write> {\n return new WriteImpl(\n await this.#kv.write(),\n this.#chunkHasher,\n this.#assertValidHash,\n );\n }\n\n close(): Promise<void> {\n return this.#kv.close();\n }\n}\n\nexport class ReadImpl implements Read {\n protected readonly _tx: KVRead;\n readonly assertValidHash: (hash: Hash) => void;\n\n constructor(kv: KVRead, assertValidHash: (hash: Hash) => void) {\n this._tx = kv;\n this.assertValidHash = assertValidHash;\n }\n\n hasChunk(hash: Hash): Promise<boolean> {\n return this._tx.has(chunkDataKey(hash));\n }\n\n async getChunk(hash: Hash): Promise<Chunk | undefined> {\n const dataPromise = this._tx.get(chunkDataKey(hash));\n const refsPromise = this._tx.get(chunkMetaKey(hash));\n const data = await dataPromise;\n if (data === undefined) {\n refsPromise.catch(() => {\n // Ignore error since we will return undefined anyway.\n });\n return undefined;\n }\n\n const refsVal = await refsPromise;\n let refs: Refs;\n if (refsVal !== undefined) {\n assertRefs(refsVal);\n refs = refsVal;\n } else {\n refs = [];\n }\n return new Chunk(hash, data, refs);\n }\n\n mustGetChunk(hash: Hash): Promise<Chunk> {\n return mustGetChunk(this, hash);\n }\n\n async getHead(name: string): Promise<Hash | undefined> {\n const data = await this._tx.get(headKey(name));\n if (data === undefined) {\n return undefined;\n }\n assertHash(data);\n return data;\n }\n\n release(): void {\n this._tx.release();\n }\n\n get closed(): boolean {\n return this._tx.closed;\n }\n}\n\ntype HeadChange = {\n new: Hash | undefined;\n old: Hash | undefined;\n};\n\nexport class WriteImpl\n extends ReadImpl\n implements Write, RefCountUpdatesDelegate\n{\n declare protected readonly _tx: KVWrite;\n readonly #chunkHasher: ChunkHasher;\n\n readonly #putChunks = new Set<Hash>();\n readonly #changedHeads = new Map<string, HeadChange>();\n\n constructor(\n kvw: KVWrite,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n ) {\n super(kvw, assertValidHash);\n this.#chunkHasher = chunkHasher;\n }\n\n createChunk = <V>(data: V, refs: Refs): Chunk<V> =>\n createChunk(data, refs, this.#chunkHasher);\n\n get kvWrite(): KVWrite {\n return this._tx;\n }\n\n async putChunk(c: Chunk): Promise<void> {\n const {hash, data, meta} = c;\n // We never want to write temp hashes to the underlying store.\n this.assertValidHash(hash);\n const key = chunkDataKey(hash);\n // Commit contains InternalValue and Hash which are opaque types.\n const p1 = this._tx.put(key, data as ReadonlyJSONValue);\n let p2;\n if (meta.length > 0) {\n for (const h of meta) {\n this.assertValidHash(h);\n }\n p2 = this._tx.put(chunkMetaKey(hash), meta);\n }\n this.#putChunks.add(hash);\n await p1;\n await p2;\n }\n\n setHead(name: string, hash: Hash): Promise<void> {\n return this.#setHead(name, hash);\n }\n\n removeHead(name: string): Promise<void> {\n return this.#setHead(name, undefined);\n }\n\n async #setHead(name: string, hash: Hash | undefined): Promise<void> {\n const oldHash = await this.getHead(name);\n const hk = headKey(name);\n\n let p1: Promise<void>;\n if (hash === undefined) {\n p1 = this._tx.del(hk);\n } else {\n p1 = this._tx.put(hk, hash);\n }\n\n const v = this.#changedHeads.get(name);\n if (v === undefined) {\n this.#changedHeads.set(name, {new: hash, old: oldHash});\n } else {\n // Keep old if existing\n v.new = hash;\n }\n\n await p1;\n }\n\n async commit(): Promise<void> {\n const refCountUpdates = await computeRefCountUpdates(\n this.#changedHeads.values(),\n this.#putChunks,\n this,\n );\n await this.#applyRefCountUpdates(refCountUpdates);\n await this._tx.commit();\n }\n\n async getRefCount(hash: Hash): Promise<number | undefined> {\n const value = await this._tx.get(chunkRefCountKey(hash));\n if (value === undefined) {\n return undefined;\n }\n assertNumber(value);\n if (value < 0 || value > 0xffff || value !== (value | 0)) {\n throw new Error(\n `Invalid ref count ${value}. We expect the value to be a Uint16`,\n );\n }\n return value;\n }\n\n async getRefs(hash: Hash): Promise<readonly Hash[]> {\n const meta = await this._tx.get(chunkMetaKey(hash));\n if (meta === undefined) {\n return [];\n }\n assertRefs(meta);\n return meta;\n }\n\n async #applyRefCountUpdates(refCountCache: Map<Hash, number>): Promise<void> {\n const ps: Promise<void>[] = [];\n for (const [hash, count] of refCountCache) {\n if (count === 0) {\n ps.push(this.#removeAllRelatedKeys(hash));\n } else {\n const refCountKey = chunkRefCountKey(hash);\n ps.push(this._tx.put(refCountKey, count));\n }\n }\n await Promise.all(ps);\n }\n\n async #removeAllRelatedKeys(hash: Hash): Promise<void> {\n await Promise.all([\n this._tx.del(chunkDataKey(hash)),\n this._tx.del(chunkMetaKey(hash)),\n this._tx.del(chunkRefCountKey(hash)),\n ]);\n\n this.#putChunks.delete(hash);\n }\n\n release(): void {\n this._tx.release();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"store-impl.js","names":["#kv","#chunkHasher","#assertValidHash","#putChunks","#changedHeads","#setHead","#applyRefCountUpdates","#removeAllRelatedKeys"],"sources":["../../../../../replicache/src/dag/store-impl.ts"],"sourcesContent":["import {assertNumber} from '../../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {type Hash, assertHash} from '../hash.ts';\nimport type {\n Read as KVRead,\n Store as KVStore,\n Write as KVWrite,\n} from '../kv/store.ts';\nimport {\n Chunk,\n type ChunkHasher,\n type Refs,\n assertRefs,\n createChunk,\n} from './chunk.ts';\nimport {type RefCountUpdatesDelegate, computeRefCountUpdates} from './gc.ts';\nimport {chunkDataKey, chunkMetaKey, chunkRefCountKey, headKey} from './key.ts';\nimport {type Read, type Store, type Write, mustGetChunk} from './store.ts';\n\nexport class StoreImpl implements Store {\n readonly #kv: KVStore;\n readonly #chunkHasher: ChunkHasher;\n readonly #assertValidHash: (hash: Hash) => void;\n\n constructor(\n kv: KVStore,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n ) {\n this.#kv = kv;\n this.#chunkHasher = chunkHasher;\n this.#assertValidHash = assertValidHash;\n }\n\n async read(): Promise<Read> {\n return new ReadImpl(await this.#kv.read(), this.#assertValidHash);\n }\n\n async write(): Promise<Write> {\n return new WriteImpl(\n await this.#kv.write(),\n this.#chunkHasher,\n this.#assertValidHash,\n );\n }\n\n close(): Promise<void> {\n return this.#kv.close();\n }\n}\n\nexport class ReadImpl implements Read {\n protected readonly _tx: KVRead;\n readonly assertValidHash: (hash: Hash) => void;\n\n constructor(kv: KVRead, assertValidHash: (hash: Hash) => void) {\n this._tx = kv;\n this.assertValidHash = assertValidHash;\n }\n\n hasChunk(hash: Hash): Promise<boolean> {\n return this._tx.has(chunkDataKey(hash));\n }\n\n async getChunk(hash: Hash): Promise<Chunk | undefined> {\n const dataPromise = this._tx.get(chunkDataKey(hash));\n const refsPromise = this._tx.get(chunkMetaKey(hash));\n const data = await dataPromise;\n if (data === undefined) {\n refsPromise.catch(() => {\n // Ignore error since we will return undefined anyway.\n });\n return undefined;\n }\n\n const refsVal = await refsPromise;\n let refs: Refs;\n if (refsVal !== undefined) {\n assertRefs(refsVal);\n refs = refsVal;\n } else {\n refs = [];\n }\n return new Chunk(hash, data, refs);\n }\n\n mustGetChunk(hash: Hash): Promise<Chunk> {\n return mustGetChunk(this, hash);\n }\n\n async getHead(name: string): Promise<Hash | undefined> {\n const data = await this._tx.get(headKey(name));\n if (data === undefined) {\n return undefined;\n }\n assertHash(data);\n return data;\n }\n\n release(): void {\n this._tx.release();\n }\n\n get closed(): boolean {\n return this._tx.closed;\n }\n}\n\ntype HeadChange = {\n new: Hash | undefined;\n old: Hash | undefined;\n};\n\nexport class WriteImpl\n extends ReadImpl\n implements Write, RefCountUpdatesDelegate\n{\n declare protected readonly _tx: KVWrite;\n readonly #chunkHasher: ChunkHasher;\n\n readonly #putChunks = new Set<Hash>();\n readonly #changedHeads = new Map<string, HeadChange>();\n\n constructor(\n kvw: KVWrite,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n ) {\n super(kvw, assertValidHash);\n this.#chunkHasher = chunkHasher;\n }\n\n createChunk = <V>(data: V, refs: Refs): Chunk<V> =>\n createChunk(data, refs, this.#chunkHasher);\n\n get kvWrite(): KVWrite {\n return this._tx;\n }\n\n async putChunk(c: Chunk): Promise<void> {\n const {hash, data, meta} = c;\n // We never want to write temp hashes to the underlying store.\n this.assertValidHash(hash);\n const key = chunkDataKey(hash);\n // Commit contains InternalValue and Hash which are opaque types.\n const p1 = this._tx.put(key, data as ReadonlyJSONValue);\n let p2;\n if (meta.length > 0) {\n for (const h of meta) {\n this.assertValidHash(h);\n }\n p2 = this._tx.put(chunkMetaKey(hash), meta);\n }\n this.#putChunks.add(hash);\n await p1;\n await p2;\n }\n\n setHead(name: string, hash: Hash): Promise<void> {\n return this.#setHead(name, hash);\n }\n\n removeHead(name: string): Promise<void> {\n return this.#setHead(name, undefined);\n }\n\n async #setHead(name: string, hash: Hash | undefined): Promise<void> {\n const oldHash = await this.getHead(name);\n const hk = headKey(name);\n\n let p1: Promise<void>;\n if (hash === undefined) {\n p1 = this._tx.del(hk);\n } else {\n p1 = this._tx.put(hk, hash);\n }\n\n const v = this.#changedHeads.get(name);\n if (v === undefined) {\n this.#changedHeads.set(name, {new: hash, old: oldHash});\n } else {\n // Keep old if existing\n v.new = hash;\n }\n\n await p1;\n }\n\n async commit(): Promise<void> {\n const refCountUpdates = await computeRefCountUpdates(\n this.#changedHeads.values(),\n this.#putChunks,\n this,\n );\n await this.#applyRefCountUpdates(refCountUpdates);\n await this._tx.commit();\n }\n\n async getRefCount(hash: Hash): Promise<number | undefined> {\n const value = await this._tx.get(chunkRefCountKey(hash));\n if (value === undefined) {\n return undefined;\n }\n assertNumber(value);\n if (value < 0 || value > 0xffff || value !== (value | 0)) {\n throw new Error(\n `Invalid ref count ${value}. We expect the value to be a Uint16`,\n );\n }\n return value;\n }\n\n async getRefs(hash: Hash): Promise<readonly Hash[]> {\n const meta = await this._tx.get(chunkMetaKey(hash));\n if (meta === undefined) {\n return [];\n }\n assertRefs(meta);\n return meta;\n }\n\n async #applyRefCountUpdates(refCountCache: Map<Hash, number>): Promise<void> {\n const ps: Promise<void>[] = [];\n for (const [hash, count] of refCountCache) {\n if (count === 0) {\n ps.push(this.#removeAllRelatedKeys(hash));\n } else {\n const refCountKey = chunkRefCountKey(hash);\n ps.push(this._tx.put(refCountKey, count));\n }\n }\n await Promise.all(ps);\n }\n\n async #removeAllRelatedKeys(hash: Hash): Promise<void> {\n await Promise.all([\n this._tx.del(chunkDataKey(hash)),\n this._tx.del(chunkMetaKey(hash)),\n this._tx.del(chunkRefCountKey(hash)),\n ]);\n\n this.#putChunks.delete(hash);\n }\n\n release(): void {\n this._tx.release();\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","names":[],"sources":["../../../../../replicache/src/dag/store.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Hash} from '../hash.ts';\nimport type {Release} from '../with-transactions.ts';\nimport type {Chunk, Refs} from './chunk.ts';\n\nexport interface Store {\n read(): Promise<Read>;\n write(): Promise<Write>;\n close(): Promise<void>;\n}\n\ninterface GetChunk {\n getChunk(hash: Hash): Promise<Chunk | undefined>;\n}\n\nexport interface MustGetChunk {\n mustGetChunk(hash: Hash): Promise<Chunk>;\n}\n\nexport interface Read extends GetChunk, MustGetChunk, Release {\n hasChunk(hash: Hash): Promise<boolean>;\n getHead(name: string): Promise<Hash | undefined>;\n get closed(): boolean;\n}\n\nexport interface Write extends Read {\n createChunk<V>(data: V, refs: Refs): Chunk<V>;\n putChunk<V>(c: Chunk<V>): Promise<void>;\n setHead(name: string, hash: Hash): Promise<void>;\n removeHead(name: string): Promise<void>;\n assertValidHash(hash: Hash): void;\n commit(): Promise<void>;\n}\n\nexport class ChunkNotFoundError extends Error {\n name = 'ChunkNotFoundError';\n readonly hash: Hash;\n constructor(hash: Hash) {\n super(`Chunk not found ${hash}`);\n this.hash = hash;\n }\n}\n\nexport async function mustGetChunk(\n store: GetChunk,\n hash: Hash,\n): Promise<Chunk> {\n const chunk = await store.getChunk(hash);\n if (chunk) {\n return chunk;\n }\n throw new ChunkNotFoundError(hash);\n}\n\nexport async function mustGetHeadHash(\n name: string,\n store: Read,\n): Promise<Hash> {\n const hash = await store.getHead(name);\n assert(hash, `Missing head ${name}`);\n return hash;\n}\n"],"mappings":";;AAkCA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,OAAO;CACP;CACA,YAAY,MAAY;EACtB,MAAM,mBAAmB,MAAM;EAC/B,KAAK,OAAO;CACd;AACF;AAEA,eAAsB,aACpB,OACA,MACgB;CAChB,MAAM,QAAQ,MAAM,MAAM,SAAS,IAAI;CACvC,IAAI,OACF,OAAO;CAET,MAAM,IAAI,mBAAmB,IAAI;AACnC;AAEA,eAAsB,gBACpB,MACA,OACe;CACf,MAAM,OAAO,MAAM,MAAM,QAAQ,IAAI;CACrC,OAAO,MAAM,gBAAgB,MAAM;CACnC,OAAO;AACT"}
1
+ {"version":3,"file":"store.js","names":[],"sources":["../../../../../replicache/src/dag/store.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Hash} from '../hash.ts';\nimport type {Release} from '../with-transactions.ts';\nimport type {Chunk, Refs} from './chunk.ts';\n\nexport interface Store {\n read(): Promise<Read>;\n write(): Promise<Write>;\n close(): Promise<void>;\n}\n\ninterface GetChunk {\n getChunk(hash: Hash): Promise<Chunk | undefined>;\n}\n\nexport interface MustGetChunk {\n mustGetChunk(hash: Hash): Promise<Chunk>;\n}\n\nexport interface Read extends GetChunk, MustGetChunk, Release {\n hasChunk(hash: Hash): Promise<boolean>;\n getHead(name: string): Promise<Hash | undefined>;\n get closed(): boolean;\n}\n\nexport interface Write extends Read {\n createChunk<V>(data: V, refs: Refs): Chunk<V>;\n putChunk<V>(c: Chunk<V>): Promise<void>;\n setHead(name: string, hash: Hash): Promise<void>;\n removeHead(name: string): Promise<void>;\n assertValidHash(hash: Hash): void;\n commit(): Promise<void>;\n}\n\nexport class ChunkNotFoundError extends Error {\n name = 'ChunkNotFoundError';\n readonly hash: Hash;\n constructor(hash: Hash) {\n super(`Chunk not found ${hash}`);\n this.hash = hash;\n }\n}\n\nexport async function mustGetChunk(\n store: GetChunk,\n hash: Hash,\n): Promise<Chunk> {\n const chunk = await store.getChunk(hash);\n if (chunk) {\n return chunk;\n }\n throw new ChunkNotFoundError(hash);\n}\n\nexport async function mustGetHeadHash(\n name: string,\n store: Read,\n): Promise<Hash> {\n const hash = await store.getHead(name);\n assert(hash, `Missing head ${name}`);\n return hash;\n}\n"],"mappings":";;AAkCA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,OAAO;CACP;CACA,YAAY,MAAY;AACtB,QAAM,mBAAmB,OAAO;AAChC,OAAK,OAAO;;;AAIhB,eAAsB,aACpB,OACA,MACgB;CAChB,MAAM,QAAQ,MAAM,MAAM,SAAS,KAAK;AACxC,KAAI,MACF,QAAO;AAET,OAAM,IAAI,mBAAmB,KAAK;;AAGpC,eAAsB,gBACpB,MACA,OACe;CACf,MAAM,OAAO,MAAM,MAAM,QAAQ,KAAK;AACtC,QAAO,MAAM,gBAAgB,OAAO;AACpC,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"visitor.js","names":["#dagRead","#seen"],"sources":["../../../../../replicache/src/dag/visitor.ts"],"sourcesContent":["import type {Hash} from '../hash.ts';\nimport type {Chunk} from './chunk.ts';\nimport type {MustGetChunk} from './store.ts';\n\n/**\n * A visitor walks the DAG starting at a given root and visits each chunk.\n */\nexport class Visitor {\n #seen: Set<Hash> = new Set();\n #dagRead: MustGetChunk;\n\n constructor(dagRead: MustGetChunk) {\n this.#dagRead = dagRead;\n }\n\n async visit(h: Hash) {\n if (this.#seen.has(h)) {\n return;\n }\n this.#seen.add(h);\n const chunk = await this.#dagRead.mustGetChunk(h);\n await this.visitChunk(chunk);\n }\n\n async visitChunk(chunk: Chunk<unknown>) {\n await Promise.all(chunk.meta.map(ref => this.visit(ref)));\n }\n}\n"],"mappings":";;;;AAOA,IAAa,UAAb,MAAqB;CACnB,wBAAmB,IAAI,IAAI;CAC3B;CAEA,YAAY,SAAuB;EACjC,KAAKA,WAAW;CAClB;CAEA,MAAM,MAAM,GAAS;EACnB,IAAI,KAAKC,MAAM,IAAI,CAAC,GAClB;EAEF,KAAKA,MAAM,IAAI,CAAC;EAChB,MAAM,QAAQ,MAAM,KAAKD,SAAS,aAAa,CAAC;EAChD,MAAM,KAAK,WAAW,KAAK;CAC7B;CAEA,MAAM,WAAW,OAAuB;EACtC,MAAM,QAAQ,IAAI,MAAM,KAAK,KAAI,QAAO,KAAK,MAAM,GAAG,CAAC,CAAC;CAC1D;AACF"}
1
+ {"version":3,"file":"visitor.js","names":["#dagRead","#seen"],"sources":["../../../../../replicache/src/dag/visitor.ts"],"sourcesContent":["import type {Hash} from '../hash.ts';\nimport type {Chunk} from './chunk.ts';\nimport type {MustGetChunk} from './store.ts';\n\n/**\n * A visitor walks the DAG starting at a given root and visits each chunk.\n */\nexport class Visitor {\n #seen: Set<Hash> = new Set();\n #dagRead: MustGetChunk;\n\n constructor(dagRead: MustGetChunk) {\n this.#dagRead = dagRead;\n }\n\n async visit(h: Hash) {\n if (this.#seen.has(h)) {\n return;\n }\n this.#seen.add(h);\n const chunk = await this.#dagRead.mustGetChunk(h);\n await this.visitChunk(chunk);\n }\n\n async visitChunk(chunk: Chunk<unknown>) {\n await Promise.all(chunk.meta.map(ref => this.visit(ref)));\n }\n}\n"],"mappings":";;;;AAOA,IAAa,UAAb,MAAqB;CACnB,wBAAmB,IAAI,KAAK;CAC5B;CAEA,YAAY,SAAuB;AACjC,QAAA,UAAgB;;CAGlB,MAAM,MAAM,GAAS;AACnB,MAAI,MAAA,KAAW,IAAI,EAAE,CACnB;AAEF,QAAA,KAAW,IAAI,EAAE;EACjB,MAAM,QAAQ,MAAM,MAAA,QAAc,aAAa,EAAE;AACjD,QAAM,KAAK,WAAW,MAAM;;CAG9B,MAAM,WAAW,OAAuB;AACtC,QAAM,QAAQ,IAAI,MAAM,KAAK,KAAI,QAAO,KAAK,MAAM,IAAI,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"commit.js","names":[],"sources":["../../../../../replicache/src/db/commit.ts"],"sourcesContent":["import {\n assert,\n assertArray,\n assertBoolean,\n assertNumber,\n assertObject,\n assertString,\n unreachable,\n} from '../../../shared/src/asserts.ts';\nimport {assertJSONValue} from '../../../shared/src/json.ts';\nimport {skipCommitDataAsserts} from '../config.ts';\nimport {type FrozenCookie, compareCookies} from '../cookies.ts';\nimport {type Chunk, type CreateChunk, type Refs, toRefs} from '../dag/chunk.ts';\nimport {type MustGetChunk, type Read, mustGetHeadHash} from '../dag/store.ts';\nimport {\n type FrozenJSONValue,\n type FrozenTag,\n assertDeepFrozen,\n deepFreeze,\n} from '../frozen-json.ts';\nimport {type Hash, assertHash} from '../hash.ts';\nimport type {IndexDefinition} from '../index-defs.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport * as MetaType from './meta-type-enum.ts';\n\nexport const DEFAULT_HEAD_NAME = 'main';\n\nexport function commitIsLocalDD31(\n commit: Commit<Meta>,\n): commit is Commit<LocalMetaDD31> {\n return isLocalMetaDD31(commit.meta);\n}\n\nexport function commitIsLocal(\n commit: Commit<Meta>,\n): commit is Commit<LocalMetaDD31> {\n return commitIsLocalDD31(commit);\n}\n\nexport function commitIsSnapshot(\n commit: Commit<Meta>,\n): commit is Commit<SnapshotMetaDD31> {\n return isSnapshotMetaDD31(commit.meta);\n}\n\nexport class Commit<M extends Meta> {\n readonly chunk: Chunk<CommitData<M>>;\n\n constructor(chunk: Chunk<CommitData<M>>) {\n this.chunk = chunk;\n }\n\n get meta(): M {\n return this.chunk.data.meta;\n }\n\n get valueHash(): Hash {\n // Already validated!\n return this.chunk.data.valueHash;\n }\n\n getMutationID(clientID: ClientID, dagRead: MustGetChunk): Promise<number> {\n return getMutationID(clientID, dagRead, this.meta);\n }\n\n async getNextMutationID(\n clientID: ClientID,\n dagRead: MustGetChunk,\n ): Promise<number> {\n return (await this.getMutationID(clientID, dagRead)) + 1;\n }\n\n get indexes(): readonly IndexRecord[] {\n // Already validated!\n return this.chunk.data.indexes;\n }\n}\n\nexport async function getMutationID(\n clientID: ClientID,\n dagRead: MustGetChunk,\n meta: Meta,\n): Promise<number> {\n switch (meta.type) {\n case MetaType.SnapshotDD31:\n return meta.lastMutationIDs[clientID] ?? 0;\n\n case MetaType.LocalDD31: {\n if (meta.clientID === clientID) {\n return meta.mutationID;\n }\n const {basisHash} = meta;\n const basisCommit = await commitFromHash(basisHash, dagRead);\n return getMutationID(clientID, dagRead, basisCommit.meta);\n }\n\n default:\n unreachable(meta);\n }\n}\n\n/**\n * Returns the set of local commits from the given `fromCommitHash` back to but not\n * including its base snapshot. If `fromCommitHash` is a snapshot, the returned vector\n * will be empty. When, as typical, `fromCommitHash` is the head of the default chain\n * then the returned commits are the set of pending commits, ie the set of local commits\n * that have not yet been pushed to the data layer.\n *\n * The vector of commits is returned in reverse chain order, that is, starting\n * with the commit with hash `fromCommitHash` and walking backwards.\n */\nexport async function localMutations(\n fromCommitHash: Hash,\n dagRead: Read,\n): Promise<Commit<LocalMetaDD31>[]> {\n const commits = await commitChain(fromCommitHash, dagRead);\n // Filter does not deal with type narrowing.\n return commits.filter(c => commitIsLocal(c)) as Commit<LocalMetaDD31>[];\n}\n\nexport async function localMutationsDD31(\n fromCommitHash: Hash,\n dagRead: Read,\n): Promise<Commit<LocalMetaDD31>[]> {\n const commits = await commitChain(fromCommitHash, dagRead);\n // Filter does not deal with type narrowing.\n return commits.filter(c => commitIsLocalDD31(c)) as Commit<LocalMetaDD31>[];\n}\n\nexport async function localMutationsGreaterThan(\n commit: Commit<Meta>,\n mutationIDLimits: Record<ClientID, number>,\n dagRead: Read,\n): Promise<Commit<LocalMetaDD31>[]> {\n const commits: Commit<LocalMetaDD31>[] = [];\n const remainingMutationIDLimits = new Map(Object.entries(mutationIDLimits));\n while (!commitIsSnapshot(commit) && remainingMutationIDLimits.size > 0) {\n if (commitIsLocalDD31(commit)) {\n const {meta} = commit;\n const mutationIDLowerLimit = remainingMutationIDLimits.get(meta.clientID);\n if (mutationIDLowerLimit !== undefined) {\n if (meta.mutationID <= mutationIDLowerLimit) {\n remainingMutationIDLimits.delete(meta.clientID);\n } else {\n commits.push(commit as Commit<LocalMetaDD31>);\n }\n }\n }\n const {basisHash} = commit.meta;\n if (basisHash === null) {\n throw new Error(`Commit ${commit.chunk.hash} has no basis`);\n }\n commit = await commitFromHash(basisHash, dagRead);\n }\n return commits;\n}\n\nexport async function baseSnapshotFromHead(\n name: string,\n dagRead: Read,\n): Promise<Commit<SnapshotMetaDD31>> {\n const hash = await dagRead.getHead(name);\n assert(hash, `Missing head ${name}`);\n return baseSnapshotFromHash(hash, dagRead);\n}\n\nexport async function baseSnapshotHashFromHash(\n hash: Hash,\n dagRead: Read,\n): Promise<Hash> {\n return (await baseSnapshotFromHash(hash, dagRead)).chunk.hash;\n}\n\nexport async function baseSnapshotFromHash(\n hash: Hash,\n dagRead: Read,\n): Promise<Commit<SnapshotMetaDD31>> {\n const commit = await commitFromHash(hash, dagRead);\n return baseSnapshotFromCommit(commit, dagRead);\n}\n\nexport async function baseSnapshotFromCommit(\n commit: Commit<Meta>,\n dagRead: Read,\n): Promise<Commit<SnapshotMetaDD31>> {\n while (!commitIsSnapshot(commit)) {\n const {meta} = commit;\n if (isLocalMetaDD31(meta)) {\n commit = await commitFromHash(meta.baseSnapshotHash, dagRead);\n } else {\n const {basisHash} = meta;\n if (basisHash === null) {\n throw new Error(`Commit ${commit.chunk.hash} has no basis`);\n }\n commit = await commitFromHash(basisHash, dagRead);\n }\n }\n return commit;\n}\n\nexport function snapshotMetaParts(\n c: Commit<SnapshotMetaDD31>,\n clientID: ClientID,\n): [lastMutationID: number, cookie: FrozenCookie | FrozenJSONValue] {\n const m = c.meta;\n const lmid = m.lastMutationIDs[clientID] ?? 0;\n return [lmid, m.cookieJSON];\n}\n\nexport function compareCookiesForSnapshots(\n a: Commit<SnapshotMetaDD31>,\n b: Commit<SnapshotMetaDD31>,\n): number {\n return compareCookies(a.meta.cookieJSON, b.meta.cookieJSON);\n}\n\n/**\n * Returns all commits from the commit with fromCommitHash to its base snapshot,\n * inclusive of both. Resulting vector is in chain-head-first order (so snapshot\n * comes last).\n */\nexport async function commitChain(\n fromCommitHash: Hash,\n dagRead: Read,\n): Promise<Commit<Meta>[]> {\n let commit = await commitFromHash(fromCommitHash, dagRead);\n const commits = [];\n while (!commitIsSnapshot(commit)) {\n const {meta} = commit;\n const {basisHash} = meta;\n if (basisHash === null) {\n throw new Error(`Commit ${commit.chunk.hash} has no basis`);\n }\n commits.push(commit);\n commit = await commitFromHash(basisHash, dagRead);\n }\n commits.push(commit);\n return commits;\n}\n\nexport async function commitFromHash(\n hash: Hash,\n dagRead: MustGetChunk,\n): Promise<Commit<Meta>> {\n const chunk = await dagRead.mustGetChunk(hash);\n return fromChunk(chunk);\n}\n\nexport async function commitFromHead(\n name: string,\n dagRead: Read,\n): Promise<Commit<Meta>> {\n const hash = await mustGetHeadHash(name, dagRead);\n return commitFromHash(hash, dagRead);\n}\n\nexport type LocalMetaDD31 = {\n readonly type: MetaType.LocalDD31;\n readonly basisHash: Hash;\n readonly mutationID: number;\n readonly mutatorName: string;\n readonly mutatorArgsJSON: FrozenJSONValue;\n readonly originalHash: Hash | null;\n readonly timestamp: number;\n readonly clientID: ClientID;\n readonly baseSnapshotHash: Hash;\n};\n\nexport type LocalMeta = LocalMetaDD31;\n\nexport function assertLocalMetaDD31(\n v: Record<string, unknown>,\n): asserts v is LocalMetaDD31 {\n // type already asserted\n assertString(v.clientID);\n assertNumber(v.mutationID);\n assertString(v.mutatorName);\n if (!v.mutatorName) {\n throw new Error('Missing mutator name');\n }\n assertJSONValue(v.mutatorArgsJSON);\n if (v.originalHash !== null) {\n assertHash(v.originalHash);\n }\n assertNumber(v.timestamp);\n}\n\nexport function isLocalMetaDD31(meta: Meta): meta is LocalMetaDD31 {\n return meta.type === MetaType.LocalDD31;\n}\n\nexport function assertLocalCommitDD31(\n c: Commit<Meta>,\n): asserts c is Commit<LocalMetaDD31> {\n assertLocalMetaDD31(c.meta);\n}\n\nexport type SnapshotMetaDD31 = {\n readonly type: MetaType.SnapshotDD31;\n readonly basisHash: Hash | null;\n readonly lastMutationIDs: Record<ClientID, number>;\n readonly cookieJSON: FrozenCookie;\n};\n\nexport type SnapshotMeta = SnapshotMetaDD31;\n\nexport function assertSnapshotMetaDD31(\n v: Record<string, unknown>,\n): asserts v is SnapshotMetaDD31 {\n // type already asserted\n if (v.basisHash !== null) {\n assertHash(v.basisHash);\n }\n assertJSONValue(v.cookieJSON);\n assertLastMutationIDs(v.lastMutationIDs);\n}\n\nfunction assertLastMutationIDs(\n v: unknown,\n): asserts v is Record<ClientID, number> {\n assertObject(v);\n for (const e of Object.values(v)) {\n assertNumber(e);\n }\n}\n\nexport type Meta = LocalMetaDD31 | SnapshotMetaDD31;\n\nexport function assertSnapshotCommitDD31(\n c: Commit<Meta>,\n): asserts c is Commit<SnapshotMetaDD31> {\n assertSnapshotMetaDD31(c.meta);\n}\n\nfunction isSnapshotMetaDD31(meta: Meta): meta is SnapshotMetaDD31 {\n return meta.type === MetaType.SnapshotDD31;\n}\n\nfunction assertMeta(v: unknown): asserts v is Meta {\n assertObject(v);\n assertDeepFrozen(v);\n if (v.basisHash !== null) {\n assertString(v.basisHash);\n }\n\n assertNumber(v.type);\n switch (v.type) {\n case MetaType.LocalDD31:\n assertLocalMetaDD31(v);\n break;\n case MetaType.SnapshotDD31:\n assertSnapshotMetaDD31(v);\n break;\n default:\n throw new Error(`Invalid enum value ${v.type}`);\n }\n}\n\n/**\n * This is the type used for index definitions as defined in the Commit chunk data.\n *\n * Changing this requires a REPLICACHE_FORMAT_VERSION bump.\n */\nexport type ChunkIndexDefinition = {\n readonly name: string;\n readonly keyPrefix: string;\n readonly jsonPointer: string;\n // Used to not exist\n readonly allowEmpty?: boolean;\n};\n\nexport function chunkIndexDefinitionEqualIgnoreName(\n a: ChunkIndexDefinition,\n b: ChunkIndexDefinition,\n): boolean {\n return (\n a.jsonPointer === b.jsonPointer &&\n (a.allowEmpty ?? false) === (b.allowEmpty ?? false) &&\n a.keyPrefix === b.keyPrefix\n );\n}\n\nfunction assertChunkIndexDefinition(\n v: unknown,\n): asserts v is ChunkIndexDefinition {\n assertObject(v);\n assertDeepFrozen(v);\n assertString(v.name);\n assertString(v.keyPrefix);\n assertString(v.jsonPointer);\n if (v.allowEmpty !== undefined) {\n assertBoolean(v.allowEmpty);\n }\n}\n\nexport function toChunkIndexDefinition(\n name: string,\n indexDefinition: IndexDefinition,\n): Required<ChunkIndexDefinition> {\n return {\n name,\n keyPrefix: indexDefinition.prefix ?? '',\n jsonPointer: indexDefinition.jsonPointer,\n allowEmpty: indexDefinition.allowEmpty ?? false,\n };\n}\n\nexport type IndexRecord = {\n readonly definition: ChunkIndexDefinition;\n readonly valueHash: Hash;\n};\n\nfunction assertIndexRecord(v: unknown): asserts v is IndexRecord {\n assertObject(v);\n assertDeepFrozen(v);\n assertChunkIndexDefinition(v.definition);\n assertString(v.valueHash);\n}\n\nfunction assertIndexRecords(v: unknown): asserts v is IndexRecord[] {\n assertArray(v);\n assertDeepFrozen(v);\n for (const ir of v) {\n assertIndexRecord(ir);\n }\n}\n\nexport function newLocalDD31(\n createChunk: CreateChunk,\n basisHash: Hash,\n baseSnapshotHash: Hash,\n mutationID: number,\n mutatorName: string,\n mutatorArgsJSON: FrozenJSONValue,\n originalHash: Hash | null,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n timestamp: number,\n clientID: ClientID,\n): Commit<LocalMetaDD31> {\n const meta: LocalMetaDD31 = {\n type: MetaType.LocalDD31,\n basisHash,\n baseSnapshotHash,\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n timestamp,\n clientID,\n };\n return commitFromCommitData(\n createChunk,\n makeCommitData(meta, valueHash, indexes),\n );\n}\n\nexport function newSnapshotDD31(\n createChunk: CreateChunk,\n basisHash: Hash | null,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n): Commit<SnapshotMetaDD31> {\n return commitFromCommitData(\n createChunk,\n newSnapshotCommitDataDD31(\n basisHash,\n lastMutationIDs,\n cookieJSON,\n valueHash,\n indexes,\n ),\n );\n}\n\nexport function newSnapshotCommitDataDD31(\n basisHash: Hash | null,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n): CommitData<SnapshotMetaDD31> {\n const meta: SnapshotMetaDD31 = {\n type: MetaType.SnapshotDD31,\n basisHash,\n lastMutationIDs,\n cookieJSON,\n };\n return makeCommitData(meta, valueHash, indexes);\n}\n\nexport function fromChunk(chunk: Chunk): Commit<Meta> {\n validateChunk(chunk);\n return new Commit(chunk);\n}\n\nfunction commitFromCommitData<M extends Meta>(\n createChunk: CreateChunk,\n data: CommitData<M>,\n): Commit<M> {\n return new Commit(createChunk(data, getRefs(data)));\n}\n\nexport function getRefs(data: CommitData<Meta>): Refs {\n const refs: Set<Hash> = new Set();\n refs.add(data.valueHash);\n const {meta} = data;\n switch (meta.type) {\n case MetaType.LocalDD31:\n meta.basisHash && refs.add(meta.basisHash);\n // Local has weak originalHash\n break;\n case MetaType.SnapshotDD31:\n // Snapshot has weak basisHash\n break;\n default:\n unreachable(meta);\n }\n\n for (const index of data.indexes) {\n refs.add(index.valueHash);\n }\n\n return toRefs(refs);\n}\n\nexport type CommitData<M extends Meta> = FrozenTag<{\n readonly meta: M;\n readonly valueHash: Hash;\n readonly indexes: readonly IndexRecord[];\n}>;\n\nexport function makeCommitData<M extends Meta>(\n meta: M,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n): CommitData<M> {\n return deepFreeze({\n meta,\n valueHash,\n indexes,\n }) as unknown as CommitData<M>;\n}\n\nexport function assertCommitData(v: unknown): asserts v is CommitData<Meta> {\n if (skipCommitDataAsserts) {\n return;\n }\n\n assertObject(v);\n assertDeepFrozen(v);\n assertMeta(v.meta);\n assertString(v.valueHash);\n assertIndexRecords(v.indexes);\n}\n\nfunction validateChunk(chunk: Chunk): asserts chunk is Chunk<CommitData<Meta>> {\n const {data} = chunk;\n assertCommitData(data);\n\n const seen = new Set();\n for (const index of data.indexes) {\n const {name} = index.definition;\n if (seen.has(name)) {\n throw new Error(`Duplicate index ${name}`);\n }\n seen.add(name);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"commit.js","names":[],"sources":["../../../../../replicache/src/db/commit.ts"],"sourcesContent":["import {\n assert,\n assertArray,\n assertBoolean,\n assertNumber,\n assertObject,\n assertString,\n unreachable,\n} from '../../../shared/src/asserts.ts';\nimport {assertJSONValue} from '../../../shared/src/json.ts';\nimport {skipCommitDataAsserts} from '../config.ts';\nimport {type FrozenCookie, compareCookies} from '../cookies.ts';\nimport {type Chunk, type CreateChunk, type Refs, toRefs} from '../dag/chunk.ts';\nimport {type MustGetChunk, type Read, mustGetHeadHash} from '../dag/store.ts';\nimport {\n type FrozenJSONValue,\n type FrozenTag,\n assertDeepFrozen,\n deepFreeze,\n} from '../frozen-json.ts';\nimport {type Hash, assertHash} from '../hash.ts';\nimport type {IndexDefinition} from '../index-defs.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport * as MetaType from './meta-type-enum.ts';\n\nexport const DEFAULT_HEAD_NAME = 'main';\n\nexport function commitIsLocalDD31(\n commit: Commit<Meta>,\n): commit is Commit<LocalMetaDD31> {\n return isLocalMetaDD31(commit.meta);\n}\n\nexport function commitIsLocal(\n commit: Commit<Meta>,\n): commit is Commit<LocalMetaDD31> {\n return commitIsLocalDD31(commit);\n}\n\nexport function commitIsSnapshot(\n commit: Commit<Meta>,\n): commit is Commit<SnapshotMetaDD31> {\n return isSnapshotMetaDD31(commit.meta);\n}\n\nexport class Commit<M extends Meta> {\n readonly chunk: Chunk<CommitData<M>>;\n\n constructor(chunk: Chunk<CommitData<M>>) {\n this.chunk = chunk;\n }\n\n get meta(): M {\n return this.chunk.data.meta;\n }\n\n get valueHash(): Hash {\n // Already validated!\n return this.chunk.data.valueHash;\n }\n\n getMutationID(clientID: ClientID, dagRead: MustGetChunk): Promise<number> {\n return getMutationID(clientID, dagRead, this.meta);\n }\n\n async getNextMutationID(\n clientID: ClientID,\n dagRead: MustGetChunk,\n ): Promise<number> {\n return (await this.getMutationID(clientID, dagRead)) + 1;\n }\n\n get indexes(): readonly IndexRecord[] {\n // Already validated!\n return this.chunk.data.indexes;\n }\n}\n\nexport async function getMutationID(\n clientID: ClientID,\n dagRead: MustGetChunk,\n meta: Meta,\n): Promise<number> {\n switch (meta.type) {\n case MetaType.SnapshotDD31:\n return meta.lastMutationIDs[clientID] ?? 0;\n\n case MetaType.LocalDD31: {\n if (meta.clientID === clientID) {\n return meta.mutationID;\n }\n const {basisHash} = meta;\n const basisCommit = await commitFromHash(basisHash, dagRead);\n return getMutationID(clientID, dagRead, basisCommit.meta);\n }\n\n default:\n unreachable(meta);\n }\n}\n\n/**\n * Returns the set of local commits from the given `fromCommitHash` back to but not\n * including its base snapshot. If `fromCommitHash` is a snapshot, the returned vector\n * will be empty. When, as typical, `fromCommitHash` is the head of the default chain\n * then the returned commits are the set of pending commits, ie the set of local commits\n * that have not yet been pushed to the data layer.\n *\n * The vector of commits is returned in reverse chain order, that is, starting\n * with the commit with hash `fromCommitHash` and walking backwards.\n */\nexport async function localMutations(\n fromCommitHash: Hash,\n dagRead: Read,\n): Promise<Commit<LocalMetaDD31>[]> {\n const commits = await commitChain(fromCommitHash, dagRead);\n // Filter does not deal with type narrowing.\n return commits.filter(c => commitIsLocal(c)) as Commit<LocalMetaDD31>[];\n}\n\nexport async function localMutationsDD31(\n fromCommitHash: Hash,\n dagRead: Read,\n): Promise<Commit<LocalMetaDD31>[]> {\n const commits = await commitChain(fromCommitHash, dagRead);\n // Filter does not deal with type narrowing.\n return commits.filter(c => commitIsLocalDD31(c)) as Commit<LocalMetaDD31>[];\n}\n\nexport async function localMutationsGreaterThan(\n commit: Commit<Meta>,\n mutationIDLimits: Record<ClientID, number>,\n dagRead: Read,\n): Promise<Commit<LocalMetaDD31>[]> {\n const commits: Commit<LocalMetaDD31>[] = [];\n const remainingMutationIDLimits = new Map(Object.entries(mutationIDLimits));\n while (!commitIsSnapshot(commit) && remainingMutationIDLimits.size > 0) {\n if (commitIsLocalDD31(commit)) {\n const {meta} = commit;\n const mutationIDLowerLimit = remainingMutationIDLimits.get(meta.clientID);\n if (mutationIDLowerLimit !== undefined) {\n if (meta.mutationID <= mutationIDLowerLimit) {\n remainingMutationIDLimits.delete(meta.clientID);\n } else {\n commits.push(commit as Commit<LocalMetaDD31>);\n }\n }\n }\n const {basisHash} = commit.meta;\n if (basisHash === null) {\n throw new Error(`Commit ${commit.chunk.hash} has no basis`);\n }\n commit = await commitFromHash(basisHash, dagRead);\n }\n return commits;\n}\n\nexport async function baseSnapshotFromHead(\n name: string,\n dagRead: Read,\n): Promise<Commit<SnapshotMetaDD31>> {\n const hash = await dagRead.getHead(name);\n assert(hash, `Missing head ${name}`);\n return baseSnapshotFromHash(hash, dagRead);\n}\n\nexport async function baseSnapshotHashFromHash(\n hash: Hash,\n dagRead: Read,\n): Promise<Hash> {\n return (await baseSnapshotFromHash(hash, dagRead)).chunk.hash;\n}\n\nexport async function baseSnapshotFromHash(\n hash: Hash,\n dagRead: Read,\n): Promise<Commit<SnapshotMetaDD31>> {\n const commit = await commitFromHash(hash, dagRead);\n return baseSnapshotFromCommit(commit, dagRead);\n}\n\nexport async function baseSnapshotFromCommit(\n commit: Commit<Meta>,\n dagRead: Read,\n): Promise<Commit<SnapshotMetaDD31>> {\n while (!commitIsSnapshot(commit)) {\n const {meta} = commit;\n if (isLocalMetaDD31(meta)) {\n commit = await commitFromHash(meta.baseSnapshotHash, dagRead);\n } else {\n const {basisHash} = meta;\n if (basisHash === null) {\n throw new Error(`Commit ${commit.chunk.hash} has no basis`);\n }\n commit = await commitFromHash(basisHash, dagRead);\n }\n }\n return commit;\n}\n\nexport function snapshotMetaParts(\n c: Commit<SnapshotMetaDD31>,\n clientID: ClientID,\n): [lastMutationID: number, cookie: FrozenCookie | FrozenJSONValue] {\n const m = c.meta;\n const lmid = m.lastMutationIDs[clientID] ?? 0;\n return [lmid, m.cookieJSON];\n}\n\nexport function compareCookiesForSnapshots(\n a: Commit<SnapshotMetaDD31>,\n b: Commit<SnapshotMetaDD31>,\n): number {\n return compareCookies(a.meta.cookieJSON, b.meta.cookieJSON);\n}\n\n/**\n * Returns all commits from the commit with fromCommitHash to its base snapshot,\n * inclusive of both. Resulting vector is in chain-head-first order (so snapshot\n * comes last).\n */\nexport async function commitChain(\n fromCommitHash: Hash,\n dagRead: Read,\n): Promise<Commit<Meta>[]> {\n let commit = await commitFromHash(fromCommitHash, dagRead);\n const commits = [];\n while (!commitIsSnapshot(commit)) {\n const {meta} = commit;\n const {basisHash} = meta;\n if (basisHash === null) {\n throw new Error(`Commit ${commit.chunk.hash} has no basis`);\n }\n commits.push(commit);\n commit = await commitFromHash(basisHash, dagRead);\n }\n commits.push(commit);\n return commits;\n}\n\nexport async function commitFromHash(\n hash: Hash,\n dagRead: MustGetChunk,\n): Promise<Commit<Meta>> {\n const chunk = await dagRead.mustGetChunk(hash);\n return fromChunk(chunk);\n}\n\nexport async function commitFromHead(\n name: string,\n dagRead: Read,\n): Promise<Commit<Meta>> {\n const hash = await mustGetHeadHash(name, dagRead);\n return commitFromHash(hash, dagRead);\n}\n\nexport type LocalMetaDD31 = {\n readonly type: MetaType.LocalDD31;\n readonly basisHash: Hash;\n readonly mutationID: number;\n readonly mutatorName: string;\n readonly mutatorArgsJSON: FrozenJSONValue;\n readonly originalHash: Hash | null;\n readonly timestamp: number;\n readonly clientID: ClientID;\n readonly baseSnapshotHash: Hash;\n};\n\nexport type LocalMeta = LocalMetaDD31;\n\nexport function assertLocalMetaDD31(\n v: Record<string, unknown>,\n): asserts v is LocalMetaDD31 {\n // type already asserted\n assertString(v.clientID);\n assertNumber(v.mutationID);\n assertString(v.mutatorName);\n if (!v.mutatorName) {\n throw new Error('Missing mutator name');\n }\n assertJSONValue(v.mutatorArgsJSON);\n if (v.originalHash !== null) {\n assertHash(v.originalHash);\n }\n assertNumber(v.timestamp);\n}\n\nexport function isLocalMetaDD31(meta: Meta): meta is LocalMetaDD31 {\n return meta.type === MetaType.LocalDD31;\n}\n\nexport function assertLocalCommitDD31(\n c: Commit<Meta>,\n): asserts c is Commit<LocalMetaDD31> {\n assertLocalMetaDD31(c.meta);\n}\n\nexport type SnapshotMetaDD31 = {\n readonly type: MetaType.SnapshotDD31;\n readonly basisHash: Hash | null;\n readonly lastMutationIDs: Record<ClientID, number>;\n readonly cookieJSON: FrozenCookie;\n};\n\nexport type SnapshotMeta = SnapshotMetaDD31;\n\nexport function assertSnapshotMetaDD31(\n v: Record<string, unknown>,\n): asserts v is SnapshotMetaDD31 {\n // type already asserted\n if (v.basisHash !== null) {\n assertHash(v.basisHash);\n }\n assertJSONValue(v.cookieJSON);\n assertLastMutationIDs(v.lastMutationIDs);\n}\n\nfunction assertLastMutationIDs(\n v: unknown,\n): asserts v is Record<ClientID, number> {\n assertObject(v);\n for (const e of Object.values(v)) {\n assertNumber(e);\n }\n}\n\nexport type Meta = LocalMetaDD31 | SnapshotMetaDD31;\n\nexport function assertSnapshotCommitDD31(\n c: Commit<Meta>,\n): asserts c is Commit<SnapshotMetaDD31> {\n assertSnapshotMetaDD31(c.meta);\n}\n\nfunction isSnapshotMetaDD31(meta: Meta): meta is SnapshotMetaDD31 {\n return meta.type === MetaType.SnapshotDD31;\n}\n\nfunction assertMeta(v: unknown): asserts v is Meta {\n assertObject(v);\n assertDeepFrozen(v);\n if (v.basisHash !== null) {\n assertString(v.basisHash);\n }\n\n assertNumber(v.type);\n switch (v.type) {\n case MetaType.LocalDD31:\n assertLocalMetaDD31(v);\n break;\n case MetaType.SnapshotDD31:\n assertSnapshotMetaDD31(v);\n break;\n default:\n throw new Error(`Invalid enum value ${v.type}`);\n }\n}\n\n/**\n * This is the type used for index definitions as defined in the Commit chunk data.\n *\n * Changing this requires a REPLICACHE_FORMAT_VERSION bump.\n */\nexport type ChunkIndexDefinition = {\n readonly name: string;\n readonly keyPrefix: string;\n readonly jsonPointer: string;\n // Used to not exist\n readonly allowEmpty?: boolean;\n};\n\nexport function chunkIndexDefinitionEqualIgnoreName(\n a: ChunkIndexDefinition,\n b: ChunkIndexDefinition,\n): boolean {\n return (\n a.jsonPointer === b.jsonPointer &&\n (a.allowEmpty ?? false) === (b.allowEmpty ?? false) &&\n a.keyPrefix === b.keyPrefix\n );\n}\n\nfunction assertChunkIndexDefinition(\n v: unknown,\n): asserts v is ChunkIndexDefinition {\n assertObject(v);\n assertDeepFrozen(v);\n assertString(v.name);\n assertString(v.keyPrefix);\n assertString(v.jsonPointer);\n if (v.allowEmpty !== undefined) {\n assertBoolean(v.allowEmpty);\n }\n}\n\nexport function toChunkIndexDefinition(\n name: string,\n indexDefinition: IndexDefinition,\n): Required<ChunkIndexDefinition> {\n return {\n name,\n keyPrefix: indexDefinition.prefix ?? '',\n jsonPointer: indexDefinition.jsonPointer,\n allowEmpty: indexDefinition.allowEmpty ?? false,\n };\n}\n\nexport type IndexRecord = {\n readonly definition: ChunkIndexDefinition;\n readonly valueHash: Hash;\n};\n\nfunction assertIndexRecord(v: unknown): asserts v is IndexRecord {\n assertObject(v);\n assertDeepFrozen(v);\n assertChunkIndexDefinition(v.definition);\n assertString(v.valueHash);\n}\n\nfunction assertIndexRecords(v: unknown): asserts v is IndexRecord[] {\n assertArray(v);\n assertDeepFrozen(v);\n for (const ir of v) {\n assertIndexRecord(ir);\n }\n}\n\nexport function newLocalDD31(\n createChunk: CreateChunk,\n basisHash: Hash,\n baseSnapshotHash: Hash,\n mutationID: number,\n mutatorName: string,\n mutatorArgsJSON: FrozenJSONValue,\n originalHash: Hash | null,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n timestamp: number,\n clientID: ClientID,\n): Commit<LocalMetaDD31> {\n const meta: LocalMetaDD31 = {\n type: MetaType.LocalDD31,\n basisHash,\n baseSnapshotHash,\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n timestamp,\n clientID,\n };\n return commitFromCommitData(\n createChunk,\n makeCommitData(meta, valueHash, indexes),\n );\n}\n\nexport function newSnapshotDD31(\n createChunk: CreateChunk,\n basisHash: Hash | null,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n): Commit<SnapshotMetaDD31> {\n return commitFromCommitData(\n createChunk,\n newSnapshotCommitDataDD31(\n basisHash,\n lastMutationIDs,\n cookieJSON,\n valueHash,\n indexes,\n ),\n );\n}\n\nexport function newSnapshotCommitDataDD31(\n basisHash: Hash | null,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n): CommitData<SnapshotMetaDD31> {\n const meta: SnapshotMetaDD31 = {\n type: MetaType.SnapshotDD31,\n basisHash,\n lastMutationIDs,\n cookieJSON,\n };\n return makeCommitData(meta, valueHash, indexes);\n}\n\nexport function fromChunk(chunk: Chunk): Commit<Meta> {\n validateChunk(chunk);\n return new Commit(chunk);\n}\n\nfunction commitFromCommitData<M extends Meta>(\n createChunk: CreateChunk,\n data: CommitData<M>,\n): Commit<M> {\n return new Commit(createChunk(data, getRefs(data)));\n}\n\nexport function getRefs(data: CommitData<Meta>): Refs {\n const refs: Set<Hash> = new Set();\n refs.add(data.valueHash);\n const {meta} = data;\n switch (meta.type) {\n case MetaType.LocalDD31:\n meta.basisHash && refs.add(meta.basisHash);\n // Local has weak originalHash\n break;\n case MetaType.SnapshotDD31:\n // Snapshot has weak basisHash\n break;\n default:\n unreachable(meta);\n }\n\n for (const index of data.indexes) {\n refs.add(index.valueHash);\n }\n\n return toRefs(refs);\n}\n\nexport type CommitData<M extends Meta> = FrozenTag<{\n readonly meta: M;\n readonly valueHash: Hash;\n readonly indexes: readonly IndexRecord[];\n}>;\n\nexport function makeCommitData<M extends Meta>(\n meta: M,\n valueHash: Hash,\n indexes: readonly IndexRecord[],\n): CommitData<M> {\n return deepFreeze({\n meta,\n valueHash,\n indexes,\n }) as unknown as CommitData<M>;\n}\n\nexport function assertCommitData(v: unknown): asserts v is CommitData<Meta> {\n if (skipCommitDataAsserts) {\n return;\n }\n\n assertObject(v);\n assertDeepFrozen(v);\n assertMeta(v.meta);\n assertString(v.valueHash);\n assertIndexRecords(v.indexes);\n}\n\nfunction validateChunk(chunk: Chunk): asserts chunk is Chunk<CommitData<Meta>> {\n const {data} = chunk;\n assertCommitData(data);\n\n const seen = new Set();\n for (const index of data.indexes) {\n const {name} = index.definition;\n if (seen.has(name)) {\n throw new Error(`Duplicate index ${name}`);\n }\n seen.add(name);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../../replicache/src/db/index.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {BTreeRead} from '../btree/read.ts';\nimport type {BTreeWrite} from '../btree/write.ts';\nimport type {FrozenJSONObject, FrozenJSONValue} from '../frozen-json.ts';\nimport type {Hash} from '../hash.ts';\nimport type {IndexRecord} from './commit.ts';\nimport * as IndexOperation from './index-operation-enum.ts';\n\ntype IndexOperation = Enum<typeof IndexOperation>;\n\nexport class IndexRead<BTree = BTreeRead> {\n readonly meta: IndexRecord;\n readonly map: BTree;\n\n constructor(meta: IndexRecord, map: BTree) {\n this.meta = meta;\n this.map = map;\n }\n}\n\nexport class IndexWrite extends IndexRead<BTreeWrite> {\n // Note: does not update self.meta.valueHash (doesn't need to at this point as flush\n // is only called during commit.)\n flush(): Promise<Hash> {\n return this.map.flush();\n }\n\n clear(): Promise<void> {\n return this.map.clear();\n }\n}\n\n// Index or de-index a single primary entry.\nexport async function indexValue(\n lc: LogContext,\n index: BTreeWrite,\n op: IndexOperation,\n key: string,\n val: FrozenJSONValue,\n jsonPointer: string,\n allowEmpty: boolean,\n): Promise<void> {\n try {\n for (const entry of getIndexKeys(key, val, jsonPointer, allowEmpty)) {\n switch (op) {\n case IndexOperation.Add:\n await index.put(entry, val);\n break;\n case IndexOperation.Remove:\n await index.del(entry);\n break;\n }\n }\n } catch (e) {\n // Right now all the errors that index_value() returns are customers dev\n // problems: either the value is not json, the pointer is into nowhere, etc.\n // So we ignore them.\n lc.info?.('Not indexing value', val, ':', e);\n }\n}\n\n// Gets the set of index keys for a given primary key and value.\nexport function getIndexKeys(\n primary: string,\n value: FrozenJSONValue,\n jsonPointer: string,\n allowEmpty: boolean,\n): string[] {\n const target = evaluateJSONPointer(value, jsonPointer);\n if (target === undefined) {\n if (allowEmpty) {\n return [];\n }\n throw new Error(`No value at path: ${jsonPointer}`);\n }\n\n const values = Array.isArray(target) ? target : [target];\n\n const indexKeys: string[] = [];\n for (const value of values) {\n if (typeof value === 'string') {\n indexKeys.push(encodeIndexKey([value, primary]));\n } else {\n throw new Error('Unsupported target type');\n }\n }\n\n return indexKeys;\n}\n\nexport const KEY_VERSION_0 = '\\u0000';\nexport const KEY_SEPARATOR = '\\u0000';\n\n/**\n * When using indexes the key is a tuple of the secondary key and the primary\n * key.\n */\nexport type IndexKey = readonly [secondary: string, primary: string];\n\n// An index key is encoded to vec of bytes in the following order:\n// - key version byte(s), followed by\n// - the secondary key bytes (which for now is a UTF8 encoded string), followed by\n// - the key separator, a null byte, followed by\n// - the primary key bytes\n//\n// The null separator byte ensures that if a secondary key A is longer than B then\n// A always sorts after B. Appending the primary key ensures index keys with\n// identical secondary keys sort in primary key order. Secondary keys must not\n// contain a zero (null) byte.\nexport function encodeIndexKey(indexKey: IndexKey): string {\n const secondary = indexKey[0];\n const primary = indexKey[1];\n\n if (secondary.includes('\\u0000')) {\n throw new Error('Secondary key cannot contain null byte');\n }\n return KEY_VERSION_0 + secondary + KEY_SEPARATOR + primary;\n}\n\n// Returns bytes that can be used to scan for the given secondary index value.\n//\n// Consider a scan for start_secondary_key=\"a\" (97). We want to scan with scan\n// key [0, 97]. We could also scan with [0, 97, 0], but then we couldn't use\n// this function for prefix scans, so we lop off the null byte. If we want\n// the scan to be exclusive, we scan with the next greater value, [0, 97, 1]\n// (we disallow zero bytes in secondary keys).\n//\n// Now it gets a little tricky. We also want to be able to scan using the\n// primary key, start_key. When we do this we have to encode the scan key\n// a little differently We essentially have to fix the value of the\n// secondary key so we can vary the start_key. That is, the match on\n// start_secondary_key becomes an exact match.\n//\n// Consider the scan for start_secondary_key=\"a\" and start_key=[2]. We want\n// to scan with [0, 97, 0, 2]. If we want exclusive we want to scan with\n// the next highest value, [0, 97, 0, 2, 0] (zero bytes are allowed in primary\n// keys). So far so good. It is important to notice that we need to\n// be able to distinguish between not wanting use start_key and wanting to\n// use start_key=[]. In the former case we want to scan with the secondary\n// key value, possibly followed by a 1 with no trailing zero byte ([0, 97]\n// or [0, 97, 1]). In the latter case we want to scan by the secondary\n// key value, followed by the zero byte, followed by the primary key value\n// and another zero if it is exclusive ([0, 97, 0] or [0, 97, 0, 0]).\n// This explains why we need the Option around start_key.\nexport function encodeIndexScanKey(\n secondary: string,\n primary: string | undefined,\n): string {\n const k = encodeIndexKey([secondary, primary || '']);\n if (primary === undefined) {\n return k.slice(0, k.length - 1);\n }\n return k;\n}\n\n// Decodes an IndexKey encoded by encode_index_key.\nexport function decodeIndexKey(encodedIndexKey: string): IndexKey {\n if (encodedIndexKey[0] !== KEY_VERSION_0) {\n throw new Error('Invalid version');\n }\n\n const versionLen = KEY_VERSION_0.length;\n const separatorLen = KEY_SEPARATOR.length;\n const separatorOffset = encodedIndexKey.indexOf(KEY_SEPARATOR, versionLen);\n if (separatorOffset === -1) {\n throw new Error('Invalid formatting');\n }\n\n const secondary = encodedIndexKey.slice(versionLen, separatorOffset);\n const primary = encodedIndexKey.slice(separatorOffset + separatorLen);\n return [secondary, primary];\n}\n\nexport function evaluateJSONPointer(\n value: FrozenJSONValue,\n pointer: string,\n): FrozenJSONValue | undefined {\n function parseIndex(s: string): number | undefined {\n if (s.startsWith('+') || (s.startsWith('0') && s.length !== 1)) {\n return undefined;\n }\n return parseInt(s, 10);\n }\n\n if (pointer === '') {\n return value;\n }\n if (!pointer.startsWith('/')) {\n throw new Error(`Invalid JSON pointer: ${pointer}`);\n }\n\n const tokens = pointer\n .split('/')\n .slice(1)\n .map(x => x.replace(/~1/g, '/').replace(/~0/g, '~'));\n\n let target = value;\n for (const token of tokens) {\n let targetOpt;\n if (Array.isArray(target)) {\n const i = parseIndex(token);\n if (i === undefined) {\n return undefined;\n }\n targetOpt = target[i];\n } else if (target === null) {\n return undefined;\n } else if (typeof target === 'object') {\n target = target as FrozenJSONObject;\n targetOpt = target[token];\n }\n if (targetOpt === undefined) {\n return undefined;\n }\n target = targetOpt;\n }\n return target;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../../replicache/src/db/index.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {BTreeRead} from '../btree/read.ts';\nimport type {BTreeWrite} from '../btree/write.ts';\nimport type {FrozenJSONObject, FrozenJSONValue} from '../frozen-json.ts';\nimport type {Hash} from '../hash.ts';\nimport type {IndexRecord} from './commit.ts';\nimport * as IndexOperation from './index-operation-enum.ts';\n\ntype IndexOperation = Enum<typeof IndexOperation>;\n\nexport class IndexRead<BTree = BTreeRead> {\n readonly meta: IndexRecord;\n readonly map: BTree;\n\n constructor(meta: IndexRecord, map: BTree) {\n this.meta = meta;\n this.map = map;\n }\n}\n\nexport class IndexWrite extends IndexRead<BTreeWrite> {\n // Note: does not update self.meta.valueHash (doesn't need to at this point as flush\n // is only called during commit.)\n flush(): Promise<Hash> {\n return this.map.flush();\n }\n\n clear(): Promise<void> {\n return this.map.clear();\n }\n}\n\n// Index or de-index a single primary entry.\nexport async function indexValue(\n lc: LogContext,\n index: BTreeWrite,\n op: IndexOperation,\n key: string,\n val: FrozenJSONValue,\n jsonPointer: string,\n allowEmpty: boolean,\n): Promise<void> {\n try {\n for (const entry of getIndexKeys(key, val, jsonPointer, allowEmpty)) {\n switch (op) {\n case IndexOperation.Add:\n await index.put(entry, val);\n break;\n case IndexOperation.Remove:\n await index.del(entry);\n break;\n }\n }\n } catch (e) {\n // Right now all the errors that index_value() returns are customers dev\n // problems: either the value is not json, the pointer is into nowhere, etc.\n // So we ignore them.\n lc.info?.('Not indexing value', val, ':', e);\n }\n}\n\n// Gets the set of index keys for a given primary key and value.\nexport function getIndexKeys(\n primary: string,\n value: FrozenJSONValue,\n jsonPointer: string,\n allowEmpty: boolean,\n): string[] {\n const target = evaluateJSONPointer(value, jsonPointer);\n if (target === undefined) {\n if (allowEmpty) {\n return [];\n }\n throw new Error(`No value at path: ${jsonPointer}`);\n }\n\n const values = Array.isArray(target) ? target : [target];\n\n const indexKeys: string[] = [];\n for (const value of values) {\n if (typeof value === 'string') {\n indexKeys.push(encodeIndexKey([value, primary]));\n } else {\n throw new Error('Unsupported target type');\n }\n }\n\n return indexKeys;\n}\n\nexport const KEY_VERSION_0 = '\\u0000';\nexport const KEY_SEPARATOR = '\\u0000';\n\n/**\n * When using indexes the key is a tuple of the secondary key and the primary\n * key.\n */\nexport type IndexKey = readonly [secondary: string, primary: string];\n\n// An index key is encoded to vec of bytes in the following order:\n// - key version byte(s), followed by\n// - the secondary key bytes (which for now is a UTF8 encoded string), followed by\n// - the key separator, a null byte, followed by\n// - the primary key bytes\n//\n// The null separator byte ensures that if a secondary key A is longer than B then\n// A always sorts after B. Appending the primary key ensures index keys with\n// identical secondary keys sort in primary key order. Secondary keys must not\n// contain a zero (null) byte.\nexport function encodeIndexKey(indexKey: IndexKey): string {\n const secondary = indexKey[0];\n const primary = indexKey[1];\n\n if (secondary.includes('\\u0000')) {\n throw new Error('Secondary key cannot contain null byte');\n }\n return KEY_VERSION_0 + secondary + KEY_SEPARATOR + primary;\n}\n\n// Returns bytes that can be used to scan for the given secondary index value.\n//\n// Consider a scan for start_secondary_key=\"a\" (97). We want to scan with scan\n// key [0, 97]. We could also scan with [0, 97, 0], but then we couldn't use\n// this function for prefix scans, so we lop off the null byte. If we want\n// the scan to be exclusive, we scan with the next greater value, [0, 97, 1]\n// (we disallow zero bytes in secondary keys).\n//\n// Now it gets a little tricky. We also want to be able to scan using the\n// primary key, start_key. When we do this we have to encode the scan key\n// a little differently We essentially have to fix the value of the\n// secondary key so we can vary the start_key. That is, the match on\n// start_secondary_key becomes an exact match.\n//\n// Consider the scan for start_secondary_key=\"a\" and start_key=[2]. We want\n// to scan with [0, 97, 0, 2]. If we want exclusive we want to scan with\n// the next highest value, [0, 97, 0, 2, 0] (zero bytes are allowed in primary\n// keys). So far so good. It is important to notice that we need to\n// be able to distinguish between not wanting use start_key and wanting to\n// use start_key=[]. In the former case we want to scan with the secondary\n// key value, possibly followed by a 1 with no trailing zero byte ([0, 97]\n// or [0, 97, 1]). In the latter case we want to scan by the secondary\n// key value, followed by the zero byte, followed by the primary key value\n// and another zero if it is exclusive ([0, 97, 0] or [0, 97, 0, 0]).\n// This explains why we need the Option around start_key.\nexport function encodeIndexScanKey(\n secondary: string,\n primary: string | undefined,\n): string {\n const k = encodeIndexKey([secondary, primary || '']);\n if (primary === undefined) {\n return k.slice(0, k.length - 1);\n }\n return k;\n}\n\n// Decodes an IndexKey encoded by encode_index_key.\nexport function decodeIndexKey(encodedIndexKey: string): IndexKey {\n if (encodedIndexKey[0] !== KEY_VERSION_0) {\n throw new Error('Invalid version');\n }\n\n const versionLen = KEY_VERSION_0.length;\n const separatorLen = KEY_SEPARATOR.length;\n const separatorOffset = encodedIndexKey.indexOf(KEY_SEPARATOR, versionLen);\n if (separatorOffset === -1) {\n throw new Error('Invalid formatting');\n }\n\n const secondary = encodedIndexKey.slice(versionLen, separatorOffset);\n const primary = encodedIndexKey.slice(separatorOffset + separatorLen);\n return [secondary, primary];\n}\n\nexport function evaluateJSONPointer(\n value: FrozenJSONValue,\n pointer: string,\n): FrozenJSONValue | undefined {\n function parseIndex(s: string): number | undefined {\n if (s.startsWith('+') || (s.startsWith('0') && s.length !== 1)) {\n return undefined;\n }\n return parseInt(s, 10);\n }\n\n if (pointer === '') {\n return value;\n }\n if (!pointer.startsWith('/')) {\n throw new Error(`Invalid JSON pointer: ${pointer}`);\n }\n\n const tokens = pointer\n .split('/')\n .slice(1)\n .map(x => x.replace(/~1/g, '/').replace(/~0/g, '~'));\n\n let target = value;\n for (const token of tokens) {\n let targetOpt;\n if (Array.isArray(target)) {\n const i = parseIndex(token);\n if (i === undefined) {\n return undefined;\n }\n targetOpt = target[i];\n } else if (target === null) {\n return undefined;\n } else if (typeof target === 'object') {\n target = target as FrozenJSONObject;\n targetOpt = target[token];\n }\n if (targetOpt === undefined) {\n return undefined;\n }\n target = targetOpt;\n }\n return target;\n}\n"],"mappings":";AAWA,IAAa,YAAb,MAA0C;CACxC;CACA;CAEA,YAAY,MAAmB,KAAY;AACzC,OAAK,OAAO;AACZ,OAAK,MAAM;;;AAIf,IAAa,aAAb,cAAgC,UAAsB;CAGpD,QAAuB;AACrB,SAAO,KAAK,IAAI,OAAO;;CAGzB,QAAuB;AACrB,SAAO,KAAK,IAAI,OAAO;;;AAK3B,eAAsB,WACpB,IACA,OACA,IACA,KACA,KACA,aACA,YACe;AACf,KAAI;AACF,OAAK,MAAM,SAAS,aAAa,KAAK,KAAK,aAAa,WAAW,CACjE,SAAQ,IAAR;GACE,KAAK;AACH,UAAM,MAAM,IAAI,OAAO,IAAI;AAC3B;GACF,KAAK;AACH,UAAM,MAAM,IAAI,MAAM;AACtB;;UAGC,GAAG;AAIV,KAAG,OAAO,sBAAsB,KAAK,KAAK,EAAE;;;AAKhD,SAAgB,aACd,SACA,OACA,aACA,YACU;CACV,MAAM,SAAS,oBAAoB,OAAO,YAAY;AACtD,KAAI,WAAW,KAAA,GAAW;AACxB,MAAI,WACF,QAAO,EAAE;AAEX,QAAM,IAAI,MAAM,qBAAqB,cAAc;;CAGrD,MAAM,SAAS,MAAM,QAAQ,OAAO,GAAG,SAAS,CAAC,OAAO;CAExD,MAAM,YAAsB,EAAE;AAC9B,MAAK,MAAM,SAAS,OAClB,KAAI,OAAO,UAAU,SACnB,WAAU,KAAK,eAAe,CAAC,OAAO,QAAQ,CAAC,CAAC;KAEhD,OAAM,IAAI,MAAM,0BAA0B;AAI9C,QAAO;;AAsBT,SAAgB,eAAe,UAA4B;CACzD,MAAM,YAAY,SAAS;CAC3B,MAAM,UAAU,SAAS;AAEzB,KAAI,UAAU,SAAS,KAAS,CAC9B,OAAM,IAAI,MAAM,yCAAyC;AAE3D,QAAA,OAAuB,YAAA,OAA4B;;AA4BrD,SAAgB,mBACd,WACA,SACQ;CACR,MAAM,IAAI,eAAe,CAAC,WAAW,WAAW,GAAG,CAAC;AACpD,KAAI,YAAY,KAAA,EACd,QAAO,EAAE,MAAM,GAAG,EAAE,SAAS,EAAE;AAEjC,QAAO;;AAIT,SAAgB,eAAe,iBAAmC;AAChE,KAAI,gBAAgB,OAAA,KAClB,OAAM,IAAI,MAAM,kBAAkB;CAGpC,MAAM,aAAa;CACnB,MAAM,eAAe;CACrB,MAAM,kBAAkB,gBAAgB,QAAA,MAAuB,WAAW;AAC1E,KAAI,oBAAoB,GACtB,OAAM,IAAI,MAAM,qBAAqB;AAKvC,QAAO,CAFW,gBAAgB,MAAM,YAAY,gBAAgB,EACpD,gBAAgB,MAAM,kBAAkB,aAAa,CAC1C;;AAG7B,SAAgB,oBACd,OACA,SAC6B;CAC7B,SAAS,WAAW,GAA+B;AACjD,MAAI,EAAE,WAAW,IAAI,IAAK,EAAE,WAAW,IAAI,IAAI,EAAE,WAAW,EAC1D;AAEF,SAAO,SAAS,GAAG,GAAG;;AAGxB,KAAI,YAAY,GACd,QAAO;AAET,KAAI,CAAC,QAAQ,WAAW,IAAI,CAC1B,OAAM,IAAI,MAAM,yBAAyB,UAAU;CAGrD,MAAM,SAAS,QACZ,MAAM,IAAI,CACV,MAAM,EAAE,CACR,KAAI,MAAK,EAAE,QAAQ,OAAO,IAAI,CAAC,QAAQ,OAAO,IAAI,CAAC;CAEtD,IAAI,SAAS;AACb,MAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI;AACJ,MAAI,MAAM,QAAQ,OAAO,EAAE;GACzB,MAAM,IAAI,WAAW,MAAM;AAC3B,OAAI,MAAM,KAAA,EACR;AAEF,eAAY,OAAO;aACV,WAAW,KACpB;WACS,OAAO,WAAW,UAAU;AACrC,YAAS;AACT,eAAY,OAAO;;AAErB,MAAI,cAAc,KAAA,EAChB;AAEF,WAAS;;AAEX,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","names":["#dagRead"],"sources":["../../../../../replicache/src/db/read.ts"],"sourcesContent":["import type {Enum} from '../../../shared/src/enum.ts';\nimport {BTreeRead} from '../btree/read.ts';\nimport type {Read as DagRead} from '../dag/store.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport type {Hash} from '../hash.ts';\nimport type {Commit} from './commit.ts';\nimport {\n DEFAULT_HEAD_NAME,\n type Meta,\n commitFromHash,\n commitFromHead,\n} from './commit.ts';\nimport {IndexRead} from './index.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class Read {\n readonly #dagRead: DagRead;\n map: BTreeRead;\n readonly indexes: Map<string, IndexRead>;\n\n constructor(\n dagRead: DagRead,\n map: BTreeRead,\n indexes: Map<string, IndexRead>,\n ) {\n this.#dagRead = dagRead;\n this.map = map;\n this.indexes = indexes;\n }\n\n has(key: string): Promise<boolean> {\n return this.map.has(key);\n }\n\n get(key: string): Promise<FrozenJSONValue | undefined> {\n return this.map.get(key);\n }\n\n isEmpty(): Promise<boolean> {\n return this.map.isEmpty();\n }\n\n getMapForIndex(indexName: string): BTreeRead {\n const idx = this.indexes.get(indexName);\n if (idx === undefined) {\n throw new Error(`Unknown index name: ${indexName}`);\n }\n return idx.map;\n }\n\n get closed(): boolean {\n return this.#dagRead.closed;\n }\n\n close(): void {\n this.#dagRead.release();\n }\n}\n\nexport function readFromDefaultHead(\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Promise<Read> {\n return readFromHead(DEFAULT_HEAD_NAME, dagRead, formatVersion);\n}\n\nexport async function readFromHead(\n name: string,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Promise<Read> {\n const commit = await commitFromHead(name, dagRead);\n return readFromCommit(commit, dagRead, formatVersion);\n}\n\nexport async function readFromHash(\n hash: Hash,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Promise<Read> {\n const commit = await commitFromHash(hash, dagRead);\n return readFromCommit(commit, dagRead, formatVersion);\n}\n\nfunction readFromCommit(\n commit: Commit<Meta>,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Read {\n const indexes = readIndexesForRead(commit, dagRead, formatVersion);\n const map = new BTreeRead(dagRead, formatVersion, commit.valueHash);\n return new Read(dagRead, map, indexes);\n}\n\nexport function readIndexesForRead(\n commit: Commit<Meta>,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Map<string, IndexRead> {\n const m = new Map();\n for (const index of commit.indexes) {\n m.set(\n index.definition.name,\n new IndexRead(\n index,\n new BTreeRead(dagRead, formatVersion, index.valueHash),\n ),\n );\n }\n return m;\n}\n"],"mappings":";;;;AAiBA,IAAa,OAAb,MAAkB;CAChB;CACA;CACA;CAEA,YACE,SACA,KACA,SACA;EACA,KAAKA,WAAW;EAChB,KAAK,MAAM;EACX,KAAK,UAAU;CACjB;CAEA,IAAI,KAA+B;EACjC,OAAO,KAAK,IAAI,IAAI,GAAG;CACzB;CAEA,IAAI,KAAmD;EACrD,OAAO,KAAK,IAAI,IAAI,GAAG;CACzB;CAEA,UAA4B;EAC1B,OAAO,KAAK,IAAI,QAAQ;CAC1B;CAEA,eAAe,WAA8B;EAC3C,MAAM,MAAM,KAAK,QAAQ,IAAI,SAAS;EACtC,IAAI,QAAQ,KAAA,GACV,MAAM,IAAI,MAAM,uBAAuB,WAAW;EAEpD,OAAO,IAAI;CACb;CAEA,IAAI,SAAkB;EACpB,OAAO,KAAKA,SAAS;CACvB;CAEA,QAAc;EACZ,KAAKA,SAAS,QAAQ;CACxB;AACF;AAEA,SAAgB,oBACd,SACA,eACe;CACf,OAAO,aAAa,mBAAmB,SAAS,aAAa;AAC/D;AAEA,eAAsB,aACpB,MACA,SACA,eACe;CAEf,OAAO,eAAe,MADD,eAAe,MAAM,OAAO,GACnB,SAAS,aAAa;AACtD;AAEA,eAAsB,aACpB,MACA,SACA,eACe;CAEf,OAAO,eAAe,MADD,eAAe,MAAM,OAAO,GACnB,SAAS,aAAa;AACtD;AAEA,SAAS,eACP,QACA,SACA,eACM;CACN,MAAM,UAAU,mBAAmB,QAAQ,SAAS,aAAa;CAEjE,OAAO,IAAI,KAAK,SAAS,IADT,UAAU,SAAS,eAAe,OAAO,SAChC,GAAK,OAAO;AACvC;AAEA,SAAgB,mBACd,QACA,SACA,eACwB;CACxB,MAAM,oBAAI,IAAI,IAAI;CAClB,KAAK,MAAM,SAAS,OAAO,SACzB,EAAE,IACA,MAAM,WAAW,MACjB,IAAI,UACF,OACA,IAAI,UAAU,SAAS,eAAe,MAAM,SAAS,CACvD,CACF;CAEF,OAAO;AACT"}
1
+ {"version":3,"file":"read.js","names":["#dagRead"],"sources":["../../../../../replicache/src/db/read.ts"],"sourcesContent":["import type {Enum} from '../../../shared/src/enum.ts';\nimport {BTreeRead} from '../btree/read.ts';\nimport type {Read as DagRead} from '../dag/store.ts';\nimport type * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport type {Hash} from '../hash.ts';\nimport type {Commit} from './commit.ts';\nimport {\n DEFAULT_HEAD_NAME,\n type Meta,\n commitFromHash,\n commitFromHead,\n} from './commit.ts';\nimport {IndexRead} from './index.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class Read {\n readonly #dagRead: DagRead;\n map: BTreeRead;\n readonly indexes: Map<string, IndexRead>;\n\n constructor(\n dagRead: DagRead,\n map: BTreeRead,\n indexes: Map<string, IndexRead>,\n ) {\n this.#dagRead = dagRead;\n this.map = map;\n this.indexes = indexes;\n }\n\n has(key: string): Promise<boolean> {\n return this.map.has(key);\n }\n\n get(key: string): Promise<FrozenJSONValue | undefined> {\n return this.map.get(key);\n }\n\n isEmpty(): Promise<boolean> {\n return this.map.isEmpty();\n }\n\n getMapForIndex(indexName: string): BTreeRead {\n const idx = this.indexes.get(indexName);\n if (idx === undefined) {\n throw new Error(`Unknown index name: ${indexName}`);\n }\n return idx.map;\n }\n\n get closed(): boolean {\n return this.#dagRead.closed;\n }\n\n close(): void {\n this.#dagRead.release();\n }\n}\n\nexport function readFromDefaultHead(\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Promise<Read> {\n return readFromHead(DEFAULT_HEAD_NAME, dagRead, formatVersion);\n}\n\nexport async function readFromHead(\n name: string,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Promise<Read> {\n const commit = await commitFromHead(name, dagRead);\n return readFromCommit(commit, dagRead, formatVersion);\n}\n\nexport async function readFromHash(\n hash: Hash,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Promise<Read> {\n const commit = await commitFromHash(hash, dagRead);\n return readFromCommit(commit, dagRead, formatVersion);\n}\n\nfunction readFromCommit(\n commit: Commit<Meta>,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Read {\n const indexes = readIndexesForRead(commit, dagRead, formatVersion);\n const map = new BTreeRead(dagRead, formatVersion, commit.valueHash);\n return new Read(dagRead, map, indexes);\n}\n\nexport function readIndexesForRead(\n commit: Commit<Meta>,\n dagRead: DagRead,\n formatVersion: FormatVersion,\n): Map<string, IndexRead> {\n const m = new Map();\n for (const index of commit.indexes) {\n m.set(\n index.definition.name,\n new IndexRead(\n index,\n new BTreeRead(dagRead, formatVersion, index.valueHash),\n ),\n );\n }\n return m;\n}\n"],"mappings":";;;;AAiBA,IAAa,OAAb,MAAkB;CAChB;CACA;CACA;CAEA,YACE,SACA,KACA,SACA;AACA,QAAA,UAAgB;AAChB,OAAK,MAAM;AACX,OAAK,UAAU;;CAGjB,IAAI,KAA+B;AACjC,SAAO,KAAK,IAAI,IAAI,IAAI;;CAG1B,IAAI,KAAmD;AACrD,SAAO,KAAK,IAAI,IAAI,IAAI;;CAG1B,UAA4B;AAC1B,SAAO,KAAK,IAAI,SAAS;;CAG3B,eAAe,WAA8B;EAC3C,MAAM,MAAM,KAAK,QAAQ,IAAI,UAAU;AACvC,MAAI,QAAQ,KAAA,EACV,OAAM,IAAI,MAAM,uBAAuB,YAAY;AAErD,SAAO,IAAI;;CAGb,IAAI,SAAkB;AACpB,SAAO,MAAA,QAAc;;CAGvB,QAAc;AACZ,QAAA,QAAc,SAAS;;;AAI3B,SAAgB,oBACd,SACA,eACe;AACf,QAAO,aAAa,mBAAmB,SAAS,cAAc;;AAGhE,eAAsB,aACpB,MACA,SACA,eACe;AAEf,QAAO,eADQ,MAAM,eAAe,MAAM,QAAQ,EACpB,SAAS,cAAc;;AAGvD,eAAsB,aACpB,MACA,SACA,eACe;AAEf,QAAO,eADQ,MAAM,eAAe,MAAM,QAAQ,EACpB,SAAS,cAAc;;AAGvD,SAAS,eACP,QACA,SACA,eACM;CACN,MAAM,UAAU,mBAAmB,QAAQ,SAAS,cAAc;AAElE,QAAO,IAAI,KAAK,SADJ,IAAI,UAAU,SAAS,eAAe,OAAO,UAAU,EACrC,QAAQ;;AAGxC,SAAgB,mBACd,QACA,SACA,eACwB;CACxB,MAAM,oBAAI,IAAI,KAAK;AACnB,MAAK,MAAM,SAAS,OAAO,QACzB,GAAE,IACA,MAAM,WAAW,MACjB,IAAI,UACF,OACA,IAAI,UAAU,SAAS,eAAe,MAAM,UAAU,CACvD,CACF;AAEH,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"rebase.js","names":[],"sources":["../../../../../replicache/src/db/rebase.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroTxData} from '../replicache-options.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {WriteTransactionImpl} from '../transactions.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {\n type Commit,\n type LocalMeta,\n type LocalMetaDD31,\n type Meta,\n assertLocalMetaDD31,\n commitFromHash,\n isLocalMetaDD31,\n} from './commit.ts';\nimport type {Write} from './write.ts';\nimport {newWriteLocal} from './write.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nasync function rebaseMutation(\n mutation: Commit<LocalMetaDD31>,\n dagWrite: DagWrite,\n basisHash: Hash,\n mutators: MutatorDefs,\n lc: LogContext,\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Write> {\n const localMeta = mutation.meta;\n const name = localMeta.mutatorName;\n if (isLocalMetaDD31(localMeta)) {\n assert(\n localMeta.clientID === mutationClientID,\n 'mutationClientID must match clientID of LocalMeta',\n );\n }\n const maybeMutatorImpl = mutators[name];\n if (!maybeMutatorImpl) {\n // Developers must not remove mutator names from code deployed with the\n // same schemaVersion because Replicache needs to be able to replay\n // mutations during pull.\n //\n // If we detect that this has happened, stub in a no-op mutator so that at\n // least sync can move forward. Note that the server-side mutation will\n // still get sent. This doesn't remove the queued local mutation, it just\n // removes its visible effects.\n lc.error?.(`Cannot rebase unknown mutator ${name}`);\n }\n const mutatorImpl =\n maybeMutatorImpl ||\n (async () => {\n // no op\n });\n\n const args = localMeta.mutatorArgsJSON;\n\n const basisCommit = await commitFromHash(basisHash, dagWrite);\n const nextMutationID = await basisCommit.getNextMutationID(\n mutationClientID,\n dagWrite,\n );\n if (nextMutationID !== localMeta.mutationID) {\n throw new Error(\n `Inconsistent mutation ID: original: ${localMeta.mutationID}, next: ${nextMutationID} - mutationClientID: ${mutationClientID} mutatorName: ${name}`,\n );\n }\n\n if (formatVersion >= FormatVersion.DD31) {\n assertLocalMetaDD31(localMeta);\n }\n\n const dbWrite = await newWriteLocal(\n basisHash,\n name,\n args,\n mutation.chunk.hash,\n dagWrite,\n localMeta.timestamp,\n mutationClientID,\n formatVersion,\n );\n\n const tx = new WriteTransactionImpl(\n mutationClientID,\n await dbWrite.getMutationID(),\n 'rebase',\n zeroData,\n dbWrite,\n lc,\n );\n await mutatorImpl(tx, args);\n return dbWrite;\n}\n\nexport async function rebaseMutationAndPutCommit(\n mutation: Commit<LocalMeta>,\n dagWrite: DagWrite,\n basis: Hash,\n mutators: MutatorDefs,\n lc: LogContext,\n // TODO(greg): mutationClientID can be retrieved from mutation if LocalMeta\n // is a LocalMetaDD31. As part of DD31 cleanup we can remove this arg.\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Commit<Meta>> {\n const tx = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return tx.putCommit();\n}\n\nexport async function rebaseMutationAndCommit(\n mutation: Commit<LocalMeta>,\n dagWrite: DagWrite,\n basis: Hash,\n headName: string,\n mutators: MutatorDefs,\n lc: LogContext,\n // TODO(greg): mutationClientID can be retrieved from mutation if LocalMeta\n // is a LocalMetaDD31. As part of DD31 cleanup we can remove this arg.\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Hash> {\n const dbWrite = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return dbWrite.commit(headName);\n}\n"],"mappings":";;;;;AAwBA,eAAe,eACb,UACA,UACA,WACA,UACA,IACA,kBACA,eACA,UACgB;CAChB,MAAM,YAAY,SAAS;CAC3B,MAAM,OAAO,UAAU;CACvB,IAAI,gBAAgB,SAAS,GAC3B,OACE,UAAU,aAAa,kBACvB,mDACF;CAEF,MAAM,mBAAmB,SAAS;CAClC,IAAI,CAAC,kBASH,GAAG,QAAQ,iCAAiC,MAAM;CAEpD,MAAM,cACJ,qBACC,YAAY,CAEb;CAEF,MAAM,OAAO,UAAU;CAGvB,MAAM,iBAAiB,OAAM,MADH,eAAe,WAAW,QAAQ,GACnB,kBACvC,kBACA,QACF;CACA,IAAI,mBAAmB,UAAU,YAC/B,MAAM,IAAI,MACR,uCAAuC,UAAU,WAAW,UAAU,eAAe,uBAAuB,iBAAiB,gBAAgB,MAC/I;CAGF,IAAI,iBAAiB,GACnB,oBAAoB,SAAS;CAG/B,MAAM,UAAU,MAAM,cACpB,WACA,MACA,MACA,SAAS,MAAM,MACf,UACA,UAAU,WACV,kBACA,aACF;CAUA,MAAM,YAAY,IARH,qBACb,kBACA,MAAM,QAAQ,cAAc,GAC5B,UACA,UACA,SACA,EAEgB,GAAI,IAAI;CAC1B,OAAO;AACT;AAEA,eAAsB,2BACpB,UACA,UACA,OACA,UACA,IAGA,kBACA,eACA,UACuB;CAWvB,QAAO,MAVU,eACf,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,QACF,GACU,UAAU;AACtB;AAEA,eAAsB,wBACpB,UACA,UACA,OACA,UACA,UACA,IAGA,kBACA,eACA,UACe;CAWf,QAAO,MAVe,eACpB,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,QACF,GACe,OAAO,QAAQ;AAChC"}
1
+ {"version":3,"file":"rebase.js","names":[],"sources":["../../../../../replicache/src/db/rebase.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {Hash} from '../hash.ts';\nimport type {ZeroTxData} from '../replicache-options.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {WriteTransactionImpl} from '../transactions.ts';\nimport type {MutatorDefs} from '../types.ts';\nimport {\n type Commit,\n type LocalMeta,\n type LocalMetaDD31,\n type Meta,\n assertLocalMetaDD31,\n commitFromHash,\n isLocalMetaDD31,\n} from './commit.ts';\nimport type {Write} from './write.ts';\nimport {newWriteLocal} from './write.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nasync function rebaseMutation(\n mutation: Commit<LocalMetaDD31>,\n dagWrite: DagWrite,\n basisHash: Hash,\n mutators: MutatorDefs,\n lc: LogContext,\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Write> {\n const localMeta = mutation.meta;\n const name = localMeta.mutatorName;\n if (isLocalMetaDD31(localMeta)) {\n assert(\n localMeta.clientID === mutationClientID,\n 'mutationClientID must match clientID of LocalMeta',\n );\n }\n const maybeMutatorImpl = mutators[name];\n if (!maybeMutatorImpl) {\n // Developers must not remove mutator names from code deployed with the\n // same schemaVersion because Replicache needs to be able to replay\n // mutations during pull.\n //\n // If we detect that this has happened, stub in a no-op mutator so that at\n // least sync can move forward. Note that the server-side mutation will\n // still get sent. This doesn't remove the queued local mutation, it just\n // removes its visible effects.\n lc.error?.(`Cannot rebase unknown mutator ${name}`);\n }\n const mutatorImpl =\n maybeMutatorImpl ||\n (async () => {\n // no op\n });\n\n const args = localMeta.mutatorArgsJSON;\n\n const basisCommit = await commitFromHash(basisHash, dagWrite);\n const nextMutationID = await basisCommit.getNextMutationID(\n mutationClientID,\n dagWrite,\n );\n if (nextMutationID !== localMeta.mutationID) {\n throw new Error(\n `Inconsistent mutation ID: original: ${localMeta.mutationID}, next: ${nextMutationID} - mutationClientID: ${mutationClientID} mutatorName: ${name}`,\n );\n }\n\n if (formatVersion >= FormatVersion.DD31) {\n assertLocalMetaDD31(localMeta);\n }\n\n const dbWrite = await newWriteLocal(\n basisHash,\n name,\n args,\n mutation.chunk.hash,\n dagWrite,\n localMeta.timestamp,\n mutationClientID,\n formatVersion,\n );\n\n const tx = new WriteTransactionImpl(\n mutationClientID,\n await dbWrite.getMutationID(),\n 'rebase',\n zeroData,\n dbWrite,\n lc,\n );\n await mutatorImpl(tx, args);\n return dbWrite;\n}\n\nexport async function rebaseMutationAndPutCommit(\n mutation: Commit<LocalMeta>,\n dagWrite: DagWrite,\n basis: Hash,\n mutators: MutatorDefs,\n lc: LogContext,\n // TODO(greg): mutationClientID can be retrieved from mutation if LocalMeta\n // is a LocalMetaDD31. As part of DD31 cleanup we can remove this arg.\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Commit<Meta>> {\n const tx = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return tx.putCommit();\n}\n\nexport async function rebaseMutationAndCommit(\n mutation: Commit<LocalMeta>,\n dagWrite: DagWrite,\n basis: Hash,\n headName: string,\n mutators: MutatorDefs,\n lc: LogContext,\n // TODO(greg): mutationClientID can be retrieved from mutation if LocalMeta\n // is a LocalMetaDD31. As part of DD31 cleanup we can remove this arg.\n mutationClientID: ClientID,\n formatVersion: FormatVersion,\n zeroData: ZeroTxData | undefined,\n): Promise<Hash> {\n const dbWrite = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return dbWrite.commit(headName);\n}\n"],"mappings":";;;;;AAwBA,eAAe,eACb,UACA,UACA,WACA,UACA,IACA,kBACA,eACA,UACgB;CAChB,MAAM,YAAY,SAAS;CAC3B,MAAM,OAAO,UAAU;AACvB,KAAI,gBAAgB,UAAU,CAC5B,QACE,UAAU,aAAa,kBACvB,oDACD;CAEH,MAAM,mBAAmB,SAAS;AAClC,KAAI,CAAC,iBASH,IAAG,QAAQ,iCAAiC,OAAO;CAErD,MAAM,cACJ,qBACC,YAAY;CAIf,MAAM,OAAO,UAAU;CAGvB,MAAM,iBAAiB,OADH,MAAM,eAAe,WAAW,SAAS,EACpB,kBACvC,kBACA,SACD;AACD,KAAI,mBAAmB,UAAU,WAC/B,OAAM,IAAI,MACR,uCAAuC,UAAU,WAAW,UAAU,eAAe,uBAAuB,iBAAiB,gBAAgB,OAC9I;AAGH,KAAI,iBAAiB,EACnB,qBAAoB,UAAU;CAGhC,MAAM,UAAU,MAAM,cACpB,WACA,MACA,MACA,SAAS,MAAM,MACf,UACA,UAAU,WACV,kBACA,cACD;AAUD,OAAM,YARK,IAAI,qBACb,kBACA,MAAM,QAAQ,eAAe,EAC7B,UACA,UACA,SACA,GACD,EACqB,KAAK;AAC3B,QAAO;;AAGT,eAAsB,2BACpB,UACA,UACA,OACA,UACA,IAGA,kBACA,eACA,UACuB;AAWvB,SAVW,MAAM,eACf,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,SACD,EACS,WAAW;;AAGvB,eAAsB,wBACpB,UACA,UACA,OACA,UACA,UACA,IAGA,kBACA,eACA,UACe;AAWf,SAVgB,MAAM,eACpB,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,SACD,EACc,OAAO,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","names":["#dagWrite","#basis","#meta","#clientID","#formatVersion","#generateDiffs"],"sources":["../../../../../replicache/src/db/write.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {diff} from '../btree/diff.ts';\nimport type {InternalDiff} from '../btree/node.ts';\nimport {BTreeRead, allEntriesAsDiff} from '../btree/read.ts';\nimport {BTreeWrite} from '../btree/write.ts';\nimport type {FrozenCookie} from '../cookies.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\nimport {lazy} from '../lazy.ts';\nimport type {DiffComputationConfig} from '../sync/diff.ts';\nimport {DiffsMap} from '../sync/diff.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport type {Commit} from './commit.ts';\nimport {\n type Meta as CommitMeta,\n type IndexRecord,\n type Meta,\n baseSnapshotHashFromHash,\n commitFromHash,\n newLocalDD31 as commitNewLocalDD31,\n newSnapshotDD31 as commitNewSnapshotDD31,\n getMutationID,\n} from './commit.ts';\nimport * as IndexOperation from './index-operation-enum.ts';\nimport type {IndexRead} from './index.ts';\nimport {IndexWrite, indexValue} from './index.ts';\nimport * as MetaType from './meta-type-enum.ts';\nimport {Read, readIndexesForRead} from './read.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class Write extends Read {\n readonly #dagWrite: DagWrite;\n readonly #basis: Commit<CommitMeta> | undefined;\n readonly #meta: CommitMeta;\n\n declare map: BTreeWrite;\n\n declare readonly indexes: Map<string, IndexWrite>;\n readonly #clientID: ClientID;\n readonly #formatVersion: FormatVersion;\n\n constructor(\n dagWrite: DagWrite,\n map: BTreeWrite,\n basis: Commit<CommitMeta> | undefined,\n meta: CommitMeta,\n indexes: Map<string, IndexWrite>,\n clientID: ClientID,\n formatVersion: FormatVersion,\n ) {\n // TypeScript has trouble\n super(dagWrite, map, indexes);\n this.#dagWrite = dagWrite;\n this.#basis = basis;\n this.#meta = meta;\n this.#clientID = clientID;\n this.#formatVersion = formatVersion;\n\n // TODO(arv): if (DEBUG) { ...\n if (basis === undefined) {\n assert(\n meta.basisHash === emptyHash,\n 'Expected basisHash to be emptyHash when basis is undefined',\n );\n } else {\n assert(\n meta.basisHash === basis.chunk.hash,\n 'Expected meta.basisHash to equal basis.chunk.hash',\n );\n }\n }\n\n /**\n * The value needs to be frozen since it is kept in memory and used later for\n * comparison as well as returned in `get`.\n */\n async put(\n lc: LogContext,\n key: string,\n value: FrozenJSONValue,\n ): Promise<void> {\n const oldVal = lazy(() => this.map.get(key));\n await updateIndexes(lc, this.indexes, key, oldVal, value);\n\n await this.map.put(key, value);\n }\n\n /**\n * Inserts or updates multiple entries in a single bulk operation. More\n * efficient than calling {@link put} in a loop because it uses\n * {@link BTreeWrite.putMany} to build or merge tree nodes in one pass.\n *\n * `entries` must be sorted in ascending key order.\n */\n async putMany(\n lc: LogContext,\n entries: ReadonlyArray<readonly [string, FrozenJSONValue]>,\n ): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n\n // Update indexes for all entries\n if (this.indexes.size > 0) {\n // TODO(arv): Indexes can use the optimizePatch pattern too.\n for (const [key, value] of entries) {\n const oldVal = lazy(() => this.map.get(key));\n await updateIndexes(lc, this.indexes, key, oldVal, value);\n }\n }\n\n // BTreeWrite.putMany handles both empty and non-empty trees optimally\n await this.map.putMany(entries);\n }\n\n getMutationID(): Promise<number> {\n return getMutationID(this.#clientID, this.#dagWrite, this.#meta);\n }\n\n async del(lc: LogContext, key: string): Promise<boolean> {\n // TODO(arv): This does the binary search twice. We can do better.\n const oldVal = lazy(() => this.map.get(key));\n if (oldVal !== undefined) {\n await updateIndexes(lc, this.indexes, key, oldVal, undefined);\n }\n return this.map.del(key);\n }\n\n async clear(): Promise<void> {\n await this.map.clear();\n const ps = [];\n for (const idx of this.indexes.values()) {\n ps.push(idx.clear());\n }\n await Promise.all(ps);\n }\n\n async putCommit(): Promise<Commit<CommitMeta>> {\n const valueHash = await this.map.flush();\n const indexRecords: IndexRecord[] = [];\n\n for (const index of this.indexes.values()) {\n const valueHash = await index.flush();\n const indexRecord: IndexRecord = {\n definition: index.meta.definition,\n valueHash,\n };\n indexRecords.push(indexRecord);\n }\n\n let commit: Commit<Meta>;\n const meta = this.#meta;\n switch (meta.type) {\n case MetaType.LocalDD31: {\n assert(\n this.#formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for LocalDD31 commit',\n );\n const {\n basisHash,\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n timestamp,\n } = meta;\n commit = commitNewLocalDD31(\n this.#dagWrite.createChunk,\n basisHash,\n await baseSnapshotHashFromHash(basisHash, this.#dagWrite),\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n valueHash,\n indexRecords,\n timestamp,\n this.#clientID,\n );\n break;\n }\n\n case MetaType.SnapshotDD31: {\n assert(\n this.#formatVersion > FormatVersion.DD31,\n 'Expected formatVersion > DD31 for SnapshotDD31 commit',\n );\n const {basisHash, lastMutationIDs, cookieJSON} = meta;\n commit = commitNewSnapshotDD31(\n this.#dagWrite.createChunk,\n basisHash,\n lastMutationIDs,\n cookieJSON,\n valueHash,\n indexRecords,\n );\n break;\n }\n }\n await this.#dagWrite.putChunk(commit.chunk);\n return commit;\n }\n\n // Return value is the hash of the new commit.\n async commit(headName: string): Promise<Hash> {\n const commit = await this.putCommit();\n const commitHash = commit.chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return commitHash;\n }\n\n async commitWithDiffs(\n headName: string,\n diffConfig: DiffComputationConfig,\n ): Promise<[Hash, DiffsMap]> {\n const commit = this.putCommit();\n const diffMap = await this.#generateDiffs(diffConfig);\n const commitHash = (await commit).chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return [commitHash, diffMap];\n }\n\n async #generateDiffs(diffConfig: DiffComputationConfig): Promise<DiffsMap> {\n const diffsMap = new DiffsMap();\n if (!diffConfig.shouldComputeDiffs()) {\n return diffsMap;\n }\n\n let valueDiff: InternalDiff = [];\n if (this.#basis) {\n const basisMap = new BTreeRead(\n this.#dagWrite,\n this.#formatVersion,\n this.#basis.valueHash,\n );\n valueDiff = await diff(basisMap, this.map);\n }\n diffsMap.set('', valueDiff);\n let basisIndexes: Map<string, IndexRead>;\n if (this.#basis) {\n basisIndexes = readIndexesForRead(\n this.#basis,\n this.#dagWrite,\n this.#formatVersion,\n );\n } else {\n basisIndexes = new Map();\n }\n\n for (const [name, index] of this.indexes) {\n if (!diffConfig.shouldComputeDiffsForIndex(name)) {\n continue;\n }\n const basisIndex = basisIndexes.get(name);\n assert(index !== basisIndex, 'Expected index to differ from basisIndex');\n\n const indexDiffResult = await (basisIndex\n ? diff(basisIndex.map, index.map)\n : // No basis. All keys are new.\n allEntriesAsDiff(index.map, 'add'));\n diffsMap.set(name, indexDiffResult);\n }\n\n // Handle indexes in basisIndex but not in this.indexes. All keys are\n // deleted.\n for (const [name, basisIndex] of basisIndexes) {\n if (\n !this.indexes.has(name) &&\n diffConfig.shouldComputeDiffsForIndex(name)\n ) {\n const indexDiffResult = await allEntriesAsDiff(basisIndex.map, 'del');\n diffsMap.set(name, indexDiffResult);\n }\n }\n return diffsMap;\n }\n\n close(): void {\n this.#dagWrite.release();\n }\n}\n\nexport async function newWriteLocal(\n basisHash: Hash,\n mutatorName: string,\n mutatorArgsJSON: FrozenJSONValue,\n originalHash: Hash | null,\n dagWrite: DagWrite,\n timestamp: number,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n const mutationID = await basis.getNextMutationID(clientID, dagWrite);\n const indexes = readIndexesForWrite(basis, dagWrite, formatVersion);\n assert(\n formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for newWriteLocal',\n );\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n\n {\n type: MetaType.LocalDD31,\n basisHash,\n baseSnapshotHash: await baseSnapshotHashFromHash(basisHash, dagWrite),\n mutatorName,\n mutatorArgsJSON,\n mutationID,\n originalHash,\n timestamp,\n clientID,\n },\n indexes,\n clientID,\n formatVersion,\n );\n}\n\nexport async function newWriteSnapshotDD31(\n basisHash: Hash,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n dagWrite: DagWrite,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n {basisHash, type: MetaType.SnapshotDD31, lastMutationIDs, cookieJSON},\n readIndexesForWrite(basis, dagWrite, formatVersion),\n clientID,\n formatVersion,\n );\n}\n\nexport async function updateIndexes(\n lc: LogContext,\n indexes: Map<string, IndexWrite>,\n key: string,\n oldValGetter: () => Promise<FrozenJSONValue | undefined>,\n newVal: FrozenJSONValue | undefined,\n): Promise<void> {\n const ps: Promise<void>[] = [];\n for (const idx of indexes.values()) {\n const {keyPrefix} = idx.meta.definition;\n if (!keyPrefix || key.startsWith(keyPrefix)) {\n const oldVal = await oldValGetter();\n if (oldVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Remove,\n key,\n oldVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n if (newVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Add,\n key,\n newVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n }\n }\n await Promise.all(ps);\n}\n\nexport function readIndexesForWrite(\n commit: Commit<CommitMeta>,\n dagWrite: DagWrite,\n formatVersion: FormatVersion,\n): Map<string, IndexWrite> {\n const m = new Map();\n for (const index of commit.indexes) {\n m.set(\n index.definition.name,\n new IndexWrite(\n index,\n new BTreeWrite(dagWrite, formatVersion, index.valueHash),\n ),\n );\n }\n return m;\n}\n\nexport async function createIndexBTree(\n lc: LogContext,\n dagWrite: DagWrite,\n valueMap: BTreeRead,\n prefix: string,\n jsonPointer: string,\n allowEmpty: boolean,\n formatVersion: FormatVersion,\n): Promise<BTreeWrite> {\n const indexMap = new BTreeWrite(dagWrite, formatVersion);\n for await (const entry of valueMap.scan(prefix)) {\n const key = entry[0];\n if (!key.startsWith(prefix)) {\n break;\n }\n await indexValue(\n lc,\n indexMap,\n IndexOperation.Add,\n key,\n entry[1],\n jsonPointer,\n allowEmpty,\n );\n }\n return indexMap;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"write.js","names":["#dagWrite","#basis","#meta","#clientID","#formatVersion","#generateDiffs"],"sources":["../../../../../replicache/src/db/write.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Enum} from '../../../shared/src/enum.ts';\nimport {diff} from '../btree/diff.ts';\nimport type {InternalDiff} from '../btree/node.ts';\nimport {BTreeRead, allEntriesAsDiff} from '../btree/read.ts';\nimport {BTreeWrite} from '../btree/write.ts';\nimport type {FrozenCookie} from '../cookies.ts';\nimport type {Write as DagWrite} from '../dag/store.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\nimport {lazy} from '../lazy.ts';\nimport type {DiffComputationConfig} from '../sync/diff.ts';\nimport {DiffsMap} from '../sync/diff.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport type {Commit} from './commit.ts';\nimport {\n type Meta as CommitMeta,\n type IndexRecord,\n type Meta,\n baseSnapshotHashFromHash,\n commitFromHash,\n newLocalDD31 as commitNewLocalDD31,\n newSnapshotDD31 as commitNewSnapshotDD31,\n getMutationID,\n} from './commit.ts';\nimport * as IndexOperation from './index-operation-enum.ts';\nimport type {IndexRead} from './index.ts';\nimport {IndexWrite, indexValue} from './index.ts';\nimport * as MetaType from './meta-type-enum.ts';\nimport {Read, readIndexesForRead} from './read.ts';\n\ntype FormatVersion = Enum<typeof FormatVersion>;\n\nexport class Write extends Read {\n readonly #dagWrite: DagWrite;\n readonly #basis: Commit<CommitMeta> | undefined;\n readonly #meta: CommitMeta;\n\n declare map: BTreeWrite;\n\n declare readonly indexes: Map<string, IndexWrite>;\n readonly #clientID: ClientID;\n readonly #formatVersion: FormatVersion;\n\n constructor(\n dagWrite: DagWrite,\n map: BTreeWrite,\n basis: Commit<CommitMeta> | undefined,\n meta: CommitMeta,\n indexes: Map<string, IndexWrite>,\n clientID: ClientID,\n formatVersion: FormatVersion,\n ) {\n // TypeScript has trouble\n super(dagWrite, map, indexes);\n this.#dagWrite = dagWrite;\n this.#basis = basis;\n this.#meta = meta;\n this.#clientID = clientID;\n this.#formatVersion = formatVersion;\n\n // TODO(arv): if (DEBUG) { ...\n if (basis === undefined) {\n assert(\n meta.basisHash === emptyHash,\n 'Expected basisHash to be emptyHash when basis is undefined',\n );\n } else {\n assert(\n meta.basisHash === basis.chunk.hash,\n 'Expected meta.basisHash to equal basis.chunk.hash',\n );\n }\n }\n\n /**\n * The value needs to be frozen since it is kept in memory and used later for\n * comparison as well as returned in `get`.\n */\n async put(\n lc: LogContext,\n key: string,\n value: FrozenJSONValue,\n ): Promise<void> {\n const oldVal = lazy(() => this.map.get(key));\n await updateIndexes(lc, this.indexes, key, oldVal, value);\n\n await this.map.put(key, value);\n }\n\n /**\n * Inserts or updates multiple entries in a single bulk operation. More\n * efficient than calling {@link put} in a loop because it uses\n * {@link BTreeWrite.putMany} to build or merge tree nodes in one pass.\n *\n * `entries` must be sorted in ascending key order.\n */\n async putMany(\n lc: LogContext,\n entries: ReadonlyArray<readonly [string, FrozenJSONValue]>,\n ): Promise<void> {\n if (entries.length === 0) {\n return;\n }\n\n // Update indexes for all entries\n if (this.indexes.size > 0) {\n // TODO(arv): Indexes can use the optimizePatch pattern too.\n for (const [key, value] of entries) {\n const oldVal = lazy(() => this.map.get(key));\n await updateIndexes(lc, this.indexes, key, oldVal, value);\n }\n }\n\n // BTreeWrite.putMany handles both empty and non-empty trees optimally\n await this.map.putMany(entries);\n }\n\n getMutationID(): Promise<number> {\n return getMutationID(this.#clientID, this.#dagWrite, this.#meta);\n }\n\n async del(lc: LogContext, key: string): Promise<boolean> {\n // TODO(arv): This does the binary search twice. We can do better.\n const oldVal = lazy(() => this.map.get(key));\n if (oldVal !== undefined) {\n await updateIndexes(lc, this.indexes, key, oldVal, undefined);\n }\n return this.map.del(key);\n }\n\n async clear(): Promise<void> {\n await this.map.clear();\n const ps = [];\n for (const idx of this.indexes.values()) {\n ps.push(idx.clear());\n }\n await Promise.all(ps);\n }\n\n async putCommit(): Promise<Commit<CommitMeta>> {\n const valueHash = await this.map.flush();\n const indexRecords: IndexRecord[] = [];\n\n for (const index of this.indexes.values()) {\n const valueHash = await index.flush();\n const indexRecord: IndexRecord = {\n definition: index.meta.definition,\n valueHash,\n };\n indexRecords.push(indexRecord);\n }\n\n let commit: Commit<Meta>;\n const meta = this.#meta;\n switch (meta.type) {\n case MetaType.LocalDD31: {\n assert(\n this.#formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for LocalDD31 commit',\n );\n const {\n basisHash,\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n timestamp,\n } = meta;\n commit = commitNewLocalDD31(\n this.#dagWrite.createChunk,\n basisHash,\n await baseSnapshotHashFromHash(basisHash, this.#dagWrite),\n mutationID,\n mutatorName,\n mutatorArgsJSON,\n originalHash,\n valueHash,\n indexRecords,\n timestamp,\n this.#clientID,\n );\n break;\n }\n\n case MetaType.SnapshotDD31: {\n assert(\n this.#formatVersion > FormatVersion.DD31,\n 'Expected formatVersion > DD31 for SnapshotDD31 commit',\n );\n const {basisHash, lastMutationIDs, cookieJSON} = meta;\n commit = commitNewSnapshotDD31(\n this.#dagWrite.createChunk,\n basisHash,\n lastMutationIDs,\n cookieJSON,\n valueHash,\n indexRecords,\n );\n break;\n }\n }\n await this.#dagWrite.putChunk(commit.chunk);\n return commit;\n }\n\n // Return value is the hash of the new commit.\n async commit(headName: string): Promise<Hash> {\n const commit = await this.putCommit();\n const commitHash = commit.chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return commitHash;\n }\n\n async commitWithDiffs(\n headName: string,\n diffConfig: DiffComputationConfig,\n ): Promise<[Hash, DiffsMap]> {\n const commit = this.putCommit();\n const diffMap = await this.#generateDiffs(diffConfig);\n const commitHash = (await commit).chunk.hash;\n await this.#dagWrite.setHead(headName, commitHash);\n await this.#dagWrite.commit();\n return [commitHash, diffMap];\n }\n\n async #generateDiffs(diffConfig: DiffComputationConfig): Promise<DiffsMap> {\n const diffsMap = new DiffsMap();\n if (!diffConfig.shouldComputeDiffs()) {\n return diffsMap;\n }\n\n let valueDiff: InternalDiff = [];\n if (this.#basis) {\n const basisMap = new BTreeRead(\n this.#dagWrite,\n this.#formatVersion,\n this.#basis.valueHash,\n );\n valueDiff = await diff(basisMap, this.map);\n }\n diffsMap.set('', valueDiff);\n let basisIndexes: Map<string, IndexRead>;\n if (this.#basis) {\n basisIndexes = readIndexesForRead(\n this.#basis,\n this.#dagWrite,\n this.#formatVersion,\n );\n } else {\n basisIndexes = new Map();\n }\n\n for (const [name, index] of this.indexes) {\n if (!diffConfig.shouldComputeDiffsForIndex(name)) {\n continue;\n }\n const basisIndex = basisIndexes.get(name);\n assert(index !== basisIndex, 'Expected index to differ from basisIndex');\n\n const indexDiffResult = await (basisIndex\n ? diff(basisIndex.map, index.map)\n : // No basis. All keys are new.\n allEntriesAsDiff(index.map, 'add'));\n diffsMap.set(name, indexDiffResult);\n }\n\n // Handle indexes in basisIndex but not in this.indexes. All keys are\n // deleted.\n for (const [name, basisIndex] of basisIndexes) {\n if (\n !this.indexes.has(name) &&\n diffConfig.shouldComputeDiffsForIndex(name)\n ) {\n const indexDiffResult = await allEntriesAsDiff(basisIndex.map, 'del');\n diffsMap.set(name, indexDiffResult);\n }\n }\n return diffsMap;\n }\n\n close(): void {\n this.#dagWrite.release();\n }\n}\n\nexport async function newWriteLocal(\n basisHash: Hash,\n mutatorName: string,\n mutatorArgsJSON: FrozenJSONValue,\n originalHash: Hash | null,\n dagWrite: DagWrite,\n timestamp: number,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n const mutationID = await basis.getNextMutationID(clientID, dagWrite);\n const indexes = readIndexesForWrite(basis, dagWrite, formatVersion);\n assert(\n formatVersion >= FormatVersion.DD31,\n 'Expected formatVersion >= DD31 for newWriteLocal',\n );\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n\n {\n type: MetaType.LocalDD31,\n basisHash,\n baseSnapshotHash: await baseSnapshotHashFromHash(basisHash, dagWrite),\n mutatorName,\n mutatorArgsJSON,\n mutationID,\n originalHash,\n timestamp,\n clientID,\n },\n indexes,\n clientID,\n formatVersion,\n );\n}\n\nexport async function newWriteSnapshotDD31(\n basisHash: Hash,\n lastMutationIDs: Record<ClientID, number>,\n cookieJSON: FrozenCookie,\n dagWrite: DagWrite,\n clientID: ClientID,\n formatVersion: FormatVersion,\n): Promise<Write> {\n const basis = await commitFromHash(basisHash, dagWrite);\n const bTreeWrite = new BTreeWrite(dagWrite, formatVersion, basis.valueHash);\n return new Write(\n dagWrite,\n bTreeWrite,\n basis,\n {basisHash, type: MetaType.SnapshotDD31, lastMutationIDs, cookieJSON},\n readIndexesForWrite(basis, dagWrite, formatVersion),\n clientID,\n formatVersion,\n );\n}\n\nexport async function updateIndexes(\n lc: LogContext,\n indexes: Map<string, IndexWrite>,\n key: string,\n oldValGetter: () => Promise<FrozenJSONValue | undefined>,\n newVal: FrozenJSONValue | undefined,\n): Promise<void> {\n const ps: Promise<void>[] = [];\n for (const idx of indexes.values()) {\n const {keyPrefix} = idx.meta.definition;\n if (!keyPrefix || key.startsWith(keyPrefix)) {\n const oldVal = await oldValGetter();\n if (oldVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Remove,\n key,\n oldVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n if (newVal !== undefined) {\n ps.push(\n indexValue(\n lc,\n idx.map,\n IndexOperation.Add,\n key,\n newVal,\n idx.meta.definition.jsonPointer,\n idx.meta.definition.allowEmpty ?? false,\n ),\n );\n }\n }\n }\n await Promise.all(ps);\n}\n\nexport function readIndexesForWrite(\n commit: Commit<CommitMeta>,\n dagWrite: DagWrite,\n formatVersion: FormatVersion,\n): Map<string, IndexWrite> {\n const m = new Map();\n for (const index of commit.indexes) {\n m.set(\n index.definition.name,\n new IndexWrite(\n index,\n new BTreeWrite(dagWrite, formatVersion, index.valueHash),\n ),\n );\n }\n return m;\n}\n\nexport async function createIndexBTree(\n lc: LogContext,\n dagWrite: DagWrite,\n valueMap: BTreeRead,\n prefix: string,\n jsonPointer: string,\n allowEmpty: boolean,\n formatVersion: FormatVersion,\n): Promise<BTreeWrite> {\n const indexMap = new BTreeWrite(dagWrite, formatVersion);\n for await (const entry of valueMap.scan(prefix)) {\n const key = entry[0];\n if (!key.startsWith(prefix)) {\n break;\n }\n await indexValue(\n lc,\n indexMap,\n IndexOperation.Add,\n key,\n entry[1],\n jsonPointer,\n allowEmpty,\n );\n }\n return indexMap;\n}\n"],"mappings":"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"}