@rocicorp/zero 1.11.0-canary.7 → 1.11.0-canary.9

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 (258) hide show
  1. package/out/replicache/src/dag/gc.d.ts.map +1 -1
  2. package/out/replicache/src/dag/gc.js +2 -1
  3. package/out/replicache/src/dag/gc.js.map +1 -1
  4. package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
  5. package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
  6. package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
  7. package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
  8. package/out/replicache/src/dag/store-impl.d.ts +12 -2
  9. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  10. package/out/replicache/src/dag/store-impl.js +22 -8
  11. package/out/replicache/src/dag/store-impl.js.map +1 -1
  12. package/out/replicache/src/db/rebase.d.ts +14 -2
  13. package/out/replicache/src/db/rebase.d.ts.map +1 -1
  14. package/out/replicache/src/db/rebase.js +27 -5
  15. package/out/replicache/src/db/rebase.js.map +1 -1
  16. package/out/replicache/src/persist/collect-idb-databases.d.ts +10 -5
  17. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  18. package/out/replicache/src/persist/collect-idb-databases.js +16 -9
  19. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  20. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  21. package/out/replicache/src/persist/persist.js +12 -5
  22. package/out/replicache/src/persist/persist.js.map +1 -1
  23. package/out/replicache/src/persist/refresh.js +6 -2
  24. package/out/replicache/src/persist/refresh.js.map +1 -1
  25. package/out/replicache/src/replicache-impl.d.ts +7 -3
  26. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  27. package/out/replicache/src/replicache-impl.js +98 -11
  28. package/out/replicache/src/replicache-impl.js.map +1 -1
  29. package/out/replicache/src/replicache-options.d.ts +9 -0
  30. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  31. package/out/shared/src/iterables.d.ts +2 -0
  32. package/out/shared/src/iterables.d.ts.map +1 -1
  33. package/out/shared/src/iterables.js +18 -1
  34. package/out/shared/src/iterables.js.map +1 -1
  35. package/out/shared/src/ring-buffer.d.ts +8 -0
  36. package/out/shared/src/ring-buffer.d.ts.map +1 -1
  37. package/out/shared/src/ring-buffer.js +22 -1
  38. package/out/shared/src/ring-buffer.js.map +1 -1
  39. package/out/zero/package.js +2 -3
  40. package/out/zero/package.js.map +1 -1
  41. package/out/zero-cache/src/config/zero-config.js +2 -2
  42. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  43. package/out/zero-cache/src/server/change-streamer.js +1 -1
  44. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  45. package/out/zero-cache/src/server/inspector-delegate.d.ts +18 -2
  46. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  47. package/out/zero-cache/src/server/inspector-delegate.js +20 -7
  48. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  49. package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts +156 -0
  50. package/out/zero-cache/src/services/change-source/pg/backfill-resume.d.ts.map +1 -0
  51. package/out/zero-cache/src/services/change-source/pg/backfill-resume.js +15 -0
  52. package/out/zero-cache/src/services/change-source/pg/backfill-resume.js.map +1 -0
  53. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +15 -0
  54. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -0
  55. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +99 -0
  56. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -0
  57. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +14 -16
  58. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  59. package/out/zero-cache/src/services/change-source/pg/change-source.js +33 -126
  60. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  61. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +16 -1
  62. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  63. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +8 -4
  64. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  65. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +16 -0
  66. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +1 -0
  67. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +113 -0
  68. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -0
  69. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +1 -15
  70. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  71. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +18 -21
  72. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  73. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +8 -9
  74. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  75. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +17 -1
  76. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -1
  77. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +19 -3
  78. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  79. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -1
  80. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +1 -0
  81. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -1
  82. package/out/zero-cache/src/services/replicator/change-processor.js +1 -0
  83. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  84. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
  85. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  86. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
  87. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  88. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +1 -1
  89. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  90. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +2 -2
  91. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  92. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  93. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +6 -10
  94. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  95. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  96. package/out/zero-cache/src/services/view-syncer/view-syncer.js +14 -4
  97. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  98. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  99. package/out/zero-cache/src/types/streams.js +1 -1
  100. package/out/zero-cache/src/types/streams.js.map +1 -1
  101. package/out/zero-client/src/client/connection-manager.d.ts +20 -0
  102. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  103. package/out/zero-client/src/client/connection-manager.js +32 -14
  104. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  105. package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
  106. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
  107. package/out/zero-client/src/client/connection-status-enum.js +3 -1
  108. package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
  109. package/out/zero-client/src/client/connection.d.ts +6 -0
  110. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  111. package/out/zero-client/src/client/connection.js +2 -1
  112. package/out/zero-client/src/client/connection.js.map +1 -1
  113. package/out/zero-client/src/client/context.d.ts +8 -11
  114. package/out/zero-client/src/client/context.d.ts.map +1 -1
  115. package/out/zero-client/src/client/context.js +11 -15
  116. package/out/zero-client/src/client/context.js.map +1 -1
  117. package/out/zero-client/src/client/custom.js +2 -2
  118. package/out/zero-client/src/client/custom.js.map +1 -1
  119. package/out/zero-client/src/client/error.js +1 -1
  120. package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
  121. package/out/zero-client/src/client/mutator-proxy.js +2 -1
  122. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  123. package/out/zero-client/src/client/options.d.ts +3 -0
  124. package/out/zero-client/src/client/options.d.ts.map +1 -1
  125. package/out/zero-client/src/client/options.js.map +1 -1
  126. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  127. package/out/zero-client/src/client/query-manager.js +42 -6
  128. package/out/zero-client/src/client/query-manager.js.map +1 -1
  129. package/out/zero-client/src/client/version.js +1 -1
  130. package/out/zero-client/src/client/zero-rep.d.ts +1 -0
  131. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  132. package/out/zero-client/src/client/zero-rep.js +7 -5
  133. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  134. package/out/zero-client/src/client/zero.d.ts +9 -3
  135. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  136. package/out/zero-client/src/client/zero.js +48 -11
  137. package/out/zero-client/src/client/zero.js.map +1 -1
  138. package/out/zero-client/src/types/query-result.d.ts +2 -0
  139. package/out/zero-client/src/types/query-result.d.ts.map +1 -1
  140. package/out/zero-react/src/use-query.d.ts +5 -4
  141. package/out/zero-react/src/use-query.d.ts.map +1 -1
  142. package/out/zero-react/src/use-query.js +12 -1
  143. package/out/zero-react/src/use-query.js.map +1 -1
  144. package/out/zero-server/src/adapters/drizzle.d.ts +2 -1
  145. package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
  146. package/out/zero-server/src/adapters/drizzle.js +12 -7
  147. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  148. package/out/zero-server/src/adapters/kysely.d.ts +2 -1
  149. package/out/zero-server/src/adapters/kysely.d.ts.map +1 -1
  150. package/out/zero-server/src/adapters/kysely.js +8 -2
  151. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  152. package/out/zero-server/src/adapters/pg.d.ts +1 -0
  153. package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
  154. package/out/zero-server/src/adapters/pg.js +8 -2
  155. package/out/zero-server/src/adapters/pg.js.map +1 -1
  156. package/out/zero-server/src/adapters/postgresjs.d.ts +1 -0
  157. package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
  158. package/out/zero-server/src/adapters/postgresjs.js +7 -1
  159. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  160. package/out/zero-server/src/adapters/prisma.d.ts +3 -2
  161. package/out/zero-server/src/adapters/prisma.d.ts.map +1 -1
  162. package/out/zero-server/src/adapters/prisma.js +8 -2
  163. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  164. package/out/zero-server/src/custom.d.ts +5 -1
  165. package/out/zero-server/src/custom.d.ts.map +1 -1
  166. package/out/zero-server/src/custom.js +6 -3
  167. package/out/zero-server/src/custom.js.map +1 -1
  168. package/out/zero-server/src/mod.d.ts +1 -1
  169. package/out/zero-server/src/mod.d.ts.map +1 -1
  170. package/out/zero-server/src/pg-query-executor.d.ts +2 -2
  171. package/out/zero-server/src/pg-query-executor.d.ts.map +1 -1
  172. package/out/zero-server/src/pg-query-executor.js +2 -2
  173. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  174. package/out/zero-server/src/schema.d.ts +2 -2
  175. package/out/zero-server/src/schema.d.ts.map +1 -1
  176. package/out/zero-server/src/schema.js +2 -2
  177. package/out/zero-server/src/schema.js.map +1 -1
  178. package/out/zero-server/src/zql-database.d.ts +21 -1
  179. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  180. package/out/zero-server/src/zql-database.js +33 -2
  181. package/out/zero-server/src/zql-database.js.map +1 -1
  182. package/out/zero-solid/src/solid-view.d.ts +10 -0
  183. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  184. package/out/zero-solid/src/solid-view.js +27 -0
  185. package/out/zero-solid/src/solid-view.js.map +1 -1
  186. package/out/zql/src/ivm/array-view.d.ts +10 -0
  187. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  188. package/out/zql/src/ivm/array-view.js +20 -8
  189. package/out/zql/src/ivm/array-view.js.map +1 -1
  190. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  191. package/out/zql/src/ivm/cap.js +5 -3
  192. package/out/zql/src/ivm/cap.js.map +1 -1
  193. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -1
  194. package/out/zql/src/ivm/deferred-input.js +3 -2
  195. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  196. package/out/zql/src/ivm/exists.d.ts +1 -1
  197. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  198. package/out/zql/src/ivm/exists.js.map +1 -1
  199. package/out/zql/src/ivm/fan-in.d.ts +2 -2
  200. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  201. package/out/zql/src/ivm/fan-in.js +6 -5
  202. package/out/zql/src/ivm/fan-in.js.map +1 -1
  203. package/out/zql/src/ivm/fan-out.d.ts +2 -2
  204. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  205. package/out/zql/src/ivm/fan-out.js.map +1 -1
  206. package/out/zql/src/ivm/filter-operators.d.ts +6 -6
  207. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  208. package/out/zql/src/ivm/filter-operators.js +10 -8
  209. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  210. package/out/zql/src/ivm/filter.d.ts +2 -2
  211. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  212. package/out/zql/src/ivm/filter.js +7 -4
  213. package/out/zql/src/ivm/filter.js.map +1 -1
  214. package/out/zql/src/ivm/flipped-join.js +2 -2
  215. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  216. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  217. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  218. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
  219. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  220. package/out/zql/src/ivm/memory-source.d.ts +2 -2
  221. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  222. package/out/zql/src/ivm/memory-source.js +9 -9
  223. package/out/zql/src/ivm/memory-source.js.map +1 -1
  224. package/out/zql/src/ivm/take.d.ts.map +1 -1
  225. package/out/zql/src/ivm/take.js +3 -4
  226. package/out/zql/src/ivm/take.js.map +1 -1
  227. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  228. package/out/zql/src/ivm/union-fan-in.js +9 -7
  229. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  230. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  231. package/out/zql/src/ivm/view-apply-change.js +3 -3
  232. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  233. package/out/zql/src/mutate/custom.d.ts +24 -1
  234. package/out/zql/src/mutate/custom.d.ts.map +1 -1
  235. package/out/zql/src/mutate/custom.js.map +1 -1
  236. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  237. package/out/zql/src/mutate/mutator-registry.js +2 -2
  238. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  239. package/out/zql/src/query/query-delegate-base.d.ts +2 -0
  240. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  241. package/out/zql/src/query/query-delegate-base.js +55 -28
  242. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  243. package/out/zql/src/query/query-delegate.d.ts +11 -1
  244. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  245. package/out/zql/src/query/query-impl.d.ts +1 -0
  246. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  247. package/out/zql/src/query/query-impl.js.map +1 -1
  248. package/out/zql/src/query/query.d.ts +7 -1
  249. package/out/zql/src/query/query.d.ts.map +1 -1
  250. package/out/zql/src/query/runnable-query-impl.d.ts +1 -0
  251. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  252. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  253. package/out/zql/src/query/typed-view.d.ts +1 -1
  254. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  255. package/out/zqlite/src/table-source.d.ts.map +1 -1
  256. package/out/zqlite/src/table-source.js +7 -5
  257. package/out/zqlite/src/table-source.js.map +1 -1
  258. package/package.json +2 -3
@@ -1 +1 @@
1
- {"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/dag/gc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,8BAA8B,CAAC;AAE/D,OAAO,EAAC,KAAK,IAAI,EAAY,MAAM,YAAY,CAAC;AAEhD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,IAAI,GAAG,SAAS,CAAC;IACtB,GAAG,EAAE,IAAI,GAAG,SAAS,CAAC;CACvB,CAAC;AAIF,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,YAAY,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9D,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,YAAY,CAAC,SAAS,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC;IACnE;;;;;;;;;;;;;;;;;;OAkBG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EACjC,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,EAC5B,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAE5B"}
1
+ {"version":3,"file":"gc.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/dag/gc.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,8BAA8B,CAAC;AAE/D,OAAO,EAAC,KAAK,IAAI,EAAY,MAAM,YAAY,CAAC;AAGhD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,IAAI,GAAG,SAAS,CAAC;IACtB,GAAG,EAAE,IAAI,GAAG,SAAS,CAAC;CACvB,CAAC;AAIF,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,YAAY,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC9D,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,YAAY,CAAC,SAAS,IAAI,EAAE,GAAG,SAAS,CAAC,CAAC;IACnE;;;;;;;;;;;;;;;;;;OAkBG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;CAC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,EACjC,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,EAC5B,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAE5B"}
@@ -1,6 +1,7 @@
1
1
  import { assert, assertNumber } from "../../../shared/src/asserts.js";
2
2
  import { isProd } from "../../../shared/src/config.js";
3
3
  import "../config.js";
4
+ import { InvalidRefCountError } from "./invalid-ref-count-error.js";
4
5
  import { emptyHash } from "../hash.js";
5
6
  //#region ../replicache/src/dag/gc.ts
6
7
  /**
@@ -58,7 +59,7 @@ var RefCountUpdates = class {
58
59
  } while (refCountsUpdated);
59
60
  }
60
61
  for (const o of this.#oldHeads) await this.#changeRefCount(o, -1);
61
- if (!isProd) for (const [hash, update] of this.#refCountUpdates) assert(update >= 0, `ref count update must be non-negative. ${hash}:${update}`);
62
+ for (const [hash, update] of this.#refCountUpdates) if (!(update >= 0)) throw new InvalidRefCountError(hash, update);
62
63
  return this.#refCountUpdates;
63
64
  }
64
65
  async #changeRefCount(hash, delta) {
@@ -1 +1 @@
1
- {"version":3,"file":"gc.js","names":["#newHeads","#oldHeads","#putChunks","#delegate","#refsCounted","#refCountUpdates","#loadedRefCountPromises","#isLazyDelegate","#changeRefCount","#ensureRefCountLoaded","#updateRefsCounts","#updateRefCount"],"sources":["../../../../../replicache/src/dag/gc.ts"],"sourcesContent":["import {assert, assertNumber} from '../../../shared/src/asserts.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport {skipGCAsserts} from '../config.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\n\nexport type HeadChange = {\n new: Hash | undefined;\n old: Hash | undefined;\n};\n\ntype LoadedRefCountPromises = Map<Hash, Promise<number>>;\n\nexport interface RefCountUpdatesDelegate {\n getRefCount: (hash: Hash) => MaybePromise<number | undefined>;\n getRefs: (hash: Hash) => MaybePromise<readonly Hash[] | undefined>;\n /**\n * Should be implemented if the store lazily loads refs, returning whether\n * or not the chunks refs have already been counted (i.e. are reflected\n * in `getRefCount`).\n *\n * If defined then:\n * - `getRefs` should return undefined for refs that have not been loaded,\n * but should never return undefined for hashes in `putChunks`.\n * - it is assumed that chunks in `putChunks` may have been reachable before\n * the write, but may not have been counted. This method is used to\n * determine if they have been counted or not. If they have not been\n * counted, and are reachable with the write applied, the returned\n * ref count updates will include updates for counting them.\n *\n * If undefined then:\n * - `getRefs` should never return undefined\n * - it is assumed that the refs of any chunks which were reachable before\n * the write are already counted\n */\n areRefsCounted?: (hash: Hash) => boolean;\n}\n\n/**\n * Computes how ref counts should be updated when a dag write is committed.\n * Does not modify the dag store.\n * @param headChanges Heads that were changed by the dag write.\n * @param putChunks Chunks that were put by the dag write.\n * @param delegate Delegate used for getting ref information from the dag store.\n * @returns Map from chunk Hash to changed ref counts. Chunks with a new ref\n * count of 0 should be deleted. All hashes in `putChunks` will have an entry\n * (which will be zero if the newly put chunk is not reachable from any head).\n */\nexport function computeRefCountUpdates(\n headChanges: Iterable<HeadChange>,\n putChunks: ReadonlySet<Hash>,\n delegate: RefCountUpdatesDelegate,\n): Promise<Map<Hash, number>> {\n return new RefCountUpdates(headChanges, putChunks, delegate).compute();\n}\n\nclass RefCountUpdates {\n readonly #newHeads: Hash[];\n readonly #oldHeads: Hash[];\n readonly #putChunks: ReadonlySet<Hash>;\n readonly #delegate: RefCountUpdatesDelegate;\n readonly #refsCounted: Set<Hash> | null;\n readonly #refCountUpdates: Map<Hash, number>;\n readonly #loadedRefCountPromises: LoadedRefCountPromises;\n readonly #isLazyDelegate: boolean;\n\n constructor(\n headChanges: Iterable<HeadChange>,\n putChunks: ReadonlySet<Hash>,\n delegate: RefCountUpdatesDelegate,\n ) {\n const newHeads: Hash[] = [];\n const oldHeads: Hash[] = [];\n for (const changedHead of headChanges) {\n if (changedHead.old !== changedHead.new) {\n changedHead.old && oldHeads.push(changedHead.old);\n changedHead.new && newHeads.push(changedHead.new);\n }\n }\n this.#newHeads = newHeads;\n this.#oldHeads = oldHeads;\n this.#putChunks = putChunks;\n this.#delegate = delegate;\n this.#refCountUpdates = new Map();\n // This map is used to ensure we do not load the ref count key more than once.\n // Once it is loaded we only operate on a cache of the ref counts.\n this.#loadedRefCountPromises = new Map();\n this.#isLazyDelegate = delegate.areRefsCounted !== undefined;\n this.#refsCounted = this.#isLazyDelegate ? new Set() : null;\n }\n\n async compute(): Promise<Map<Hash, number>> {\n for (const n of this.#newHeads) {\n await this.#changeRefCount(n, 1);\n }\n\n // Now go through the put chunks to ensure each has an entry in\n // refCountUpdates (zero for new chunks which are not reachable from\n // newHeads).\n await Promise.all(\n Array.from(this.#putChunks.values(), hash =>\n this.#ensureRefCountLoaded(hash),\n ),\n );\n\n if (this.#isLazyDelegate) {\n assert(\n this.#delegate.areRefsCounted,\n 'Expected delegate.areRefsCounted to be defined',\n );\n assert(this.#refsCounted, 'Expected refsCounted to be defined');\n let refCountsUpdated;\n do {\n refCountsUpdated = false;\n for (const hash of this.#putChunks.values()) {\n if (\n !this.#delegate.areRefsCounted(hash) &&\n !this.#refsCounted.has(hash) &&\n this.#refCountUpdates.get(hash) !== 0\n ) {\n await this.#updateRefsCounts(hash, 1);\n refCountsUpdated = true;\n break;\n }\n }\n } while (refCountsUpdated);\n }\n\n for (const o of this.#oldHeads) {\n await this.#changeRefCount(o, -1);\n }\n\n if (!skipGCAsserts) {\n for (const [hash, update] of this.#refCountUpdates) {\n assert(\n update >= 0,\n `ref count update must be non-negative. ${hash}:${update}`,\n );\n }\n }\n\n return this.#refCountUpdates;\n }\n\n async #changeRefCount(hash: Hash, delta: number): Promise<void> {\n // First make sure that we have the ref count in the cache. This is async\n // because it might need to load the ref count from the store (via the delegate).\n //\n // Once we have loaded the ref count all the updates to it are sync to\n // prevent race conditions.\n await this.#ensureRefCountLoaded(hash);\n if (this.#updateRefCount(hash, delta)) {\n await this.#updateRefsCounts(hash, delta);\n }\n }\n\n async #updateRefsCounts(hash: Hash, delta: number) {\n if (hash === emptyHash) {\n return;\n }\n const refs = await this.#delegate.getRefs(hash);\n if (!skipGCAsserts) {\n assert(\n refs || (this.#isLazyDelegate && !this.#putChunks.has(hash)),\n 'refs must be defined',\n );\n }\n\n if (refs !== undefined) {\n this.#refsCounted?.add(hash);\n const ps = refs.map(ref => this.#changeRefCount(ref, delta));\n await Promise.all(ps);\n }\n }\n\n #ensureRefCountLoaded(hash: Hash): Promise<number> {\n // Only get the ref count once.\n let p = this.#loadedRefCountPromises.get(hash);\n if (p === undefined) {\n p = (async () => {\n const value = (await this.#delegate.getRefCount(hash)) || 0;\n this.#refCountUpdates.set(hash, value);\n return value;\n })();\n this.#loadedRefCountPromises.set(hash, p);\n }\n return p;\n }\n\n #updateRefCount(hash: Hash, delta: number): boolean {\n const oldCount = this.#refCountUpdates.get(hash);\n assertNumber(oldCount);\n this.#refCountUpdates.set(hash, oldCount + delta);\n return (oldCount === 0 && delta === 1) || (oldCount === 1 && delta === -1);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gc.js","names":["#newHeads","#oldHeads","#putChunks","#delegate","#refsCounted","#refCountUpdates","#loadedRefCountPromises","#isLazyDelegate","#changeRefCount","#ensureRefCountLoaded","#updateRefsCounts","#updateRefCount"],"sources":["../../../../../replicache/src/dag/gc.ts"],"sourcesContent":["import {assert, assertNumber} from '../../../shared/src/asserts.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport {skipGCAsserts} from '../config.ts';\nimport {type Hash, emptyHash} from '../hash.ts';\nimport {InvalidRefCountError} from './invalid-ref-count-error.ts';\n\nexport type HeadChange = {\n new: Hash | undefined;\n old: Hash | undefined;\n};\n\ntype LoadedRefCountPromises = Map<Hash, Promise<number>>;\n\nexport interface RefCountUpdatesDelegate {\n getRefCount: (hash: Hash) => MaybePromise<number | undefined>;\n getRefs: (hash: Hash) => MaybePromise<readonly Hash[] | undefined>;\n /**\n * Should be implemented if the store lazily loads refs, returning whether\n * or not the chunks refs have already been counted (i.e. are reflected\n * in `getRefCount`).\n *\n * If defined then:\n * - `getRefs` should return undefined for refs that have not been loaded,\n * but should never return undefined for hashes in `putChunks`.\n * - it is assumed that chunks in `putChunks` may have been reachable before\n * the write, but may not have been counted. This method is used to\n * determine if they have been counted or not. If they have not been\n * counted, and are reachable with the write applied, the returned\n * ref count updates will include updates for counting them.\n *\n * If undefined then:\n * - `getRefs` should never return undefined\n * - it is assumed that the refs of any chunks which were reachable before\n * the write are already counted\n */\n areRefsCounted?: (hash: Hash) => boolean;\n}\n\n/**\n * Computes how ref counts should be updated when a dag write is committed.\n * Does not modify the dag store.\n * @param headChanges Heads that were changed by the dag write.\n * @param putChunks Chunks that were put by the dag write.\n * @param delegate Delegate used for getting ref information from the dag store.\n * @returns Map from chunk Hash to changed ref counts. Chunks with a new ref\n * count of 0 should be deleted. All hashes in `putChunks` will have an entry\n * (which will be zero if the newly put chunk is not reachable from any head).\n */\nexport function computeRefCountUpdates(\n headChanges: Iterable<HeadChange>,\n putChunks: ReadonlySet<Hash>,\n delegate: RefCountUpdatesDelegate,\n): Promise<Map<Hash, number>> {\n return new RefCountUpdates(headChanges, putChunks, delegate).compute();\n}\n\nclass RefCountUpdates {\n readonly #newHeads: Hash[];\n readonly #oldHeads: Hash[];\n readonly #putChunks: ReadonlySet<Hash>;\n readonly #delegate: RefCountUpdatesDelegate;\n readonly #refsCounted: Set<Hash> | null;\n readonly #refCountUpdates: Map<Hash, number>;\n readonly #loadedRefCountPromises: LoadedRefCountPromises;\n readonly #isLazyDelegate: boolean;\n\n constructor(\n headChanges: Iterable<HeadChange>,\n putChunks: ReadonlySet<Hash>,\n delegate: RefCountUpdatesDelegate,\n ) {\n const newHeads: Hash[] = [];\n const oldHeads: Hash[] = [];\n for (const changedHead of headChanges) {\n if (changedHead.old !== changedHead.new) {\n changedHead.old && oldHeads.push(changedHead.old);\n changedHead.new && newHeads.push(changedHead.new);\n }\n }\n this.#newHeads = newHeads;\n this.#oldHeads = oldHeads;\n this.#putChunks = putChunks;\n this.#delegate = delegate;\n this.#refCountUpdates = new Map();\n // This map is used to ensure we do not load the ref count key more than once.\n // Once it is loaded we only operate on a cache of the ref counts.\n this.#loadedRefCountPromises = new Map();\n this.#isLazyDelegate = delegate.areRefsCounted !== undefined;\n this.#refsCounted = this.#isLazyDelegate ? new Set() : null;\n }\n\n async compute(): Promise<Map<Hash, number>> {\n for (const n of this.#newHeads) {\n await this.#changeRefCount(n, 1);\n }\n\n // Now go through the put chunks to ensure each has an entry in\n // refCountUpdates (zero for new chunks which are not reachable from\n // newHeads).\n await Promise.all(\n Array.from(this.#putChunks.values(), hash =>\n this.#ensureRefCountLoaded(hash),\n ),\n );\n\n if (this.#isLazyDelegate) {\n assert(\n this.#delegate.areRefsCounted,\n 'Expected delegate.areRefsCounted to be defined',\n );\n assert(this.#refsCounted, 'Expected refsCounted to be defined');\n let refCountsUpdated;\n do {\n refCountsUpdated = false;\n for (const hash of this.#putChunks.values()) {\n if (\n !this.#delegate.areRefsCounted(hash) &&\n !this.#refsCounted.has(hash) &&\n this.#refCountUpdates.get(hash) !== 0\n ) {\n await this.#updateRefsCounts(hash, 1);\n refCountsUpdated = true;\n break;\n }\n }\n } while (refCountsUpdated);\n }\n\n for (const o of this.#oldHeads) {\n await this.#changeRefCount(o, -1);\n }\n\n // A negative count means a chunk that is still referenced had no (or too\n // low a) ref count in the store, so the store is already corrupt. Always\n // check this, even in production: writing the negative count would only\n // spread the corruption, and the typed error lets the store owner recover.\n // Written as `!(update >= 0)` so that NaN is rejected as well.\n for (const [hash, update] of this.#refCountUpdates) {\n if (!(update >= 0)) {\n throw new InvalidRefCountError(hash, update);\n }\n }\n\n return this.#refCountUpdates;\n }\n\n async #changeRefCount(hash: Hash, delta: number): Promise<void> {\n // First make sure that we have the ref count in the cache. This is async\n // because it might need to load the ref count from the store (via the delegate).\n //\n // Once we have loaded the ref count all the updates to it are sync to\n // prevent race conditions.\n await this.#ensureRefCountLoaded(hash);\n if (this.#updateRefCount(hash, delta)) {\n await this.#updateRefsCounts(hash, delta);\n }\n }\n\n async #updateRefsCounts(hash: Hash, delta: number) {\n if (hash === emptyHash) {\n return;\n }\n const refs = await this.#delegate.getRefs(hash);\n if (!skipGCAsserts) {\n assert(\n refs || (this.#isLazyDelegate && !this.#putChunks.has(hash)),\n 'refs must be defined',\n );\n }\n\n if (refs !== undefined) {\n this.#refsCounted?.add(hash);\n const ps = refs.map(ref => this.#changeRefCount(ref, delta));\n await Promise.all(ps);\n }\n }\n\n #ensureRefCountLoaded(hash: Hash): Promise<number> {\n // Only get the ref count once.\n let p = this.#loadedRefCountPromises.get(hash);\n if (p === undefined) {\n p = (async () => {\n const value = (await this.#delegate.getRefCount(hash)) || 0;\n this.#refCountUpdates.set(hash, value);\n return value;\n })();\n this.#loadedRefCountPromises.set(hash, p);\n }\n return p;\n }\n\n #updateRefCount(hash: Hash, delta: number): boolean {\n const oldCount = this.#refCountUpdates.get(hash);\n assertNumber(oldCount);\n this.#refCountUpdates.set(hash, oldCount + delta);\n return (oldCount === 0 && delta === 1) || (oldCount === 1 && delta === -1);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Hash } from '../hash.ts';
2
+ /**
3
+ * Thrown when a ref count read from the store is not a valid Uint16. This
4
+ * means the store is corrupt (due to some unknown bug) and cannot be repaired.
5
+ */
6
+ export declare class InvalidRefCountError extends Error {
7
+ name: string;
8
+ readonly hash: Hash;
9
+ readonly value: unknown;
10
+ constructor(hash: Hash, value: unknown);
11
+ }
12
+ //# sourceMappingURL=invalid-ref-count-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-ref-count-error.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/dag/invalid-ref-count-error.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AAErC;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,SAA0B;IAC9B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,YAAY,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAMrC;CACF"}
@@ -0,0 +1,19 @@
1
+ //#region ../replicache/src/dag/invalid-ref-count-error.ts
2
+ /**
3
+ * Thrown when a ref count read from the store is not a valid Uint16. This
4
+ * means the store is corrupt (due to some unknown bug) and cannot be repaired.
5
+ */
6
+ var InvalidRefCountError = class extends Error {
7
+ name = "InvalidRefCountError";
8
+ hash;
9
+ value;
10
+ constructor(hash, value) {
11
+ super(`Invalid ref count ${String(value)} for ${hash}. We expect the value to be a Uint16`);
12
+ this.hash = hash;
13
+ this.value = value;
14
+ }
15
+ };
16
+ //#endregion
17
+ export { InvalidRefCountError };
18
+
19
+ //# sourceMappingURL=invalid-ref-count-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invalid-ref-count-error.js","names":[],"sources":["../../../../../replicache/src/dag/invalid-ref-count-error.ts"],"sourcesContent":["import type {Hash} from '../hash.ts';\n\n/**\n * Thrown when a ref count read from the store is not a valid Uint16. This\n * means the store is corrupt (due to some unknown bug) and cannot be repaired.\n */\nexport class InvalidRefCountError extends Error {\n name = 'InvalidRefCountError';\n readonly hash: Hash;\n readonly value: unknown;\n constructor(hash: Hash, value: unknown) {\n super(\n `Invalid ref count ${String(value)} for ${hash}. We expect the value to be a Uint16`,\n );\n this.hash = hash;\n this.value = value;\n }\n}\n"],"mappings":";;;;;AAMA,IAAa,uBAAb,cAA0C,MAAM;CAC9C,OAAO;CACP;CACA;CACA,YAAY,MAAY,OAAgB;EACtC,MACE,qBAAqB,OAAO,KAAK,EAAE,OAAO,KAAK,qCACjD;EACA,KAAK,OAAO;EACZ,KAAK,QAAQ;CACf;AACF"}
@@ -2,10 +2,20 @@ import { type Hash } from '../hash.ts';
2
2
  import type { Read as KVRead, Store as KVStore, Write as KVWrite } from '../kv/store.ts';
3
3
  import { Chunk, type ChunkHasher, type Refs } from './chunk.ts';
4
4
  import { type RefCountUpdatesDelegate } from './gc.ts';
5
+ import { InvalidRefCountError } from './invalid-ref-count-error.ts';
5
6
  import { type Read, type Store, type Write } from './store.ts';
7
+ /**
8
+ * Called when a write found an invalid ref count in the store. This is the
9
+ * single place where store corruption is detected, so the owner of the store
10
+ * can start recovery no matter which code path performed the write.
11
+ *
12
+ * The callback runs after the write transaction has been released, so it is
13
+ * safe for it to close or drop the underlying kv store.
14
+ */
15
+ export type OnInvalidRefCount = (e: InvalidRefCountError) => void;
6
16
  export declare class StoreImpl implements Store {
7
17
  #private;
8
- constructor(kv: KVStore, chunkHasher: ChunkHasher, assertValidHash: (hash: Hash) => void);
18
+ constructor(kv: KVStore, chunkHasher: ChunkHasher, assertValidHash: (hash: Hash) => void, onInvalidRefCount?: OnInvalidRefCount | undefined);
9
19
  read(): Promise<Read>;
10
20
  write(): Promise<Write>;
11
21
  close(): Promise<void>;
@@ -24,7 +34,7 @@ export declare class ReadImpl implements Read {
24
34
  export declare class WriteImpl extends ReadImpl implements Write, RefCountUpdatesDelegate {
25
35
  #private;
26
36
  protected readonly _tx: KVWrite;
27
- constructor(kvw: KVWrite, chunkHasher: ChunkHasher, assertValidHash: (hash: Hash) => void);
37
+ constructor(kvw: KVWrite, chunkHasher: ChunkHasher, assertValidHash: (hash: Hash) => void, onInvalidRefCount?: OnInvalidRefCount | undefined);
28
38
  createChunk: <V>(data: V, refs: Refs) => Chunk<V>;
29
39
  get kvWrite(): KVWrite;
30
40
  putChunk(c: Chunk): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"store-impl.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/dag/store-impl.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,KAAK,IAAI,EAAa,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EACV,IAAI,IAAI,MAAM,EACd,KAAK,IAAI,OAAO,EAChB,KAAK,IAAI,OAAO,EACjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,EACL,KAAK,WAAW,EAChB,KAAK,IAAI,EAGV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAC,KAAK,uBAAuB,EAAyB,MAAM,SAAS,CAAC;AAE7E,OAAO,EAAC,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK,EAAe,MAAM,YAAY,CAAC;AAE3E,qBAAa,SAAU,YAAW,KAAK;;IAKrC,YACE,EAAE,EAAE,OAAO,EACX,WAAW,EAAE,WAAW,EACxB,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAKtC;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1B;IAEK,KAAK,IAAI,OAAO,CAAC,KAAK,CAAC,CAM5B;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAErB;CACF;AAED,qBAAa,QAAS,YAAW,IAAI;IACnC,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAE/C,YAAY,EAAE,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAG5D;IAED,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAErC;IAEK,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAoBrD;IAED,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAEvC;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAOrD;IAED,OAAO,IAAI,IAAI,CAEd;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF;AAOD,qBAAa,SACX,SAAQ,QACR,YAAW,KAAK,EAAE,uBAAuB;;IAEzC,mBAA2B,GAAG,EAAE,OAAO,CAAC;IAMxC,YACE,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,WAAW,EACxB,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAItC;IAED,WAAW,GAAI,CAAC,QAAQ,CAAC,QAAQ,IAAI,KAAG,KAAK,CAAC,CAAC,CAAC,CACH;IAE7C,IAAI,OAAO,IAAI,OAAO,CAErB;IAEK,QAAQ,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBtC;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtC;IAwBK,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ5B;IAEK,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAYzD;IAEK,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAOlD;IAyBD,OAAO,IAAI,IAAI,CAEd;CACF"}
1
+ {"version":3,"file":"store-impl.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/dag/store-impl.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { assertNumber } from "../../../shared/src/asserts.js";
1
+ import { InvalidRefCountError } from "./invalid-ref-count-error.js";
2
2
  import { assertHash } from "../hash.js";
3
3
  import { Chunk, assertRefs, createChunk } from "./chunk.js";
4
4
  import { computeRefCountUpdates } from "./gc.js";
@@ -9,16 +9,18 @@ var StoreImpl = class {
9
9
  #kv;
10
10
  #chunkHasher;
11
11
  #assertValidHash;
12
- constructor(kv, chunkHasher, assertValidHash) {
12
+ #onInvalidRefCount;
13
+ constructor(kv, chunkHasher, assertValidHash, onInvalidRefCount) {
13
14
  this.#kv = kv;
14
15
  this.#chunkHasher = chunkHasher;
15
16
  this.#assertValidHash = assertValidHash;
17
+ this.#onInvalidRefCount = onInvalidRefCount;
16
18
  }
17
19
  async read() {
18
20
  return new ReadImpl(await this.#kv.read(), this.#assertValidHash);
19
21
  }
20
22
  async write() {
21
- return new WriteImpl(await this.#kv.write(), this.#chunkHasher, this.#assertValidHash);
23
+ return new WriteImpl(await this.#kv.write(), this.#chunkHasher, this.#assertValidHash, this.#onInvalidRefCount);
22
24
  }
23
25
  close() {
24
26
  return this.#kv.close();
@@ -68,11 +70,14 @@ var ReadImpl = class {
68
70
  };
69
71
  var WriteImpl = class extends ReadImpl {
70
72
  #chunkHasher;
73
+ #onInvalidRefCount;
74
+ #invalidRefCountError;
71
75
  #putChunks = /* @__PURE__ */ new Set();
72
76
  #changedHeads = /* @__PURE__ */ new Map();
73
- constructor(kvw, chunkHasher, assertValidHash) {
77
+ constructor(kvw, chunkHasher, assertValidHash, onInvalidRefCount) {
74
78
  super(kvw, assertValidHash);
75
79
  this.#chunkHasher = chunkHasher;
80
+ this.#onInvalidRefCount = onInvalidRefCount;
76
81
  }
77
82
  createChunk = (data, refs) => createChunk(data, refs, this.#chunkHasher);
78
83
  get kvWrite() {
@@ -113,15 +118,20 @@ var WriteImpl = class extends ReadImpl {
113
118
  await p1;
114
119
  }
115
120
  async commit() {
116
- const refCountUpdates = await computeRefCountUpdates(this.#changedHeads.values(), this.#putChunks, this);
121
+ let refCountUpdates;
122
+ try {
123
+ refCountUpdates = await computeRefCountUpdates(this.#changedHeads.values(), this.#putChunks, this);
124
+ } catch (e) {
125
+ if (e instanceof InvalidRefCountError) this.#invalidRefCountError = e;
126
+ throw e;
127
+ }
117
128
  await this.#applyRefCountUpdates(refCountUpdates);
118
129
  await this._tx.commit();
119
130
  }
120
131
  async getRefCount(hash) {
121
132
  const value = await this._tx.get(chunkRefCountKey(hash));
122
133
  if (value === void 0) return;
123
- assertNumber(value);
124
- if (value < 0 || value > 65535 || value !== (value | 0)) throw new Error(`Invalid ref count ${value}. We expect the value to be a Uint16`);
134
+ if (typeof value !== "number" || value < 0 || value > 65535 || value !== (value | 0)) throw new InvalidRefCountError(hash, value);
125
135
  return value;
126
136
  }
127
137
  async getRefs(hash) {
@@ -148,7 +158,11 @@ var WriteImpl = class extends ReadImpl {
148
158
  this.#putChunks.delete(hash);
149
159
  }
150
160
  release() {
151
- this._tx.release();
161
+ try {
162
+ this._tx.release();
163
+ } finally {
164
+ if (this.#invalidRefCountError) this.#onInvalidRefCount?.(this.#invalidRefCountError);
165
+ }
152
166
  }
153
167
  };
154
168
  //#endregion
@@ -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":";;;;;;;AAmBA,IAAa,YAAb,MAAwC;CACtC;CACA;CACA;CAEA,YACE,IACA,aACA,iBACA;EACA,KAAKA,MAAM;EACX,KAAKC,eAAe;EACpB,KAAKC,mBAAmB;CAC1B;CAEA,MAAM,OAAsB;EAC1B,OAAO,IAAI,SAAS,MAAM,KAAKF,IAAI,KAAK,GAAG,KAAKE,gBAAgB;CAClE;CAEA,MAAM,QAAwB;EAC5B,OAAO,IAAI,UACT,MAAM,KAAKF,IAAI,MAAM,GACrB,KAAKC,cACL,KAAKC,gBACP;CACF;CAEA,QAAuB;EACrB,OAAO,KAAKF,IAAI,MAAM;CACxB;AACF;AAEA,IAAa,WAAb,MAAsC;CACpC;CACA;CAEA,YAAY,IAAY,iBAAuC;EAC7D,KAAK,MAAM;EACX,KAAK,kBAAkB;CACzB;CAEA,SAAS,MAA8B;EACrC,OAAO,KAAK,IAAI,IAAI,aAAa,IAAI,CAAC;CACxC;CAEA,MAAM,SAAS,MAAwC;EACrD,MAAM,cAAc,KAAK,IAAI,IAAI,aAAa,IAAI,CAAC;EACnD,MAAM,cAAc,KAAK,IAAI,IAAI,aAAa,IAAI,CAAC;EACnD,MAAM,OAAO,MAAM;EACnB,IAAI,SAAS,KAAA,GAAW;GACtB,YAAY,YAAY,CAExB,CAAC;GACD;EACF;EAEA,MAAM,UAAU,MAAM;EACtB,IAAI;EACJ,IAAI,YAAY,KAAA,GAAW;GACzB,WAAW,OAAO;GAClB,OAAO;EACT,OACE,OAAO,CAAC;EAEV,OAAO,IAAI,MAAM,MAAM,MAAM,IAAI;CACnC;CAEA,aAAa,MAA4B;EACvC,OAAO,aAAa,MAAM,IAAI;CAChC;CAEA,MAAM,QAAQ,MAAyC;EACrD,MAAM,OAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,IAAI,CAAC;EAC7C,IAAI,SAAS,KAAA,GACX;EAEF,WAAW,IAAI;EACf,OAAO;CACT;CAEA,UAAgB;EACd,KAAK,IAAI,QAAQ;CACnB;CAEA,IAAI,SAAkB;EACpB,OAAO,KAAK,IAAI;CAClB;AACF;AAOA,IAAa,YAAb,cACU,SAEV;CAEE;CAEA,6BAAsB,IAAI,IAAU;CACpC,gCAAyB,IAAI,IAAwB;CAErD,YACE,KACA,aACA,iBACA;EACA,MAAM,KAAK,eAAe;EAC1B,KAAKC,eAAe;CACtB;CAEA,eAAkB,MAAS,SACzB,YAAY,MAAM,MAAM,KAAKA,YAAY;CAE3C,IAAI,UAAmB;EACrB,OAAO,KAAK;CACd;CAEA,MAAM,SAAS,GAAyB;EACtC,MAAM,EAAC,MAAM,MAAM,SAAQ;EAE3B,KAAK,gBAAgB,IAAI;EACzB,MAAM,MAAM,aAAa,IAAI;EAE7B,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,IAAyB;EACtD,IAAI;EACJ,IAAI,KAAK,SAAS,GAAG;GACnB,KAAK,MAAM,KAAK,MACd,KAAK,gBAAgB,CAAC;GAExB,KAAK,KAAK,IAAI,IAAI,aAAa,IAAI,GAAG,IAAI;EAC5C;EACA,KAAKE,WAAW,IAAI,IAAI;EACxB,MAAM;EACN,MAAM;CACR;CAEA,QAAQ,MAAc,MAA2B;EAC/C,OAAO,KAAKE,SAAS,MAAM,IAAI;CACjC;CAEA,WAAW,MAA6B;EACtC,OAAO,KAAKA,SAAS,MAAM,KAAA,CAAS;CACtC;CAEA,MAAMA,SAAS,MAAc,MAAuC;EAClE,MAAM,UAAU,MAAM,KAAK,QAAQ,IAAI;EACvC,MAAM,KAAK,QAAQ,IAAI;EAEvB,IAAI;EACJ,IAAI,SAAS,KAAA,GACX,KAAK,KAAK,IAAI,IAAI,EAAE;OAEpB,KAAK,KAAK,IAAI,IAAI,IAAI,IAAI;EAG5B,MAAM,IAAI,KAAKD,cAAc,IAAI,IAAI;EACrC,IAAI,MAAM,KAAA,GACR,KAAKA,cAAc,IAAI,MAAM;GAAC,KAAK;GAAM,KAAK;EAAO,CAAC;OAGtD,EAAE,MAAM;EAGV,MAAM;CACR;CAEA,MAAM,SAAwB;EAC5B,MAAM,kBAAkB,MAAM,uBAC5B,KAAKA,cAAc,OAAO,GAC1B,KAAKD,YACL,IACF;EACA,MAAM,KAAKG,sBAAsB,eAAe;EAChD,MAAM,KAAK,IAAI,OAAO;CACxB;CAEA,MAAM,YAAY,MAAyC;EACzD,MAAM,QAAQ,MAAM,KAAK,IAAI,IAAI,iBAAiB,IAAI,CAAC;EACvD,IAAI,UAAU,KAAA,GACZ;EAEF,aAAa,KAAK;EAClB,IAAI,QAAQ,KAAK,QAAQ,SAAU,WAAW,QAAQ,IACpD,MAAM,IAAI,MACR,qBAAqB,MAAM,qCAC7B;EAEF,OAAO;CACT;CAEA,MAAM,QAAQ,MAAsC;EAClD,MAAM,OAAO,MAAM,KAAK,IAAI,IAAI,aAAa,IAAI,CAAC;EAClD,IAAI,SAAS,KAAA,GACX,OAAO,CAAC;EAEV,WAAW,IAAI;EACf,OAAO;CACT;CAEA,MAAMA,sBAAsB,eAAiD;EAC3E,MAAM,KAAsB,CAAC;EAC7B,KAAK,MAAM,CAAC,MAAM,UAAU,eAC1B,IAAI,UAAU,GACZ,GAAG,KAAK,KAAKC,sBAAsB,IAAI,CAAC;OACnC;GACL,MAAM,cAAc,iBAAiB,IAAI;GACzC,GAAG,KAAK,KAAK,IAAI,IAAI,aAAa,KAAK,CAAC;EAC1C;EAEF,MAAM,QAAQ,IAAI,EAAE;CACtB;CAEA,MAAMA,sBAAsB,MAA2B;EACrD,MAAM,QAAQ,IAAI;GAChB,KAAK,IAAI,IAAI,aAAa,IAAI,CAAC;GAC/B,KAAK,IAAI,IAAI,aAAa,IAAI,CAAC;GAC/B,KAAK,IAAI,IAAI,iBAAiB,IAAI,CAAC;EACrC,CAAC;EAED,KAAKJ,WAAW,OAAO,IAAI;CAC7B;CAEA,UAAgB;EACd,KAAK,IAAI,QAAQ;CACnB;AACF"}
1
+ {"version":3,"file":"store-impl.js","names":["#kv","#chunkHasher","#assertValidHash","#onInvalidRefCount","#putChunks","#changedHeads","#setHead","#invalidRefCountError","#applyRefCountUpdates","#removeAllRelatedKeys"],"sources":["../../../../../replicache/src/dag/store-impl.ts"],"sourcesContent":["import 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 {InvalidRefCountError} from './invalid-ref-count-error.ts';\nimport {chunkDataKey, chunkMetaKey, chunkRefCountKey, headKey} from './key.ts';\nimport {type Read, type Store, type Write, mustGetChunk} from './store.ts';\n\n/**\n * Called when a write found an invalid ref count in the store. This is the\n * single place where store corruption is detected, so the owner of the store\n * can start recovery no matter which code path performed the write.\n *\n * The callback runs after the write transaction has been released, so it is\n * safe for it to close or drop the underlying kv store.\n */\nexport type OnInvalidRefCount = (e: InvalidRefCountError) => void;\n\nexport class StoreImpl implements Store {\n readonly #kv: KVStore;\n readonly #chunkHasher: ChunkHasher;\n readonly #assertValidHash: (hash: Hash) => void;\n readonly #onInvalidRefCount: OnInvalidRefCount | undefined;\n\n constructor(\n kv: KVStore,\n chunkHasher: ChunkHasher,\n assertValidHash: (hash: Hash) => void,\n onInvalidRefCount?: OnInvalidRefCount | undefined,\n ) {\n this.#kv = kv;\n this.#chunkHasher = chunkHasher;\n this.#assertValidHash = assertValidHash;\n this.#onInvalidRefCount = onInvalidRefCount;\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 this.#onInvalidRefCount,\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 readonly #onInvalidRefCount: OnInvalidRefCount | undefined;\n #invalidRefCountError: InvalidRefCountError | undefined;\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 onInvalidRefCount?: OnInvalidRefCount | undefined,\n ) {\n super(kvw, assertValidHash);\n this.#chunkHasher = chunkHasher;\n this.#onInvalidRefCount = onInvalidRefCount;\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 let refCountUpdates: Map<Hash, number>;\n try {\n refCountUpdates = await computeRefCountUpdates(\n this.#changedHeads.values(),\n this.#putChunks,\n this,\n );\n } catch (e) {\n if (e instanceof InvalidRefCountError) {\n // Reported from release() rather than here: the owner may drop the\n // store in response and the kv transaction still needs to roll back.\n this.#invalidRefCountError = e;\n }\n throw e;\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 if (\n typeof value !== 'number' ||\n value < 0 ||\n value > 0xffff ||\n value !== (value | 0)\n ) {\n throw new InvalidRefCountError(hash, value);\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 try {\n this._tx.release();\n } finally {\n // Report even if the kv release threw (for example a failed SQLite\n // ROLLBACK): the store is corrupt either way and the owner must still\n // recover. The release error keeps propagating to the caller.\n if (this.#invalidRefCountError) {\n this.#onInvalidRefCount?.(this.#invalidRefCountError);\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -8,7 +8,19 @@ import type { ClientID } from '../sync/ids.ts';
8
8
  import type { MutatorDefs } from '../types.ts';
9
9
  import { type Commit, type LocalMeta, type Meta } from './commit.ts';
10
10
  type FormatVersion = Enum<typeof FormatVersion>;
11
- export declare function rebaseMutationAndPutCommit(mutation: Commit<LocalMeta>, dagWrite: DagWrite, basis: Hash, mutators: MutatorDefs, lc: LogContext, mutationClientID: ClientID, formatVersion: FormatVersion, zeroData: ZeroTxData | undefined): Promise<Commit<Meta>>;
12
- export declare function rebaseMutationAndCommit(mutation: Commit<LocalMeta>, dagWrite: DagWrite, basis: Hash, headName: string, mutators: MutatorDefs, lc: LogContext, mutationClientID: ClientID, formatVersion: FormatVersion, zeroData: ZeroTxData | undefined): Promise<Hash>;
11
+ /**
12
+ * The result of replaying one mutation.
13
+ *
14
+ * `zeroData` is the Zero transaction data to use for the next mutation in the
15
+ * same replay. Each mutation runs against its own fork. On success that fork is
16
+ * returned, and if the mutator throws the caller gets back the one it passed
17
+ * in, without the failed mutation's IVM writes. See `rebaseMutation`.
18
+ */
19
+ export type RebaseResult<T> = {
20
+ result: T;
21
+ zeroData: ZeroTxData | undefined;
22
+ };
23
+ export declare function rebaseMutationAndPutCommit(mutation: Commit<LocalMeta>, dagWrite: DagWrite, basis: Hash, mutators: MutatorDefs, lc: LogContext, mutationClientID: ClientID, formatVersion: FormatVersion, zeroData: ZeroTxData | undefined): Promise<RebaseResult<Commit<Meta>>>;
24
+ export declare function rebaseMutationAndCommit(mutation: Commit<LocalMeta>, dagWrite: DagWrite, basis: Hash, headName: string, mutators: MutatorDefs, lc: LogContext, mutationClientID: ClientID, formatVersion: FormatVersion, zeroData: ZeroTxData | undefined): Promise<RebaseResult<Hash>>;
13
25
  export {};
14
26
  //# sourceMappingURL=rebase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rebase.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/db/rebase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AACtD,OAAO,KAAK,EAAC,KAAK,IAAI,QAAQ,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AACrC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAE7C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,KAAK,MAAM,EACX,KAAK,SAAS,EAEd,KAAK,IAAI,EAIV,MAAM,aAAa,CAAC;AAIrB,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AA8EhD,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,UAAU,EAGd,gBAAgB,EAAE,QAAQ,EAC1B,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,GAC/B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAYvB;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,UAAU,EAGd,gBAAgB,EAAE,QAAQ,EAC1B,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,GAC/B,OAAO,CAAC,IAAI,CAAC,CAYf"}
1
+ {"version":3,"file":"rebase.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/db/rebase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AACtD,OAAO,KAAK,EAAC,KAAK,IAAI,QAAQ,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AACrC,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAE7C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,KAAK,MAAM,EACX,KAAK,SAAS,EAEd,KAAK,IAAI,EAIV,MAAM,aAAa,CAAC;AAIrB,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,MAAM,EAAE,CAAC,CAAC;IACV,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;CAClC,CAAC;AA8GF,wBAAsB,0BAA0B,CAC9C,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,UAAU,EAGd,gBAAgB,EAAE,QAAQ,EAC1B,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,GAC/B,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAYrC;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,EAC3B,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,EACrB,EAAE,EAAE,UAAU,EAGd,gBAAgB,EAAE,QAAQ,EAC1B,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,UAAU,GAAG,SAAS,GAC/B,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAY7B"}
@@ -14,15 +14,37 @@ async function rebaseMutation(mutation, dagWrite, basisHash, mutators, lc, mutat
14
14
  const nextMutationID = await (await commitFromHash(basisHash, dagWrite)).getNextMutationID(mutationClientID, dagWrite);
15
15
  if (nextMutationID !== localMeta.mutationID) throw new Error(`Inconsistent mutation ID: original: ${localMeta.mutationID}, next: ${nextMutationID} - mutationClientID: ${mutationClientID} mutatorName: ${name}`);
16
16
  if (formatVersion >= 5) assertLocalMetaDD31(localMeta);
17
- const dbWrite = await newWriteLocal(basisHash, name, args, mutation.chunk.hash, dagWrite, localMeta.timestamp, mutationClientID, formatVersion);
18
- await mutatorImpl(new WriteTransactionImpl(mutationClientID, await dbWrite.getMutationID(), "rebase", zeroData, dbWrite, lc), args);
19
- return dbWrite;
17
+ const newWrite = () => newWriteLocal(basisHash, name, args, mutation.chunk.hash, dagWrite, localMeta.timestamp, mutationClientID, formatVersion);
18
+ const dbWrite = await newWrite();
19
+ const txData = zeroData?.fork();
20
+ const tx = new WriteTransactionImpl(mutationClientID, await dbWrite.getMutationID(), "rebase", txData, dbWrite, lc);
21
+ try {
22
+ await mutatorImpl(tx, args);
23
+ } catch (e) {
24
+ lc.info?.(`Rebase of mutator ${name} threw, abandoning its prediction`, e);
25
+ return {
26
+ result: await newWrite(),
27
+ zeroData
28
+ };
29
+ }
30
+ return {
31
+ result: dbWrite,
32
+ zeroData: txData
33
+ };
20
34
  }
21
35
  async function rebaseMutationAndPutCommit(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData) {
22
- return (await rebaseMutation(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData)).putCommit();
36
+ const { result: tx, zeroData: next } = await rebaseMutation(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData);
37
+ return {
38
+ result: await tx.putCommit(),
39
+ zeroData: next
40
+ };
23
41
  }
24
42
  async function rebaseMutationAndCommit(mutation, dagWrite, basis, headName, mutators, lc, mutationClientID, formatVersion, zeroData) {
25
- return (await rebaseMutation(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData)).commit(headName);
43
+ const { result: dbWrite, zeroData: next } = await rebaseMutation(mutation, dagWrite, basis, mutators, lc, mutationClientID, formatVersion, zeroData);
44
+ return {
45
+ result: await dbWrite.commit(headName),
46
+ zeroData: next
47
+ };
26
48
  }
27
49
  //#endregion
28
50
  export { rebaseMutationAndCommit, rebaseMutationAndPutCommit };
@@ -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\n/**\n * The result of replaying one mutation.\n *\n * `zeroData` is the Zero transaction data to use for the next mutation in the\n * same replay. Each mutation runs against its own fork. On success that fork is\n * returned, and if the mutator throws the caller gets back the one it passed\n * in, without the failed mutation's IVM writes. See `rebaseMutation`.\n */\nexport type RebaseResult<T> = {\n result: T;\n zeroData: ZeroTxData | undefined;\n};\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<RebaseResult<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 newWrite = () =>\n newWriteLocal(\n basisHash,\n name,\n args,\n mutation.chunk.hash,\n dagWrite,\n localMeta.timestamp,\n mutationClientID,\n formatVersion,\n );\n\n const dbWrite = await newWrite();\n\n // Run the mutator against a fork so that if it throws, its IVM writes are\n // discarded along with its `Write`.\n const txData = zeroData?.fork();\n\n const tx = new WriteTransactionImpl(\n mutationClientID,\n await dbWrite.getMutationID(),\n 'rebase',\n txData,\n dbWrite,\n lc,\n );\n\n try {\n await mutatorImpl(tx, args);\n } catch (e) {\n // A mutator can throw here without anything being wrong: it is being run\n // again against a newer server snapshot, so a check that passed when the\n // user first ran it can fail now. Rethrowing fails the whole rebase, which\n // in Zero fails the poke and disconnects the client. The mutation stays\n // pending, so the next poke fails the same way.\n //\n // Only the local prediction is dropped. The mutation is still pending and\n // is still sent to the server, which decides whether it applied.\n //\n // `dbWrite` is dropped rather than closed: it shares `dagWrite` with the\n // caller. Dropping it is enough because a `Write` does not modify anything\n // outside itself and only writes chunks when it is committed.\n //\n // Engine errors raised inside the mutator are retired the same way, rather\n // than being sorted out here. A client whose state was garbage collected\n // throws ChunkNotFoundError from its reads, and that is detected on its\n // next mutation, where replicache-impl converts it to a\n // ClientStateNotFoundError and calls onClientStateNotFound.\n lc.info?.(`Rebase of mutator ${name} threw, abandoning its prediction`, e);\n return {result: await newWrite(), zeroData};\n }\n\n return {result: dbWrite, zeroData: txData};\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<RebaseResult<Commit<Meta>>> {\n const {result: tx, zeroData: next} = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return {result: await tx.putCommit(), zeroData: next};\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<RebaseResult<Hash>> {\n const {result: dbWrite, zeroData: next} = await rebaseMutation(\n mutation,\n dagWrite,\n basis,\n mutators,\n lc,\n mutationClientID,\n formatVersion,\n zeroData,\n );\n return {result: await dbWrite.commit(headName), zeroData: next};\n}\n"],"mappings":";;;;;AAqCA,eAAe,eACb,UACA,UACA,WACA,UACA,IACA,kBACA,eACA,UAC8B;CAC9B,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,iBACJ,cACE,WACA,MACA,MACA,SAAS,MAAM,MACf,UACA,UAAU,WACV,kBACA,aACF;CAEF,MAAM,UAAU,MAAM,SAAS;CAI/B,MAAM,SAAS,UAAU,KAAK;CAE9B,MAAM,KAAK,IAAI,qBACb,kBACA,MAAM,QAAQ,cAAc,GAC5B,UACA,QACA,SACA,EACF;CAEA,IAAI;EACF,MAAM,YAAY,IAAI,IAAI;CAC5B,SAAS,GAAG;EAmBV,GAAG,OAAO,qBAAqB,KAAK,oCAAoC,CAAC;EACzE,OAAO;GAAC,QAAQ,MAAM,SAAS;GAAG;EAAQ;CAC5C;CAEA,OAAO;EAAC,QAAQ;EAAS,UAAU;CAAM;AAC3C;AAEA,eAAsB,2BACpB,UACA,UACA,OACA,UACA,IAGA,kBACA,eACA,UACqC;CACrC,MAAM,EAAC,QAAQ,IAAI,UAAU,SAAQ,MAAM,eACzC,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,QACF;CACA,OAAO;EAAC,QAAQ,MAAM,GAAG,UAAU;EAAG,UAAU;CAAI;AACtD;AAEA,eAAsB,wBACpB,UACA,UACA,OACA,UACA,UACA,IAGA,kBACA,eACA,UAC6B;CAC7B,MAAM,EAAC,QAAQ,SAAS,UAAU,SAAQ,MAAM,eAC9C,UACA,UACA,OACA,UACA,IACA,kBACA,eACA,QACF;CACA,OAAO;EAAC,QAAQ,MAAM,QAAQ,OAAO,QAAQ;EAAG,UAAU;CAAI;AAChE"}
@@ -1,6 +1,6 @@
1
1
  import type { LogContext, LogLevel, LogSink } from '@rocicorp/logger';
2
2
  import type { Store } from '../dag/store.ts';
3
- import type { CreateStore, StoreProvider } from '../kv/store.ts';
3
+ import type { CreateStore, DropStore, StoreProvider } from '../kv/store.ts';
4
4
  import type { OnClientsDeleted } from './clients.ts';
5
5
  import { IDBDatabasesStore } from './idb-databases-store.ts';
6
6
  /**
@@ -11,12 +11,18 @@ export declare const COLLECT_IDB_INTERVAL: number;
11
11
  * We delay the initial collection to prevent doing it at startup.
12
12
  */
13
13
  export declare const INITIAL_COLLECT_IDB_DELAY: number;
14
- export declare function initCollectIDBDatabases(idbDatabasesStore: IDBDatabasesStore, kvStoreProvider: StoreProvider, collectInterval: number, initialCollectDelay: number, maxAge: number, enableMutationRecovery: boolean, onClientsDeleted: OnClientsDeleted, lc: LogContext, signal: AbortSignal): void;
14
+ export declare function initCollectIDBDatabases(idbDatabasesStore: IDBDatabasesStore, kvStoreProvider: StoreProvider, collectInterval: number, initialCollectDelay: number, maxAge: number, enableMutationRecovery: boolean, onClientsDeleted: OnClientsDeleted, lc: LogContext, signal: AbortSignal, newDagStore?: NewDagStore): void;
15
15
  /**
16
16
  * Collects IDB databases that are no longer needed.
17
17
  */
18
- export declare function collectIDBDatabases(idbDatabasesStore: IDBDatabasesStore, now: number, maxAge: number, kvStoreProvider: StoreProvider, enableMutationRecovery: boolean, onClientsDeleted: OnClientsDeleted, newDagStore?: typeof defaultNewDagStore): Promise<void>;
19
- declare function defaultNewDagStore(name: string, kvCreateStore: CreateStore): Store;
18
+ export declare function collectIDBDatabases(idbDatabasesStore: IDBDatabasesStore, now: number, maxAge: number, kvStoreProvider: StoreProvider, enableMutationRecovery: boolean, onClientsDeleted: OnClientsDeleted, newDagStore?: NewDagStore): Promise<void>;
19
+ export declare function dropDatabaseInternal(name: string, idbDatabasesStore: IDBDatabasesStore, kvDropStore: DropStore): Promise<void>;
20
+ /**
21
+ * Creates the dag store used to inspect and update a database during
22
+ * collection. Callers can override it to install store hooks, for example so
23
+ * a corrupt ref count found while writing deleted clients is recovered from.
24
+ */
25
+ export type NewDagStore = (name: string, kvCreateStore: CreateStore) => Store;
20
26
  /**
21
27
  * Options for `dropDatabase` and `dropAllDatabases`.
22
28
  */
@@ -79,5 +85,4 @@ export declare function deleteAllReplicacheData(opts?: DropDatabaseOptions): Pro
79
85
  dropped: string[];
80
86
  errors: unknown[];
81
87
  }>;
82
- export {};
83
88
  //# sourceMappingURL=collect-idb-databases.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect-idb-databases.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAC,MAAM,kBAAkB,CAAC;AAKpE,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,iBAAiB,CAAC;AAY3C,OAAO,KAAK,EAAC,WAAW,EAAa,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAO1E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAGnD,OAAO,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAsB,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,yBAAyB,QAAgB,CAAC;AAEvD,wBAAgB,uBAAuB,CACrC,iBAAiB,EAAE,iBAAiB,EACpC,eAAe,EAAE,aAAa,EAC9B,eAAe,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,EAC3B,MAAM,EAAE,MAAM,EACd,sBAAsB,EAAE,OAAO,EAC/B,gBAAgB,EAAE,gBAAgB,EAClC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,WAAW,GAClB,IAAI,CAwBN;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,iBAAiB,EAAE,iBAAiB,EACpC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,aAAa,EAC9B,sBAAsB,EAAE,OAAO,EAC/B,gBAAgB,EAAE,gBAAgB,EAClC,WAAW,4BAAqB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAmEf;AAsCD,iBAAS,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,GAAG,KAAK,CAG3E;AAqCD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,aAAa,GAAG,SAAS,CAAC;IACpD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,iBAU5E;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB,CAAC,CASD;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,CAAC,EAAE,mBAAmB;aArBvD,MAAM,EAAE;YACT,OAAO,EAAE;GAsBlB"}
1
+ {"version":3,"file":"collect-idb-databases.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAC,MAAM,kBAAkB,CAAC;AAMpE,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,iBAAiB,CAAC;AAY3C,OAAO,KAAK,EAAC,WAAW,EAAE,SAAS,EAAE,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAO1E,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,cAAc,CAAC;AAGnD,OAAO,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAsB,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,yBAAyB,QAAgB,CAAC;AAEvD,wBAAgB,uBAAuB,CACrC,iBAAiB,EAAE,iBAAiB,EACpC,eAAe,EAAE,aAAa,EAC9B,eAAe,EAAE,MAAM,EACvB,mBAAmB,EAAE,MAAM,EAC3B,MAAM,EAAE,MAAM,EACd,sBAAsB,EAAE,OAAO,EAC/B,gBAAgB,EAAE,gBAAgB,EAClC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,WAAW,EACnB,WAAW,GAAE,WAAgC,GAC5C,IAAI,CAyBN;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,iBAAiB,EAAE,iBAAiB,EACpC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,aAAa,EAC9B,sBAAsB,EAAE,OAAO,EAC/B,gBAAgB,EAAE,gBAAgB,EAClC,WAAW,GAAE,WAAgC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAyEf;AAED,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,iBAAiB,EACpC,WAAW,EAAE,SAAS,iBAIvB;AA6BD;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,KAAK,KAAK,CAAC;AA0C9E;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,aAAa,GAAG,SAAS,CAAC;IACpD;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC;CAClC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,iBAU5E;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB,CAAC,CASD;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,CAAC,EAAE,mBAAmB;aArBvD,MAAM,EAAE;YACT,OAAO,EAAE;GAsBlB"}
@@ -1,5 +1,6 @@
1
1
  import { assert } from "../../../shared/src/asserts.js";
2
2
  import { initBgIntervalProcess } from "../bg-interval.js";
3
+ import { InvalidRefCountError } from "../dag/invalid-ref-count-error.js";
3
4
  import { assertHash, newRandomHash } from "../hash.js";
4
5
  import { StoreImpl } from "../dag/store-impl.js";
5
6
  import { withRead, withWrite } from "../with-transactions.js";
@@ -18,10 +19,10 @@ var COLLECT_IDB_INTERVAL = 720 * 60 * 1e3;
18
19
  * We delay the initial collection to prevent doing it at startup.
19
20
  */
20
21
  var INITIAL_COLLECT_IDB_DELAY = 300 * 1e3;
21
- function initCollectIDBDatabases(idbDatabasesStore, kvStoreProvider, collectInterval, initialCollectDelay, maxAge, enableMutationRecovery, onClientsDeleted, lc, signal) {
22
+ function initCollectIDBDatabases(idbDatabasesStore, kvStoreProvider, collectInterval, initialCollectDelay, maxAge, enableMutationRecovery, onClientsDeleted, lc, signal, newDagStore = defaultNewDagStore) {
22
23
  let initial = true;
23
24
  initBgIntervalProcess("CollectIDBDatabases", async () => {
24
- await collectIDBDatabases(idbDatabasesStore, Date.now(), maxAge, kvStoreProvider, enableMutationRecovery, onClientsDeleted);
25
+ await collectIDBDatabases(idbDatabasesStore, Date.now(), maxAge, kvStoreProvider, enableMutationRecovery, onClientsDeleted, newDagStore);
25
26
  }, () => {
26
27
  if (initial) {
27
28
  initial = false;
@@ -47,12 +48,18 @@ async function collectIDBDatabases(idbDatabasesStore, now, maxAge, kvStoreProvid
47
48
  const { errors } = await dropDatabases(idbDatabasesStore, dbNamesToRemove, kvStoreProvider.drop);
48
49
  if (errors.length) throw errors[0];
49
50
  if (deletedClientsToRemove.length > 0) {
50
- let allDeletedClients = deletedClientsToRemove;
51
- for (const name of dbNamesToKeep) await withWrite(newDagStore(name, kvStoreProvider.create), async (dagWrite) => {
52
- const newDeletedClients = await addDeletedClients(dagWrite, deletedClientsToRemove);
53
- allDeletedClients = mergeDeletedClients(allDeletedClients, newDeletedClients);
54
- });
55
- await onClientsDeleted(normalizeDeletedClients(allDeletedClients));
51
+ let newDeletedClients = deletedClientsToRemove;
52
+ for (const name of dbNamesToKeep) {
53
+ let dbDeletedClients;
54
+ try {
55
+ dbDeletedClients = await withWrite(newDagStore(name, kvStoreProvider.create), (dagWrite) => addDeletedClients(dagWrite, deletedClientsToRemove));
56
+ } catch (e) {
57
+ if (!(e instanceof InvalidRefCountError)) throw e;
58
+ continue;
59
+ }
60
+ newDeletedClients = mergeDeletedClients(newDeletedClients, dbDeletedClients);
61
+ }
62
+ await onClientsDeleted(normalizeDeletedClients(newDeletedClients));
56
63
  }
57
64
  }
58
65
  async function dropDatabaseInternal(name, idbDatabasesStore, kvDropStore) {
@@ -128,6 +135,6 @@ function canDatabaseBeCollectedAndGetDeletedClientIDs(enableMutationRecovery, pe
128
135
  });
129
136
  }
130
137
  //#endregion
131
- export { COLLECT_IDB_INTERVAL, INITIAL_COLLECT_IDB_DELAY, dropAllDatabases, dropDatabase, initCollectIDBDatabases };
138
+ export { COLLECT_IDB_INTERVAL, INITIAL_COLLECT_IDB_DELAY, dropAllDatabases, dropDatabase, dropDatabaseInternal, initCollectIDBDatabases };
132
139
 
133
140
  //# sourceMappingURL=collect-idb-databases.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect-idb-databases.js","names":[],"sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"sourcesContent":["import type {LogContext, LogLevel, LogSink} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport {StoreImpl} from '../dag/store-impl.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n getDeletedClients,\n mergeDeletedClients,\n normalizeDeletedClients,\n type DeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {getKVStoreProvider} from '../get-kv-store-provider.ts';\nimport {assertHash, newRandomHash} from '../hash.ts';\nimport type {CreateStore, DropStore, StoreProvider} from '../kv/store.ts';\nimport {createLogContext} from '../log-options.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n} from './client-groups.ts';\nimport type {OnClientsDeleted} from './clients.ts';\nimport {getClients} from './clients.ts';\nimport type {IndexedDBDatabase} from './idb-databases-store.ts';\nimport {IDBDatabasesStore} from './idb-databases-store.ts';\n\n/**\n * How frequently to try to collect\n */\nexport const COLLECT_IDB_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * We delay the initial collection to prevent doing it at startup.\n */\nexport const INITIAL_COLLECT_IDB_DELAY = 5 * 60 * 1000; // 5 minutes\n\nexport function initCollectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n kvStoreProvider: StoreProvider,\n collectInterval: number,\n initialCollectDelay: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n): void {\n let initial = true;\n initBgIntervalProcess(\n 'CollectIDBDatabases',\n async () => {\n await collectIDBDatabases(\n idbDatabasesStore,\n Date.now(),\n maxAge,\n kvStoreProvider,\n enableMutationRecovery,\n onClientsDeleted,\n );\n },\n () => {\n if (initial) {\n initial = false;\n return initialCollectDelay;\n }\n return collectInterval;\n },\n lc,\n signal,\n );\n}\n\n/**\n * Collects IDB databases that are no longer needed.\n */\nexport async function collectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n now: number,\n maxAge: number,\n kvStoreProvider: StoreProvider,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n newDagStore = defaultNewDagStore,\n): Promise<void> {\n const databases = await idbDatabasesStore.getDatabases();\n\n const dbs = Object.values(databases);\n const collectResults = await Promise.all(\n dbs.map(\n async db =>\n [\n db.name,\n await gatherDatabaseInfoForCollect(\n db,\n now,\n maxAge,\n enableMutationRecovery,\n kvStoreProvider.create,\n newDagStore,\n ),\n ] as const,\n ),\n );\n\n const dbNamesToRemove: string[] = [];\n const dbNamesToKeep: string[] = [];\n const deletedClientsToRemove: WritableDeletedClients = [];\n for (const [dbName, [canCollect, deletedClients]] of collectResults) {\n if (canCollect) {\n dbNamesToRemove.push(dbName);\n deletedClientsToRemove.push(...deletedClients);\n } else {\n dbNamesToKeep.push(dbName);\n }\n }\n\n const {errors} = await dropDatabases(\n idbDatabasesStore,\n dbNamesToRemove,\n kvStoreProvider.drop,\n );\n if (errors.length) {\n throw errors[0];\n }\n\n if (deletedClientsToRemove.length > 0) {\n // Add the deleted clients to all the dbs that survived the collection.\n let allDeletedClients: DeletedClients = deletedClientsToRemove;\n for (const name of dbNamesToKeep) {\n await withWrite(\n newDagStore(name, kvStoreProvider.create),\n async dagWrite => {\n const newDeletedClients = await addDeletedClients(\n dagWrite,\n deletedClientsToRemove,\n );\n\n allDeletedClients = mergeDeletedClients(\n allDeletedClients,\n newDeletedClients,\n );\n },\n );\n }\n // normalize and dedupe\n const normalizedDeletedClients = normalizeDeletedClients(allDeletedClients);\n\n // Call the callback with the normalized deleted clients\n await onClientsDeleted(normalizedDeletedClients);\n }\n}\n\nasync function dropDatabaseInternal(\n name: string,\n idbDatabasesStore: IDBDatabasesStore,\n kvDropStore: DropStore,\n) {\n await kvDropStore(name);\n await idbDatabasesStore.deleteDatabases([name]);\n}\n\nasync function dropDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n namesToRemove: string[],\n kvDropStore: DropStore,\n): Promise<{dropped: string[]; errors: unknown[]}> {\n // Try to remove the databases in parallel. Don't let a single reject fail the\n // other ones. We will check for failures afterwards.\n const dropStoreResults = await Promise.allSettled(\n namesToRemove.map(async name => {\n await dropDatabaseInternal(name, idbDatabasesStore, kvDropStore);\n return name;\n }),\n );\n\n const dropped: string[] = [];\n const errors: unknown[] = [];\n for (const result of dropStoreResults) {\n if (result.status === 'fulfilled') {\n dropped.push(result.value);\n } else {\n errors.push(result.reason);\n }\n }\n\n return {dropped, errors};\n}\n\nfunction defaultNewDagStore(name: string, kvCreateStore: CreateStore): Store {\n const perKvStore = kvCreateStore(name);\n return new StoreImpl(perKvStore, newRandomHash, assertHash);\n}\n\n/**\n * If any client has a recent heartbeat or there are pending mutations we\n * return `[false]`. Otherwise we return `[true, deletedClients]`.\n */\nfunction gatherDatabaseInfoForCollect(\n db: IndexedDBDatabase,\n now: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n kvCreateStore: CreateStore,\n newDagStore: typeof defaultNewDagStore,\n): MaybePromise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n if (db.replicacheFormatVersion > FormatVersion.Latest) {\n return [false];\n }\n\n // If increase the format version we need to decide how to deal with this\n // logic.\n assert(\n db.replicacheFormatVersion === FormatVersion.DD31 ||\n db.replicacheFormatVersion === FormatVersion.V6 ||\n db.replicacheFormatVersion === FormatVersion.V7,\n () =>\n `Expected replicacheFormatVersion to be DD31, V6, or V7, got ${db.replicacheFormatVersion}`,\n );\n return canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery,\n newDagStore(db.name, kvCreateStore),\n now,\n maxAge,\n );\n}\n\n/**\n * Options for `dropDatabase` and `dropAllDatabases`.\n */\nexport type DropDatabaseOptions = {\n /**\n * Allows providing a custom implementation of the underlying storage layer.\n * Default is `'idb'`.\n */\n kvStore?: 'idb' | 'mem' | StoreProvider | undefined;\n /**\n * Determines how much logging to do. When this is set to `'debug'`,\n * Replicache will also log `'info'` and `'error'` messages. When set to\n * `'info'` we log `'info'` and `'error'` but not `'debug'`. When set to\n * `'error'` we only log `'error'` messages.\n * Default is `'info'`.\n */\n logLevel?: LogLevel | undefined;\n /**\n * Enables custom handling of logs.\n *\n * By default logs are logged to the console. If you would like logs to be\n * sent elsewhere (e.g. to a cloud logging service like DataDog) you can\n * provide an array of {@link LogSink}s. Logs at or above\n * {@link DropDatabaseOptions.logLevel} are sent to each of these {@link LogSink}s.\n * If you would still like logs to go to the console, include\n * `consoleLogSink` in the array.\n *\n * ```ts\n * logSinks: [consoleLogSink, myCloudLogSink],\n * ```\n * Default is `[consoleLogSink]`.\n */\n logSinks?: LogSink[] | undefined;\n};\n\n/**\n * Drops the specified database.\n * @param dbName The name of the database to drop.\n * @param opts Options for dropping the database.\n */\nexport async function dropDatabase(dbName: string, opts?: DropDatabaseOptions) {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabase: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n await dropDatabaseInternal(\n dbName,\n new IDBDatabasesStore(kvStoreProvider.create),\n kvStoreProvider.drop,\n );\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n */\nexport async function dropAllDatabases(opts?: DropDatabaseOptions): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropAllDatabases: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n const store = new IDBDatabasesStore(kvStoreProvider.create);\n const databases = await store.getDatabases();\n const dbNames = Object.values(databases).map(db => db.name);\n return dropDatabases(store, dbNames, kvStoreProvider.drop);\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n *\n * @deprecated Use `dropAllDatabases` instead.\n */\nexport function deleteAllReplicacheData(opts?: DropDatabaseOptions) {\n return dropAllDatabases(opts);\n}\n\n/**\n * If there are pending mutations in any of the clients in this db we return\n * `[false]`. If any client has a recent heartbeat we also return `[false]`.\n * Otherwise we return `[true, deletedClients]`.\n */\nfunction canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery: boolean,\n perdag: Store,\n now: number,\n maxAge: number,\n): Promise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n return withRead(perdag, async read => {\n // If mutation recovery is disabled we do not care if there are pending\n // mutations when we decide if we can collect the database.\n if (enableMutationRecovery) {\n const clientGroups = await getClientGroups(read);\n for (const clientGroup of clientGroups.values()) {\n if (clientGroupHasPendingMutations(clientGroup)) {\n return [false];\n }\n }\n }\n\n const clients = await getClients(read);\n\n // Don't collect if any client has a recent heartbeat (is still active).\n // This is defense in depth - normally lastOpenedTimestampMS is kept fresh\n // by the heartbeat, but this check protects against edge cases.\n for (const [, client] of clients) {\n if (now - client.heartbeatTimestampMs < maxAge) {\n return [false];\n }\n }\n\n const existingDeletedClients = await getDeletedClients(read);\n const deletedClients: WritableDeletedClients = [...existingDeletedClients];\n\n // Add all current clients to the deleted clients list\n for (const [clientID, client] of clients) {\n deletedClients.push({\n clientID,\n clientGroupID: client.clientGroupID,\n });\n }\n\n // The normalization (deduping and sorting) will be done when storing\n return [true, deletedClients];\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"collect-idb-databases.js","names":[],"sources":["../../../../../replicache/src/persist/collect-idb-databases.ts"],"sourcesContent":["import type {LogContext, LogLevel, LogSink} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport {InvalidRefCountError} from '../dag/invalid-ref-count-error.ts';\nimport {StoreImpl} from '../dag/store-impl.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n getDeletedClients,\n mergeDeletedClients,\n normalizeDeletedClients,\n type DeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport * as FormatVersion from '../format-version-enum.ts';\nimport {getKVStoreProvider} from '../get-kv-store-provider.ts';\nimport {assertHash, newRandomHash} from '../hash.ts';\nimport type {CreateStore, DropStore, StoreProvider} from '../kv/store.ts';\nimport {createLogContext} from '../log-options.ts';\nimport {withRead, withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n} from './client-groups.ts';\nimport type {OnClientsDeleted} from './clients.ts';\nimport {getClients} from './clients.ts';\nimport type {IndexedDBDatabase} from './idb-databases-store.ts';\nimport {IDBDatabasesStore} from './idb-databases-store.ts';\n\n/**\n * How frequently to try to collect\n */\nexport const COLLECT_IDB_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours\n\n/**\n * We delay the initial collection to prevent doing it at startup.\n */\nexport const INITIAL_COLLECT_IDB_DELAY = 5 * 60 * 1000; // 5 minutes\n\nexport function initCollectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n kvStoreProvider: StoreProvider,\n collectInterval: number,\n initialCollectDelay: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n newDagStore: NewDagStore = defaultNewDagStore,\n): void {\n let initial = true;\n initBgIntervalProcess(\n 'CollectIDBDatabases',\n async () => {\n await collectIDBDatabases(\n idbDatabasesStore,\n Date.now(),\n maxAge,\n kvStoreProvider,\n enableMutationRecovery,\n onClientsDeleted,\n newDagStore,\n );\n },\n () => {\n if (initial) {\n initial = false;\n return initialCollectDelay;\n }\n return collectInterval;\n },\n lc,\n signal,\n );\n}\n\n/**\n * Collects IDB databases that are no longer needed.\n */\nexport async function collectIDBDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n now: number,\n maxAge: number,\n kvStoreProvider: StoreProvider,\n enableMutationRecovery: boolean,\n onClientsDeleted: OnClientsDeleted,\n newDagStore: NewDagStore = defaultNewDagStore,\n): Promise<void> {\n const databases = await idbDatabasesStore.getDatabases();\n\n const dbs = Object.values(databases);\n const collectResults = await Promise.all(\n dbs.map(\n async db =>\n [\n db.name,\n await gatherDatabaseInfoForCollect(\n db,\n now,\n maxAge,\n enableMutationRecovery,\n kvStoreProvider.create,\n newDagStore,\n ),\n ] as const,\n ),\n );\n\n const dbNamesToRemove: string[] = [];\n const dbNamesToKeep: string[] = [];\n const deletedClientsToRemove: WritableDeletedClients = [];\n for (const [dbName, [canCollect, deletedClients]] of collectResults) {\n if (canCollect) {\n dbNamesToRemove.push(dbName);\n deletedClientsToRemove.push(...deletedClients);\n } else {\n dbNamesToKeep.push(dbName);\n }\n }\n\n const {errors} = await dropDatabases(\n idbDatabasesStore,\n dbNamesToRemove,\n kvStoreProvider.drop,\n );\n if (errors.length) {\n throw errors[0];\n }\n\n if (deletedClientsToRemove.length > 0) {\n // Add the deleted clients to all the dbs that survived the collection.\n let newDeletedClients: DeletedClients = deletedClientsToRemove;\n for (const name of dbNamesToKeep) {\n let dbDeletedClients: DeletedClients;\n try {\n dbDeletedClients = await withWrite(\n newDagStore(name, kvStoreProvider.create),\n dagWrite => addDeletedClients(dagWrite, deletedClientsToRemove),\n );\n } catch (e) {\n if (!(e instanceof InvalidRefCountError)) {\n throw e;\n }\n // This database is corrupt. Its own instance will find out and\n // recover the next time it writes to it; here, just skip it and keep\n // collecting into the others, instead of letting one corrupt\n // database break every collection run forever.\n continue;\n }\n newDeletedClients = mergeDeletedClients(\n newDeletedClients,\n dbDeletedClients,\n );\n }\n // normalize and dedupe\n const normalizedDeletedClients = normalizeDeletedClients(newDeletedClients);\n\n // Call the callback with the normalized deleted clients\n await onClientsDeleted(normalizedDeletedClients);\n }\n}\n\nexport async function dropDatabaseInternal(\n name: string,\n idbDatabasesStore: IDBDatabasesStore,\n kvDropStore: DropStore,\n) {\n await kvDropStore(name);\n await idbDatabasesStore.deleteDatabases([name]);\n}\n\nasync function dropDatabases(\n idbDatabasesStore: IDBDatabasesStore,\n namesToRemove: string[],\n kvDropStore: DropStore,\n): Promise<{dropped: string[]; errors: unknown[]}> {\n // Try to remove the databases in parallel. Don't let a single reject fail the\n // other ones. We will check for failures afterwards.\n const dropStoreResults = await Promise.allSettled(\n namesToRemove.map(async name => {\n await dropDatabaseInternal(name, idbDatabasesStore, kvDropStore);\n return name;\n }),\n );\n\n const dropped: string[] = [];\n const errors: unknown[] = [];\n for (const result of dropStoreResults) {\n if (result.status === 'fulfilled') {\n dropped.push(result.value);\n } else {\n errors.push(result.reason);\n }\n }\n\n return {dropped, errors};\n}\n\n/**\n * Creates the dag store used to inspect and update a database during\n * collection. Callers can override it to install store hooks, for example so\n * a corrupt ref count found while writing deleted clients is recovered from.\n */\nexport type NewDagStore = (name: string, kvCreateStore: CreateStore) => Store;\n\nfunction defaultNewDagStore(name: string, kvCreateStore: CreateStore): Store {\n const perKvStore = kvCreateStore(name);\n return new StoreImpl(perKvStore, newRandomHash, assertHash);\n}\n\n/**\n * If any client has a recent heartbeat or there are pending mutations we\n * return `[false]`. Otherwise we return `[true, deletedClients]`.\n */\nfunction gatherDatabaseInfoForCollect(\n db: IndexedDBDatabase,\n now: number,\n maxAge: number,\n enableMutationRecovery: boolean,\n kvCreateStore: CreateStore,\n newDagStore: NewDagStore,\n): MaybePromise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n if (db.replicacheFormatVersion > FormatVersion.Latest) {\n return [false];\n }\n\n // If increase the format version we need to decide how to deal with this\n // logic.\n assert(\n db.replicacheFormatVersion === FormatVersion.DD31 ||\n db.replicacheFormatVersion === FormatVersion.V6 ||\n db.replicacheFormatVersion === FormatVersion.V7,\n () =>\n `Expected replicacheFormatVersion to be DD31, V6, or V7, got ${db.replicacheFormatVersion}`,\n );\n return canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery,\n newDagStore(db.name, kvCreateStore),\n now,\n maxAge,\n );\n}\n\n/**\n * Options for `dropDatabase` and `dropAllDatabases`.\n */\nexport type DropDatabaseOptions = {\n /**\n * Allows providing a custom implementation of the underlying storage layer.\n * Default is `'idb'`.\n */\n kvStore?: 'idb' | 'mem' | StoreProvider | undefined;\n /**\n * Determines how much logging to do. When this is set to `'debug'`,\n * Replicache will also log `'info'` and `'error'` messages. When set to\n * `'info'` we log `'info'` and `'error'` but not `'debug'`. When set to\n * `'error'` we only log `'error'` messages.\n * Default is `'info'`.\n */\n logLevel?: LogLevel | undefined;\n /**\n * Enables custom handling of logs.\n *\n * By default logs are logged to the console. If you would like logs to be\n * sent elsewhere (e.g. to a cloud logging service like DataDog) you can\n * provide an array of {@link LogSink}s. Logs at or above\n * {@link DropDatabaseOptions.logLevel} are sent to each of these {@link LogSink}s.\n * If you would still like logs to go to the console, include\n * `consoleLogSink` in the array.\n *\n * ```ts\n * logSinks: [consoleLogSink, myCloudLogSink],\n * ```\n * Default is `[consoleLogSink]`.\n */\n logSinks?: LogSink[] | undefined;\n};\n\n/**\n * Drops the specified database.\n * @param dbName The name of the database to drop.\n * @param opts Options for dropping the database.\n */\nexport async function dropDatabase(dbName: string, opts?: DropDatabaseOptions) {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropDatabase: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n await dropDatabaseInternal(\n dbName,\n new IDBDatabasesStore(kvStoreProvider.create),\n kvStoreProvider.drop,\n );\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n */\nexport async function dropAllDatabases(opts?: DropDatabaseOptions): Promise<{\n dropped: string[];\n errors: unknown[];\n}> {\n const logContext = createLogContext(opts?.logLevel, opts?.logSinks, {\n dropAllDatabases: undefined,\n });\n const kvStoreProvider = getKVStoreProvider(logContext, opts?.kvStore);\n const store = new IDBDatabasesStore(kvStoreProvider.create);\n const databases = await store.getDatabases();\n const dbNames = Object.values(databases).map(db => db.name);\n return dropDatabases(store, dbNames, kvStoreProvider.drop);\n}\n\n/**\n * Deletes all IndexedDB data associated with Replicache.\n *\n * Returns an object with the names of the successfully dropped databases\n * and any errors encountered while dropping.\n *\n * @deprecated Use `dropAllDatabases` instead.\n */\nexport function deleteAllReplicacheData(opts?: DropDatabaseOptions) {\n return dropAllDatabases(opts);\n}\n\n/**\n * If there are pending mutations in any of the clients in this db we return\n * `[false]`. If any client has a recent heartbeat we also return `[false]`.\n * Otherwise we return `[true, deletedClients]`.\n */\nfunction canDatabaseBeCollectedAndGetDeletedClientIDs(\n enableMutationRecovery: boolean,\n perdag: Store,\n now: number,\n maxAge: number,\n): Promise<\n [canCollect: false] | [canCollect: true, deletedClients: DeletedClients]\n> {\n return withRead(perdag, async read => {\n // If mutation recovery is disabled we do not care if there are pending\n // mutations when we decide if we can collect the database.\n if (enableMutationRecovery) {\n const clientGroups = await getClientGroups(read);\n for (const clientGroup of clientGroups.values()) {\n if (clientGroupHasPendingMutations(clientGroup)) {\n return [false];\n }\n }\n }\n\n const clients = await getClients(read);\n\n // Don't collect if any client has a recent heartbeat (is still active).\n // This is defense in depth - normally lastOpenedTimestampMS is kept fresh\n // by the heartbeat, but this check protects against edge cases.\n for (const [, client] of clients) {\n if (now - client.heartbeatTimestampMs < maxAge) {\n return [false];\n }\n }\n\n const existingDeletedClients = await getDeletedClients(read);\n const deletedClients: WritableDeletedClients = [...existingDeletedClients];\n\n // Add all current clients to the deleted clients list\n for (const [clientID, client] of clients) {\n deletedClients.push({\n clientID,\n clientGroupID: client.clientGroupID,\n });\n }\n\n // The normalization (deduping and sorting) will be done when storing\n return [true, deletedClients];\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/persist/persist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AAGtD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAO,KAAK,EAAQ,MAAM,iBAAiB,CAAC;AAexD,OAAO,KAAK,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAEhE,OAAO,KAAK,EAAC,UAAU,EAAa,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAgB,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAgB7C,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,KAAK,EACb,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,OAAO,EACrB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS,EAChD,4BAA4B,sBAA0B,GACrD,OAAO,CAAC,IAAI,CAAC,CA2Lf"}
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../../../../replicache/src/persist/persist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,6BAA6B,CAAC;AAGtD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAO,KAAK,EAAQ,MAAM,iBAAiB,CAAC;AAexD,OAAO,KAAK,KAAK,aAAa,MAAM,2BAA2B,CAAC;AAEhE,OAAO,KAAK,EAAC,UAAU,EAAa,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAgB,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,aAAa,CAAC;AAgB7C,KAAK,aAAa,GAAG,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,WAAW,CAC/B,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,KAAK,EACb,QAAQ,EAAE,WAAW,EACrB,MAAM,EAAE,MAAM,OAAO,EACrB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,SAAS,EAChD,4BAA4B,sBAA0B,GACrD,OAAO,CAAC,IAAI,CAAC,CAmMf"}