@opendatalabs/personal-server-ts-core 1.34.2 → 1.35.1

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 (414) hide show
  1. package/dist/additions/durable.d.ts +32 -2
  2. package/dist/additions/durable.d.ts.map +1 -1
  3. package/dist/additions/durable.js +237 -88
  4. package/dist/additions/durable.js.map +1 -1
  5. package/dist/additions/version-inventory.d.ts +4 -0
  6. package/dist/additions/version-inventory.d.ts.map +1 -1
  7. package/dist/additions/version-inventory.js +11 -1
  8. package/dist/additions/version-inventory.js.map +1 -1
  9. package/dist/api/index.d.ts +18 -1
  10. package/dist/api/index.d.ts.map +1 -1
  11. package/dist/api/index.js +172 -4
  12. package/dist/api/index.js.map +1 -1
  13. package/dist/api/oura-collection-requirements.d.ts +16 -0
  14. package/dist/api/oura-collection-requirements.d.ts.map +1 -0
  15. package/dist/api/oura-collection-requirements.js +70 -0
  16. package/dist/api/oura-collection-requirements.js.map +1 -0
  17. package/dist/api/scope-upload-batches.d.ts +18 -0
  18. package/dist/api/scope-upload-batches.d.ts.map +1 -0
  19. package/dist/api/scope-upload-batches.js +71 -0
  20. package/dist/api/scope-upload-batches.js.map +1 -0
  21. package/dist/api/scope-uploads.d.ts +112 -0
  22. package/dist/api/scope-uploads.d.ts.map +1 -0
  23. package/dist/api/scope-uploads.js +369 -0
  24. package/dist/api/scope-uploads.js.map +1 -0
  25. package/dist/client.d.ts +70 -0
  26. package/dist/client.d.ts.map +1 -1
  27. package/dist/client.js +1 -0
  28. package/dist/client.js.map +1 -1
  29. package/dist/contracts/data.d.ts +8 -0
  30. package/dist/contracts/data.d.ts.map +1 -1
  31. package/dist/contracts/data.js +31 -1
  32. package/dist/contracts/data.js.map +1 -1
  33. package/dist/contracts/grants.d.ts.map +1 -1
  34. package/dist/contracts/grants.js +3 -0
  35. package/dist/contracts/grants.js.map +1 -1
  36. package/dist/contracts/index.d.ts +3 -1
  37. package/dist/contracts/index.d.ts.map +1 -1
  38. package/dist/contracts/index.js +3 -1
  39. package/dist/contracts/index.js.map +1 -1
  40. package/dist/contracts/oura-collection-requirements.d.ts +17 -0
  41. package/dist/contracts/oura-collection-requirements.d.ts.map +1 -0
  42. package/dist/contracts/oura-collection-requirements.js +49 -0
  43. package/dist/contracts/oura-collection-requirements.js.map +1 -0
  44. package/dist/legacy-projection/bindings.d.ts +1 -7
  45. package/dist/legacy-projection/bindings.d.ts.map +1 -1
  46. package/dist/legacy-projection/bindings.js +162 -28
  47. package/dist/legacy-projection/bindings.js.map +1 -1
  48. package/dist/legacy-projection/index.d.ts +6 -5
  49. package/dist/legacy-projection/index.d.ts.map +1 -1
  50. package/dist/legacy-projection/index.js +9 -21
  51. package/dist/legacy-projection/index.js.map +1 -1
  52. package/dist/legacy-projection/oura-browser-bindings.d.ts +2 -0
  53. package/dist/legacy-projection/oura-browser-bindings.d.ts.map +1 -1
  54. package/dist/legacy-projection/oura-browser-bindings.js +2 -0
  55. package/dist/legacy-projection/oura-browser-bindings.js.map +1 -1
  56. package/dist/legacy-projection/strava-browser-bindings.d.ts +3 -0
  57. package/dist/legacy-projection/strava-browser-bindings.d.ts.map +1 -0
  58. package/dist/legacy-projection/strava-browser-bindings.js +32 -0
  59. package/dist/legacy-projection/strava-browser-bindings.js.map +1 -0
  60. package/dist/legacy-projection/types.d.ts +44 -12
  61. package/dist/legacy-projection/types.d.ts.map +1 -1
  62. package/dist/mcp/connection-api.d.ts +11 -1
  63. package/dist/mcp/connection-api.d.ts.map +1 -1
  64. package/dist/mcp/connection-api.js +94 -9
  65. package/dist/mcp/connection-api.js.map +1 -1
  66. package/dist/mcp/index.d.ts +1 -0
  67. package/dist/mcp/index.d.ts.map +1 -1
  68. package/dist/mcp/index.js +1 -0
  69. package/dist/mcp/index.js.map +1 -1
  70. package/dist/mcp/server.d.ts +9 -0
  71. package/dist/mcp/server.d.ts.map +1 -1
  72. package/dist/mcp/server.js +19 -6
  73. package/dist/mcp/server.js.map +1 -1
  74. package/dist/mcp/store.d.ts +10 -1
  75. package/dist/mcp/store.d.ts.map +1 -1
  76. package/dist/mcp/store.js +51 -0
  77. package/dist/mcp/store.js.map +1 -1
  78. package/dist/mcp/tools.d.ts +52 -2
  79. package/dist/mcp/tools.d.ts.map +1 -1
  80. package/dist/mcp/tools.js +395 -11
  81. package/dist/mcp/tools.js.map +1 -1
  82. package/dist/mcp/types.d.ts +36 -2
  83. package/dist/mcp/types.d.ts.map +1 -1
  84. package/dist/mcp/types.js +12 -0
  85. package/dist/mcp/types.js.map +1 -1
  86. package/dist/mcp/write-client.d.ts +81 -0
  87. package/dist/mcp/write-client.d.ts.map +1 -0
  88. package/dist/mcp/write-client.js +140 -0
  89. package/dist/mcp/write-client.js.map +1 -0
  90. package/dist/oura/history-policy.d.ts +12 -0
  91. package/dist/oura/history-policy.d.ts.map +1 -0
  92. package/dist/oura/history-policy.js +54 -0
  93. package/dist/oura/history-policy.js.map +1 -0
  94. package/dist/oura/index.d.ts +51 -0
  95. package/dist/oura/index.d.ts.map +1 -0
  96. package/dist/oura/index.js +315 -0
  97. package/dist/oura/index.js.map +1 -0
  98. package/dist/pdpp/approval.d.ts +1 -1
  99. package/dist/pdpp/approval.d.ts.map +1 -1
  100. package/dist/pdpp/approval.js +29 -10
  101. package/dist/pdpp/approval.js.map +1 -1
  102. package/dist/pdpp/declaration-limits.d.ts +7 -0
  103. package/dist/pdpp/declaration-limits.d.ts.map +1 -0
  104. package/dist/pdpp/declaration-limits.js +7 -0
  105. package/dist/pdpp/declaration-limits.js.map +1 -0
  106. package/dist/pdpp/declaration.d.ts +2 -1
  107. package/dist/pdpp/declaration.d.ts.map +1 -1
  108. package/dist/pdpp/declaration.js +2 -1
  109. package/dist/pdpp/declaration.js.map +1 -1
  110. package/dist/pdpp/index.d.ts +3 -2
  111. package/dist/pdpp/index.d.ts.map +1 -1
  112. package/dist/pdpp/index.js +3 -2
  113. package/dist/pdpp/index.js.map +1 -1
  114. package/dist/pdpp/issuance.d.ts.map +1 -1
  115. package/dist/pdpp/issuance.js +6 -2
  116. package/dist/pdpp/issuance.js.map +1 -1
  117. package/dist/pdpp/resolve.d.ts +12 -1
  118. package/dist/pdpp/resolve.d.ts.map +1 -1
  119. package/dist/pdpp/resolve.js +156 -58
  120. package/dist/pdpp/resolve.js.map +1 -1
  121. package/dist/pdpp/selection.d.ts +30 -1
  122. package/dist/pdpp/selection.d.ts.map +1 -1
  123. package/dist/pdpp/selection.js +77 -26
  124. package/dist/pdpp/selection.js.map +1 -1
  125. package/dist/pdpp/time.d.ts +39 -0
  126. package/dist/pdpp/time.d.ts.map +1 -0
  127. package/dist/pdpp/time.js +173 -0
  128. package/dist/pdpp/time.js.map +1 -0
  129. package/dist/pdpp/tokens.d.ts.map +1 -1
  130. package/dist/pdpp/tokens.js +4 -2
  131. package/dist/pdpp/tokens.js.map +1 -1
  132. package/dist/pdpp/types.d.ts +18 -2
  133. package/dist/pdpp/types.d.ts.map +1 -1
  134. package/dist/pdpp/types.js +7 -0
  135. package/dist/pdpp/types.js.map +1 -1
  136. package/dist/policy/reserved-scopes.d.ts +7 -0
  137. package/dist/policy/reserved-scopes.d.ts.map +1 -1
  138. package/dist/policy/reserved-scopes.js +12 -1
  139. package/dist/policy/reserved-scopes.js.map +1 -1
  140. package/dist/ports/index.d.ts +85 -1
  141. package/dist/ports/index.d.ts.map +1 -1
  142. package/dist/ports/index.js.map +1 -1
  143. package/dist/ports/pdpp-auth.d.ts +17 -27
  144. package/dist/ports/pdpp-auth.d.ts.map +1 -1
  145. package/dist/ports/pdpp-auth.js +14 -28
  146. package/dist/ports/pdpp-auth.js.map +1 -1
  147. package/dist/publication/publish.d.ts +96 -0
  148. package/dist/publication/publish.d.ts.map +1 -0
  149. package/dist/publication/publish.js +261 -0
  150. package/dist/publication/publish.js.map +1 -0
  151. package/dist/replication/completion.d.ts +20 -0
  152. package/dist/replication/completion.d.ts.map +1 -0
  153. package/dist/replication/completion.js +73 -0
  154. package/dist/replication/completion.js.map +1 -0
  155. package/dist/replication/coordinator-state.d.ts +29 -0
  156. package/dist/replication/coordinator-state.d.ts.map +1 -0
  157. package/dist/replication/coordinator-state.js +88 -0
  158. package/dist/replication/coordinator-state.js.map +1 -0
  159. package/dist/replication/coordinator-state.test-utils.d.ts +32 -0
  160. package/dist/replication/coordinator-state.test-utils.d.ts.map +1 -0
  161. package/dist/replication/coordinator-state.test-utils.js +113 -0
  162. package/dist/replication/coordinator-state.test-utils.js.map +1 -0
  163. package/dist/replication/coordinator.d.ts +264 -0
  164. package/dist/replication/coordinator.d.ts.map +1 -0
  165. package/dist/replication/coordinator.js +499 -0
  166. package/dist/replication/coordinator.js.map +1 -0
  167. package/dist/replication/copies.d.ts +126 -0
  168. package/dist/replication/copies.d.ts.map +1 -0
  169. package/dist/replication/copies.js +437 -0
  170. package/dist/replication/copies.js.map +1 -0
  171. package/dist/replication/copy-promotion.d.ts +132 -0
  172. package/dist/replication/copy-promotion.d.ts.map +1 -0
  173. package/dist/replication/copy-promotion.js +126 -0
  174. package/dist/replication/copy-promotion.js.map +1 -0
  175. package/dist/replication/door.d.ts +33 -0
  176. package/dist/replication/door.d.ts.map +1 -0
  177. package/dist/replication/door.js +10 -0
  178. package/dist/replication/door.js.map +1 -0
  179. package/dist/replication/dpv2.d.ts +141 -0
  180. package/dist/replication/dpv2.d.ts.map +1 -0
  181. package/dist/replication/dpv2.js +320 -0
  182. package/dist/replication/dpv2.js.map +1 -0
  183. package/dist/replication/effects.d.ts +68 -0
  184. package/dist/replication/effects.d.ts.map +1 -0
  185. package/dist/replication/effects.js +89 -0
  186. package/dist/replication/effects.js.map +1 -0
  187. package/dist/replication/engine.d.ts +378 -0
  188. package/dist/replication/engine.d.ts.map +1 -0
  189. package/dist/replication/engine.js +3197 -0
  190. package/dist/replication/engine.js.map +1 -0
  191. package/dist/replication/envelope.d.ts +481 -0
  192. package/dist/replication/envelope.d.ts.map +1 -0
  193. package/dist/replication/envelope.js +192 -0
  194. package/dist/replication/envelope.js.map +1 -0
  195. package/dist/replication/evidence.d.ts +118 -0
  196. package/dist/replication/evidence.d.ts.map +1 -0
  197. package/dist/replication/evidence.js +198 -0
  198. package/dist/replication/evidence.js.map +1 -0
  199. package/dist/replication/finality.d.ts +16 -0
  200. package/dist/replication/finality.d.ts.map +1 -0
  201. package/dist/replication/finality.js +11 -0
  202. package/dist/replication/finality.js.map +1 -0
  203. package/dist/replication/fixtures.d.ts +16 -0
  204. package/dist/replication/fixtures.d.ts.map +1 -0
  205. package/dist/replication/fixtures.js +47 -0
  206. package/dist/replication/fixtures.js.map +1 -0
  207. package/dist/replication/host-config.d.ts +52 -0
  208. package/dist/replication/host-config.d.ts.map +1 -0
  209. package/dist/replication/host-config.js +111 -0
  210. package/dist/replication/host-config.js.map +1 -0
  211. package/dist/replication/host-driver.d.ts +95 -0
  212. package/dist/replication/host-driver.d.ts.map +1 -0
  213. package/dist/replication/host-driver.js +202 -0
  214. package/dist/replication/host-driver.js.map +1 -0
  215. package/dist/replication/host-ports.d.ts +48 -0
  216. package/dist/replication/host-ports.d.ts.map +1 -0
  217. package/dist/replication/host-ports.js +69 -0
  218. package/dist/replication/host-ports.js.map +1 -0
  219. package/dist/replication/identity.d.ts +13 -0
  220. package/dist/replication/identity.d.ts.map +1 -0
  221. package/dist/replication/identity.js +22 -0
  222. package/dist/replication/identity.js.map +1 -0
  223. package/dist/replication/limits.d.ts +124 -0
  224. package/dist/replication/limits.d.ts.map +1 -0
  225. package/dist/replication/limits.js +182 -0
  226. package/dist/replication/limits.js.map +1 -0
  227. package/dist/replication/merge-plan.d.ts +92 -0
  228. package/dist/replication/merge-plan.d.ts.map +1 -0
  229. package/dist/replication/merge-plan.js +229 -0
  230. package/dist/replication/merge-plan.js.map +1 -0
  231. package/dist/replication/namespace.d.ts +4 -0
  232. package/dist/replication/namespace.d.ts.map +1 -0
  233. package/dist/replication/namespace.js +7 -0
  234. package/dist/replication/namespace.js.map +1 -0
  235. package/dist/replication/outcome.d.ts +178 -0
  236. package/dist/replication/outcome.d.ts.map +1 -0
  237. package/dist/replication/outcome.js +83 -0
  238. package/dist/replication/outcome.js.map +1 -0
  239. package/dist/replication/payload.d.ts +39 -0
  240. package/dist/replication/payload.d.ts.map +1 -0
  241. package/dist/replication/payload.js +137 -0
  242. package/dist/replication/payload.js.map +1 -0
  243. package/dist/replication/preparation.d.ts +42 -0
  244. package/dist/replication/preparation.d.ts.map +1 -0
  245. package/dist/replication/preparation.js +118 -0
  246. package/dist/replication/preparation.js.map +1 -0
  247. package/dist/replication/rebaseline.d.ts +33 -0
  248. package/dist/replication/rebaseline.d.ts.map +1 -0
  249. package/dist/replication/rebaseline.js +40 -0
  250. package/dist/replication/rebaseline.js.map +1 -0
  251. package/dist/replication/rebinding.d.ts +43 -0
  252. package/dist/replication/rebinding.d.ts.map +1 -0
  253. package/dist/replication/rebinding.js +52 -0
  254. package/dist/replication/rebinding.js.map +1 -0
  255. package/dist/replication/receive.d.ts +57 -0
  256. package/dist/replication/receive.d.ts.map +1 -0
  257. package/dist/replication/receive.js +114 -0
  258. package/dist/replication/receive.js.map +1 -0
  259. package/dist/replication/record-reducer.d.ts +16 -0
  260. package/dist/replication/record-reducer.d.ts.map +1 -0
  261. package/dist/replication/record-reducer.js +52 -0
  262. package/dist/replication/record-reducer.js.map +1 -0
  263. package/dist/replication/revision.d.ts +24 -0
  264. package/dist/replication/revision.d.ts.map +1 -0
  265. package/dist/replication/revision.js +35 -0
  266. package/dist/replication/revision.js.map +1 -0
  267. package/dist/replication/roster.d.ts +53 -0
  268. package/dist/replication/roster.d.ts.map +1 -0
  269. package/dist/replication/roster.js +115 -0
  270. package/dist/replication/roster.js.map +1 -0
  271. package/dist/replication/schedule.d.ts +73 -0
  272. package/dist/replication/schedule.d.ts.map +1 -0
  273. package/dist/replication/schedule.js +182 -0
  274. package/dist/replication/schedule.js.map +1 -0
  275. package/dist/replication/stream.d.ts +73 -0
  276. package/dist/replication/stream.d.ts.map +1 -0
  277. package/dist/replication/stream.js +542 -0
  278. package/dist/replication/stream.js.map +1 -0
  279. package/dist/replication/transport.d.ts +236 -0
  280. package/dist/replication/transport.d.ts.map +1 -0
  281. package/dist/replication/transport.js +440 -0
  282. package/dist/replication/transport.js.map +1 -0
  283. package/dist/replication/visibility-engine.d.ts +337 -0
  284. package/dist/replication/visibility-engine.d.ts.map +1 -0
  285. package/dist/replication/visibility-engine.js +602 -0
  286. package/dist/replication/visibility-engine.js.map +1 -0
  287. package/dist/replication/visibility-policy.d.ts +26 -0
  288. package/dist/replication/visibility-policy.d.ts.map +1 -0
  289. package/dist/replication/visibility-policy.js +33 -0
  290. package/dist/replication/visibility-policy.js.map +1 -0
  291. package/dist/replication/writer-identity-store.d.ts +100 -0
  292. package/dist/replication/writer-identity-store.d.ts.map +1 -0
  293. package/dist/replication/writer-identity-store.js +218 -0
  294. package/dist/replication/writer-identity-store.js.map +1 -0
  295. package/dist/replication/writer-identity.d.ts +252 -0
  296. package/dist/replication/writer-identity.d.ts.map +1 -0
  297. package/dist/replication/writer-identity.js +281 -0
  298. package/dist/replication/writer-identity.js.map +1 -0
  299. package/dist/replication/writer-identity.test-utils.d.ts +24 -0
  300. package/dist/replication/writer-identity.test-utils.d.ts.map +1 -0
  301. package/dist/replication/writer-identity.test-utils.js +86 -0
  302. package/dist/replication/writer-identity.test-utils.js.map +1 -0
  303. package/dist/schemas/server-config.d.ts +13 -0
  304. package/dist/schemas/server-config.d.ts.map +1 -1
  305. package/dist/schemas/server-config.js +24 -0
  306. package/dist/schemas/server-config.js.map +1 -1
  307. package/dist/storage/adapters/index.d.ts +3 -1
  308. package/dist/storage/adapters/index.d.ts.map +1 -1
  309. package/dist/storage/adapters/index.js +2 -0
  310. package/dist/storage/adapters/index.js.map +1 -1
  311. package/dist/storage/adapters/interface.d.ts +18 -1
  312. package/dist/storage/adapters/interface.d.ts.map +1 -1
  313. package/dist/storage/adapters/interface.js +13 -1
  314. package/dist/storage/adapters/interface.js.map +1 -1
  315. package/dist/storage/adapters/sdk.d.ts +2 -1
  316. package/dist/storage/adapters/sdk.d.ts.map +1 -1
  317. package/dist/storage/adapters/sdk.js +21 -3
  318. package/dist/storage/adapters/sdk.js.map +1 -1
  319. package/dist/storage/adapters/vana-audience.d.ts +17 -5
  320. package/dist/storage/adapters/vana-audience.d.ts.map +1 -1
  321. package/dist/storage/adapters/vana-audience.js +24 -9
  322. package/dist/storage/adapters/vana-audience.js.map +1 -1
  323. package/dist/storage/adapters/vana.d.ts +2 -2
  324. package/dist/storage/adapters/vana.d.ts.map +1 -1
  325. package/dist/storage/adapters/vana.js +30 -22
  326. package/dist/storage/adapters/vana.js.map +1 -1
  327. package/dist/storage/canonical-legacy-projection.d.ts +1 -1
  328. package/dist/storage/canonical-legacy-projection.d.ts.map +1 -1
  329. package/dist/storage/canonical-legacy-projection.js +13 -15
  330. package/dist/storage/canonical-legacy-projection.js.map +1 -1
  331. package/dist/storage/legacy-projection.d.ts.map +1 -1
  332. package/dist/storage/legacy-projection.js +16 -1
  333. package/dist/storage/legacy-projection.js.map +1 -1
  334. package/dist/storage/legacy-scope-coverage-exceptions.d.ts +5 -0
  335. package/dist/storage/legacy-scope-coverage-exceptions.d.ts.map +1 -1
  336. package/dist/storage/legacy-scope-coverage-exceptions.js +4 -0
  337. package/dist/storage/legacy-scope-coverage-exceptions.js.map +1 -1
  338. package/dist/storage/legacy-scope-coverage.d.ts.map +1 -1
  339. package/dist/storage/legacy-scope-coverage.js +14 -1
  340. package/dist/storage/legacy-scope-coverage.js.map +1 -1
  341. package/dist/storage/legacy-scope-entity-keys.d.ts +14 -1
  342. package/dist/storage/legacy-scope-entity-keys.d.ts.map +1 -1
  343. package/dist/storage/legacy-scope-entity-keys.js +18 -1
  344. package/dist/storage/legacy-scope-entity-keys.js.map +1 -1
  345. package/dist/storage/legacy-scope-view.d.ts +57 -5
  346. package/dist/storage/legacy-scope-view.d.ts.map +1 -1
  347. package/dist/storage/legacy-scope-view.js +644 -318
  348. package/dist/storage/legacy-scope-view.js.map +1 -1
  349. package/dist/storage/pdpp-records/cursor.d.ts +47 -2
  350. package/dist/storage/pdpp-records/cursor.d.ts.map +1 -1
  351. package/dist/storage/pdpp-records/cursor.js +7 -0
  352. package/dist/storage/pdpp-records/cursor.js.map +1 -1
  353. package/dist/storage/pdpp-records/enforcement.d.ts +3 -16
  354. package/dist/storage/pdpp-records/enforcement.d.ts.map +1 -1
  355. package/dist/storage/pdpp-records/enforcement.js +40 -13
  356. package/dist/storage/pdpp-records/enforcement.js.map +1 -1
  357. package/dist/storage/pdpp-records/index.d.ts +2 -0
  358. package/dist/storage/pdpp-records/index.d.ts.map +1 -1
  359. package/dist/storage/pdpp-records/index.js +1 -0
  360. package/dist/storage/pdpp-records/index.js.map +1 -1
  361. package/dist/storage/pdpp-records/interface.d.ts +8 -1
  362. package/dist/storage/pdpp-records/interface.d.ts.map +1 -1
  363. package/dist/storage/pdpp-records/list-page.d.ts +35 -0
  364. package/dist/storage/pdpp-records/list-page.d.ts.map +1 -0
  365. package/dist/storage/pdpp-records/list-page.js +106 -0
  366. package/dist/storage/pdpp-records/list-page.js.map +1 -0
  367. package/dist/storage/pdpp-records/memory-store.d.ts.map +1 -1
  368. package/dist/storage/pdpp-records/memory-store.js +95 -99
  369. package/dist/storage/pdpp-records/memory-store.js.map +1 -1
  370. package/dist/storage/pdpp-records/portable.d.ts +3 -0
  371. package/dist/storage/pdpp-records/portable.d.ts.map +1 -1
  372. package/dist/storage/pdpp-records/portable.js +3 -0
  373. package/dist/storage/pdpp-records/portable.js.map +1 -1
  374. package/dist/storage/pdpp-records/replication-journal.d.ts +20 -0
  375. package/dist/storage/pdpp-records/replication-journal.d.ts.map +1 -0
  376. package/dist/storage/pdpp-records/replication-journal.js +2 -0
  377. package/dist/storage/pdpp-records/replication-journal.js.map +1 -0
  378. package/dist/storage/pdpp-records/stream-declaration.d.ts +0 -2
  379. package/dist/storage/pdpp-records/stream-declaration.d.ts.map +1 -1
  380. package/dist/storage/pdpp-records/stream-declaration.js +0 -9
  381. package/dist/storage/pdpp-records/stream-declaration.js.map +1 -1
  382. package/dist/storage/pdpp-records/sync-selection.d.ts +4 -0
  383. package/dist/storage/pdpp-records/sync-selection.d.ts.map +1 -0
  384. package/dist/storage/pdpp-records/sync-selection.js +37 -0
  385. package/dist/storage/pdpp-records/sync-selection.js.map +1 -0
  386. package/dist/storage/pdpp-records/types.d.ts +64 -0
  387. package/dist/storage/pdpp-records/types.d.ts.map +1 -1
  388. package/dist/storage/pdpp-records/types.js.map +1 -1
  389. package/dist/sync/delete-data-port.d.ts +15 -0
  390. package/dist/sync/delete-data-port.d.ts.map +1 -1
  391. package/dist/sync/delete-data-port.js +19 -7
  392. package/dist/sync/delete-data-port.js.map +1 -1
  393. package/dist/sync/index.d.ts +2 -1
  394. package/dist/sync/index.d.ts.map +1 -1
  395. package/dist/sync/index.js +2 -1
  396. package/dist/sync/index.js.map +1 -1
  397. package/dist/sync/pending-blob-deletions.d.ts +18 -2
  398. package/dist/sync/pending-blob-deletions.d.ts.map +1 -1
  399. package/dist/sync/pending-blob-deletions.js +69 -23
  400. package/dist/sync/pending-blob-deletions.js.map +1 -1
  401. package/dist/sync/workers/delete.d.ts.map +1 -1
  402. package/dist/sync/workers/delete.js +332 -142
  403. package/dist/sync/workers/delete.js.map +1 -1
  404. package/dist/sync/workers/download.d.ts.map +1 -1
  405. package/dist/sync/workers/download.js +53 -5
  406. package/dist/sync/workers/download.js.map +1 -1
  407. package/dist/sync/workers/upload.d.ts +24 -5
  408. package/dist/sync/workers/upload.d.ts.map +1 -1
  409. package/dist/sync/workers/upload.js +160 -275
  410. package/dist/sync/workers/upload.js.map +1 -1
  411. package/dist/test-utils/memory-storage.d.ts.map +1 -1
  412. package/dist/test-utils/memory-storage.js +17 -2
  413. package/dist/test-utils/memory-storage.js.map +1 -1
  414. package/package.json +24 -2
@@ -1,4 +1,7 @@
1
+ import { withScopeLock } from "../../additions/scope-lock.js";
1
2
  import { computeDataPointId } from "../data-point-id.js";
3
+ import { isGatewayReservedScope } from "../delete-data-port.js";
4
+ import { createMemoryPendingBlobDeletionStore } from "../pending-blob-deletions.js";
2
5
  import { deletionTimestamp, isEntryCoveredByTombstone, tombstoneVersion, } from "../scope-deletions.js";
3
6
  /**
4
7
  * Exact blob deletes sent per pass. vana-storage allows 30 DELETE requests
@@ -38,6 +41,63 @@ export function planBlobDeletions(storage, scope, tombstoneVersionValue) {
38
41
  range: last === null || last < 1n ? null : { from: "1", to: last.toString() },
39
42
  };
40
43
  }
44
+ /**
45
+ * Snapshot matching rows synchronously. Owner-intent capture precedes remote
46
+ * work; tombstone capture precedes blob cleanup. Later rows stay outside each fence.
47
+ */
48
+ function captureEntries(storage, scope, includes) {
49
+ const covered = [];
50
+ const PAGE_SIZE = 500;
51
+ for (let offset = 0;; offset += PAGE_SIZE) {
52
+ const entries = storage.listVersions(scope, { limit: PAGE_SIZE, offset });
53
+ for (const entry of entries) {
54
+ if (includes(entry)) {
55
+ covered.push({
56
+ id: entry.id,
57
+ path: entry.path,
58
+ collectedAt: entry.collectedAt,
59
+ afterTombstoneVersion: entry.afterTombstoneVersion,
60
+ });
61
+ }
62
+ }
63
+ if (entries.length < PAGE_SIZE)
64
+ break;
65
+ }
66
+ return covered;
67
+ }
68
+ /**
69
+ * Erase exactly the fenced versions, one at a time, each through the storage's
70
+ * fenced erase. The storage checks the row's id and path at the erase itself,
71
+ * under its own lock or transaction, so a replacement that took a fenced stamp
72
+ * after capture is left alone and reported as not erased. No snapshot is taken
73
+ * here, because a snapshot is stale by the time the erase runs.
74
+ *
75
+ * The scope's first-seen sidecar was derived from the fenced rows, so it is
76
+ * dropped after the erases, under the scope lock: a rebuild that read the old
77
+ * rows cannot write it back once this returns. The next read rebuilds it from
78
+ * the rows that survive (a re-add starts afresh there).
79
+ */
80
+ async function eraseCoveredEntries(storage, scope, fence) {
81
+ if (fence.length === 0)
82
+ return 0;
83
+ let erased = 0;
84
+ try {
85
+ for (const captured of fence) {
86
+ const removed = await storage.deleteVersion(scope, captured.collectedAt, {
87
+ id: captured.id,
88
+ path: captured.path,
89
+ });
90
+ if (removed)
91
+ erased += 1;
92
+ }
93
+ }
94
+ finally {
95
+ await withScopeLock(scope, async () => {
96
+ await storage.deleteFirstSeenLedger?.(scope);
97
+ });
98
+ }
99
+ return erased;
100
+ }
41
101
  /** Take up to `count` versions from the head of a range; `rest` is what remains. */
42
102
  function takeFromRange(range, count) {
43
103
  const from = BigInt(range.from);
@@ -113,6 +173,15 @@ export async function deleteScope(deps, scope) {
113
173
  },
114
174
  pendingBlobDeletion: false,
115
175
  };
176
+ // A new owner request also covers already-acknowledged local re-adds. Capture
177
+ // them before remote work; background retries never get this owner-intent fence.
178
+ const requestedReadds = deleteData
179
+ ? captureEntries(storage, scope, (entry) => entry.dataPointId === null &&
180
+ Number.isSafeInteger(entry.afterTombstoneVersion) &&
181
+ Number(entry.afterTombstoneVersion) > 0)
182
+ : [];
183
+ // Set only once a gateway tombstone has landed (see step 2).
184
+ let covered = null;
116
185
  if (deleteData) {
117
186
  // 1. Gateway tombstone: the durable fact. Nothing else happens if it
118
187
  // fails, so the owner gets a clear "nothing was deleted" instead of a
@@ -176,31 +245,38 @@ export async function deleteScope(deps, scope) {
176
245
  return result;
177
246
  }
178
247
  // 2. Storage blobs: exact keys, one pass of at most a batch, the rest
179
- // queued. A tombstone whose version the gateway did not report cannot be
180
- // expanded into registry keys yet; a version-less marker asks the retry
181
- // to expand it once the registry answers.
182
- const plan = planBlobDeletions(storage, scope, tombstoneVersionValue);
183
- const batch = plan.keys.slice(0, BLOB_DELETE_BATCH_SIZE);
184
- const leftovers = plan.keys
185
- .slice(BLOB_DELETE_BATCH_SIZE)
186
- .map((version) => ({ scope, version }));
187
- if (plan.range) {
188
- const taken = takeFromRange(plan.range, BLOB_DELETE_BATCH_SIZE - batch.length);
189
- batch.push(...taken.versions);
190
- if (taken.rest)
191
- leftovers.push({ scope, version: null, range: taken.rest });
192
- }
193
- if (tombstoneKnown && tombstoneVersionValue === null) {
194
- leftovers.push({ scope, version: null });
248
+ // queued. A repeated delete of a scope whose cleanup is still running
249
+ // resumes from the queue: only work not taken on before is recorded, and
250
+ // the pass drains the scope's queue head, so no confirmed key is re-sent
251
+ // and no queued key is dropped. A tombstone whose version the gateway did
252
+ // not report cannot be expanded into registry keys yet; a version-less
253
+ // marker asks the retry to expand it once the registry answers.
254
+ // Fence first: the covered entries as they stand before the remote await.
255
+ covered = captureEntries(storage, scope, (entry) => isEntryCoveredByTombstone(entry, { version: tombstoneVersionValue }));
256
+ if (tombstoneVersionValue !== null) {
257
+ const anchor = BigInt(tombstoneVersionValue);
258
+ const coveredIds = new Set(covered.map((entry) => entry.id));
259
+ for (const entry of requestedReadds) {
260
+ if (typeof entry.afterTombstoneVersion === "number" &&
261
+ BigInt(entry.afterTombstoneVersion) <= anchor &&
262
+ !coveredIds.has(entry.id))
263
+ covered.push(entry);
264
+ }
195
265
  }
196
- const storageStep = await deleteBlobKeys({ deleteData, pendingBlobDeletions, logger }, scope, batch, leftovers);
266
+ const plan = planBlobDeletions(storage, scope, tombstoneVersionValue);
267
+ const storageStep = await deleteScopeBlobs({ deleteData, pendingBlobDeletions, logger }, scope, plan, tombstoneKnown && tombstoneVersionValue === null);
197
268
  result.steps.storage = storageStep.step;
198
269
  result.pendingBlobDeletion = storageStep.pending > 0;
199
270
  }
200
271
  // 3. Local copy. Safe now: the registry row is a tombstone (or the point
201
- // was never registered / sync is off), so nothing can re-pull it.
272
+ // was never registered / sync is off), so nothing can re-pull it. Only the
273
+ // versions fenced by the tombstone or this owner request are erased: a
274
+ // later re-add stays outside the owner fence. With no gateway
275
+ // there is no fence and the whole local copy goes.
202
276
  try {
203
- const deletedCount = await storage.deleteScope(scope);
277
+ const deletedCount = covered
278
+ ? await eraseCoveredEntries(storage, scope, covered)
279
+ : await storage.deleteScope(scope);
204
280
  result.steps.local = { status: "ok", deletedCount };
205
281
  }
206
282
  catch (err) {
@@ -219,51 +295,206 @@ export async function deleteScope(deps, scope) {
219
295
  return result;
220
296
  }
221
297
  /**
222
- * Delete one pass worth of exact keys and reconcile the retry markers:
223
- * completed keys (deleted or already absent) leave the store, failed and
224
- * not-yet-attempted keys enter it.
298
+ * Where a repeated delete of `scope` resumes: the highest tombstone version
299
+ * whose covered range was already taken on. The store's watermark says so
300
+ * directly; a queued range also proves its own upper bound was planned
301
+ * (which covers marker files written before the watermark existed).
225
302
  */
226
- async function deleteBlobKeys(deps, scope, batch, leftovers) {
227
- const { deleteData, pendingBlobDeletions, logger } = deps;
228
- let outcome;
229
- try {
230
- outcome =
231
- batch.length > 0 && deleteData
232
- ? await deleteData.deleteBlobVersions(scope, batch)
233
- : { deleted: [], missing: [], failed: [] };
303
+ async function resumePoint(store, scope, markers) {
304
+ const stored = store.plannedThrough
305
+ ? await store.plannedThrough(scope)
306
+ : null;
307
+ let through = stored === null ? null : BigInt(stored);
308
+ for (const marker of markers) {
309
+ if (marker.scope !== scope || marker.version !== null || !marker.range) {
310
+ continue;
311
+ }
312
+ const to = BigInt(marker.range.to);
313
+ if (through === null || to > through)
314
+ through = to;
234
315
  }
235
- catch (err) {
236
- // The port reports per-key failures; a thrown error means none of the
237
- // batch can be trusted as done.
238
- outcome = {
239
- deleted: [],
240
- missing: [],
241
- failed: batch.map((version) => ({ version, error: errorMessage(err) })),
242
- };
316
+ return through;
317
+ }
318
+ /**
319
+ * The part of `plan` not taken on yet: its exact keys, and the registry range
320
+ * above `through`. Keys at or below `through` are already deleted or still
321
+ * queued, so planning them again would only re-send confirmed deletes.
322
+ */
323
+ function unplannedWork(scope, plan, through) {
324
+ const work = plan.keys.map((version) => ({
325
+ scope,
326
+ version,
327
+ }));
328
+ if (plan.range) {
329
+ let from = BigInt(plan.range.from);
330
+ if (through !== null && through + 1n > from)
331
+ from = through + 1n;
332
+ if (from <= BigInt(plan.range.to)) {
333
+ work.push({
334
+ scope,
335
+ version: null,
336
+ range: { from: from.toString(), to: plan.range.to },
337
+ });
338
+ }
243
339
  }
244
- const completed = [...outcome.deleted, ...outcome.missing].map((version) => ({ scope, version }));
245
- const remaining = [
246
- ...outcome.failed.map(({ version }) => ({ scope, version })),
247
- ...leftovers,
248
- ];
249
- const remainingKeys = countPendingKeys(remaining);
250
- let recorded = remainingKeys;
251
- if (pendingBlobDeletions) {
340
+ return work;
341
+ }
342
+ /** Record work and its watermark together when the store supports it. */
343
+ async function recordPlannedWork(store, scope, work, through) {
344
+ if (store.enqueue)
345
+ await store.enqueue(scope, work, through);
346
+ else
347
+ await store.add(work);
348
+ }
349
+ /**
350
+ * Send one rate-limited batch taken from `markers` (exact keys first, then
351
+ * range heads, in stored order) and reconcile the store: acknowledged exact
352
+ * markers (2xx or 404) leave it, a failed key taken from a range becomes an
353
+ * exact marker, and each consumed range is advanced in place. A key queued
354
+ * twice (an exact marker inside a range) is sent once.
355
+ */
356
+ async function drainOnePass(deleteData, store, markers) {
357
+ let budget = BLOB_DELETE_BATCH_SIZE;
358
+ const byScope = new Map();
359
+ const queued = new Set();
360
+ const exactOrigin = new Set();
361
+ const advancedRanges = [];
362
+ const keyOf = (scope, version) => `${scope}\u0000${version}`;
363
+ const take = (scope, version) => {
364
+ const id = keyOf(scope, version);
365
+ if (queued.has(id))
366
+ return false;
367
+ queued.add(id);
368
+ const versions = byScope.get(scope) ?? [];
369
+ versions.push(version);
370
+ byScope.set(scope, versions);
371
+ return true;
372
+ };
373
+ for (const marker of markers) {
374
+ if (budget === 0)
375
+ break;
376
+ if (marker.version !== null) {
377
+ exactOrigin.add(keyOf(marker.scope, marker.version));
378
+ if (take(marker.scope, marker.version))
379
+ budget -= 1;
380
+ }
381
+ }
382
+ for (const marker of markers) {
383
+ if (budget === 0)
384
+ break;
385
+ if (marker.version === null && marker.range) {
386
+ const taken = takeFromRange(marker.range, budget);
387
+ for (const version of taken.versions) {
388
+ if (take(marker.scope, version))
389
+ budget -= 1;
390
+ }
391
+ advancedRanges.push({
392
+ old: marker,
393
+ next: taken.rest
394
+ ? { scope: marker.scope, version: null, range: taken.rest }
395
+ : null,
396
+ });
397
+ }
398
+ }
399
+ const outcomes = new Map();
400
+ for (const [scope, versions] of byScope) {
401
+ let outcome;
252
402
  try {
253
- if (completed.length > 0)
254
- await pendingBlobDeletions.remove(completed);
255
- if (remaining.length > 0)
256
- await pendingBlobDeletions.add(remaining);
403
+ outcome = await deleteData.deleteBlobVersions(scope, versions);
404
+ }
405
+ catch (err) {
406
+ // The port reports per-key failures; a thrown error means none of the
407
+ // batch can be trusted as done.
408
+ const message = errorMessage(err);
409
+ outcome = {
410
+ deleted: [],
411
+ missing: [],
412
+ failed: versions.map((version) => ({ version, error: message })),
413
+ };
414
+ }
415
+ outcomes.set(scope, outcome);
416
+ const completed = [...outcome.deleted, ...outcome.missing]
417
+ .filter((version) => exactOrigin.has(keyOf(scope, version)))
418
+ .map((version) => ({ scope, version }));
419
+ if (completed.length > 0)
420
+ await store.remove(completed);
421
+ // A failed key that came from a range becomes an exact marker; a failed
422
+ // exact marker simply stays.
423
+ const failedFromRange = outcome.failed
424
+ .filter(({ version }) => !exactOrigin.has(keyOf(scope, version)))
425
+ .map(({ version }) => ({ scope, version }));
426
+ if (failedFromRange.length > 0)
427
+ await store.add(failedFromRange);
428
+ }
429
+ // Advance the ranges only now: a crash mid-pass re-sends already-deleted
430
+ // keys (404, harmless) rather than skipping keys that were never sent. The
431
+ // successor is persisted before its predecessor is dropped, so a failed or
432
+ // interrupted write leaves the old range standing, never a gap.
433
+ for (const { old, next } of advancedRanges) {
434
+ if (next)
435
+ await store.add([next]);
436
+ await store.remove([old]);
437
+ }
438
+ return outcomes;
439
+ }
440
+ /**
441
+ * The storage step of one DELETE call: record the work not taken on yet
442
+ * (with the scope's planned watermark), then send one batch from the head of
443
+ * the scope's queue and report what is still owed. A first delete and a
444
+ * repeat of it run the same path, so a repeat resumes where the queue stands.
445
+ */
446
+ async function deleteScopeBlobs(deps, scope, plan, versionUnknown) {
447
+ const { deleteData, logger } = deps;
448
+ const withUnknown = (work) => versionUnknown ? [...work, { scope, version: null }] : work;
449
+ let work = withUnknown(unplannedWork(scope, plan, null));
450
+ let store = deps.pendingBlobDeletions ?? null;
451
+ // This scope's markers already in the durable store before this call.
452
+ let queuedBefore = [];
453
+ if (store) {
454
+ try {
455
+ const markers = await store.list();
456
+ queuedBefore = markers.filter((m) => m.scope === scope);
457
+ const through = await resumePoint(store, scope, markers);
458
+ work = withUnknown(unplannedWork(scope, plan, through));
459
+ await recordPlannedWork(store, scope, work, plan.range?.to ?? null);
257
460
  }
258
461
  catch (markerErr) {
259
- recorded = 0;
260
- logger.error({ scope, error: errorMessage(markerErr), keys: remainingKeys }, "Could not record pending blob deletion markers");
462
+ logger.error({ scope, error: errorMessage(markerErr), keys: countPendingKeys(work) }, "Could not record pending blob deletion markers");
463
+ store = null;
261
464
  }
262
465
  }
263
- else if (remaining.length > 0) {
264
- recorded = 0;
466
+ // Without a usable marker store this pass still sends a batch, taken from
467
+ // the scope's queue as last read plus the new work. The new work it cannot
468
+ // send is reported, not recorded; the durable queue keeps its old markers
469
+ // untouched, so that work is still owed and reported as pending.
470
+ const recorded = store !== null;
471
+ const passStore = store ?? createMemoryPendingBlobDeletionStore();
472
+ if (!recorded)
473
+ await passStore.add([...queuedBefore, ...work]);
474
+ const durablyOwed = recorded ? 0 : countPendingKeys(queuedBefore);
475
+ let outcome;
476
+ let remainingKeys;
477
+ try {
478
+ const queued = (await passStore.list()).filter((m) => m.scope === scope);
479
+ outcome = (await drainOnePass(deleteData, passStore, queued)).get(scope) ?? { deleted: [], missing: [], failed: [] };
480
+ remainingKeys = countPendingKeys((await passStore.list()).filter((m) => m.scope === scope));
481
+ }
482
+ catch (markerErr) {
483
+ // A marker write failed mid-pass. The store never loses owed keys (see
484
+ // drainOnePass), so the queue still holds this scope's work.
485
+ const message = errorMessage(markerErr);
486
+ logger.error({ scope, error: message }, "Could not update pending blob deletion markers");
487
+ return {
488
+ step: { status: "failed", error: message },
489
+ pending: recorded || durablyOwed > 0 ? 1 : 0,
490
+ };
491
+ }
492
+ if (!recorded && remainingKeys > durablyOwed) {
265
493
  logger.error({ scope, keys: remainingKeys }, "Blob deletions left unfinished with no marker store to retry them");
266
494
  }
495
+ if (!recorded)
496
+ remainingKeys = Math.max(remainingKeys, durablyOwed);
497
+ const pending = recorded ? remainingKeys : durablyOwed;
267
498
  const counts = {
268
499
  blobsDeleted: outcome.deleted.length,
269
500
  blobsMissing: outcome.missing.length,
@@ -271,24 +502,19 @@ async function deleteBlobKeys(deps, scope, batch, leftovers) {
271
502
  };
272
503
  if (outcome.failed.length > 0) {
273
504
  const first = outcome.failed[0];
274
- logger.warn({
275
- scope,
276
- failed: outcome.failed.length,
277
- pending: recorded,
278
- error: first.error,
279
- }, "Storage blob deletion failed for some keys after gateway tombstone; will retry");
505
+ logger.warn({ scope, failed: outcome.failed.length, pending, error: first.error }, "Storage blob deletion failed for some keys after gateway tombstone; will retry");
280
506
  return {
281
507
  step: {
282
508
  status: "failed",
283
509
  error: `${outcome.failed.length} blob delete(s) failed: ${first.error}`,
284
510
  ...counts,
285
511
  },
286
- pending: recorded,
512
+ pending,
287
513
  };
288
514
  }
289
- if (remaining.length > 0) {
515
+ if (remainingKeys > 0) {
290
516
  logger.info({ scope, ...counts }, "Storage blob deletion continues on later sync cycles (rate-limited batch)");
291
- return { step: { status: "deferred", ...counts }, pending: recorded };
517
+ return { step: { status: "deferred", ...counts }, pending };
292
518
  }
293
519
  return { step: { status: "ok", ...counts }, pending: 0 };
294
520
  }
@@ -332,85 +558,26 @@ export async function retryPendingBlobDeletions(deps) {
332
558
  result.superseded.push(marker.scope);
333
559
  logger.warn({
334
560
  scope: marker.scope,
335
- dataPointId: registry.record.id,
336
- version: registry.record.expectedVersion,
561
+ dataPointId: registry.id,
562
+ version: registry.expectedVersion,
337
563
  }, "Scope was re-added after its tombstone; dropping the unexpanded blob deletion marker so the live version's ciphertext survives");
338
564
  continue;
339
565
  }
340
566
  const plan = planBlobDeletions(deps.storage ?? { listVersions: () => [] }, marker.scope, registry.status === "deleted" ? registry.version : null);
341
- const expanded = plan.keys.map((version) => ({
342
- scope: marker.scope,
343
- version,
344
- }));
345
- if (plan.range) {
346
- expanded.push({ scope: marker.scope, version: null, range: plan.range });
347
- }
567
+ // Only the part not taken on yet: a repeated delete may already have
568
+ // queued (or finished) the same tombstone's range.
569
+ const expanded = unplannedWork(marker.scope, plan, await resumePoint(pendingBlobDeletions, marker.scope, markers));
570
+ // Persist the expansion before dropping the unexpanded marker: a failed or
571
+ // interrupted write leaves the marker to expand again (the same keys), and
572
+ // never drops keys that are still owed.
573
+ await recordPlannedWork(pendingBlobDeletions, marker.scope, expanded, plan.range?.to ?? null);
348
574
  await pendingBlobDeletions.remove([marker]);
349
- await pendingBlobDeletions.add(expanded);
350
575
  }
351
576
  markers = await pendingBlobDeletions.list();
352
577
  // One rate-limited batch in stored order: exact keys, then range heads.
353
- let budget = BLOB_DELETE_BATCH_SIZE;
354
- const byScope = new Map();
355
- const exactOrigin = new Set();
356
- const advancedRanges = [];
357
- const enqueue = (scope, version) => {
358
- const versions = byScope.get(scope) ?? [];
359
- versions.push(version);
360
- byScope.set(scope, versions);
361
- };
362
- for (const marker of markers) {
363
- if (budget === 0)
364
- break;
365
- if (marker.version !== null) {
366
- enqueue(marker.scope, marker.version);
367
- exactOrigin.add(`${marker.scope}\u0000${marker.version}`);
368
- budget -= 1;
369
- }
370
- }
371
- for (const marker of markers) {
372
- if (budget === 0)
373
- break;
374
- if (marker.version === null && marker.range) {
375
- const taken = takeFromRange(marker.range, budget);
376
- for (const version of taken.versions)
377
- enqueue(marker.scope, version);
378
- budget -= taken.versions.length;
379
- advancedRanges.push({
380
- old: marker,
381
- next: taken.rest
382
- ? { scope: marker.scope, version: null, range: taken.rest }
383
- : null,
384
- });
385
- }
386
- }
387
- for (const [scope, versions] of byScope) {
388
- let outcome;
389
- try {
390
- outcome = await deleteData.deleteBlobVersions(scope, versions);
391
- }
392
- catch (err) {
393
- const message = errorMessage(err);
394
- outcome = {
395
- deleted: [],
396
- missing: [],
397
- failed: versions.map((version) => ({ version, error: message })),
398
- };
399
- }
400
- const completed = [...outcome.deleted, ...outcome.missing]
401
- .filter((version) => exactOrigin.has(`${scope}\u0000${version}`))
402
- .map((version) => ({ scope, version }));
403
- if (completed.length > 0)
404
- await pendingBlobDeletions.remove(completed);
578
+ const outcomes = await drainOnePass(deleteData, pendingBlobDeletions, markers);
579
+ for (const [scope, outcome] of outcomes) {
405
580
  result.completed.push(...[...outcome.deleted, ...outcome.missing].map((version) => ({ scope, version })));
406
- // A failed key that came from a range becomes an exact marker; a failed
407
- // exact marker simply stays.
408
- const failedFromRange = outcome.failed
409
- .filter(({ version }) => !exactOrigin.has(`${scope}\u0000${version}`))
410
- .map(({ version }) => ({ scope, version }));
411
- if (failedFromRange.length > 0) {
412
- await pendingBlobDeletions.add(failedFromRange);
413
- }
414
581
  for (const failure of outcome.failed) {
415
582
  result.failed.push({ scope, ...failure });
416
583
  }
@@ -429,13 +596,6 @@ export async function retryPendingBlobDeletions(deps) {
429
596
  }, "Pending blob deletion failed again");
430
597
  }
431
598
  }
432
- // Advance the ranges only now: a crash mid-pass re-sends already-deleted
433
- // keys (404, harmless) rather than skipping keys that were never sent.
434
- for (const { old, next } of advancedRanges) {
435
- await pendingBlobDeletions.remove([old]);
436
- if (next)
437
- await pendingBlobDeletions.add([next]);
438
- }
439
599
  result.remaining = (await pendingBlobDeletions.list()).length;
440
600
  return result;
441
601
  }
@@ -446,6 +606,31 @@ export async function retryPendingBlobDeletions(deps) {
446
606
  * wired) or the row is gone.
447
607
  */
448
608
  async function registryState(deps, scope) {
609
+ // The gateway refuses an unsigned read of a reserved point; ask the port.
610
+ if (isGatewayReservedScope(scope) &&
611
+ deps.deleteData?.head &&
612
+ deps.serverOwner) {
613
+ let head;
614
+ try {
615
+ head = await deps.deleteData.head(scope);
616
+ }
617
+ catch (err) {
618
+ return {
619
+ status: "unknown",
620
+ error: err instanceof Error ? err : new Error(String(err)),
621
+ };
622
+ }
623
+ if (head === null)
624
+ return { status: "deleted-or-absent" };
625
+ if (head.deletedAt === null) {
626
+ return {
627
+ status: "live",
628
+ id: computeDataPointId(deps.serverOwner, scope),
629
+ expectedVersion: head.expectedVersion,
630
+ };
631
+ }
632
+ return { status: "deleted", version: tombstoneVersion(head) };
633
+ }
449
634
  if (!deps.dataPointFeed || !deps.serverOwner) {
450
635
  return { status: "deleted-or-absent" };
451
636
  }
@@ -464,8 +649,13 @@ async function registryState(deps, scope) {
464
649
  }
465
650
  if (record === null)
466
651
  return { status: "deleted-or-absent" };
467
- if (deletionTimestamp(record) === null)
468
- return { status: "live", record };
652
+ if (deletionTimestamp(record) === null) {
653
+ return {
654
+ status: "live",
655
+ id: record.id,
656
+ expectedVersion: record.expectedVersion,
657
+ };
658
+ }
469
659
  return { status: "deleted", version: tombstoneVersion(record) };
470
660
  }
471
661
  function errorMessage(err) {