@indigoai-us/hq-cloud 6.18.44-alpha.8 → 6.18.44

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 (1068) hide show
  1. package/dist/bin/sync-runner-planning.d.ts +12 -0
  2. package/dist/bin/sync-runner-planning.d.ts.map +1 -1
  3. package/dist/bin/sync-runner-planning.js +18 -0
  4. package/dist/bin/sync-runner-planning.js.map +1 -1
  5. package/dist/bin/sync-runner-watch-loop.d.ts +18 -0
  6. package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
  7. package/dist/bin/sync-runner-watch-loop.js +75 -6
  8. package/dist/bin/sync-runner-watch-loop.js.map +1 -1
  9. package/dist/bin/sync-runner.d.ts +22 -1
  10. package/dist/bin/sync-runner.d.ts.map +1 -1
  11. package/dist/bin/sync-runner.js +33 -4
  12. package/dist/bin/sync-runner.js.map +1 -1
  13. package/dist/cli/conflict.d.ts.map +1 -1
  14. package/dist/cli/conflict.js +2 -1
  15. package/dist/cli/conflict.js.map +1 -1
  16. package/dist/cli/rescue-core.d.ts +6 -0
  17. package/dist/cli/rescue-core.d.ts.map +1 -1
  18. package/dist/cli/rescue-core.js +10 -1
  19. package/dist/cli/rescue-core.js.map +1 -1
  20. package/dist/cli/rescue-windows-test-harness.d.ts +19 -0
  21. package/dist/cli/rescue-windows-test-harness.d.ts.map +1 -0
  22. package/dist/cli/rescue-windows-test-harness.js +443 -0
  23. package/dist/cli/rescue-windows-test-harness.js.map +1 -0
  24. package/dist/company-resolver.d.ts +22 -0
  25. package/dist/company-resolver.d.ts.map +1 -1
  26. package/dist/company-resolver.js +1 -1
  27. package/dist/company-resolver.js.map +1 -1
  28. package/dist/ignore.d.ts +4 -1
  29. package/dist/ignore.d.ts.map +1 -1
  30. package/dist/ignore.js +10 -12
  31. package/dist/ignore.js.map +1 -1
  32. package/dist/journal-snapshot-decoder.js +15 -2
  33. package/dist/journal-snapshot-decoder.js.map +1 -1
  34. package/dist/sync/durable-apply.d.ts +2 -0
  35. package/dist/sync/durable-apply.d.ts.map +1 -1
  36. package/dist/sync/durable-apply.js +9 -12
  37. package/dist/sync/durable-apply.js.map +1 -1
  38. package/dist/sync/fsync-compat.d.ts +13 -0
  39. package/dist/sync/fsync-compat.d.ts.map +1 -0
  40. package/dist/sync/fsync-compat.js +33 -0
  41. package/dist/sync/fsync-compat.js.map +1 -0
  42. package/dist/sync/multipart-uploader.d.ts.map +1 -1
  43. package/dist/sync/multipart-uploader.js +2 -1
  44. package/dist/sync/multipart-uploader.js.map +1 -1
  45. package/dist/sync/scope-inventory-client.d.ts.map +1 -1
  46. package/dist/sync/scope-inventory-client.js +7 -2
  47. package/dist/sync/scope-inventory-client.js.map +1 -1
  48. package/dist/telemetry.d.ts +31 -0
  49. package/dist/telemetry.d.ts.map +1 -1
  50. package/dist/telemetry.js +177 -17
  51. package/dist/telemetry.js.map +1 -1
  52. package/dist/usage-company-fallback.d.ts +104 -0
  53. package/dist/usage-company-fallback.d.ts.map +1 -0
  54. package/dist/usage-company-fallback.js +599 -0
  55. package/dist/usage-company-fallback.js.map +1 -0
  56. package/dist/watch-owner-lease.d.ts +3 -0
  57. package/dist/watch-owner-lease.d.ts.map +1 -1
  58. package/dist/watch-owner-lease.js +14 -3
  59. package/dist/watch-owner-lease.js.map +1 -1
  60. package/package.json +2 -2
  61. package/dist/active-company.test.d.ts +0 -2
  62. package/dist/active-company.test.d.ts.map +0 -1
  63. package/dist/active-company.test.js +0 -149
  64. package/dist/active-company.test.js.map +0 -1
  65. package/dist/agent-codex-instructions.test.d.ts +0 -2
  66. package/dist/agent-codex-instructions.test.d.ts.map +0 -1
  67. package/dist/agent-codex-instructions.test.js +0 -271
  68. package/dist/agent-codex-instructions.test.js.map +0 -1
  69. package/dist/backup-prune.test.d.ts +0 -2
  70. package/dist/backup-prune.test.d.ts.map +0 -1
  71. package/dist/backup-prune.test.js +0 -154
  72. package/dist/backup-prune.test.js.map +0 -1
  73. package/dist/bandwidth.test.d.ts +0 -2
  74. package/dist/bandwidth.test.d.ts.map +0 -1
  75. package/dist/bandwidth.test.js +0 -342
  76. package/dist/bandwidth.test.js.map +0 -1
  77. package/dist/best-effort-error.test.d.ts +0 -2
  78. package/dist/best-effort-error.test.d.ts.map +0 -1
  79. package/dist/best-effort-error.test.js +0 -54
  80. package/dist/best-effort-error.test.js.map +0 -1
  81. package/dist/bin/area-ledger-migration.test.d.ts +0 -2
  82. package/dist/bin/area-ledger-migration.test.d.ts.map +0 -1
  83. package/dist/bin/area-ledger-migration.test.js +0 -410
  84. package/dist/bin/area-ledger-migration.test.js.map +0 -1
  85. package/dist/bin/backup-prune-runner.test.d.ts +0 -2
  86. package/dist/bin/backup-prune-runner.test.d.ts.map +0 -1
  87. package/dist/bin/backup-prune-runner.test.js +0 -22
  88. package/dist/bin/backup-prune-runner.test.js.map +0 -1
  89. package/dist/bin/journal-repair.test.d.ts +0 -2
  90. package/dist/bin/journal-repair.test.d.ts.map +0 -1
  91. package/dist/bin/journal-repair.test.js +0 -53
  92. package/dist/bin/journal-repair.test.js.map +0 -1
  93. package/dist/bin/layout-compatibility.test.d.ts +0 -2
  94. package/dist/bin/layout-compatibility.test.d.ts.map +0 -1
  95. package/dist/bin/layout-compatibility.test.js +0 -87
  96. package/dist/bin/layout-compatibility.test.js.map +0 -1
  97. package/dist/bin/outpost-heartbeat.test.d.ts +0 -7
  98. package/dist/bin/outpost-heartbeat.test.d.ts.map +0 -1
  99. package/dist/bin/outpost-heartbeat.test.js +0 -84
  100. package/dist/bin/outpost-heartbeat.test.js.map +0 -1
  101. package/dist/bin/outpost-session-host.test.d.ts +0 -7
  102. package/dist/bin/outpost-session-host.test.d.ts.map +0 -1
  103. package/dist/bin/outpost-session-host.test.js +0 -52
  104. package/dist/bin/outpost-session-host.test.js.map +0 -1
  105. package/dist/bin/sync-mutation.test.d.ts +0 -2
  106. package/dist/bin/sync-mutation.test.d.ts.map +0 -1
  107. package/dist/bin/sync-mutation.test.js +0 -64
  108. package/dist/bin/sync-mutation.test.js.map +0 -1
  109. package/dist/bin/sync-runner-args.test.d.ts +0 -2
  110. package/dist/bin/sync-runner-args.test.d.ts.map +0 -1
  111. package/dist/bin/sync-runner-args.test.js +0 -82
  112. package/dist/bin/sync-runner-args.test.js.map +0 -1
  113. package/dist/bin/sync-runner-auth-wait.test.d.ts +0 -2
  114. package/dist/bin/sync-runner-auth-wait.test.d.ts.map +0 -1
  115. package/dist/bin/sync-runner-auth-wait.test.js +0 -406
  116. package/dist/bin/sync-runner-auth-wait.test.js.map +0 -1
  117. package/dist/bin/sync-runner-backoff.regression.test.d.ts +0 -29
  118. package/dist/bin/sync-runner-backoff.regression.test.d.ts.map +0 -1
  119. package/dist/bin/sync-runner-backoff.regression.test.js +0 -224
  120. package/dist/bin/sync-runner-backoff.regression.test.js.map +0 -1
  121. package/dist/bin/sync-runner-company.test.d.ts +0 -2
  122. package/dist/bin/sync-runner-company.test.d.ts.map +0 -1
  123. package/dist/bin/sync-runner-company.test.js +0 -2439
  124. package/dist/bin/sync-runner-company.test.js.map +0 -1
  125. package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.d.ts +0 -2
  126. package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.d.ts.map +0 -1
  127. package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.js +0 -648
  128. package/dist/bin/sync-runner-delete-snapshot-mixed.bench.test.js.map +0 -1
  129. package/dist/bin/sync-runner-delete-snapshot.bench.test.d.ts +0 -2
  130. package/dist/bin/sync-runner-delete-snapshot.bench.test.d.ts.map +0 -1
  131. package/dist/bin/sync-runner-delete-snapshot.bench.test.js +0 -329
  132. package/dist/bin/sync-runner-delete-snapshot.bench.test.js.map +0 -1
  133. package/dist/bin/sync-runner-events.test.d.ts +0 -2
  134. package/dist/bin/sync-runner-events.test.d.ts.map +0 -1
  135. package/dist/bin/sync-runner-events.test.js +0 -364
  136. package/dist/bin/sync-runner-events.test.js.map +0 -1
  137. package/dist/bin/sync-runner-heap-telemetry.test.d.ts +0 -2
  138. package/dist/bin/sync-runner-heap-telemetry.test.d.ts.map +0 -1
  139. package/dist/bin/sync-runner-heap-telemetry.test.js +0 -1966
  140. package/dist/bin/sync-runner-heap-telemetry.test.js.map +0 -1
  141. package/dist/bin/sync-runner-manifest.test.d.ts +0 -12
  142. package/dist/bin/sync-runner-manifest.test.d.ts.map +0 -1
  143. package/dist/bin/sync-runner-manifest.test.js +0 -921
  144. package/dist/bin/sync-runner-manifest.test.js.map +0 -1
  145. package/dist/bin/sync-runner-planning.test.d.ts +0 -2
  146. package/dist/bin/sync-runner-planning.test.d.ts.map +0 -1
  147. package/dist/bin/sync-runner-planning.test.js +0 -448
  148. package/dist/bin/sync-runner-planning.test.js.map +0 -1
  149. package/dist/bin/sync-runner-post-sync-flags.test.d.ts +0 -2
  150. package/dist/bin/sync-runner-post-sync-flags.test.d.ts.map +0 -1
  151. package/dist/bin/sync-runner-post-sync-flags.test.js +0 -64
  152. package/dist/bin/sync-runner-post-sync-flags.test.js.map +0 -1
  153. package/dist/bin/sync-runner-publish-expansion.test.d.ts +0 -2
  154. package/dist/bin/sync-runner-publish-expansion.test.d.ts.map +0 -1
  155. package/dist/bin/sync-runner-publish-expansion.test.js +0 -54
  156. package/dist/bin/sync-runner-publish-expansion.test.js.map +0 -1
  157. package/dist/bin/sync-runner-receiver-unresolved-company.test.d.ts +0 -2
  158. package/dist/bin/sync-runner-receiver-unresolved-company.test.d.ts.map +0 -1
  159. package/dist/bin/sync-runner-receiver-unresolved-company.test.js +0 -379
  160. package/dist/bin/sync-runner-receiver-unresolved-company.test.js.map +0 -1
  161. package/dist/bin/sync-runner-rollup.test.d.ts +0 -2
  162. package/dist/bin/sync-runner-rollup.test.d.ts.map +0 -1
  163. package/dist/bin/sync-runner-rollup.test.js +0 -24
  164. package/dist/bin/sync-runner-rollup.test.js.map +0 -1
  165. package/dist/bin/sync-runner-watch-loop.test.d.ts +0 -2
  166. package/dist/bin/sync-runner-watch-loop.test.d.ts.map +0 -1
  167. package/dist/bin/sync-runner-watch-loop.test.js +0 -734
  168. package/dist/bin/sync-runner-watch-loop.test.js.map +0 -1
  169. package/dist/bin/sync-runner-watch-routes.test.d.ts +0 -2
  170. package/dist/bin/sync-runner-watch-routes.test.d.ts.map +0 -1
  171. package/dist/bin/sync-runner-watch-routes.test.js +0 -181
  172. package/dist/bin/sync-runner-watch-routes.test.js.map +0 -1
  173. package/dist/bin/sync-runner.test.d.ts +0 -10
  174. package/dist/bin/sync-runner.test.d.ts.map +0 -1
  175. package/dist/bin/sync-runner.test.js +0 -18984
  176. package/dist/bin/sync-runner.test.js.map +0 -1
  177. package/dist/cli/case-alias.test.d.ts +0 -2
  178. package/dist/cli/case-alias.test.d.ts.map +0 -1
  179. package/dist/cli/case-alias.test.js +0 -96
  180. package/dist/cli/case-alias.test.js.map +0 -1
  181. package/dist/cli/conflict-recovery.test.d.ts +0 -2
  182. package/dist/cli/conflict-recovery.test.d.ts.map +0 -1
  183. package/dist/cli/conflict-recovery.test.js +0 -201
  184. package/dist/cli/conflict-recovery.test.js.map +0 -1
  185. package/dist/cli/conflict-winner.test.d.ts +0 -7
  186. package/dist/cli/conflict-winner.test.d.ts.map +0 -1
  187. package/dist/cli/conflict-winner.test.js +0 -98
  188. package/dist/cli/conflict-winner.test.js.map +0 -1
  189. package/dist/cli/doctor.test.d.ts +0 -13
  190. package/dist/cli/doctor.test.d.ts.map +0 -1
  191. package/dist/cli/doctor.test.js +0 -607
  192. package/dist/cli/doctor.test.js.map +0 -1
  193. package/dist/cli/invite.test.d.ts +0 -5
  194. package/dist/cli/invite.test.d.ts.map +0 -1
  195. package/dist/cli/invite.test.js +0 -178
  196. package/dist/cli/invite.test.js.map +0 -1
  197. package/dist/cli/onepass-pull-planner-regressions.test.d.ts +0 -2
  198. package/dist/cli/onepass-pull-planner-regressions.test.d.ts.map +0 -1
  199. package/dist/cli/onepass-pull-planner-regressions.test.js +0 -300
  200. package/dist/cli/onepass-pull-planner-regressions.test.js.map +0 -1
  201. package/dist/cli/onepass-pull-planner.test.d.ts +0 -2
  202. package/dist/cli/onepass-pull-planner.test.d.ts.map +0 -1
  203. package/dist/cli/onepass-pull-planner.test.js +0 -266
  204. package/dist/cli/onepass-pull-planner.test.js.map +0 -1
  205. package/dist/cli/push-scan-exclusions.test.d.ts +0 -2
  206. package/dist/cli/push-scan-exclusions.test.d.ts.map +0 -1
  207. package/dist/cli/push-scan-exclusions.test.js +0 -99
  208. package/dist/cli/push-scan-exclusions.test.js.map +0 -1
  209. package/dist/cli/reindex-knowledge.test.d.ts +0 -12
  210. package/dist/cli/reindex-knowledge.test.d.ts.map +0 -1
  211. package/dist/cli/reindex-knowledge.test.js +0 -260
  212. package/dist/cli/reindex-knowledge.test.js.map +0 -1
  213. package/dist/cli/reindex.test.d.ts +0 -11
  214. package/dist/cli/reindex.test.d.ts.map +0 -1
  215. package/dist/cli/reindex.test.js +0 -811
  216. package/dist/cli/reindex.test.js.map +0 -1
  217. package/dist/cli/rescue-breaker.test.d.ts +0 -2
  218. package/dist/cli/rescue-breaker.test.d.ts.map +0 -1
  219. package/dist/cli/rescue-breaker.test.js +0 -812
  220. package/dist/cli/rescue-breaker.test.js.map +0 -1
  221. package/dist/cli/rescue-classify-ordering.test.d.ts +0 -2
  222. package/dist/cli/rescue-classify-ordering.test.d.ts.map +0 -1
  223. package/dist/cli/rescue-classify-ordering.test.js +0 -500
  224. package/dist/cli/rescue-classify-ordering.test.js.map +0 -1
  225. package/dist/cli/rescue-clone-diagnostics.test.d.ts +0 -2
  226. package/dist/cli/rescue-clone-diagnostics.test.d.ts.map +0 -1
  227. package/dist/cli/rescue-clone-diagnostics.test.js +0 -101
  228. package/dist/cli/rescue-clone-diagnostics.test.js.map +0 -1
  229. package/dist/cli/rescue-clone-network-retry.test.d.ts +0 -2
  230. package/dist/cli/rescue-clone-network-retry.test.d.ts.map +0 -1
  231. package/dist/cli/rescue-clone-network-retry.test.js +0 -415
  232. package/dist/cli/rescue-clone-network-retry.test.js.map +0 -1
  233. package/dist/cli/rescue-core-stamp.test.d.ts +0 -2
  234. package/dist/cli/rescue-core-stamp.test.d.ts.map +0 -1
  235. package/dist/cli/rescue-core-stamp.test.js +0 -364
  236. package/dist/cli/rescue-core-stamp.test.js.map +0 -1
  237. package/dist/cli/rescue-drift-reconcile.test.d.ts +0 -2
  238. package/dist/cli/rescue-drift-reconcile.test.d.ts.map +0 -1
  239. package/dist/cli/rescue-drift-reconcile.test.js +0 -162
  240. package/dist/cli/rescue-drift-reconcile.test.js.map +0 -1
  241. package/dist/cli/rescue-drop-dir-symlink.test.d.ts +0 -2
  242. package/dist/cli/rescue-drop-dir-symlink.test.d.ts.map +0 -1
  243. package/dist/cli/rescue-drop-dir-symlink.test.js +0 -255
  244. package/dist/cli/rescue-drop-dir-symlink.test.js.map +0 -1
  245. package/dist/cli/rescue-exec-bit-preserve.test.d.ts +0 -2
  246. package/dist/cli/rescue-exec-bit-preserve.test.d.ts.map +0 -1
  247. package/dist/cli/rescue-exec-bit-preserve.test.js +0 -177
  248. package/dist/cli/rescue-exec-bit-preserve.test.js.map +0 -1
  249. package/dist/cli/rescue-hq-root-guard.test.d.ts +0 -2
  250. package/dist/cli/rescue-hq-root-guard.test.d.ts.map +0 -1
  251. package/dist/cli/rescue-hq-root-guard.test.js +0 -210
  252. package/dist/cli/rescue-hq-root-guard.test.js.map +0 -1
  253. package/dist/cli/rescue-journal-reconcile.test.d.ts +0 -2
  254. package/dist/cli/rescue-journal-reconcile.test.d.ts.map +0 -1
  255. package/dist/cli/rescue-journal-reconcile.test.js +0 -186
  256. package/dist/cli/rescue-journal-reconcile.test.js.map +0 -1
  257. package/dist/cli/rescue-keep-local-ignore.test.d.ts +0 -2
  258. package/dist/cli/rescue-keep-local-ignore.test.d.ts.map +0 -1
  259. package/dist/cli/rescue-keep-local-ignore.test.js +0 -315
  260. package/dist/cli/rescue-keep-local-ignore.test.js.map +0 -1
  261. package/dist/cli/rescue-move.test.d.ts +0 -2
  262. package/dist/cli/rescue-move.test.d.ts.map +0 -1
  263. package/dist/cli/rescue-move.test.js +0 -166
  264. package/dist/cli/rescue-move.test.js.map +0 -1
  265. package/dist/cli/rescue-mtime-preserve.test.d.ts +0 -2
  266. package/dist/cli/rescue-mtime-preserve.test.d.ts.map +0 -1
  267. package/dist/cli/rescue-mtime-preserve.test.js +0 -186
  268. package/dist/cli/rescue-mtime-preserve.test.js.map +0 -1
  269. package/dist/cli/rescue-preserve-copy-symlink.test.d.ts +0 -2
  270. package/dist/cli/rescue-preserve-copy-symlink.test.d.ts.map +0 -1
  271. package/dist/cli/rescue-preserve-copy-symlink.test.js +0 -483
  272. package/dist/cli/rescue-preserve-copy-symlink.test.js.map +0 -1
  273. package/dist/cli/rescue-realpath.test.d.ts +0 -2
  274. package/dist/cli/rescue-realpath.test.d.ts.map +0 -1
  275. package/dist/cli/rescue-realpath.test.js +0 -109
  276. package/dist/cli/rescue-realpath.test.js.map +0 -1
  277. package/dist/cli/rescue-restore-flag.test.d.ts +0 -2
  278. package/dist/cli/rescue-restore-flag.test.d.ts.map +0 -1
  279. package/dist/cli/rescue-restore-flag.test.js +0 -82
  280. package/dist/cli/rescue-restore-flag.test.js.map +0 -1
  281. package/dist/cli/rescue-rsync-preflight.test.d.ts +0 -2
  282. package/dist/cli/rescue-rsync-preflight.test.d.ts.map +0 -1
  283. package/dist/cli/rescue-rsync-preflight.test.js +0 -579
  284. package/dist/cli/rescue-rsync-preflight.test.js.map +0 -1
  285. package/dist/cli/rescue-rsync-status.test.d.ts +0 -2
  286. package/dist/cli/rescue-rsync-status.test.d.ts.map +0 -1
  287. package/dist/cli/rescue-rsync-status.test.js +0 -406
  288. package/dist/cli/rescue-rsync-status.test.js.map +0 -1
  289. package/dist/cli/rescue-settings-reconcile.test.d.ts +0 -2
  290. package/dist/cli/rescue-settings-reconcile.test.d.ts.map +0 -1
  291. package/dist/cli/rescue-settings-reconcile.test.js +0 -610
  292. package/dist/cli/rescue-settings-reconcile.test.js.map +0 -1
  293. package/dist/cli/rescue-snapshot-copy.test.d.ts +0 -2
  294. package/dist/cli/rescue-snapshot-copy.test.d.ts.map +0 -1
  295. package/dist/cli/rescue-snapshot-copy.test.js +0 -790
  296. package/dist/cli/rescue-snapshot-copy.test.js.map +0 -1
  297. package/dist/cli/rescue-snapshot-recovery.test.d.ts +0 -2
  298. package/dist/cli/rescue-snapshot-recovery.test.d.ts.map +0 -1
  299. package/dist/cli/rescue-snapshot-recovery.test.js +0 -265
  300. package/dist/cli/rescue-snapshot-recovery.test.js.map +0 -1
  301. package/dist/cli/rescue-snapshot.test.d.ts +0 -2
  302. package/dist/cli/rescue-snapshot.test.d.ts.map +0 -1
  303. package/dist/cli/rescue-snapshot.test.js +0 -46
  304. package/dist/cli/rescue-snapshot.test.js.map +0 -1
  305. package/dist/cli/rescue-source-switch-floor.test.d.ts +0 -2
  306. package/dist/cli/rescue-source-switch-floor.test.d.ts.map +0 -1
  307. package/dist/cli/rescue-source-switch-floor.test.js +0 -247
  308. package/dist/cli/rescue-source-switch-floor.test.js.map +0 -1
  309. package/dist/cli/rescue-unchanged-delete.test.d.ts +0 -2
  310. package/dist/cli/rescue-unchanged-delete.test.d.ts.map +0 -1
  311. package/dist/cli/rescue-unchanged-delete.test.js +0 -305
  312. package/dist/cli/rescue-unchanged-delete.test.js.map +0 -1
  313. package/dist/cli/rescue.reindex.test.d.ts +0 -2
  314. package/dist/cli/rescue.reindex.test.d.ts.map +0 -1
  315. package/dist/cli/rescue.reindex.test.js +0 -72
  316. package/dist/cli/rescue.reindex.test.js.map +0 -1
  317. package/dist/cli/rescue.test.d.ts +0 -2
  318. package/dist/cli/rescue.test.d.ts.map +0 -1
  319. package/dist/cli/rescue.test.js +0 -127
  320. package/dist/cli/rescue.test.js.map +0 -1
  321. package/dist/cli/share-plan-limit-presign.test.d.ts +0 -2
  322. package/dist/cli/share-plan-limit-presign.test.d.ts.map +0 -1
  323. package/dist/cli/share-plan-limit-presign.test.js +0 -271
  324. package/dist/cli/share-plan-limit-presign.test.js.map +0 -1
  325. package/dist/cli/share-presign-transport.test.d.ts +0 -22
  326. package/dist/cli/share-presign-transport.test.d.ts.map +0 -1
  327. package/dist/cli/share-presign-transport.test.js +0 -388
  328. package/dist/cli/share-presign-transport.test.js.map +0 -1
  329. package/dist/cli/share.test.d.ts +0 -5
  330. package/dist/cli/share.test.d.ts.map +0 -1
  331. package/dist/cli/share.test.js +0 -12335
  332. package/dist/cli/share.test.js.map +0 -1
  333. package/dist/cli/streaming-pull-planner.bench.test.d.ts +0 -15
  334. package/dist/cli/streaming-pull-planner.bench.test.d.ts.map +0 -1
  335. package/dist/cli/streaming-pull-planner.bench.test.js +0 -273
  336. package/dist/cli/streaming-pull-planner.bench.test.js.map +0 -1
  337. package/dist/cli/sync-scope.test.d.ts +0 -22
  338. package/dist/cli/sync-scope.test.d.ts.map +0 -1
  339. package/dist/cli/sync-scope.test.js +0 -903
  340. package/dist/cli/sync-scope.test.js.map +0 -1
  341. package/dist/cli/sync.test.d.ts +0 -5
  342. package/dist/cli/sync.test.d.ts.map +0 -1
  343. package/dist/cli/sync.test.js +0 -9028
  344. package/dist/cli/sync.test.js.map +0 -1
  345. package/dist/cli/tombstones.test.d.ts +0 -2
  346. package/dist/cli/tombstones.test.d.ts.map +0 -1
  347. package/dist/cli/tombstones.test.js +0 -120
  348. package/dist/cli/tombstones.test.js.map +0 -1
  349. package/dist/cli/watch-event-push-conflict.test.d.ts +0 -25
  350. package/dist/cli/watch-event-push-conflict.test.d.ts.map +0 -1
  351. package/dist/cli/watch-event-push-conflict.test.js +0 -230
  352. package/dist/cli/watch-event-push-conflict.test.js.map +0 -1
  353. package/dist/client-info.test.d.ts +0 -11
  354. package/dist/client-info.test.d.ts.map +0 -1
  355. package/dist/client-info.test.js +0 -168
  356. package/dist/client-info.test.js.map +0 -1
  357. package/dist/cognito-auth-lock-no-hard-links.test.d.ts +0 -11
  358. package/dist/cognito-auth-lock-no-hard-links.test.d.ts.map +0 -1
  359. package/dist/cognito-auth-lock-no-hard-links.test.js +0 -76
  360. package/dist/cognito-auth-lock-no-hard-links.test.js.map +0 -1
  361. package/dist/cognito-auth.test.d.ts +0 -9
  362. package/dist/cognito-auth.test.d.ts.map +0 -1
  363. package/dist/cognito-auth.test.js +0 -745
  364. package/dist/cognito-auth.test.js.map +0 -1
  365. package/dist/company-resolver.test.d.ts +0 -7
  366. package/dist/company-resolver.test.d.ts.map +0 -1
  367. package/dist/company-resolver.test.js +0 -533
  368. package/dist/company-resolver.test.js.map +0 -1
  369. package/dist/company-slug.test.d.ts +0 -7
  370. package/dist/company-slug.test.d.ts.map +0 -1
  371. package/dist/company-slug.test.js +0 -43
  372. package/dist/company-slug.test.js.map +0 -1
  373. package/dist/company-vault-transport.test.d.ts +0 -12
  374. package/dist/company-vault-transport.test.d.ts.map +0 -1
  375. package/dist/company-vault-transport.test.js +0 -97
  376. package/dist/company-vault-transport.test.js.map +0 -1
  377. package/dist/context.test.d.ts +0 -7
  378. package/dist/context.test.d.ts.map +0 -1
  379. package/dist/context.test.js +0 -646
  380. package/dist/context.test.js.map +0 -1
  381. package/dist/entity-resolver.test.d.ts +0 -10
  382. package/dist/entity-resolver.test.d.ts.map +0 -1
  383. package/dist/entity-resolver.test.js +0 -244
  384. package/dist/entity-resolver.test.js.map +0 -1
  385. package/dist/getindigo-dead-refs-contract.test.d.ts +0 -2
  386. package/dist/getindigo-dead-refs-contract.test.d.ts.map +0 -1
  387. package/dist/getindigo-dead-refs-contract.test.js +0 -58
  388. package/dist/getindigo-dead-refs-contract.test.js.map +0 -1
  389. package/dist/hermetic-test-env.test.d.ts +0 -2
  390. package/dist/hermetic-test-env.test.d.ts.map +0 -1
  391. package/dist/hermetic-test-env.test.js +0 -61
  392. package/dist/hermetic-test-env.test.js.map +0 -1
  393. package/dist/hq-root-recovery.test.d.ts +0 -2
  394. package/dist/hq-root-recovery.test.d.ts.map +0 -1
  395. package/dist/hq-root-recovery.test.js +0 -65
  396. package/dist/hq-root-recovery.test.js.map +0 -1
  397. package/dist/ignore.test.d.ts +0 -10
  398. package/dist/ignore.test.d.ts.map +0 -1
  399. package/dist/ignore.test.js +0 -926
  400. package/dist/ignore.test.js.map +0 -1
  401. package/dist/index.test.d.ts +0 -2
  402. package/dist/index.test.d.ts.map +0 -1
  403. package/dist/index.test.js +0 -26
  404. package/dist/index.test.js.map +0 -1
  405. package/dist/journal-area-ledger-read-boundary.test.d.ts +0 -2
  406. package/dist/journal-area-ledger-read-boundary.test.d.ts.map +0 -1
  407. package/dist/journal-area-ledger-read-boundary.test.js +0 -420
  408. package/dist/journal-area-ledger-read-boundary.test.js.map +0 -1
  409. package/dist/journal-baseline.test.d.ts +0 -2
  410. package/dist/journal-baseline.test.d.ts.map +0 -1
  411. package/dist/journal-baseline.test.js +0 -218
  412. package/dist/journal-baseline.test.js.map +0 -1
  413. package/dist/journal-decode-window.test.d.ts +0 -2
  414. package/dist/journal-decode-window.test.d.ts.map +0 -1
  415. package/dist/journal-decode-window.test.js +0 -99
  416. package/dist/journal-decode-window.test.js.map +0 -1
  417. package/dist/journal-delta-writes.test.d.ts +0 -2
  418. package/dist/journal-delta-writes.test.d.ts.map +0 -1
  419. package/dist/journal-delta-writes.test.js +0 -159
  420. package/dist/journal-delta-writes.test.js.map +0 -1
  421. package/dist/journal-ledger-index.test.d.ts +0 -15
  422. package/dist/journal-ledger-index.test.d.ts.map +0 -1
  423. package/dist/journal-ledger-index.test.js +0 -269
  424. package/dist/journal-ledger-index.test.js.map +0 -1
  425. package/dist/journal-public-boundary.test.d.ts +0 -2
  426. package/dist/journal-public-boundary.test.d.ts.map +0 -1
  427. package/dist/journal-public-boundary.test.js +0 -216
  428. package/dist/journal-public-boundary.test.js.map +0 -1
  429. package/dist/journal-row-store.test.d.ts +0 -2
  430. package/dist/journal-row-store.test.d.ts.map +0 -1
  431. package/dist/journal-row-store.test.js +0 -729
  432. package/dist/journal-row-store.test.js.map +0 -1
  433. package/dist/journal-snapshot-memory-helpers.d.ts +0 -33
  434. package/dist/journal-snapshot-memory-helpers.d.ts.map +0 -1
  435. package/dist/journal-snapshot-memory-helpers.js +0 -212
  436. package/dist/journal-snapshot-memory-helpers.js.map +0 -1
  437. package/dist/journal-snapshot-memory.test.d.ts +0 -2
  438. package/dist/journal-snapshot-memory.test.d.ts.map +0 -1
  439. package/dist/journal-snapshot-memory.test.js +0 -47
  440. package/dist/journal-snapshot-memory.test.js.map +0 -1
  441. package/dist/journal-snapshot-v4-memory.test.d.ts +0 -2
  442. package/dist/journal-snapshot-v4-memory.test.d.ts.map +0 -1
  443. package/dist/journal-snapshot-v4-memory.test.js +0 -24
  444. package/dist/journal-snapshot-v4-memory.test.js.map +0 -1
  445. package/dist/journal-snapshot-v4-writer-memory.test.d.ts +0 -2
  446. package/dist/journal-snapshot-v4-writer-memory.test.d.ts.map +0 -1
  447. package/dist/journal-snapshot-v4-writer-memory.test.js +0 -76
  448. package/dist/journal-snapshot-v4-writer-memory.test.js.map +0 -1
  449. package/dist/journal-snapshot-v4-writer.test.d.ts +0 -2
  450. package/dist/journal-snapshot-v4-writer.test.d.ts.map +0 -1
  451. package/dist/journal-snapshot-v4-writer.test.js +0 -282
  452. package/dist/journal-snapshot-v4-writer.test.js.map +0 -1
  453. package/dist/journal-snapshot-v4.test.d.ts +0 -2
  454. package/dist/journal-snapshot-v4.test.d.ts.map +0 -1
  455. package/dist/journal-snapshot-v4.test.js +0 -281
  456. package/dist/journal-snapshot-v4.test.js.map +0 -1
  457. package/dist/journal-store.test.d.ts +0 -2
  458. package/dist/journal-store.test.d.ts.map +0 -1
  459. package/dist/journal-store.test.js +0 -1227
  460. package/dist/journal-store.test.js.map +0 -1
  461. package/dist/journal-streaming.heap.test.d.ts +0 -2
  462. package/dist/journal-streaming.heap.test.d.ts.map +0 -1
  463. package/dist/journal-streaming.heap.test.js +0 -190
  464. package/dist/journal-streaming.heap.test.js.map +0 -1
  465. package/dist/journal.test.d.ts +0 -9
  466. package/dist/journal.test.d.ts.map +0 -1
  467. package/dist/journal.test.js +0 -1074
  468. package/dist/journal.test.js.map +0 -1
  469. package/dist/kms10-etag-safe-pull.test.d.ts +0 -2
  470. package/dist/kms10-etag-safe-pull.test.d.ts.map +0 -1
  471. package/dist/kms10-etag-safe-pull.test.js +0 -53
  472. package/dist/kms10-etag-safe-pull.test.js.map +0 -1
  473. package/dist/lib/cloud-authoritative.test.d.ts +0 -2
  474. package/dist/lib/cloud-authoritative.test.d.ts.map +0 -1
  475. package/dist/lib/cloud-authoritative.test.js +0 -42
  476. package/dist/lib/cloud-authoritative.test.js.map +0 -1
  477. package/dist/lib/conflict-reconcile.test.d.ts +0 -7
  478. package/dist/lib/conflict-reconcile.test.d.ts.map +0 -1
  479. package/dist/lib/conflict-reconcile.test.js +0 -246
  480. package/dist/lib/conflict-reconcile.test.js.map +0 -1
  481. package/dist/lib/conflict.test.d.ts +0 -9
  482. package/dist/lib/conflict.test.d.ts.map +0 -1
  483. package/dist/lib/conflict.test.js +0 -340
  484. package/dist/lib/conflict.test.js.map +0 -1
  485. package/dist/lib/describe-error.test.d.ts +0 -2
  486. package/dist/lib/describe-error.test.d.ts.map +0 -1
  487. package/dist/lib/describe-error.test.js +0 -148
  488. package/dist/lib/describe-error.test.js.map +0 -1
  489. package/dist/lib/machine-id.test.d.ts +0 -8
  490. package/dist/lib/machine-id.test.d.ts.map +0 -1
  491. package/dist/lib/machine-id.test.js +0 -228
  492. package/dist/lib/machine-id.test.js.map +0 -1
  493. package/dist/lib/net-errors.test.d.ts +0 -2
  494. package/dist/lib/net-errors.test.d.ts.map +0 -1
  495. package/dist/lib/net-errors.test.js +0 -111
  496. package/dist/lib/net-errors.test.js.map +0 -1
  497. package/dist/lib/ontology-entity-meta.test.d.ts +0 -2
  498. package/dist/lib/ontology-entity-meta.test.d.ts.map +0 -1
  499. package/dist/lib/ontology-entity-meta.test.js +0 -79
  500. package/dist/lib/ontology-entity-meta.test.js.map +0 -1
  501. package/dist/lib/ontology-entity-push.test.d.ts +0 -2
  502. package/dist/lib/ontology-entity-push.test.d.ts.map +0 -1
  503. package/dist/lib/ontology-entity-push.test.js +0 -156
  504. package/dist/lib/ontology-entity-push.test.js.map +0 -1
  505. package/dist/lib/personal-connector-settings-sync.test.d.ts +0 -2
  506. package/dist/lib/personal-connector-settings-sync.test.d.ts.map +0 -1
  507. package/dist/lib/personal-connector-settings-sync.test.js +0 -34
  508. package/dist/lib/personal-connector-settings-sync.test.js.map +0 -1
  509. package/dist/lib/readlink-safe.test.d.ts +0 -2
  510. package/dist/lib/readlink-safe.test.d.ts.map +0 -1
  511. package/dist/lib/readlink-safe.test.js +0 -34
  512. package/dist/lib/readlink-safe.test.js.map +0 -1
  513. package/dist/lib/s3-content-checksum.test.d.ts +0 -2
  514. package/dist/lib/s3-content-checksum.test.d.ts.map +0 -1
  515. package/dist/lib/s3-content-checksum.test.js +0 -26
  516. package/dist/lib/s3-content-checksum.test.js.map +0 -1
  517. package/dist/lib/skill-register-landed-bytes.test.d.ts +0 -2
  518. package/dist/lib/skill-register-landed-bytes.test.d.ts.map +0 -1
  519. package/dist/lib/skill-register-landed-bytes.test.js +0 -83
  520. package/dist/lib/skill-register-landed-bytes.test.js.map +0 -1
  521. package/dist/local-path-codec.test.d.ts +0 -14
  522. package/dist/local-path-codec.test.d.ts.map +0 -1
  523. package/dist/local-path-codec.test.js +0 -122
  524. package/dist/local-path-codec.test.js.map +0 -1
  525. package/dist/machine-auth.test.d.ts +0 -14
  526. package/dist/machine-auth.test.d.ts.map +0 -1
  527. package/dist/machine-auth.test.js +0 -1482
  528. package/dist/machine-auth.test.js.map +0 -1
  529. package/dist/machine-token-cache-isolation.test.d.ts +0 -24
  530. package/dist/machine-token-cache-isolation.test.d.ts.map +0 -1
  531. package/dist/machine-token-cache-isolation.test.js +0 -284
  532. package/dist/machine-token-cache-isolation.test.js.map +0 -1
  533. package/dist/manifest/US-002.acceptance.test.d.ts +0 -11
  534. package/dist/manifest/US-002.acceptance.test.d.ts.map +0 -1
  535. package/dist/manifest/US-002.acceptance.test.js +0 -160
  536. package/dist/manifest/US-002.acceptance.test.js.map +0 -1
  537. package/dist/manifest/US-004.acceptance.test.d.ts +0 -12
  538. package/dist/manifest/US-004.acceptance.test.d.ts.map +0 -1
  539. package/dist/manifest/US-004.acceptance.test.js +0 -173
  540. package/dist/manifest/US-004.acceptance.test.js.map +0 -1
  541. package/dist/manifest/build-manifest-resident-parity.test.d.ts +0 -2
  542. package/dist/manifest/build-manifest-resident-parity.test.d.ts.map +0 -1
  543. package/dist/manifest/build-manifest-resident-parity.test.js +0 -69
  544. package/dist/manifest/build-manifest-resident-parity.test.js.map +0 -1
  545. package/dist/manifest/build-manifest.bench.test.d.ts +0 -22
  546. package/dist/manifest/build-manifest.bench.test.d.ts.map +0 -1
  547. package/dist/manifest/build-manifest.bench.test.js +0 -930
  548. package/dist/manifest/build-manifest.bench.test.js.map +0 -1
  549. package/dist/manifest/build-manifest.test.d.ts +0 -10
  550. package/dist/manifest/build-manifest.test.d.ts.map +0 -1
  551. package/dist/manifest/build-manifest.test.js +0 -1217
  552. package/dist/manifest/build-manifest.test.js.map +0 -1
  553. package/dist/manifest/contract.test.d.ts +0 -17
  554. package/dist/manifest/contract.test.d.ts.map +0 -1
  555. package/dist/manifest/contract.test.js +0 -346
  556. package/dist/manifest/contract.test.js.map +0 -1
  557. package/dist/manifest/import-graph.test.d.ts +0 -25
  558. package/dist/manifest/import-graph.test.d.ts.map +0 -1
  559. package/dist/manifest/import-graph.test.js +0 -120
  560. package/dist/manifest/import-graph.test.js.map +0 -1
  561. package/dist/manifest/ledger-shard-isolation.test.d.ts +0 -18
  562. package/dist/manifest/ledger-shard-isolation.test.d.ts.map +0 -1
  563. package/dist/manifest/ledger-shard-isolation.test.js +0 -153
  564. package/dist/manifest/ledger-shard-isolation.test.js.map +0 -1
  565. package/dist/manifest/snapshot-store.test.d.ts +0 -11
  566. package/dist/manifest/snapshot-store.test.d.ts.map +0 -1
  567. package/dist/manifest/snapshot-store.test.js +0 -206
  568. package/dist/manifest/snapshot-store.test.js.map +0 -1
  569. package/dist/manifest/upload-manifest.test.d.ts +0 -10
  570. package/dist/manifest/upload-manifest.test.d.ts.map +0 -1
  571. package/dist/manifest/upload-manifest.test.js +0 -1335
  572. package/dist/manifest/upload-manifest.test.js.map +0 -1
  573. package/dist/manifest-reconcile.test.d.ts +0 -2
  574. package/dist/manifest-reconcile.test.d.ts.map +0 -1
  575. package/dist/manifest-reconcile.test.js +0 -1029
  576. package/dist/manifest-reconcile.test.js.map +0 -1
  577. package/dist/object-io.test.d.ts +0 -11
  578. package/dist/object-io.test.d.ts.map +0 -1
  579. package/dist/object-io.test.js +0 -2376
  580. package/dist/object-io.test.js.map +0 -1
  581. package/dist/operation-lock.test.d.ts +0 -5
  582. package/dist/operation-lock.test.d.ts.map +0 -1
  583. package/dist/operation-lock.test.js +0 -796
  584. package/dist/operation-lock.test.js.map +0 -1
  585. package/dist/outcome-telemetry.test.d.ts +0 -9
  586. package/dist/outcome-telemetry.test.d.ts.map +0 -1
  587. package/dist/outcome-telemetry.test.js +0 -663
  588. package/dist/outcome-telemetry.test.js.map +0 -1
  589. package/dist/outposts/client.test.d.ts +0 -9
  590. package/dist/outposts/client.test.d.ts.map +0 -1
  591. package/dist/outposts/client.test.js +0 -415
  592. package/dist/outposts/client.test.js.map +0 -1
  593. package/dist/outposts/command.test.d.ts +0 -18
  594. package/dist/outposts/command.test.d.ts.map +0 -1
  595. package/dist/outposts/command.test.js +0 -1230
  596. package/dist/outposts/command.test.js.map +0 -1
  597. package/dist/outposts/replica-sync.test.d.ts +0 -2
  598. package/dist/outposts/replica-sync.test.d.ts.map +0 -1
  599. package/dist/outposts/replica-sync.test.js +0 -224
  600. package/dist/outposts/replica-sync.test.js.map +0 -1
  601. package/dist/outposts/self-deploy.test.d.ts +0 -2
  602. package/dist/outposts/self-deploy.test.d.ts.map +0 -1
  603. package/dist/outposts/self-deploy.test.js +0 -218
  604. package/dist/outposts/self-deploy.test.js.map +0 -1
  605. package/dist/outposts/session-heartbeat-bounds.test.d.ts +0 -25
  606. package/dist/outposts/session-heartbeat-bounds.test.d.ts.map +0 -1
  607. package/dist/outposts/session-heartbeat-bounds.test.js +0 -156
  608. package/dist/outposts/session-heartbeat-bounds.test.js.map +0 -1
  609. package/dist/outposts/session-heartbeat-guard.test.d.ts +0 -16
  610. package/dist/outposts/session-heartbeat-guard.test.d.ts.map +0 -1
  611. package/dist/outposts/session-heartbeat-guard.test.js +0 -88
  612. package/dist/outposts/session-heartbeat-guard.test.js.map +0 -1
  613. package/dist/outposts/session-heartbeat-publisher.test.d.ts +0 -10
  614. package/dist/outposts/session-heartbeat-publisher.test.d.ts.map +0 -1
  615. package/dist/outposts/session-heartbeat-publisher.test.js +0 -153
  616. package/dist/outposts/session-heartbeat-publisher.test.js.map +0 -1
  617. package/dist/outposts/session-heartbeat-retain-guard.test.d.ts +0 -18
  618. package/dist/outposts/session-heartbeat-retain-guard.test.d.ts.map +0 -1
  619. package/dist/outposts/session-heartbeat-retain-guard.test.js +0 -106
  620. package/dist/outposts/session-heartbeat-retain-guard.test.js.map +0 -1
  621. package/dist/outposts/session-heartbeat-scan.test.d.ts +0 -20
  622. package/dist/outposts/session-heartbeat-scan.test.d.ts.map +0 -1
  623. package/dist/outposts/session-heartbeat-scan.test.js +0 -241
  624. package/dist/outposts/session-heartbeat-scan.test.js.map +0 -1
  625. package/dist/outposts/session-heartbeat.test.d.ts +0 -11
  626. package/dist/outposts/session-heartbeat.test.d.ts.map +0 -1
  627. package/dist/outposts/session-heartbeat.test.js +0 -370
  628. package/dist/outposts/session-heartbeat.test.js.map +0 -1
  629. package/dist/outposts/session-host-auth.test.d.ts +0 -16
  630. package/dist/outposts/session-host-auth.test.d.ts.map +0 -1
  631. package/dist/outposts/session-host-auth.test.js +0 -284
  632. package/dist/outposts/session-host-auth.test.js.map +0 -1
  633. package/dist/outposts/session-host-command.test.d.ts +0 -10
  634. package/dist/outposts/session-host-command.test.d.ts.map +0 -1
  635. package/dist/outposts/session-host-command.test.js +0 -180
  636. package/dist/outposts/session-host-command.test.js.map +0 -1
  637. package/dist/outposts/session-host-connection.test.d.ts +0 -18
  638. package/dist/outposts/session-host-connection.test.d.ts.map +0 -1
  639. package/dist/outposts/session-host-connection.test.js +0 -392
  640. package/dist/outposts/session-host-connection.test.js.map +0 -1
  641. package/dist/outposts/session-host-fakes.d.ts +0 -61
  642. package/dist/outposts/session-host-fakes.d.ts.map +0 -1
  643. package/dist/outposts/session-host-fakes.js +0 -115
  644. package/dist/outposts/session-host-fakes.js.map +0 -1
  645. package/dist/outposts/session-host-ndjson.test.d.ts +0 -11
  646. package/dist/outposts/session-host-ndjson.test.d.ts.map +0 -1
  647. package/dist/outposts/session-host-ndjson.test.js +0 -73
  648. package/dist/outposts/session-host-ndjson.test.js.map +0 -1
  649. package/dist/outposts/session-host-protocol.test.d.ts +0 -15
  650. package/dist/outposts/session-host-protocol.test.d.ts.map +0 -1
  651. package/dist/outposts/session-host-protocol.test.js +0 -0
  652. package/dist/outposts/session-host-protocol.test.js.map +0 -1
  653. package/dist/outposts/session-host-server.test.d.ts +0 -10
  654. package/dist/outposts/session-host-server.test.d.ts.map +0 -1
  655. package/dist/outposts/session-host-server.test.js +0 -118
  656. package/dist/outposts/session-host-server.test.js.map +0 -1
  657. package/dist/outposts/session-host-sessions.test.d.ts +0 -18
  658. package/dist/outposts/session-host-sessions.test.d.ts.map +0 -1
  659. package/dist/outposts/session-host-sessions.test.js +0 -439
  660. package/dist/outposts/session-host-sessions.test.js.map +0 -1
  661. package/dist/outposts/terminal.test.d.ts +0 -2
  662. package/dist/outposts/terminal.test.d.ts.map +0 -1
  663. package/dist/outposts/terminal.test.js +0 -146
  664. package/dist/outposts/terminal.test.js.map +0 -1
  665. package/dist/outposts/transport.test.d.ts +0 -10
  666. package/dist/outposts/transport.test.d.ts.map +0 -1
  667. package/dist/outposts/transport.test.js +0 -205
  668. package/dist/outposts/transport.test.js.map +0 -1
  669. package/dist/packed-journal-phase2.test.d.ts +0 -2
  670. package/dist/packed-journal-phase2.test.d.ts.map +0 -1
  671. package/dist/packed-journal-phase2.test.js +0 -273
  672. package/dist/packed-journal-phase2.test.js.map +0 -1
  673. package/dist/path-ancestors.test.d.ts +0 -2
  674. package/dist/path-ancestors.test.d.ts.map +0 -1
  675. package/dist/path-ancestors.test.js +0 -21
  676. package/dist/path-ancestors.test.js.map +0 -1
  677. package/dist/personal-vault-exclusions.test.d.ts +0 -22
  678. package/dist/personal-vault-exclusions.test.d.ts.map +0 -1
  679. package/dist/personal-vault-exclusions.test.js +0 -294
  680. package/dist/personal-vault-exclusions.test.js.map +0 -1
  681. package/dist/personal-vault.test.d.ts +0 -18
  682. package/dist/personal-vault.test.d.ts.map +0 -1
  683. package/dist/personal-vault.test.js +0 -1099
  684. package/dist/personal-vault.test.js.map +0 -1
  685. package/dist/plan-limit-pause.test.d.ts +0 -2
  686. package/dist/plan-limit-pause.test.d.ts.map +0 -1
  687. package/dist/plan-limit-pause.test.js +0 -150
  688. package/dist/plan-limit-pause.test.js.map +0 -1
  689. package/dist/plan-limit.fixtures.d.ts +0 -34
  690. package/dist/plan-limit.fixtures.d.ts.map +0 -1
  691. package/dist/plan-limit.fixtures.js +0 -96
  692. package/dist/plan-limit.fixtures.js.map +0 -1
  693. package/dist/plan-limit.test.d.ts +0 -2
  694. package/dist/plan-limit.test.d.ts.map +0 -1
  695. package/dist/plan-limit.test.js +0 -132
  696. package/dist/plan-limit.test.js.map +0 -1
  697. package/dist/prefix-coalesce.test.d.ts +0 -2
  698. package/dist/prefix-coalesce.test.d.ts.map +0 -1
  699. package/dist/prefix-coalesce.test.js +0 -260
  700. package/dist/prefix-coalesce.test.js.map +0 -1
  701. package/dist/private-folder-transport.test.d.ts +0 -2
  702. package/dist/private-folder-transport.test.d.ts.map +0 -1
  703. package/dist/private-folder-transport.test.js +0 -33
  704. package/dist/private-folder-transport.test.js.map +0 -1
  705. package/dist/public-surface.test.d.ts +0 -15
  706. package/dist/public-surface.test.d.ts.map +0 -1
  707. package/dist/public-surface.test.js +0 -110
  708. package/dist/public-surface.test.js.map +0 -1
  709. package/dist/qmd-reindex.test.d.ts +0 -14
  710. package/dist/qmd-reindex.test.d.ts.map +0 -1
  711. package/dist/qmd-reindex.test.js +0 -899
  712. package/dist/qmd-reindex.test.js.map +0 -1
  713. package/dist/read-only-state-dir.test.d.ts +0 -27
  714. package/dist/read-only-state-dir.test.d.ts.map +0 -1
  715. package/dist/read-only-state-dir.test.js +0 -167
  716. package/dist/read-only-state-dir.test.js.map +0 -1
  717. package/dist/remote-pull.test.d.ts +0 -2
  718. package/dist/remote-pull.test.d.ts.map +0 -1
  719. package/dist/remote-pull.test.js +0 -1147
  720. package/dist/remote-pull.test.js.map +0 -1
  721. package/dist/s3-download-stream-hash.test.d.ts +0 -2
  722. package/dist/s3-download-stream-hash.test.d.ts.map +0 -1
  723. package/dist/s3-download-stream-hash.test.js +0 -70
  724. package/dist/s3-download-stream-hash.test.js.map +0 -1
  725. package/dist/s3.list-cost.test.d.ts +0 -2
  726. package/dist/s3.list-cost.test.d.ts.map +0 -1
  727. package/dist/s3.list-cost.test.js +0 -51
  728. package/dist/s3.list-cost.test.js.map +0 -1
  729. package/dist/s3.rename-retry.test.d.ts +0 -2
  730. package/dist/s3.rename-retry.test.d.ts.map +0 -1
  731. package/dist/s3.rename-retry.test.js +0 -217
  732. package/dist/s3.rename-retry.test.js.map +0 -1
  733. package/dist/s3.symlink-materialize.test.d.ts +0 -18
  734. package/dist/s3.symlink-materialize.test.d.ts.map +0 -1
  735. package/dist/s3.symlink-materialize.test.js +0 -579
  736. package/dist/s3.symlink-materialize.test.js.map +0 -1
  737. package/dist/s3.test.d.ts +0 -9
  738. package/dist/s3.test.d.ts.map +0 -1
  739. package/dist/s3.test.js +0 -2047
  740. package/dist/s3.test.js.map +0 -1
  741. package/dist/s3.windows-portability.test.d.ts +0 -12
  742. package/dist/s3.windows-portability.test.d.ts.map +0 -1
  743. package/dist/s3.windows-portability.test.js +0 -223
  744. package/dist/s3.windows-portability.test.js.map +0 -1
  745. package/dist/schemas/signal-types.test.d.ts +0 -2
  746. package/dist/schemas/signal-types.test.d.ts.map +0 -1
  747. package/dist/schemas/signal-types.test.js +0 -65
  748. package/dist/schemas/signal-types.test.js.map +0 -1
  749. package/dist/schemas/source-channels.test.d.ts +0 -2
  750. package/dist/schemas/source-channels.test.d.ts.map +0 -1
  751. package/dist/schemas/source-channels.test.js +0 -65
  752. package/dist/schemas/source-channels.test.js.map +0 -1
  753. package/dist/scope-shrink.test.d.ts +0 -13
  754. package/dist/scope-shrink.test.d.ts.map +0 -1
  755. package/dist/scope-shrink.test.js +0 -558
  756. package/dist/scope-shrink.test.js.map +0 -1
  757. package/dist/session-log-capture.test.d.ts +0 -2
  758. package/dist/session-log-capture.test.d.ts.map +0 -1
  759. package/dist/session-log-capture.test.js +0 -533
  760. package/dist/session-log-capture.test.js.map +0 -1
  761. package/dist/session-log-retention.test.d.ts +0 -2
  762. package/dist/session-log-retention.test.d.ts.map +0 -1
  763. package/dist/session-log-retention.test.js +0 -383
  764. package/dist/session-log-retention.test.js.map +0 -1
  765. package/dist/signals/get.test.d.ts +0 -5
  766. package/dist/signals/get.test.d.ts.map +0 -1
  767. package/dist/signals/get.test.js +0 -254
  768. package/dist/signals/get.test.js.map +0 -1
  769. package/dist/signals/list.test.d.ts +0 -9
  770. package/dist/signals/list.test.d.ts.map +0 -1
  771. package/dist/signals/list.test.js +0 -330
  772. package/dist/signals/list.test.js.map +0 -1
  773. package/dist/skill-telemetry.test.d.ts +0 -2
  774. package/dist/skill-telemetry.test.d.ts.map +0 -1
  775. package/dist/skill-telemetry.test.js +0 -1484
  776. package/dist/skill-telemetry.test.js.map +0 -1
  777. package/dist/sources/get.test.d.ts +0 -5
  778. package/dist/sources/get.test.d.ts.map +0 -1
  779. package/dist/sources/get.test.js +0 -236
  780. package/dist/sources/get.test.js.map +0 -1
  781. package/dist/sources/list.test.d.ts +0 -8
  782. package/dist/sources/list.test.d.ts.map +0 -1
  783. package/dist/sources/list.test.js +0 -318
  784. package/dist/sources/list.test.js.map +0 -1
  785. package/dist/sts-credential-refresh.test.d.ts +0 -2
  786. package/dist/sts-credential-refresh.test.d.ts.map +0 -1
  787. package/dist/sts-credential-refresh.test.js +0 -181
  788. package/dist/sts-credential-refresh.test.js.map +0 -1
  789. package/dist/swallowed-error-sites.test.d.ts +0 -2
  790. package/dist/swallowed-error-sites.test.d.ts.map +0 -1
  791. package/dist/swallowed-error-sites.test.js +0 -199
  792. package/dist/swallowed-error-sites.test.js.map +0 -1
  793. package/dist/swallowed-error-w32.test.d.ts +0 -2
  794. package/dist/swallowed-error-w32.test.d.ts.map +0 -1
  795. package/dist/swallowed-error-w32.test.js +0 -171
  796. package/dist/swallowed-error-w32.test.js.map +0 -1
  797. package/dist/swallowed-error-w33.test.d.ts +0 -2
  798. package/dist/swallowed-error-w33.test.d.ts.map +0 -1
  799. package/dist/swallowed-error-w33.test.js +0 -125
  800. package/dist/swallowed-error-w33.test.js.map +0 -1
  801. package/dist/swallowed-error-w34.test.d.ts +0 -2
  802. package/dist/swallowed-error-w34.test.d.ts.map +0 -1
  803. package/dist/swallowed-error-w34.test.js +0 -110
  804. package/dist/swallowed-error-w34.test.js.map +0 -1
  805. package/dist/swallowed-error-w35.test.d.ts +0 -2
  806. package/dist/swallowed-error-w35.test.d.ts.map +0 -1
  807. package/dist/swallowed-error-w35.test.js +0 -223
  808. package/dist/swallowed-error-w35.test.js.map +0 -1
  809. package/dist/sync/area-ledger-auto-migration.test.d.ts +0 -2
  810. package/dist/sync/area-ledger-auto-migration.test.d.ts.map +0 -1
  811. package/dist/sync/area-ledger-auto-migration.test.js +0 -291
  812. package/dist/sync/area-ledger-auto-migration.test.js.map +0 -1
  813. package/dist/sync/area-ledger-migration.test.d.ts +0 -2
  814. package/dist/sync/area-ledger-migration.test.d.ts.map +0 -1
  815. package/dist/sync/area-ledger-migration.test.js +0 -554
  816. package/dist/sync/area-ledger-migration.test.js.map +0 -1
  817. package/dist/sync/area-ledger-recovery.bench.test.d.ts +0 -2
  818. package/dist/sync/area-ledger-recovery.bench.test.d.ts.map +0 -1
  819. package/dist/sync/area-ledger-recovery.bench.test.js +0 -60
  820. package/dist/sync/area-ledger-recovery.bench.test.js.map +0 -1
  821. package/dist/sync/area-ledger-writer-lock.bench.test.d.ts +0 -2
  822. package/dist/sync/area-ledger-writer-lock.bench.test.d.ts.map +0 -1
  823. package/dist/sync/area-ledger-writer-lock.bench.test.js +0 -57
  824. package/dist/sync/area-ledger-writer-lock.bench.test.js.map +0 -1
  825. package/dist/sync/area-ledger.test.d.ts +0 -2
  826. package/dist/sync/area-ledger.test.d.ts.map +0 -1
  827. package/dist/sync/area-ledger.test.js +0 -1401
  828. package/dist/sync/area-ledger.test.js.map +0 -1
  829. package/dist/sync/area-resolver.test.d.ts +0 -2
  830. package/dist/sync/area-resolver.test.d.ts.map +0 -1
  831. package/dist/sync/area-resolver.test.js +0 -234
  832. package/dist/sync/area-resolver.test.js.map +0 -1
  833. package/dist/sync/base-version-resolver.test.d.ts +0 -2
  834. package/dist/sync/base-version-resolver.test.d.ts.map +0 -1
  835. package/dist/sync/base-version-resolver.test.js +0 -24
  836. package/dist/sync/base-version-resolver.test.js.map +0 -1
  837. package/dist/sync/candidate-uploader.test.d.ts +0 -2
  838. package/dist/sync/candidate-uploader.test.d.ts.map +0 -1
  839. package/dist/sync/candidate-uploader.test.js +0 -132
  840. package/dist/sync/candidate-uploader.test.js.map +0 -1
  841. package/dist/sync/child-process-sync-worker-env.test.d.ts +0 -2
  842. package/dist/sync/child-process-sync-worker-env.test.d.ts.map +0 -1
  843. package/dist/sync/child-process-sync-worker-env.test.js +0 -38
  844. package/dist/sync/child-process-sync-worker-env.test.js.map +0 -1
  845. package/dist/sync/child-process-sync-worker-errors.test.d.ts +0 -2
  846. package/dist/sync/child-process-sync-worker-errors.test.d.ts.map +0 -1
  847. package/dist/sync/child-process-sync-worker-errors.test.js +0 -121
  848. package/dist/sync/child-process-sync-worker-errors.test.js.map +0 -1
  849. package/dist/sync/child-process-sync-worker.test.d.ts +0 -2
  850. package/dist/sync/child-process-sync-worker.test.d.ts.map +0 -1
  851. package/dist/sync/child-process-sync-worker.test.js +0 -262
  852. package/dist/sync/child-process-sync-worker.test.js.map +0 -1
  853. package/dist/sync/content-hash.test.d.ts +0 -2
  854. package/dist/sync/content-hash.test.d.ts.map +0 -1
  855. package/dist/sync/content-hash.test.js +0 -16
  856. package/dist/sync/content-hash.test.js.map +0 -1
  857. package/dist/sync/delta-client.test.d.ts +0 -2
  858. package/dist/sync/delta-client.test.d.ts.map +0 -1
  859. package/dist/sync/delta-client.test.js +0 -97
  860. package/dist/sync/delta-client.test.js.map +0 -1
  861. package/dist/sync/durable-apply.test.d.ts +0 -2
  862. package/dist/sync/durable-apply.test.d.ts.map +0 -1
  863. package/dist/sync/durable-apply.test.js +0 -180
  864. package/dist/sync/durable-apply.test.js.map +0 -1
  865. package/dist/sync/event-sync.test.d.ts +0 -14
  866. package/dist/sync/event-sync.test.d.ts.map +0 -1
  867. package/dist/sync/event-sync.test.js +0 -1714
  868. package/dist/sync/event-sync.test.js.map +0 -1
  869. package/dist/sync/feature-flags.test.d.ts +0 -24
  870. package/dist/sync/feature-flags.test.d.ts.map +0 -1
  871. package/dist/sync/feature-flags.test.js +0 -594
  872. package/dist/sync/feature-flags.test.js.map +0 -1
  873. package/dist/sync/full-pass-cap-governor.bench.test.d.ts +0 -2
  874. package/dist/sync/full-pass-cap-governor.bench.test.d.ts.map +0 -1
  875. package/dist/sync/full-pass-cap-governor.bench.test.js +0 -179
  876. package/dist/sync/full-pass-cap-governor.bench.test.js.map +0 -1
  877. package/dist/sync/full-pass-caps.test.d.ts +0 -2
  878. package/dist/sync/full-pass-caps.test.d.ts.map +0 -1
  879. package/dist/sync/full-pass-caps.test.js +0 -550
  880. package/dist/sync/full-pass-caps.test.js.map +0 -1
  881. package/dist/sync/idle-full-reconcile.test.d.ts +0 -2
  882. package/dist/sync/idle-full-reconcile.test.d.ts.map +0 -1
  883. package/dist/sync/idle-full-reconcile.test.js +0 -62
  884. package/dist/sync/idle-full-reconcile.test.js.map +0 -1
  885. package/dist/sync/inbound-event-admission.test.d.ts +0 -2
  886. package/dist/sync/inbound-event-admission.test.d.ts.map +0 -1
  887. package/dist/sync/inbound-event-admission.test.js +0 -125
  888. package/dist/sync/inbound-event-admission.test.js.map +0 -1
  889. package/dist/sync/journal-ledger-stream.test.d.ts +0 -12
  890. package/dist/sync/journal-ledger-stream.test.d.ts.map +0 -1
  891. package/dist/sync/journal-ledger-stream.test.js +0 -134
  892. package/dist/sync/journal-ledger-stream.test.js.map +0 -1
  893. package/dist/sync/lease-client.test.d.ts +0 -2
  894. package/dist/sync/lease-client.test.d.ts.map +0 -1
  895. package/dist/sync/lease-client.test.js +0 -119
  896. package/dist/sync/lease-client.test.js.map +0 -1
  897. package/dist/sync/logger.test.d.ts +0 -19
  898. package/dist/sync/logger.test.d.ts.map +0 -1
  899. package/dist/sync/logger.test.js +0 -204
  900. package/dist/sync/logger.test.js.map +0 -1
  901. package/dist/sync/metrics.test.d.ts +0 -19
  902. package/dist/sync/metrics.test.d.ts.map +0 -1
  903. package/dist/sync/metrics.test.js +0 -357
  904. package/dist/sync/metrics.test.js.map +0 -1
  905. package/dist/sync/multipart-uploader.test.d.ts +0 -2
  906. package/dist/sync/multipart-uploader.test.d.ts.map +0 -1
  907. package/dist/sync/multipart-uploader.test.js +0 -135
  908. package/dist/sync/multipart-uploader.test.js.map +0 -1
  909. package/dist/sync/mutation-client.test.d.ts +0 -2
  910. package/dist/sync/mutation-client.test.d.ts.map +0 -1
  911. package/dist/sync/mutation-client.test.js +0 -100
  912. package/dist/sync/mutation-client.test.js.map +0 -1
  913. package/dist/sync/mutation-session.test.d.ts +0 -2
  914. package/dist/sync/mutation-session.test.d.ts.map +0 -1
  915. package/dist/sync/mutation-session.test.js +0 -190
  916. package/dist/sync/mutation-session.test.js.map +0 -1
  917. package/dist/sync/parallel-sync-coordinator.test.d.ts +0 -2
  918. package/dist/sync/parallel-sync-coordinator.test.d.ts.map +0 -1
  919. package/dist/sync/parallel-sync-coordinator.test.js +0 -564
  920. package/dist/sync/parallel-sync-coordinator.test.js.map +0 -1
  921. package/dist/sync/path-leases.test.d.ts +0 -2
  922. package/dist/sync/path-leases.test.d.ts.map +0 -1
  923. package/dist/sync/path-leases.test.js +0 -51
  924. package/dist/sync/path-leases.test.js.map +0 -1
  925. package/dist/sync/per-area-maintenance.test.d.ts +0 -2
  926. package/dist/sync/per-area-maintenance.test.d.ts.map +0 -1
  927. package/dist/sync/per-area-maintenance.test.js +0 -100
  928. package/dist/sync/per-area-maintenance.test.js.map +0 -1
  929. package/dist/sync/push-event.test.d.ts +0 -15
  930. package/dist/sync/push-event.test.d.ts.map +0 -1
  931. package/dist/sync/push-event.test.js +0 -282
  932. package/dist/sync/push-event.test.js.map +0 -1
  933. package/dist/sync/push-receiver.test.d.ts +0 -25
  934. package/dist/sync/push-receiver.test.d.ts.map +0 -1
  935. package/dist/sync/push-receiver.test.js +0 -2851
  936. package/dist/sync/push-receiver.test.js.map +0 -1
  937. package/dist/sync/push-transport.test.d.ts +0 -2
  938. package/dist/sync/push-transport.test.d.ts.map +0 -1
  939. package/dist/sync/push-transport.test.js +0 -70
  940. package/dist/sync/push-transport.test.js.map +0 -1
  941. package/dist/sync/push-walk-matchers.bench.test.d.ts +0 -2
  942. package/dist/sync/push-walk-matchers.bench.test.d.ts.map +0 -1
  943. package/dist/sync/push-walk-matchers.bench.test.js +0 -230
  944. package/dist/sync/push-walk-matchers.bench.test.js.map +0 -1
  945. package/dist/sync/realtime-network-integration.test.d.ts +0 -2
  946. package/dist/sync/realtime-network-integration.test.d.ts.map +0 -1
  947. package/dist/sync/realtime-network-integration.test.js +0 -83
  948. package/dist/sync/realtime-network-integration.test.js.map +0 -1
  949. package/dist/sync/realtime-rollout.test.d.ts +0 -2
  950. package/dist/sync/realtime-rollout.test.d.ts.map +0 -1
  951. package/dist/sync/realtime-rollout.test.js +0 -94
  952. package/dist/sync/realtime-rollout.test.js.map +0 -1
  953. package/dist/sync/realtime-telemetry.test.d.ts +0 -2
  954. package/dist/sync/realtime-telemetry.test.d.ts.map +0 -1
  955. package/dist/sync/realtime-telemetry.test.js +0 -300
  956. package/dist/sync/realtime-telemetry.test.js.map +0 -1
  957. package/dist/sync/receiver-origin-config.test.d.ts +0 -2
  958. package/dist/sync/receiver-origin-config.test.d.ts.map +0 -1
  959. package/dist/sync/receiver-origin-config.test.js +0 -39
  960. package/dist/sync/receiver-origin-config.test.js.map +0 -1
  961. package/dist/sync/reconcile-cursor.test.d.ts +0 -2
  962. package/dist/sync/reconcile-cursor.test.d.ts.map +0 -1
  963. package/dist/sync/reconcile-cursor.test.js +0 -139
  964. package/dist/sync/reconcile-cursor.test.js.map +0 -1
  965. package/dist/sync/remote-file-spool.test.d.ts +0 -2
  966. package/dist/sync/remote-file-spool.test.d.ts.map +0 -1
  967. package/dist/sync/remote-file-spool.test.js +0 -77
  968. package/dist/sync/remote-file-spool.test.js.map +0 -1
  969. package/dist/sync/scope-inventory-client.test.d.ts +0 -2
  970. package/dist/sync/scope-inventory-client.test.d.ts.map +0 -1
  971. package/dist/sync/scope-inventory-client.test.js +0 -103
  972. package/dist/sync/scope-inventory-client.test.js.map +0 -1
  973. package/dist/sync/secret-path-policy.test.d.ts +0 -2
  974. package/dist/sync/secret-path-policy.test.d.ts.map +0 -1
  975. package/dist/sync/secret-path-policy.test.js +0 -183
  976. package/dist/sync/secret-path-policy.test.js.map +0 -1
  977. package/dist/sync/snapshot-client.test.d.ts +0 -2
  978. package/dist/sync/snapshot-client.test.d.ts.map +0 -1
  979. package/dist/sync/snapshot-client.test.js +0 -169
  980. package/dist/sync/snapshot-client.test.js.map +0 -1
  981. package/dist/sync/state-store.test.d.ts +0 -2
  982. package/dist/sync/state-store.test.d.ts.map +0 -1
  983. package/dist/sync/state-store.test.js +0 -1556
  984. package/dist/sync/state-store.test.js.map +0 -1
  985. package/dist/sync/sync-contract.test.d.ts +0 -2
  986. package/dist/sync/sync-contract.test.d.ts.map +0 -1
  987. package/dist/sync/sync-contract.test.js +0 -141
  988. package/dist/sync/sync-contract.test.js.map +0 -1
  989. package/dist/sync/targeted-pull-prefixes.test.d.ts +0 -2
  990. package/dist/sync/targeted-pull-prefixes.test.d.ts.map +0 -1
  991. package/dist/sync/targeted-pull-prefixes.test.js +0 -33
  992. package/dist/sync/targeted-pull-prefixes.test.js.map +0 -1
  993. package/dist/sync/targeted-upsert-exact-list.test.d.ts +0 -2
  994. package/dist/sync/targeted-upsert-exact-list.test.d.ts.map +0 -1
  995. package/dist/sync/targeted-upsert-exact-list.test.js +0 -247
  996. package/dist/sync/targeted-upsert-exact-list.test.js.map +0 -1
  997. package/dist/sync/unchanged-push-events.test.d.ts +0 -2
  998. package/dist/sync/unchanged-push-events.test.d.ts.map +0 -1
  999. package/dist/sync/unchanged-push-events.test.js +0 -76
  1000. package/dist/sync/unchanged-push-events.test.js.map +0 -1
  1001. package/dist/sync/uploader-finalization.test.d.ts +0 -2
  1002. package/dist/sync/uploader-finalization.test.d.ts.map +0 -1
  1003. package/dist/sync/uploader-finalization.test.js +0 -107
  1004. package/dist/sync/uploader-finalization.test.js.map +0 -1
  1005. package/dist/sync-core.test.d.ts +0 -2
  1006. package/dist/sync-core.test.d.ts.map +0 -1
  1007. package/dist/sync-core.test.js +0 -189
  1008. package/dist/sync-core.test.js.map +0 -1
  1009. package/dist/sync-progress.test.d.ts +0 -2
  1010. package/dist/sync-progress.test.d.ts.map +0 -1
  1011. package/dist/sync-progress.test.js +0 -277
  1012. package/dist/sync-progress.test.js.map +0 -1
  1013. package/dist/telemetry-events.test.d.ts +0 -2
  1014. package/dist/telemetry-events.test.d.ts.map +0 -1
  1015. package/dist/telemetry-events.test.js +0 -113
  1016. package/dist/telemetry-events.test.js.map +0 -1
  1017. package/dist/telemetry.test.d.ts +0 -11
  1018. package/dist/telemetry.test.d.ts.map +0 -1
  1019. package/dist/telemetry.test.js +0 -1603
  1020. package/dist/telemetry.test.js.map +0 -1
  1021. package/dist/vault-client.test.d.ts +0 -7
  1022. package/dist/vault-client.test.d.ts.map +0 -1
  1023. package/dist/vault-client.test.js +0 -1334
  1024. package/dist/vault-client.test.js.map +0 -1
  1025. package/dist/watch-coverage.test.d.ts +0 -2
  1026. package/dist/watch-coverage.test.d.ts.map +0 -1
  1027. package/dist/watch-coverage.test.js +0 -56
  1028. package/dist/watch-coverage.test.js.map +0 -1
  1029. package/dist/watch-owner-lease.test.d.ts +0 -2
  1030. package/dist/watch-owner-lease.test.d.ts.map +0 -1
  1031. package/dist/watch-owner-lease.test.js +0 -353
  1032. package/dist/watch-owner-lease.test.js.map +0 -1
  1033. package/dist/watch-parent.test.d.ts +0 -2
  1034. package/dist/watch-parent.test.d.ts.map +0 -1
  1035. package/dist/watch-parent.test.js +0 -131
  1036. package/dist/watch-parent.test.js.map +0 -1
  1037. package/dist/watch-roots.test.d.ts +0 -18
  1038. package/dist/watch-roots.test.d.ts.map +0 -1
  1039. package/dist/watch-roots.test.js +0 -395
  1040. package/dist/watch-roots.test.js.map +0 -1
  1041. package/dist/watcher-continuity-hot-path.test.d.ts +0 -2
  1042. package/dist/watcher-continuity-hot-path.test.d.ts.map +0 -1
  1043. package/dist/watcher-continuity-hot-path.test.js +0 -40
  1044. package/dist/watcher-continuity-hot-path.test.js.map +0 -1
  1045. package/dist/watcher-event-gate.test.d.ts +0 -16
  1046. package/dist/watcher-event-gate.test.d.ts.map +0 -1
  1047. package/dist/watcher-event-gate.test.js +0 -191
  1048. package/dist/watcher-event-gate.test.js.map +0 -1
  1049. package/dist/watcher-replan-sweep.test.d.ts +0 -2
  1050. package/dist/watcher-replan-sweep.test.d.ts.map +0 -1
  1051. package/dist/watcher-replan-sweep.test.js +0 -245
  1052. package/dist/watcher-replan-sweep.test.js.map +0 -1
  1053. package/dist/watcher-stdout.test.d.ts +0 -2
  1054. package/dist/watcher-stdout.test.d.ts.map +0 -1
  1055. package/dist/watcher-stdout.test.js +0 -86
  1056. package/dist/watcher-stdout.test.js.map +0 -1
  1057. package/dist/watcher.test.d.ts +0 -2
  1058. package/dist/watcher.test.d.ts.map +0 -1
  1059. package/dist/watcher.test.js +0 -4301
  1060. package/dist/watcher.test.js.map +0 -1
  1061. package/dist/watcher.warmup-cancel.test.d.ts +0 -2
  1062. package/dist/watcher.warmup-cancel.test.d.ts.map +0 -1
  1063. package/dist/watcher.warmup-cancel.test.js +0 -59
  1064. package/dist/watcher.warmup-cancel.test.js.map +0 -1
  1065. package/dist/watcher.warmup-responsive.test.d.ts +0 -2
  1066. package/dist/watcher.warmup-responsive.test.d.ts.map +0 -1
  1067. package/dist/watcher.warmup-responsive.test.js +0 -62
  1068. package/dist/watcher.warmup-responsive.test.js.map +0 -1
@@ -1,2376 +0,0 @@
1
- /**
2
- * Unit tests for the ObjectIO transport seam.
3
- *
4
- * Focus is the PresignObjectIO path (the new transport): it talks to a
5
- * VaultClient-shaped stub for URL minting and a mocked global `fetch` for the
6
- * byte movement. The S3SdkObjectIO path is already covered transitively by
7
- * s3.test.ts (which exercises s3.ts through the default factory), plus a couple
8
- * of factory-selection tests here.
9
- */
10
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
11
- import { createHash } from "node:crypto";
12
- import * as http from "node:http";
13
- import { PassThrough } from "node:stream";
14
- import { once } from "node:events";
15
- import { spawn } from "node:child_process";
16
- import { PresignObjectIO, S3SdkObjectIO, setObjectIOFactory, setObjectIOBodyIdleTimeoutMsForTesting, setObjectIORequestTimeoutMsForTesting, setPresignedGetTransportForTesting, setFilesAdmissionTimeoutMsForTesting, resolveObjectIO, presignObjectIOFactory, OBJECT_IO_REQUEST_TIMEOUT_MS, objectIORequestTimeoutMs, ObjectBodyIdleTimeoutError, ObjectLockChecksumRequiredError, } from "./object-io.js";
17
- import * as objectIoModule from "./object-io.js";
18
- import { isTransientNetworkError } from "./lib/net-errors.js";
19
- import { BandwidthGovernor, setBandwidthGovernorForTesting } from "./bandwidth.js";
20
- import { contentMd5Base64 } from "./lib/s3-content-checksum.js";
21
- import { vaultErrorFromResponse } from "./vault-client.js";
22
- import { PLAN_LIMIT_UPGRADE_URL, admissionAllowedBody, admissionUnsupportedBody, canonicalPlanLimitBody, companyPresignRefusalRow, } from "./plan-limit.fixtures.js";
23
- import { isAccessDenied, isNotFound } from "./sync-core.js";
24
- const COMPANY = "cmp_test";
25
- function fetchBackedPresignedGetTransport() {
26
- return {
27
- async get(url, headers) {
28
- const response = await fetch(url, { method: "GET", headers });
29
- const body = response.body;
30
- return {
31
- status: response.status,
32
- headers: response.headers,
33
- body: body
34
- ? (async function* () {
35
- const reader = body.getReader();
36
- let exhausted = false;
37
- try {
38
- while (true) {
39
- const { done, value } = await reader.read();
40
- if (done) {
41
- exhausted = true;
42
- return;
43
- }
44
- if (value.byteLength > 0)
45
- yield value;
46
- }
47
- }
48
- finally {
49
- if (!exhausted) {
50
- try {
51
- await reader.cancel();
52
- }
53
- catch {
54
- // The response may already have been cancelled by destroy.
55
- }
56
- }
57
- reader.releaseLock();
58
- }
59
- })()
60
- : undefined,
61
- destroy: () => {
62
- void body?.cancel().catch(() => undefined);
63
- },
64
- };
65
- },
66
- };
67
- }
68
- beforeEach(() => {
69
- setPresignedGetTransportForTesting(fetchBackedPresignedGetTransport());
70
- });
71
- afterEach(() => {
72
- setPresignedGetTransportForTesting(null);
73
- });
74
- describe("realtime ObjectIO payload priority", () => {
75
- afterEach(() => {
76
- setBandwidthGovernorForTesting(null);
77
- vi.unstubAllGlobals();
78
- });
79
- it("bypasses SDK GET and PUT pacing while retaining ordinary chore pacing", async () => {
80
- const governor = new BandwidthGovernor({ maxBytesPerSec: 1 });
81
- const acquire = vi.spyOn(governor, "acquire").mockResolvedValue();
82
- setBandwidthGovernorForTesting(governor);
83
- const body = (async function* () {
84
- yield Buffer.from("payload");
85
- })();
86
- const io = new S3SdkObjectIO(ctx());
87
- io.client = {
88
- send: async (command) => command.constructor.name === "GetObjectCommand"
89
- ? { Body: body, Metadata: {} }
90
- : { ETag: '"uploaded"' },
91
- };
92
- await io.putObject({
93
- key: "realtime-put",
94
- body: Buffer.from("realtime"),
95
- contentType: "text/plain",
96
- bypassBandwidthGovernor: true,
97
- });
98
- const realtimeGet = await io.getObjectStream("realtime-get", {
99
- bypassBandwidthGovernor: true,
100
- });
101
- await realtimeGet.body[Symbol.asyncIterator]().next();
102
- expect(acquire).not.toHaveBeenCalled();
103
- await io.putObject({
104
- key: "chore-put",
105
- body: Buffer.from("chore"),
106
- contentType: "text/plain",
107
- });
108
- expect(acquire).toHaveBeenCalledOnce();
109
- });
110
- it("bypasses presigned GET and PUT pacing for realtime transfers", async () => {
111
- const governor = new BandwidthGovernor({ maxBytesPerSec: 1 });
112
- const acquire = vi.spyOn(governor, "acquire").mockResolvedValue();
113
- setBandwidthGovernorForTesting(governor);
114
- const { vault, setPresign } = makeVault();
115
- const fetchMock = vi.fn();
116
- vi.stubGlobal("fetch", fetchMock);
117
- setPresign([
118
- { key: "realtime", op: "put", url: "https://s3/realtime-put" },
119
- { key: "realtime", op: "get", url: "https://s3/realtime-get" },
120
- { key: "chore", op: "put", url: "https://s3/chore-put" },
121
- ]);
122
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"ok"' } }));
123
- setPresignedGetTransportForTesting({
124
- get: async () => ({
125
- status: 200,
126
- headers: new Headers(),
127
- body: (async function* () { yield Buffer.from("payload"); })(),
128
- destroy: () => undefined,
129
- }),
130
- });
131
- const io = new PresignObjectIO(vault, COMPANY);
132
- await io.putObject({
133
- key: "realtime",
134
- body: Buffer.from("realtime"),
135
- contentType: "text/plain",
136
- bypassBandwidthGovernor: true,
137
- });
138
- const realtimeGet = await io.getObjectStream("realtime", {
139
- bypassBandwidthGovernor: true,
140
- });
141
- await realtimeGet.body[Symbol.asyncIterator]().next();
142
- expect(acquire).not.toHaveBeenCalled();
143
- await io.putObject({
144
- key: "chore",
145
- body: Buffer.from("chore"),
146
- contentType: "text/plain",
147
- });
148
- expect(acquire).toHaveBeenCalledOnce();
149
- });
150
- });
151
- describe("ObjectIO payload-sized request timeout", () => {
152
- it("extends large PUT deadlines while keeping the small-object base unchanged", () => {
153
- expect(objectIORequestTimeoutMs()).toBe(OBJECT_IO_REQUEST_TIMEOUT_MS);
154
- expect(objectIORequestTimeoutMs(8 * 1024 * 1024)).toBe(124_000);
155
- expect(objectIORequestTimeoutMs(48_800_000)).toBe(436_000);
156
- });
157
- });
158
- describe("ObjectIO idle timeout and bandwidth pacing", () => {
159
- afterEach(() => {
160
- setBandwidthGovernorForTesting(null);
161
- setObjectIOBodyIdleTimeoutMsForTesting(undefined);
162
- });
163
- it("does not count governor wait time as socket idle time", async () => {
164
- setObjectIOBodyIdleTimeoutMsForTesting(30);
165
- const governor = new BandwidthGovernor({ maxBytesPerSec: 10 }, {
166
- sleep: async (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
167
- });
168
- setBandwidthGovernorForTesting(governor);
169
- const body = (async function* () {
170
- yield Buffer.alloc(1);
171
- })();
172
- const io = new S3SdkObjectIO(ctx());
173
- io.client = {
174
- send: async () => ({ Body: body, Metadata: {} }),
175
- };
176
- const response = await io.getObjectStream("docs/paced.md");
177
- const chunks = [];
178
- for await (const chunk of response.body)
179
- chunks.push(chunk);
180
- expect(Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)))).toEqual(Buffer.alloc(1));
181
- }, 5_000);
182
- });
183
- describe("ObjectIO adaptive bandwidth sampling", () => {
184
- afterEach(() => {
185
- setBandwidthGovernorForTesting(null);
186
- vi.unstubAllGlobals();
187
- });
188
- for (const transport of ["S3 SDK", "presigned"]) {
189
- it(`learns raw upload capacity from an upload-only ${transport} run`, async () => {
190
- let now = 0;
191
- const governor = new BandwidthGovernor({ percent: 50 }, { now: () => now, sleep: async (ms) => { now += ms; } });
192
- setBandwidthGovernorForTesting(governor);
193
- if (transport === "S3 SDK") {
194
- const io = new S3SdkObjectIO(ctx());
195
- io.client = {
196
- send: async () => {
197
- now += 1000;
198
- return { ETag: '"uploaded"' };
199
- },
200
- };
201
- await io.putObject({
202
- key: "upload-only.bin",
203
- body: Buffer.alloc(1_000_000),
204
- contentType: "application/octet-stream",
205
- });
206
- }
207
- else {
208
- const { vault, setPresign } = makeVault();
209
- setPresign([{ key: "upload-only.bin", op: "put", url: "https://s3/upload-only" }]);
210
- const fetchMock = vi.fn(async () => {
211
- now += 1000;
212
- return new Response(null, { status: 200, headers: { etag: '"uploaded"' } });
213
- });
214
- vi.stubGlobal("fetch", fetchMock);
215
- await new PresignObjectIO(vault, COMPANY).putObject({
216
- key: "upload-only.bin",
217
- body: Buffer.alloc(1_000_000),
218
- contentType: "application/octet-stream",
219
- });
220
- expect(fetchMock).toHaveBeenCalledOnce();
221
- }
222
- expect(governor.effectiveRateBps()).toBe(500_000);
223
- });
224
- }
225
- it("samples transport arrivals before buffered GET chunks are pulled", async () => {
226
- let now = 1000;
227
- const governor = new BandwidthGovernor({ percent: 60 }, { now: () => now, sleep: async (ms) => { now += ms; } });
228
- setBandwidthGovernorForTesting(governor);
229
- const body = new PassThrough();
230
- const io = new S3SdkObjectIO(ctx());
231
- io.client = {
232
- send: async () => ({ Body: body, Metadata: {} }),
233
- };
234
- const response = await io.getObjectStream("buffered.bin");
235
- body.write(Buffer.alloc(4096));
236
- await new Promise((resolve) => setImmediate(resolve));
237
- now += 40;
238
- body.write(Buffer.alloc(4096));
239
- await new Promise((resolve) => setImmediate(resolve));
240
- now += 40;
241
- body.end(Buffer.alloc(4096));
242
- await new Promise((resolve) => setImmediate(resolve));
243
- expect(governor.effectiveRateBps()).toBeGreaterThanOrEqual(60_000);
244
- expect(governor.effectiveRateBps()).toBeLessThanOrEqual(70_000);
245
- let received = 0;
246
- for await (const chunk of response.body)
247
- received += chunk.byteLength;
248
- expect(received).toBe(12_288);
249
- });
250
- });
251
- describe("ObjectIO chore transfer admission", () => {
252
- afterEach(() => {
253
- setBandwidthGovernorForTesting(null);
254
- });
255
- it("waits for a download slot before opening the next SDK GET", async () => {
256
- setBandwidthGovernorForTesting(new BandwidthGovernor({ maxConcurrency: 1 }));
257
- const opened = [];
258
- const io = new S3SdkObjectIO(ctx());
259
- io.client = {
260
- send: async (command) => {
261
- opened.push(command.input.Key);
262
- return {
263
- Body: (async function* () { yield Buffer.from(command.input.Key); })(),
264
- Metadata: {},
265
- };
266
- },
267
- };
268
- const first = await io.getObjectStream("first.bin");
269
- const secondPromise = io.getObjectStream("second.bin");
270
- await new Promise((resolve) => setImmediate(resolve));
271
- expect(opened).toEqual(["first.bin"]);
272
- for await (const _chunk of first.body) { /* drain the first transfer */ }
273
- const second = await secondPromise;
274
- expect(opened).toEqual(["first.bin", "second.bin"]);
275
- for await (const _chunk of second.body) { /* drain the second transfer */ }
276
- });
277
- it("waits for a download slot before opening the next presigned GET", async () => {
278
- setBandwidthGovernorForTesting(new BandwidthGovernor({ maxConcurrency: 1 }));
279
- const { vault, setPresign } = makeVault();
280
- setPresign([
281
- { key: "first.bin", op: "get", url: "https://s3/first" },
282
- { key: "second.bin", op: "get", url: "https://s3/second" },
283
- ]);
284
- const opened = [];
285
- setPresignedGetTransportForTesting({
286
- get: async (url) => {
287
- opened.push(url);
288
- return {
289
- status: 200,
290
- headers: new Headers(),
291
- body: (async function* () { yield Buffer.from(url); })(),
292
- destroy: () => undefined,
293
- };
294
- },
295
- });
296
- const io = new PresignObjectIO(vault, COMPANY);
297
- const first = await io.getObjectStream("first.bin");
298
- const secondPromise = io.getObjectStream("second.bin");
299
- await new Promise((resolve) => setImmediate(resolve));
300
- expect(opened).toEqual(["https://s3/first"]);
301
- for await (const _chunk of first.body) { /* drain the first transfer */ }
302
- const second = await secondPromise;
303
- expect(opened).toHaveLength(2);
304
- for await (const _chunk of second.body) { /* drain the second transfer */ }
305
- });
306
- });
307
- describe("ObjectIO ranged GET validators", () => {
308
- afterEach(() => {
309
- setObjectIOBodyIdleTimeoutMsForTesting(undefined);
310
- setBandwidthGovernorForTesting(null);
311
- });
312
- it("sends Range and If-Match through the S3 SDK and exposes response identity", async () => {
313
- const sent = [];
314
- const body = (async function* () {
315
- yield Buffer.from("suffix");
316
- })();
317
- const io = new S3SdkObjectIO(ctx());
318
- io.client = {
319
- send: async (command) => {
320
- sent.push(command.input);
321
- return {
322
- Body: body,
323
- Metadata: {},
324
- ETag: '"etag-a"',
325
- VersionId: "version-a",
326
- ContentLength: 6,
327
- ContentRange: "bytes 20-25/26",
328
- $metadata: { httpStatusCode: 206 },
329
- };
330
- },
331
- };
332
- const streamed = await io.getObjectStream("resume.bin", {
333
- rangeStart: 20,
334
- ifMatch: '"etag-a"',
335
- });
336
- const identity = streamed;
337
- expect(sent[0]).toMatchObject({ Range: "bytes=20-", IfMatch: '"etag-a"' });
338
- expect(identity).toMatchObject({
339
- etag: '"etag-a"',
340
- versionId: "version-a",
341
- contentLength: 6,
342
- contentRange: "bytes 20-25/26",
343
- statusCode: 206,
344
- });
345
- });
346
- it("sends Range and If-Match through presigned GET and exposes response identity", async () => {
347
- const { vault, setPresign } = makeVault();
348
- setPresign([{ key: "resume.bin", op: "get", url: "https://s3/resume" }]);
349
- const get = vi.fn(async () => ({
350
- status: 206,
351
- headers: new Headers({
352
- etag: '"etag-a"',
353
- "x-amz-version-id": "version-a",
354
- "content-length": "6",
355
- "content-range": "bytes 20-25/26",
356
- }),
357
- body: (async function* () { yield Buffer.from("suffix"); })(),
358
- destroy: vi.fn(),
359
- }));
360
- setPresignedGetTransportForTesting({ get });
361
- const io = new PresignObjectIO(vault, COMPANY);
362
- const streamed = await io.getObjectStream("resume.bin", {
363
- rangeStart: 20,
364
- ifMatch: '"etag-a"',
365
- });
366
- const identity = streamed;
367
- expect(get).toHaveBeenCalledWith("https://s3/resume", expect.objectContaining({ Range: "bytes=20-", "If-Match": '"etag-a"' }));
368
- expect(identity).toMatchObject({
369
- etag: '"etag-a"',
370
- versionId: "version-a",
371
- contentLength: 6,
372
- contentRange: "bytes 20-25/26",
373
- statusCode: 206,
374
- });
375
- });
376
- it("reports a ranged presigned 412 as a precondition failure", async () => {
377
- const { vault, setPresign } = makeVault();
378
- setPresign([{ key: "resume.bin", op: "get", url: "https://s3/resume" }]);
379
- const destroy = vi.fn();
380
- setPresignedGetTransportForTesting({
381
- get: async () => ({
382
- status: 412,
383
- headers: new Headers(),
384
- body: (async function* () { yield Buffer.from("stale etag"); })(),
385
- destroy,
386
- }),
387
- });
388
- const io = new PresignObjectIO(vault, COMPANY);
389
- await expect(io.getObjectStream("resume.bin", {
390
- rangeStart: 20,
391
- ifMatch: '"etag-a"',
392
- })).rejects.toMatchObject({ name: "PreconditionFailed" });
393
- expect(destroy).toHaveBeenCalledOnce();
394
- });
395
- });
396
- function ctx() {
397
- return {
398
- uid: COMPANY,
399
- slug: "test",
400
- bucketName: "bucket-test",
401
- region: "us-east-1",
402
- credentials: {
403
- accessKeyId: "AKIA",
404
- secretAccessKey: "secret",
405
- sessionToken: "token",
406
- },
407
- expiresAt: "2099-01-01T00:00:00.000Z",
408
- };
409
- }
410
- /**
411
- * Minimal VaultClient stub. Each method returns whatever the per-test config
412
- * sets; presign records the inputs it was called with so tests can assert the
413
- * op/metadata/key shape the seam sends.
414
- */
415
- function makeVault() {
416
- const presignCalls = [];
417
- let nextPresign = [];
418
- let nextList = { objects: [], cursor: null, truncated: false };
419
- const vault = {
420
- presign: async (input) => {
421
- presignCalls.push({
422
- companyUid: input.companyUid,
423
- op: input.op,
424
- keys: input.keys,
425
- });
426
- return { results: nextPresign, expiresAt: "2099-01-01T00:00:00.000Z" };
427
- },
428
- listFiles: async () => nextList,
429
- };
430
- return {
431
- vault,
432
- presignCalls,
433
- setPresign: (rows) => {
434
- nextPresign = rows;
435
- },
436
- setList: (v) => {
437
- nextList = v;
438
- },
439
- };
440
- }
441
- function makeForbiddenPrefixVault(pages) {
442
- const calls = [];
443
- const forbiddenCharacters = new Set(["?", "#", "\\", "[", "]"]);
444
- const vault = {
445
- presign: async () => ({ results: [], expiresAt: "2099-01-01T00:00:00.000Z" }),
446
- listFiles: async (companyUid, prefix, cursor) => {
447
- calls.push({ companyUid, prefix, cursor });
448
- if (prefix && [...prefix].some((character) => forbiddenCharacters.has(character))) {
449
- throw Object.assign(new Error("Forbidden prefix characters"), {
450
- name: "VaultClientError",
451
- code: "INVALID_PREFIX_FORBIDDEN_CHARS",
452
- statusCode: 400,
453
- });
454
- }
455
- return pages.get(cursor) ?? { objects: [], cursor: null, truncated: false };
456
- },
457
- };
458
- return { vault, calls };
459
- }
460
- function vaultObject(key) {
461
- return {
462
- key,
463
- size: 1,
464
- lastModified: "2026-01-01T00:00:00.000Z",
465
- etag: "etag",
466
- permission: "read",
467
- };
468
- }
469
- function safeVaultListPrefixAncestor(prefix) {
470
- const helper = objectIoModule.getSafeVaultListPrefixAncestor;
471
- if (!helper)
472
- throw new Error("getSafeVaultListPrefixAncestor is not exported");
473
- return helper(prefix);
474
- }
475
- describe("factory selection", () => {
476
- afterEach(() => setObjectIOFactory(null));
477
- it("defaults to the S3 SDK transport", () => {
478
- const io = resolveObjectIO(ctx());
479
- expect(io).toBeInstanceOf(S3SdkObjectIO);
480
- });
481
- it("setObjectIOFactory(null) resets to the default", () => {
482
- const { vault } = makeVault();
483
- setObjectIOFactory(presignObjectIOFactory(vault));
484
- expect(resolveObjectIO(ctx())).toBeInstanceOf(PresignObjectIO);
485
- setObjectIOFactory(null);
486
- expect(resolveObjectIO(ctx())).toBeInstanceOf(S3SdkObjectIO);
487
- });
488
- it("presign factory derives companyUid from ctx.uid", async () => {
489
- const { vault, presignCalls, setPresign } = makeVault();
490
- setPresign([{ key: "k", op: "get", url: "https://x/get" }]);
491
- vi.stubGlobal("fetch", vi.fn(async () => new Response(Buffer.from("body"), { status: 200 })));
492
- const io = presignObjectIOFactory(vault)(ctx());
493
- await io.getObject("k");
494
- expect(presignCalls[0].companyUid).toBe(COMPANY);
495
- vi.unstubAllGlobals();
496
- });
497
- });
498
- describe("S3SdkObjectIO request timeout", () => {
499
- afterEach(() => {
500
- setObjectIORequestTimeoutMsForTesting(undefined);
501
- setObjectIOBodyIdleTimeoutMsForTesting(undefined);
502
- });
503
- it("aborts a hung ListObjectsV2 within OBJECT_IO_REQUEST_TIMEOUT_MS as transient", async () => {
504
- expect(OBJECT_IO_REQUEST_TIMEOUT_MS).toBe(60_000);
505
- setObjectIORequestTimeoutMsForTesting(50);
506
- const io = new S3SdkObjectIO(ctx());
507
- io.client = {
508
- send: (_cmd, opts) => new Promise((_resolve, reject) => {
509
- const signal = opts?.abortSignal;
510
- const fail = () => {
511
- const reason = signal?.reason;
512
- const err = Object.assign(new Error("This operation was aborted"), {
513
- name: reason instanceof Error && typeof reason.name === "string"
514
- ? reason.name
515
- : "AbortError",
516
- });
517
- reject(err);
518
- };
519
- if (!signal)
520
- return;
521
- if (signal.aborted) {
522
- fail();
523
- return;
524
- }
525
- signal.addEventListener("abort", fail, { once: true });
526
- }),
527
- };
528
- const started = Date.now();
529
- let caught;
530
- try {
531
- await io.listObjects({});
532
- }
533
- catch (err) {
534
- caught = err;
535
- }
536
- expect(caught).toBeInstanceOf(Error);
537
- expect(isTransientNetworkError(caught)).toBe(true);
538
- expect(Date.now() - started).toBeLessThan(1_000);
539
- }, 5_000);
540
- it("cancels a GET body that stops yielding after its headers arrive", async () => {
541
- setObjectIOBodyIdleTimeoutMsForTesting(40);
542
- let cancelled = false;
543
- const body = {
544
- [Symbol.asyncIterator]: () => ({
545
- next: () => new Promise(() => { }),
546
- return: async () => ({ done: true, value: undefined }),
547
- }),
548
- destroy: () => { cancelled = true; },
549
- };
550
- const io = new S3SdkObjectIO(ctx());
551
- io.client = {
552
- send: async () => ({ Body: body, Metadata: {} }),
553
- };
554
- const response = await io.getObjectStream("docs/stalled.md");
555
- const started = Date.now();
556
- let caught;
557
- try {
558
- await response.body[Symbol.asyncIterator]().next();
559
- }
560
- catch (error) {
561
- caught = error;
562
- }
563
- expect(caught).toBeInstanceOf(ObjectBodyIdleTimeoutError);
564
- expect(cancelled).toBe(true);
565
- expect(Date.now() - started).toBeLessThan(1_000);
566
- }, 5_000);
567
- it("cancels a stalled presigned GET body", async () => {
568
- setObjectIOBodyIdleTimeoutMsForTesting(40);
569
- const { vault, setPresign } = makeVault();
570
- setPresign([{ key: "docs/stalled.md", op: "get", url: "https://example.test/stalled" }]);
571
- let destroyed = false;
572
- const body = {
573
- [Symbol.asyncIterator]: () => ({
574
- next: () => new Promise(() => { }),
575
- return: async () => ({ done: true, value: undefined }),
576
- }),
577
- };
578
- setPresignedGetTransportForTesting({
579
- get: async () => ({
580
- status: 200,
581
- headers: new Headers(),
582
- body,
583
- destroy: () => { destroyed = true; },
584
- }),
585
- });
586
- const io = new PresignObjectIO(vault, COMPANY);
587
- const response = await io.getObjectStream("docs/stalled.md");
588
- let caught;
589
- try {
590
- await response.body[Symbol.asyncIterator]().next();
591
- }
592
- catch (error) {
593
- caught = error;
594
- }
595
- expect(caught).toBeInstanceOf(ObjectBodyIdleTimeoutError);
596
- expect(destroyed).toBe(true);
597
- }, 5_000);
598
- });
599
- describe("S3SdkObjectIO.putObject — conditional-write fence", () => {
600
- it("maps ifMatch (re-quoted) and ifNoneMatch onto the PutObjectCommand", async () => {
601
- const io = new S3SdkObjectIO(ctx());
602
- const sent = [];
603
- // Swap the private client for a recorder — the fence mapping is pure
604
- // input-shaping, no wire needed.
605
- io.client = {
606
- send: async (cmd) => {
607
- sent.push(cmd.input);
608
- return { ETag: '"landed"' };
609
- },
610
- };
611
- await io.putObject({ key: "a", body: Buffer.from("x"), contentType: "t", ifMatch: "abc" });
612
- expect(sent[0].IfMatch).toBe('"abc"'); // stripped etags re-quoted for If-Match
613
- await io.putObject({ key: "a2", body: Buffer.from("x"), contentType: "t", ifMatch: '"quoted"' });
614
- expect(sent[1].IfMatch).toBe('"quoted"'); // already-quoted passes through
615
- await io.putObject({ key: "b", body: Buffer.from("y"), contentType: "t", ifNoneMatch: "*" });
616
- expect(sent[2].IfNoneMatch).toBe("*");
617
- await io.putObject({ key: "c", body: Buffer.from("z"), contentType: "t" });
618
- expect(sent[3].IfMatch).toBeUndefined(); // unfenced PUT stays unfenced
619
- expect(sent[3].IfNoneMatch).toBeUndefined();
620
- });
621
- it("sets ChecksumAlgorithm SHA256 so Object Lock buckets accept the PUT", async () => {
622
- const io = new S3SdkObjectIO(ctx());
623
- const sent = [];
624
- io.client = {
625
- send: async (cmd) => {
626
- sent.push(cmd.input);
627
- return { ETag: '"ck"' };
628
- },
629
- };
630
- await io.putObject({ key: "a", body: Buffer.from("x"), contentType: "t" });
631
- expect(sent[0].ChecksumAlgorithm).toBe("SHA256");
632
- });
633
- });
634
- describe("PresignObjectIO.putObject", () => {
635
- let fetchMock;
636
- beforeEach(() => {
637
- fetchMock = vi.fn();
638
- vi.stubGlobal("fetch", fetchMock);
639
- });
640
- afterEach(() => vi.unstubAllGlobals());
641
- it("sends op:put with metadata and replays the signed headers verbatim", async () => {
642
- const { vault, presignCalls, setPresign } = makeVault();
643
- setPresign([
644
- {
645
- key: "shared/a.md",
646
- op: "put",
647
- url: "https://s3/put-url",
648
- headers: {
649
- "content-type": "text/markdown",
650
- "x-amz-server-side-encryption": "aws:kms",
651
- "x-amz-meta-created-by": "me@getindigo.ai",
652
- },
653
- },
654
- ]);
655
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"abc123"' } }));
656
- const io = new PresignObjectIO(vault, COMPANY);
657
- const res = await io.putObject({
658
- key: "shared/a.md",
659
- body: Buffer.from("hello"),
660
- contentType: "text/markdown",
661
- metadata: { "created-by": "me@getindigo.ai" },
662
- });
663
- // Presign request carried op + per-key metadata.
664
- expect(presignCalls[0].op).toBe("put");
665
- expect(presignCalls[0].keys[0]).toMatchObject({
666
- key: "shared/a.md",
667
- op: "put",
668
- contentType: "text/markdown",
669
- metadata: { "created-by": "me@getindigo.ai" },
670
- });
671
- // PUT replays the exact signed headers.
672
- const [url, init] = fetchMock.mock.calls[0];
673
- expect(url).toBe("https://s3/put-url");
674
- expect(init.method).toBe("PUT");
675
- expect(init.headers).toEqual({
676
- "content-type": "text/markdown",
677
- "x-amz-server-side-encryption": "aws:kms",
678
- "x-amz-meta-created-by": "me@getindigo.ai",
679
- "Content-MD5": "XUFAKrxLKna5cZ2REBfFkg==",
680
- });
681
- // ETag returned with quotes stripped.
682
- expect(res.etag).toBe("abc123");
683
- });
684
- it("keeps the server upgrade URL on a plan-limit PUT denial", async () => {
685
- const { vault, setPresign } = makeVault();
686
- const upgradeUrl = "https://billing.example.test/company/acme";
687
- setPresign([
688
- {
689
- key: "shared/paused.md",
690
- op: "put",
691
- error: "Plan limit reached",
692
- code: "PLAN_LIMIT_REACHED",
693
- upgradeUrl,
694
- },
695
- ]);
696
- const io = new PresignObjectIO(vault, COMPANY);
697
- await expect(io.putObject({
698
- key: "shared/paused.md",
699
- body: Buffer.from("hello"),
700
- contentType: "text/markdown",
701
- })).rejects.toMatchObject({
702
- statusCode: 402,
703
- code: "PLAN_LIMIT_REACHED",
704
- upgradeUrl,
705
- });
706
- });
707
- it("throws a typed plan-limit refusal for the server's per-item company presign row", async () => {
708
- const { vault, setPresign } = makeVault();
709
- setPresign([companyPresignRefusalRow("docs/paused.md")]);
710
- const io = new PresignObjectIO(vault, COMPANY);
711
- const err = await io
712
- .putObject({
713
- key: "docs/paused.md",
714
- body: Buffer.from("hello"),
715
- contentType: "text/markdown",
716
- })
717
- .catch((error) => error);
718
- expect(err).toBeInstanceOf(VaultClientError);
719
- expect(err).toMatchObject({
720
- statusCode: 402,
721
- path: "docs/paused.md",
722
- code: "PLAN_LIMIT_EXCEEDED",
723
- upgradeUrl: PLAN_LIMIT_UPGRADE_URL,
724
- message: "New files are paused while Acme is over its Starter limits.",
725
- planLimit: {
726
- code: "PLAN_LIMIT_EXCEEDED",
727
- upgradeUrl: PLAN_LIMIT_UPGRADE_URL,
728
- message: "New files are paused while Acme is over its Starter limits.",
729
- },
730
- });
731
- });
732
- it("sends Content-MD5 (base64 of raw MD5) for a known body when the presign did not sign a checksum", async () => {
733
- const body = Buffer.from("hello");
734
- expect(contentMd5Base64(body)).toBe("XUFAKrxLKna5cZ2REBfFkg==");
735
- const { vault, setPresign } = makeVault();
736
- setPresign([
737
- {
738
- key: "docs/a.md",
739
- op: "put",
740
- url: "https://s3.example/put?X-Amz-SignedHeaders=content-type%3Bx-amz-meta-created-by",
741
- headers: { "content-type": "text/plain" },
742
- },
743
- ]);
744
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"md5"' } }));
745
- const io = new PresignObjectIO(vault, COMPANY);
746
- await io.putObject({ key: "docs/a.md", body, contentType: "text/plain" });
747
- const init = fetchMock.mock.calls[0][1];
748
- expect(init.headers["Content-MD5"]).toBe("XUFAKrxLKna5cZ2REBfFkg==");
749
- expect(init.headers["x-amz-checksum-sha256"]).toBeUndefined();
750
- });
751
- it("replays a signed x-amz-checksum-sha256 instead of attaching Content-MD5", async () => {
752
- const { vault, setPresign } = makeVault();
753
- setPresign([
754
- {
755
- key: "docs/a.md",
756
- op: "put",
757
- url: "https://s3.example/put?X-Amz-SignedHeaders=content-type%3Bx-amz-checksum-sha256",
758
- headers: {
759
- "content-type": "text/plain",
760
- "x-amz-checksum-sha256": "signed-sha256-value",
761
- },
762
- },
763
- ]);
764
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"ck"' } }));
765
- const io = new PresignObjectIO(vault, COMPANY);
766
- await io.putObject({
767
- key: "docs/a.md",
768
- body: Buffer.from("hello"),
769
- contentType: "text/plain",
770
- });
771
- const init = fetchMock.mock.calls[0][1];
772
- expect(init.headers["x-amz-checksum-sha256"]).toBe("signed-sha256-value");
773
- expect(init.headers["Content-MD5"]).toBeUndefined();
774
- });
775
- it("classifies Object Lock InvalidRequest 400 without retrying", async () => {
776
- const xml = '<?xml version="1.0" encoding="UTF-8"?><Error><Code>InvalidRequest</Code>' +
777
- "<Message>Content-MD5 OR x-amz-checksum- HTTP header is required for Put Object requests with Object Lock parameters</Message></Error>";
778
- const { vault, setPresign } = makeVault();
779
- setPresign([{ key: "docs/a.md", op: "put", url: "https://s3/put" }]);
780
- fetchMock.mockResolvedValue(new Response(xml, { status: 400 }));
781
- const io = new PresignObjectIO(vault, COMPANY);
782
- let caught;
783
- try {
784
- await io.putObject({
785
- key: "docs/a.md",
786
- body: Buffer.from("hello"),
787
- contentType: "text/plain",
788
- });
789
- }
790
- catch (err) {
791
- caught = err;
792
- }
793
- expect(caught).toBeInstanceOf(ObjectLockChecksumRequiredError);
794
- expect(caught).toMatchObject({
795
- name: "ObjectLockChecksumRequired",
796
- statusCode: 400,
797
- retryable: false,
798
- });
799
- expect(caught.message).toMatch(/Object Lock requires a Content-MD5/);
800
- expect(fetchMock).toHaveBeenCalledTimes(1);
801
- expect(isTransientNetworkError(caught)).toBe(false);
802
- });
803
- it("throws on a per-key denial (error row)", async () => {
804
- const { vault, setPresign } = makeVault();
805
- setPresign([
806
- { key: "secret/x", op: "put", error: "forbidden", code: "ACL_DENY" },
807
- ]);
808
- const io = new PresignObjectIO(vault, COMPANY);
809
- await expect(io.putObject({
810
- key: "secret/x",
811
- body: Buffer.from("z"),
812
- contentType: "text/plain",
813
- })).rejects.toThrow(/denied for secret\/x.*forbidden.*ACL_DENY/);
814
- expect(fetchMock).not.toHaveBeenCalled();
815
- });
816
- it("a write denial (FILES_PRESIGN_FORBIDDEN) throws the access-denied shape so the push skips it like a denied GET", async () => {
817
- const { vault, setPresign } = makeVault();
818
- // The server refuses to MINT a PUT URL for a key the caller can't write
819
- // (private-by-default: members hold read-only on a shared knowledge prefix).
820
- setPresign([
821
- {
822
- key: "knowledge/clients/acme/brief.md",
823
- op: "put",
824
- error: "Caller lacks write permission on 'knowledge/clients/acme/brief.md'",
825
- code: "FILES_PRESIGN_FORBIDDEN",
826
- },
827
- ]);
828
- const io = new PresignObjectIO(vault, COMPANY);
829
- let caught;
830
- await io
831
- .putObject({
832
- key: "knowledge/clients/acme/brief.md",
833
- body: Buffer.from("z"),
834
- contentType: "text/markdown",
835
- })
836
- .catch((e) => {
837
- caught = e;
838
- });
839
- // Same shape a denied GET/HEAD already throws (name === "Forbidden"), so the
840
- // share.ts push catch routes it through `isAccessDenied` into the silent
841
- // per-path skip (scopeExcludedSet) + the "N paths skipped — outside your
842
- // granted access" aggregate, instead of a fatal `type:"error"` that exits
843
- // the runner non-zero and floods Sentry. This is the PUT/GET symmetry that
844
- // fixes the presign-put-denied crash-loop across user machines.
845
- expect(caught.name).toBe("Forbidden");
846
- expect(isAccessDenied(caught)).toBe(true);
847
- // Denied at mint time — the byte-moving PUT is never attempted.
848
- expect(fetchMock).not.toHaveBeenCalled();
849
- });
850
- it("US-006 AC5: a transcript-consent denial (FILES_PRESIGN_TRANSCRIPT_CONSENT_REQUIRED) is skip-with-log, not fatal", async () => {
851
- const { vault, setPresign } = makeVault();
852
- // The US-005 consent code ends `_REQUIRED`, not `_FORBIDDEN`, so the plain
853
- // suffix test alone would MISS it and let it fall through to the fatal
854
- // generic Error — the recurring partial-failure this AC5 fix prevents.
855
- setPresign([
856
- {
857
- key: "sessions/prs_1/2026-07-11T10-00-00.json",
858
- op: "get",
859
- error: "transcript consent required",
860
- code: "FILES_PRESIGN_TRANSCRIPT_CONSENT_REQUIRED",
861
- },
862
- ]);
863
- const io = new PresignObjectIO(vault, COMPANY);
864
- // Monkeypatch console.warn (vitest's console interception can shadow a spy)
865
- // to assert the REAL skip log fires.
866
- const warnings = [];
867
- const originalWarn = console.warn;
868
- console.warn = (...args) => {
869
- warnings.push(args.map(String).join(" "));
870
- };
871
- let caught;
872
- try {
873
- await io
874
- .getObject("sessions/prs_1/2026-07-11T10-00-00.json")
875
- .catch((e) => {
876
- caught = e;
877
- });
878
- }
879
- finally {
880
- console.warn = originalWarn;
881
- }
882
- // Same shape a denied GET already throws → isAccessDenied → the per-key
883
- // scope-excluded skip, NOT a fatal `type:"error"` that poisons the run.
884
- expect(caught.name).toBe("Forbidden");
885
- expect(isAccessDenied(caught)).toBe(true);
886
- // A one-line skip log fires so operators can see WHY the key was skipped.
887
- expect(warnings.some((w) => w.includes("FILES_PRESIGN_TRANSCRIPT_CONSENT_REQUIRED"))).toBe(true);
888
- });
889
- it("US-006 AC5: a sessions structural-ACL denial (FILES_PRESIGN_SESSIONS_FORBIDDEN) is skip-with-log", async () => {
890
- const { vault, setPresign } = makeVault();
891
- // A spoofed/foreign `sessions/{uid}/` prefix the hq-pro structural gate
892
- // refuses to presign. Ends `_FORBIDDEN`, so the suffix test already covers
893
- // it — this pins that coverage as a regression contract for US-006.
894
- setPresign([
895
- {
896
- key: "sessions/prs_other/y.json",
897
- op: "get",
898
- error: "not your session prefix",
899
- code: "FILES_PRESIGN_SESSIONS_FORBIDDEN",
900
- },
901
- ]);
902
- const io = new PresignObjectIO(vault, COMPANY);
903
- let caught;
904
- await io.getObject("sessions/prs_other/y.json").catch((e) => {
905
- caught = e;
906
- });
907
- expect(caught.name).toBe("Forbidden");
908
- expect(isAccessDenied(caught)).toBe(true);
909
- });
910
- it("a non-authorization presign failure stays a generic (fatal) error — not silently skipped", async () => {
911
- const { vault, setPresign } = makeVault();
912
- setPresign([
913
- { key: "secret/x", op: "put", error: "boom", code: "PRESIGN_INTERNAL" },
914
- ]);
915
- const io = new PresignObjectIO(vault, COMPANY);
916
- let caught;
917
- await io
918
- .putObject({ key: "secret/x", body: Buffer.from("z"), contentType: "x" })
919
- .catch((e) => {
920
- caught = e;
921
- });
922
- // Only `*_FORBIDDEN` authorization denials are reclassified; a real backend
923
- // failure must still surface loudly so it is not masked as an expected skip.
924
- expect(isAccessDenied(caught)).toBe(false);
925
- expect(caught.message).toMatch(/denied for secret\/x.*PRESIGN_INTERNAL/);
926
- });
927
- it("throws when the PUT itself fails", async () => {
928
- const { vault, setPresign } = makeVault();
929
- setPresign([{ key: "k", op: "put", url: "https://s3/put" }]);
930
- fetchMock.mockResolvedValue(new Response("AccessDenied", { status: 403 }));
931
- const io = new PresignObjectIO(vault, COMPANY);
932
- await expect(io.putObject({ key: "k", body: Buffer.from("z"), contentType: "x" })).rejects.toThrow(/presigned PUT failed for k: 403/);
933
- });
934
- it("F01: fails closed when a fenced PUT presign omits If-Match", async () => {
935
- const previous = process.env.HQ_PRESIGN_FENCE_STRICT;
936
- process.env.HQ_PRESIGN_FENCE_STRICT = "true";
937
- try {
938
- const { vault, setPresign } = makeVault();
939
- setPresign([
940
- {
941
- key: "stale.md",
942
- op: "put",
943
- url: "https://s3/unfenced-put",
944
- headers: { "content-type": "text/markdown" },
945
- },
946
- ]);
947
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"v2"' } }));
948
- const io = new PresignObjectIO(vault, COMPANY);
949
- await expect(io.putObject({
950
- key: "stale.md",
951
- body: Buffer.from("old bytes"),
952
- contentType: "text/markdown",
953
- ifMatch: '"v1"',
954
- })).rejects.toThrow(/if-match|condition|precondition/i);
955
- expect(fetchMock).not.toHaveBeenCalled();
956
- }
957
- finally {
958
- if (previous === undefined) {
959
- delete process.env.HQ_PRESIGN_FENCE_STRICT;
960
- }
961
- else {
962
- process.env.HQ_PRESIGN_FENCE_STRICT = previous;
963
- }
964
- }
965
- });
966
- it("F01: does not satisfy a fenced PUT with an unfenced primed URL", async () => {
967
- const presignCalls = [];
968
- const vault = {
969
- presign: async (input) => {
970
- presignCalls.push(input);
971
- const isFenced = input.keys.some((k) => k.ifNoneMatch === "*");
972
- return {
973
- results: input.keys.map((k) => ({
974
- key: k.key,
975
- op: k.op ?? input.op ?? "put",
976
- url: isFenced ? "https://s3/fenced-put" : "https://s3/unfenced-put",
977
- headers: isFenced
978
- ? { "content-type": "text/plain", "if-none-match": "*" }
979
- : { "content-type": "text/plain" },
980
- expiresIn: 900,
981
- })),
982
- expiresAt: "2099-01-01T00:00:00.000Z",
983
- };
984
- },
985
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
986
- };
987
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"created"' } }));
988
- const io = new PresignObjectIO(vault, COMPANY);
989
- await io.prime("put", [{ key: "new.md", op: "put", contentType: "text/plain" }]);
990
- const afterPrime = presignCalls.length;
991
- await io.putObject({
992
- key: "new.md",
993
- body: Buffer.from("new bytes"),
994
- contentType: "text/plain",
995
- ifNoneMatch: "*",
996
- });
997
- expect(presignCalls).toHaveLength(afterPrime + 1);
998
- expect(presignCalls[afterPrime].keys[0]).toMatchObject({
999
- key: "new.md",
1000
- op: "put",
1001
- ifNoneMatch: "*",
1002
- });
1003
- const [url, init] = fetchMock.mock.calls[0];
1004
- expect(url).toBe("https://s3/fenced-put");
1005
- expect(init.headers).toMatchObject({
1006
- "content-type": "text/plain",
1007
- "if-none-match": "*",
1008
- });
1009
- expect(init.headers["Content-MD5"]).toBe(createHash("md5").update(Buffer.from("new bytes")).digest("base64"));
1010
- });
1011
- it("forwards the conditional-write fence on the presign request (ifMatch stripped, ifNoneMatch verbatim)", async () => {
1012
- // The server signs If-Match/If-None-Match into the URL (hq-pro follow-up)
1013
- // and echoes the headers for replay; the client's job is to ASK. Servers
1014
- // that predate the fields ignore them — never broken, just unconditional.
1015
- const { vault, presignCalls, setPresign } = makeVault();
1016
- setPresign([{ key: "fenced.md", op: "put", url: "https://s3/put-url" }]);
1017
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"v2"' } }));
1018
- const io = new PresignObjectIO(vault, COMPANY);
1019
- await io.putObject({
1020
- key: "fenced.md",
1021
- body: Buffer.from("x"),
1022
- contentType: "text/markdown",
1023
- ifMatch: '"v1"',
1024
- });
1025
- expect(presignCalls[0].keys[0]).toMatchObject({
1026
- key: "fenced.md",
1027
- op: "put",
1028
- ifMatch: "v1", // quotes stripped on the wire; server re-quotes when signing
1029
- });
1030
- await io.putObject({
1031
- key: "fresh.md",
1032
- body: Buffer.from("y"),
1033
- contentType: "text/markdown",
1034
- ifNoneMatch: "*",
1035
- });
1036
- expect(presignCalls[1].keys[0]).toMatchObject({
1037
- key: "fresh.md",
1038
- op: "put",
1039
- ifNoneMatch: "*",
1040
- });
1041
- });
1042
- it("maps a 412 PUT response to name:PreconditionFailed (the fence fired — conflict, not failure)", async () => {
1043
- const { vault, setPresign } = makeVault();
1044
- setPresign([{ key: "raced.md", op: "put", url: "https://s3/put-url" }]);
1045
- fetchMock.mockResolvedValue(new Response("PreconditionFailed", { status: 412 }));
1046
- const io = new PresignObjectIO(vault, COMPANY);
1047
- await expect(io.putObject({
1048
- key: "raced.md",
1049
- body: Buffer.from("z"),
1050
- contentType: "text/plain",
1051
- ifMatch: "old-etag",
1052
- })).rejects.toMatchObject({ name: "PreconditionFailed" });
1053
- });
1054
- it("PUT presign with hq-content-sha256 sends checksumSha256 as base64 of that hex", async () => {
1055
- const hex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
1056
- const { vault, presignCalls, setPresign } = makeVault();
1057
- setPresign([{ key: "hashed.md", op: "put", url: "https://s3/put" }]);
1058
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"abc"' } }));
1059
- const io = new PresignObjectIO(vault, COMPANY);
1060
- await io.putObject({
1061
- key: "hashed.md",
1062
- body: Buffer.from(""),
1063
- contentType: "text/plain",
1064
- metadata: { "hq-content-sha256": hex },
1065
- });
1066
- expect(presignCalls[0].keys[0]).toMatchObject({
1067
- key: "hashed.md",
1068
- op: "put",
1069
- checksumSha256: Buffer.from(hex, "hex").toString("base64"),
1070
- });
1071
- });
1072
- it("PUT presign without hq-content-sha256 omits checksumSha256", async () => {
1073
- const { vault, presignCalls, setPresign } = makeVault();
1074
- setPresign([{ key: "plain.md", op: "put", url: "https://s3/put" }]);
1075
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"abc"' } }));
1076
- const io = new PresignObjectIO(vault, COMPANY);
1077
- await io.putObject({
1078
- key: "plain.md",
1079
- body: Buffer.from("z"),
1080
- contentType: "text/plain",
1081
- });
1082
- expect(presignCalls[0].keys[0]).toMatchObject({
1083
- key: "plain.md",
1084
- op: "put",
1085
- });
1086
- expect(presignCalls[0].keys[0].checksumSha256).toBeUndefined();
1087
- });
1088
- it("replays a server-echoed x-amz-checksum-sha256 header on the PUT unchanged", async () => {
1089
- const hex = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
1090
- const checksum = Buffer.from(hex, "hex").toString("base64");
1091
- const { vault, setPresign } = makeVault();
1092
- setPresign([
1093
- {
1094
- key: "locked.md",
1095
- op: "put",
1096
- url: "https://s3/put-locked",
1097
- headers: {
1098
- "content-type": "text/plain",
1099
- "x-amz-checksum-sha256": checksum,
1100
- },
1101
- },
1102
- ]);
1103
- fetchMock.mockResolvedValue(new Response(null, { status: 200, headers: { etag: '"abc"' } }));
1104
- const io = new PresignObjectIO(vault, COMPANY);
1105
- await io.putObject({
1106
- key: "locked.md",
1107
- body: Buffer.from(""),
1108
- contentType: "text/plain",
1109
- metadata: { "hq-content-sha256": hex },
1110
- });
1111
- const [, init] = fetchMock.mock.calls[0];
1112
- expect(init.headers).toEqual({
1113
- "content-type": "text/plain",
1114
- "x-amz-checksum-sha256": checksum,
1115
- });
1116
- });
1117
- });
1118
- describe("PresignObjectIO.getObject", () => {
1119
- let fetchMock;
1120
- beforeEach(() => {
1121
- fetchMock = vi.fn();
1122
- vi.stubGlobal("fetch", fetchMock);
1123
- });
1124
- afterEach(() => vi.unstubAllGlobals());
1125
- it("returns body bytes + x-amz-meta-* metadata", async () => {
1126
- const { vault, presignCalls, setPresign } = makeVault();
1127
- setPresign([{ key: "shared/a.md", op: "get", url: "https://s3/get" }]);
1128
- fetchMock.mockResolvedValue(new Response(Buffer.from("file-bytes"), {
1129
- status: 200,
1130
- headers: {
1131
- "x-amz-meta-created-by": "me@getindigo.ai",
1132
- "x-amz-meta-hq-mode": "640",
1133
- "content-type": "text/markdown",
1134
- },
1135
- }));
1136
- const io = new PresignObjectIO(vault, COMPANY);
1137
- const res = await io.getObject("shared/a.md");
1138
- expect(presignCalls[0].op).toBe("get");
1139
- expect(res.body.toString("utf-8")).toBe("file-bytes");
1140
- expect(res.metadata).toEqual({
1141
- "created-by": "me@getindigo.ai",
1142
- "hq-mode": "640",
1143
- });
1144
- });
1145
- it("GET presigns never send checksumSha256", async () => {
1146
- const { vault, presignCalls, setPresign } = makeVault();
1147
- setPresign([{ key: "shared/a.md", op: "get", url: "https://s3/get" }]);
1148
- fetchMock.mockResolvedValue(new Response(Buffer.from("file-bytes"), { status: 200 }));
1149
- const io = new PresignObjectIO(vault, COMPANY);
1150
- await io.getObject("shared/a.md");
1151
- expect(presignCalls[0].op).toBe("get");
1152
- expect(presignCalls[0].keys[0].checksumSha256).toBeUndefined();
1153
- });
1154
- it("drains the injected transport body instead of returning empty content", async () => {
1155
- const { vault, setPresign } = makeVault();
1156
- setPresign([{ key: "shared/a.md", op: "get", url: "https://s3/get" }]);
1157
- const transportGet = vi.fn(async () => ({
1158
- status: 200,
1159
- headers: new Headers({ "x-amz-meta-source": "transport" }),
1160
- body: (async function* () {
1161
- yield Buffer.from("transport-bytes");
1162
- })(),
1163
- destroy: () => undefined,
1164
- }));
1165
- setPresignedGetTransportForTesting({ get: transportGet });
1166
- const io = new PresignObjectIO(vault, COMPANY);
1167
- const res = await io.getObject("shared/a.md");
1168
- expect(res.body.toString("utf-8")).toBe("transport-bytes");
1169
- expect(res.metadata).toEqual({ source: "transport" });
1170
- expect(transportGet).toHaveBeenCalledWith("https://s3/get", undefined);
1171
- });
1172
- it("throws a NotFound-named error on 404 (so s3.ts catch sites match)", async () => {
1173
- const { vault, setPresign } = makeVault();
1174
- setPresign([{ key: "gone", op: "get", url: "https://s3/get" }]);
1175
- fetchMock.mockResolvedValue(new Response("", { status: 404 }));
1176
- const io = new PresignObjectIO(vault, COMPANY);
1177
- await expect(io.getObject("gone")).rejects.toMatchObject({
1178
- name: "NotFound",
1179
- });
1180
- });
1181
- it("throws a NotFound-named error on FILES_PRESIGN_NOT_FOUND (absence at mint, no GET issued)", async () => {
1182
- // Per-key GET presign used to throw a generic "presign get denied …
1183
- // Object not found (FILES_PRESIGN_NOT_FOUND)" Error. That is NOT
1184
- // isAccessDenied and was NOT isNotFound, so sync treated it as a hard
1185
- // per-file error and settled the company `partial`. Absence at mint is
1186
- // the same shape as a presigned GET 404.
1187
- const { vault, setPresign } = makeVault();
1188
- setPresign([
1189
- {
1190
- key: "projects/runner/state/mac-google-check-marketer-2.json",
1191
- op: "get",
1192
- error: "Object not found: 'projects/runner/state/mac-google-check-marketer-2.json'",
1193
- code: "FILES_PRESIGN_NOT_FOUND",
1194
- },
1195
- ]);
1196
- const io = new PresignObjectIO(vault, COMPANY);
1197
- let caught;
1198
- await io
1199
- .getObject("projects/runner/state/mac-google-check-marketer-2.json")
1200
- .catch((e) => {
1201
- caught = e;
1202
- });
1203
- expect(caught.name).toBe("NotFound");
1204
- expect(isNotFound(caught)).toBe(true);
1205
- expect(isAccessDenied(caught)).toBe(false);
1206
- expect(fetchMock).not.toHaveBeenCalled();
1207
- });
1208
- });
1209
- describe("PresignObjectIO.listObjects", () => {
1210
- it("maps listFiles rows into the RemoteObject shape (etag, Date, storage class)", async () => {
1211
- const { vault, setList } = makeVault();
1212
- setList({
1213
- objects: [
1214
- {
1215
- key: "shared/a.md",
1216
- size: 12,
1217
- lastModified: "2026-01-01T00:00:00.000Z",
1218
- storageClass: "DEEP_ARCHIVE",
1219
- etag: "deadbeef",
1220
- permission: "read",
1221
- },
1222
- {
1223
- key: "shared/b.md",
1224
- size: 0,
1225
- lastModified: null,
1226
- etag: null,
1227
- permission: "write",
1228
- },
1229
- ],
1230
- cursor: "next-token",
1231
- truncated: true,
1232
- });
1233
- const io = new PresignObjectIO(vault, COMPANY);
1234
- const res = await io.listObjects({ prefix: "shared/" });
1235
- expect(res.nextContinuationToken).toBe("next-token");
1236
- expect(res.objects[0]).toEqual({
1237
- key: "shared/a.md",
1238
- size: 12,
1239
- lastModified: new Date("2026-01-01T00:00:00.000Z"),
1240
- etag: "deadbeef",
1241
- storageClass: "DEEP_ARCHIVE",
1242
- });
1243
- // Omitted storageClass from an older hq-pro list server defaults Standard;
1244
- // null etag → "" ; null lastModified → a Date (not crash).
1245
- expect(res.objects[1].etag).toBe("");
1246
- expect(res.objects[1].lastModified).toBeInstanceOf(Date);
1247
- expect(res.objects[1].storageClass).toBe("STANDARD");
1248
- });
1249
- it("exhausted listing yields undefined nextContinuationToken", async () => {
1250
- const { vault, setList } = makeVault();
1251
- setList({ objects: [], cursor: null, truncated: false });
1252
- const io = new PresignObjectIO(vault, COMPANY);
1253
- const res = await io.listObjects({});
1254
- expect(res.nextContinuationToken).toBeUndefined();
1255
- });
1256
- it("lists the safe parent and filters sibling keys for a forbidden prefix", async () => {
1257
- const prefix = "a/api/issues/[id]/";
1258
- const { vault, calls } = makeForbiddenPrefixVault(new Map([
1259
- [undefined, {
1260
- objects: [
1261
- vaultObject("a/api/issues/[id]/route.ts"),
1262
- vaultObject("a/api/issues/other.js"),
1263
- ],
1264
- cursor: null,
1265
- truncated: false,
1266
- }],
1267
- ]));
1268
- const io = new PresignObjectIO(vault, COMPANY);
1269
- const res = await io.listObjects({ prefix });
1270
- expect(calls).toEqual([
1271
- { companyUid: COMPANY, prefix: "a/api/issues/", cursor: undefined },
1272
- ]);
1273
- expect(res.objects.map(({ key }) => key)).toEqual(["a/api/issues/[id]/route.ts"]);
1274
- });
1275
- it("preserves ancestor cursors across empty filtered pages", async () => {
1276
- const prefix = "a/api/issues/[id]/";
1277
- const { vault, calls } = makeForbiddenPrefixVault(new Map([
1278
- ["incoming-cursor", {
1279
- objects: [vaultObject("a/api/issues/other.js")],
1280
- cursor: "page-two-cursor",
1281
- truncated: true,
1282
- }],
1283
- ["page-two-cursor", {
1284
- objects: [
1285
- vaultObject("a/api/issues/[id]/route.ts"),
1286
- vaultObject("a/api/issues/another.js"),
1287
- ],
1288
- cursor: null,
1289
- truncated: false,
1290
- }],
1291
- ]));
1292
- const io = new PresignObjectIO(vault, COMPANY);
1293
- const first = await io.listObjects({ prefix, continuationToken: "incoming-cursor" });
1294
- expect(first.objects).toEqual([]);
1295
- expect(first.nextContinuationToken).toBe("page-two-cursor");
1296
- const second = await io.listObjects({
1297
- prefix,
1298
- continuationToken: first.nextContinuationToken,
1299
- });
1300
- expect(second.objects.map(({ key }) => key)).toEqual(["a/api/issues/[id]/route.ts"]);
1301
- expect(calls).toEqual([
1302
- { companyUid: COMPANY, prefix: "a/api/issues/", cursor: "incoming-cursor" },
1303
- { companyUid: COMPANY, prefix: "a/api/issues/", cursor: "page-two-cursor" },
1304
- ]);
1305
- });
1306
- it("leaves a normal prefix and its result page unchanged", async () => {
1307
- const { vault, calls } = makeForbiddenPrefixVault(new Map([
1308
- [undefined, {
1309
- objects: [vaultObject("a/b/file.txt"), vaultObject("outside/file.txt")],
1310
- cursor: null,
1311
- truncated: false,
1312
- }],
1313
- ]));
1314
- const io = new PresignObjectIO(vault, COMPANY);
1315
- const res = await io.listObjects({ prefix: "a/b/" });
1316
- expect(calls).toEqual([
1317
- { companyUid: COMPANY, prefix: "a/b/", cursor: undefined },
1318
- ]);
1319
- expect(res.objects.map(({ key }) => key)).toEqual([
1320
- "a/b/file.txt",
1321
- "outside/file.txt",
1322
- ]);
1323
- });
1324
- it("returns an empty ancestor when a bracket occurs in the first segment", () => {
1325
- expect(safeVaultListPrefixAncestor("[id]/route.ts")).toBe("");
1326
- });
1327
- it.each([
1328
- ["?", "a/api/issues?state=open", "a/api/"],
1329
- ["#", "a/api/issues#fragment", "a/api/"],
1330
- ["\\", "a/api\\issues/route.ts", "a/"],
1331
- ["]", "a/api/issues]/route.ts", "a/api/"],
1332
- ])("computes the ancestor before %s", (_character, prefix, expected) => {
1333
- expect(safeVaultListPrefixAncestor(prefix)).toBe(expected);
1334
- });
1335
- it("returns a prefix without forbidden characters unchanged", () => {
1336
- expect(safeVaultListPrefixAncestor("a/b/")).toBe("a/b/");
1337
- });
1338
- });
1339
- describe("PresignObjectIO.deleteObject", () => {
1340
- let fetchMock;
1341
- beforeEach(() => {
1342
- fetchMock = vi.fn();
1343
- vi.stubGlobal("fetch", fetchMock);
1344
- });
1345
- afterEach(() => vi.unstubAllGlobals());
1346
- it("sends op:delete and DELETEs the signed URL", async () => {
1347
- const { vault, presignCalls, setPresign } = makeVault();
1348
- setPresign([{ key: "shared/a.md", op: "delete", url: "https://s3/del" }]);
1349
- fetchMock.mockResolvedValue(new Response(null, { status: 204 }));
1350
- const io = new PresignObjectIO(vault, COMPANY);
1351
- await io.deleteObject("shared/a.md");
1352
- expect(presignCalls[0].op).toBe("delete");
1353
- expect(fetchMock.mock.calls[0][1].method).toBe("DELETE");
1354
- });
1355
- it("treats a 404 DELETE as success (idempotent)", async () => {
1356
- const { vault, setPresign } = makeVault();
1357
- setPresign([{ key: "gone", op: "delete", url: "https://s3/del" }]);
1358
- fetchMock.mockResolvedValue(new Response("", { status: 404 }));
1359
- const io = new PresignObjectIO(vault, COMPANY);
1360
- await expect(io.deleteObject("gone")).resolves.toBeUndefined();
1361
- });
1362
- });
1363
- describe("PresignObjectIO.headObject", () => {
1364
- let fetchMock;
1365
- beforeEach(() => {
1366
- fetchMock = vi.fn();
1367
- vi.stubGlobal("fetch", fetchMock);
1368
- });
1369
- afterEach(() => {
1370
- vi.unstubAllGlobals();
1371
- vi.restoreAllMocks();
1372
- });
1373
- it("reads headers (etag/size/lastModified/metadata) via a presigned GET", async () => {
1374
- const { vault, setPresign } = makeVault();
1375
- setPresign([{ key: "shared/a.md", op: "get", url: "https://s3/get" }]);
1376
- fetchMock.mockResolvedValue(new Response(Buffer.from("ignored-body"), {
1377
- status: 200,
1378
- headers: {
1379
- etag: '"feedface"',
1380
- "content-length": "123",
1381
- "last-modified": "Wed, 01 Jan 2026 00:00:00 GMT",
1382
- "x-amz-meta-created-at": "2026-01-01T00:00:00.000Z",
1383
- },
1384
- }));
1385
- const io = new PresignObjectIO(vault, COMPANY);
1386
- const head = await io.headObject("shared/a.md");
1387
- expect(head).not.toBeNull();
1388
- expect(head.etag).toBe("feedface");
1389
- expect(head.size).toBe(123);
1390
- expect(head.metadata).toEqual({
1391
- "created-at": "2026-01-01T00:00:00.000Z",
1392
- });
1393
- expect(head.lastModified.getTime()).toBe(Date.parse("Wed, 01 Jan 2026 00:00:00 GMT"));
1394
- });
1395
- it("returns null on 404", async () => {
1396
- const { vault, setPresign } = makeVault();
1397
- setPresign([{ key: "gone", op: "get", url: "https://s3/get" }]);
1398
- fetchMock.mockResolvedValue(new Response("", { status: 404 }));
1399
- const io = new PresignObjectIO(vault, COMPANY);
1400
- expect(await io.headObject("gone")).toBeNull();
1401
- });
1402
- it("returns null on a server-confirmed FILES_PRESIGN_NOT_FOUND (absence, not denial — no GET issued)", async () => {
1403
- // The presign lambda HEAD'd S3 and confirmed absence. Unlike a read
1404
- // DENIAL (Forbidden, below), this is unambiguous "object missing", so
1405
- // headObject reports absent (null) and the push creates the new object —
1406
- // never reaching a 403-masked presigned GET.
1407
- const { vault, setPresign } = makeVault();
1408
- setPresign([
1409
- {
1410
- key: "new.md",
1411
- op: "get",
1412
- error: "Object not found: 'new.md'",
1413
- code: "FILES_PRESIGN_NOT_FOUND",
1414
- },
1415
- ]);
1416
- const io = new PresignObjectIO(vault, COMPANY);
1417
- expect(await io.headObject("new.md")).toBeNull();
1418
- expect(fetchMock).not.toHaveBeenCalled();
1419
- });
1420
- it("reuses primed absence and refusal rows without confusing GET with PUT", async () => {
1421
- vi.spyOn(console, "warn").mockImplementation(() => undefined);
1422
- const calls = [];
1423
- let uploaded = false;
1424
- const vault = {
1425
- presign: async (input) => {
1426
- calls.push({ op: input.op, keys: input.keys.map((item) => item.key) });
1427
- return {
1428
- results: input.keys.map((item) => {
1429
- if (input.op === "put") {
1430
- return { key: item.key, op: "put", url: "https://s3/put" };
1431
- }
1432
- if (item.key === "missing.md" && uploaded) {
1433
- return { key: item.key, op: "get", url: "https://s3/get" };
1434
- }
1435
- return item.key === "missing.md"
1436
- ? {
1437
- key: item.key,
1438
- op: "get",
1439
- error: "Object not found",
1440
- code: "FILES_PRESIGN_NOT_FOUND",
1441
- }
1442
- : {
1443
- key: item.key,
1444
- op: "get",
1445
- error: "permission refused",
1446
- code: "FILES_PRESIGN_FORBIDDEN",
1447
- };
1448
- }),
1449
- expiresAt: "2099-01-01T00:00:00.000Z",
1450
- };
1451
- },
1452
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1453
- };
1454
- fetchMock.mockImplementation(async (_url, init) => {
1455
- if (init?.method === "PUT")
1456
- uploaded = true;
1457
- return new Response(null, { status: 200, headers: { etag: '"created"' } });
1458
- });
1459
- const io = new PresignObjectIO(vault, COMPANY);
1460
- await io.prime("get", [{ key: "missing.md" }, { key: "denied.md" }]);
1461
- expect(await io.headObject("missing.md")).toBeNull();
1462
- await expect(io.headObject("denied.md")).rejects.toMatchObject({ name: "Forbidden" });
1463
- await expect(io.headObject("denied.md")).rejects.toMatchObject({ name: "Forbidden" });
1464
- await io.prime("put", [{ key: "missing.md" }]);
1465
- await io.putObject({ key: "missing.md", body: Buffer.from("new"), contentType: "text/plain" });
1466
- expect(await io.headObject("missing.md")).not.toBeNull();
1467
- expect(calls).toEqual([
1468
- { op: "get", keys: ["missing.md", "denied.md"] },
1469
- { op: "put", keys: ["missing.md"] },
1470
- { op: "get", keys: ["missing.md"] },
1471
- ]);
1472
- expect(fetchMock).toHaveBeenCalledTimes(2);
1473
- });
1474
- it("throws Forbidden when presign denies the key — denial is NOT absence", async () => {
1475
- // Regression: pre-fix this returned null ("absent"), which made
1476
- // share.ts's push guard (`if (remoteMeta)`) skip every conflict check
1477
- // and issue an unconditional PUT — a transient denial mid-pass silently
1478
- // clobbered newer remote bytes (2026-06-10..12 vault regression storm).
1479
- const { vault, setPresign } = makeVault();
1480
- setPresign([{ key: "secret/x", op: "get", error: "forbidden" }]);
1481
- const io = new PresignObjectIO(vault, COMPANY);
1482
- await expect(io.headObject("secret/x")).rejects.toMatchObject({
1483
- name: "Forbidden",
1484
- });
1485
- expect(fetchMock).not.toHaveBeenCalled();
1486
- });
1487
- it("throws Forbidden on a presigned-GET 403 — same SDK-parity contract", async () => {
1488
- // Expired presigned URL, expired signing creds, KMS or bucket-policy
1489
- // denial all surface as 403 on the signed GET. Unknown state — must
1490
- // never read as "object missing".
1491
- const { vault, setPresign } = makeVault();
1492
- setPresign([{ key: "shared/a.md", op: "get", url: "https://s3/get" }]);
1493
- fetchMock.mockResolvedValue(new Response("AccessDenied", { status: 403 }));
1494
- const io = new PresignObjectIO(vault, COMPANY);
1495
- await expect(io.headObject("shared/a.md")).rejects.toMatchObject({
1496
- name: "Forbidden",
1497
- });
1498
- });
1499
- it("still returns null on a definitive 404 (key truly absent)", async () => {
1500
- const { vault, setPresign } = makeVault();
1501
- setPresign([{ key: "gone2", op: "get", url: "https://s3/get" }]);
1502
- fetchMock.mockResolvedValue(new Response("", { status: 404 }));
1503
- const io = new PresignObjectIO(vault, COMPANY);
1504
- expect(await io.headObject("gone2")).toBeNull();
1505
- });
1506
- });
1507
- describe("PresignObjectIO.prime — refusal caching", () => {
1508
- afterEach(() => vi.restoreAllMocks());
1509
- it("uses only batch calls for 8,000 refused PUT keys and retries with a new instance", async () => {
1510
- vi.spyOn(console, "warn").mockImplementation(() => undefined);
1511
- const batchCalls = [];
1512
- let singleKeyCalls = 0;
1513
- const vault = {
1514
- presign: async (input) => {
1515
- if (input.expiresIn === undefined)
1516
- singleKeyCalls++;
1517
- else
1518
- batchCalls.push({ op: input.op, count: input.keys.length });
1519
- return {
1520
- results: input.keys.map((item) => input.op === "get"
1521
- ? {
1522
- key: item.key,
1523
- op: "get",
1524
- error: "Object not found",
1525
- code: "FILES_PRESIGN_NOT_FOUND",
1526
- }
1527
- : {
1528
- key: item.key,
1529
- op: "put",
1530
- error: "forbidden",
1531
- code: "FILES_PRESIGN_FORBIDDEN",
1532
- }),
1533
- expiresAt: "2099-01-01T00:00:00.000Z",
1534
- };
1535
- },
1536
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1537
- };
1538
- const keys = Array.from({ length: 8_000 }, (_, index) => ({ key: `write/${index}` }));
1539
- const firstRun = new PresignObjectIO(vault, COMPANY);
1540
- await firstRun.prime("get", keys);
1541
- await firstRun.prime("put", keys);
1542
- for (const { key } of keys) {
1543
- expect(await firstRun.headObject(key)).toBeNull();
1544
- expect(await firstRun.headObject(key)).toBeNull();
1545
- expect(await firstRun.headObject(key)).toBeNull();
1546
- await expect(firstRun.putObject({ key, body: Buffer.from("x"), contentType: "text/plain" }))
1547
- .rejects.toMatchObject({ name: "Forbidden" });
1548
- }
1549
- expect(batchCalls).toEqual([
1550
- ...Array.from({ length: 8 }, () => ({ op: "get", count: 1_000 })),
1551
- ...Array.from({ length: 8 }, () => ({ op: "put", count: 1_000 })),
1552
- ]);
1553
- expect(singleKeyCalls).toBe(0);
1554
- const secondRun = new PresignObjectIO(vault, COMPANY);
1555
- await expect(secondRun.putObject({
1556
- key: "write/0",
1557
- body: Buffer.from("x"),
1558
- contentType: "text/plain",
1559
- })).rejects.toMatchObject({ name: "Forbidden" });
1560
- expect(singleKeyCalls).toBe(1);
1561
- });
1562
- it("uses only batch calls for 13,000 refused GET keys", async () => {
1563
- vi.spyOn(console, "warn").mockImplementation(() => undefined);
1564
- const batchCalls = [];
1565
- let singleKeyCalls = 0;
1566
- const vault = {
1567
- presign: async (input) => {
1568
- if (input.expiresIn === undefined)
1569
- singleKeyCalls++;
1570
- else
1571
- batchCalls.push({ op: input.op, count: input.keys.length });
1572
- return {
1573
- results: input.keys.map((item) => ({
1574
- key: item.key,
1575
- op: "get",
1576
- error: "permission refused",
1577
- code: "FILES_PRESIGN_FORBIDDEN",
1578
- })),
1579
- expiresAt: "2099-01-01T00:00:00.000Z",
1580
- };
1581
- },
1582
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1583
- };
1584
- const keys = Array.from({ length: 13_000 }, (_, index) => ({ key: `journal/${index}` }));
1585
- const io = new PresignObjectIO(vault, COMPANY);
1586
- await io.prime("get", keys);
1587
- for (const { key } of keys) {
1588
- await expect(io.headObject(key)).rejects.toMatchObject({ name: "Forbidden" });
1589
- }
1590
- expect(batchCalls).toEqual(Array.from({ length: 13 }, () => ({ op: "get", count: 1_000 })));
1591
- expect(singleKeyCalls).toBe(0);
1592
- });
1593
- });
1594
- /**
1595
- * Echo vault: presign returns one signed row per requested key (url derived
1596
- * from op+key) and records call count + total keys, so prime/cache tests can
1597
- * assert "N files → ceil(N/100) presign calls, then zero".
1598
- */
1599
- function makeEchoVault() {
1600
- let calls = 0;
1601
- let totalKeys = 0;
1602
- const vault = {
1603
- presign: async (input) => {
1604
- calls++;
1605
- totalKeys += input.keys.length;
1606
- return {
1607
- results: input.keys.map((k) => ({
1608
- key: k.key,
1609
- op: k.op ?? input.op ?? "get",
1610
- url: `https://signed/${k.op ?? input.op}/${encodeURIComponent(k.key)}`,
1611
- ...(k.op === "put" || input.op === "put"
1612
- ? { headers: { "content-type": "text/plain" } }
1613
- : {}),
1614
- expiresIn: input.expiresIn ?? 900,
1615
- })),
1616
- expiresAt: "2099-01-01T00:00:00.000Z",
1617
- };
1618
- },
1619
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1620
- };
1621
- return {
1622
- vault,
1623
- calls: () => calls,
1624
- totalKeys: () => totalKeys,
1625
- };
1626
- }
1627
- describe("PresignObjectIO.prime — plan-limit refusals", () => {
1628
- function refusingVault(mode) {
1629
- const presignKeys = [];
1630
- const vault = {
1631
- presign: async (input) => {
1632
- presignKeys.push(input.keys.map((k) => k.key));
1633
- if (mode === "batch-402") {
1634
- throw vaultErrorFromResponse(402, JSON.stringify(canonicalPlanLimitBody()));
1635
- }
1636
- return {
1637
- results: input.keys.map((k) => companyPresignRefusalRow(k.key)),
1638
- expiresAt: "2099-01-01T00:00:00.000Z",
1639
- };
1640
- },
1641
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1642
- };
1643
- return { vault, presignKeys };
1644
- }
1645
- for (const mode of ["batch-402", "per-item"]) {
1646
- it(`reports a primed ${mode} refusal on upload without presigning the key again`, async () => {
1647
- const { vault, presignKeys } = refusingVault(mode);
1648
- const io = new PresignObjectIO(vault, COMPANY);
1649
- await io.prime("put", [{ key: "a.md" }, { key: "b.md" }]);
1650
- expect(presignKeys).toEqual([["a.md", "b.md"]]);
1651
- for (const key of ["a.md", "b.md"]) {
1652
- await expect(io.putObject({ key, body: Buffer.from("x"), contentType: "text/markdown" })).rejects.toMatchObject({
1653
- statusCode: 402,
1654
- path: key,
1655
- upgradeUrl: PLAN_LIMIT_UPGRADE_URL,
1656
- });
1657
- }
1658
- // One batch presign for both keys; the uploads never presigned again.
1659
- expect(presignKeys).toEqual([["a.md", "b.md"]]);
1660
- // The cached refusal is consumed once: a later upload asks the server.
1661
- await expect(io.putObject({ key: "a.md", body: Buffer.from("x"), contentType: "text/markdown" })).rejects.toMatchObject({ statusCode: 402 });
1662
- expect(presignKeys).toEqual([["a.md", "b.md"], ["a.md"]]);
1663
- });
1664
- }
1665
- it("does not cache refusals for a refused GET prime", async () => {
1666
- const { vault, presignKeys } = refusingVault("batch-402");
1667
- const io = new PresignObjectIO(vault, COMPANY);
1668
- await io.prime("get", [{ key: "a.md" }]);
1669
- await expect(io.putObject({ key: "a.md", body: Buffer.from("x"), contentType: "text/markdown" })).rejects.toMatchObject({ statusCode: 402 });
1670
- expect(presignKeys).toEqual([["a.md"], ["a.md"]]);
1671
- });
1672
- });
1673
- describe("PresignObjectIO.checkFilesAdmission", () => {
1674
- afterEach(() => setFilesAdmissionTimeoutMsForTesting(undefined));
1675
- function admissionVault(answer) {
1676
- const calls = [];
1677
- const vault = {
1678
- presign: async () => ({ results: [], expiresAt: "2099-01-01T00:00:00.000Z" }),
1679
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1680
- checkFilesAdmission: async (companyUid) => {
1681
- calls.push(companyUid);
1682
- return answer();
1683
- },
1684
- };
1685
- return { vault, calls };
1686
- }
1687
- it("reports allowed on 200 {allowed:true}", async () => {
1688
- const { vault, calls } = admissionVault(async () => admissionAllowedBody());
1689
- await expect(new PresignObjectIO(vault, "cmp_acme").checkFilesAdmission()).resolves.toEqual({
1690
- state: "allowed",
1691
- });
1692
- expect(calls).toEqual(["cmp_acme"]);
1693
- });
1694
- it("reports refused with the fresh refusal on a 402 plan-limit body", async () => {
1695
- const { vault } = admissionVault(async () => {
1696
- throw vaultErrorFromResponse(402, JSON.stringify(canonicalPlanLimitBody()));
1697
- });
1698
- const outcome = await new PresignObjectIO(vault, "cmp_acme").checkFilesAdmission();
1699
- expect(outcome).toMatchObject({
1700
- state: "refused",
1701
- refusal: { upgradeUrl: PLAN_LIMIT_UPGRADE_URL, blocked: "files.create" },
1702
- });
1703
- });
1704
- it.each([
1705
- ["400 from a server without the admission check", 400, admissionUnsupportedBody()],
1706
- ["403", 403, { message: "Forbidden" }],
1707
- ["404", 404, { message: "Not found" }],
1708
- ["503", 503, { message: "unavailable" }],
1709
- ])("reports unknown for %s", async (_label, status, body) => {
1710
- const { vault } = admissionVault(async () => {
1711
- throw vaultErrorFromResponse(status, JSON.stringify(body));
1712
- });
1713
- await expect(new PresignObjectIO(vault, "cmp_acme").checkFilesAdmission()).resolves.toEqual({
1714
- state: "unknown",
1715
- reason: `http-${status}`,
1716
- });
1717
- });
1718
- it("reports unknown on a network error and on a timeout", async () => {
1719
- const failing = admissionVault(async () => {
1720
- throw new TypeError("fetch failed");
1721
- });
1722
- await expect(new PresignObjectIO(failing.vault, "cmp_acme").checkFilesAdmission()).resolves.toEqual({
1723
- state: "unknown",
1724
- reason: "request-failed",
1725
- });
1726
- setFilesAdmissionTimeoutMsForTesting(20);
1727
- const hanging = admissionVault(() => new Promise(() => { }));
1728
- await expect(new PresignObjectIO(hanging.vault, "cmp_acme").checkFilesAdmission()).resolves.toEqual({
1729
- state: "unknown",
1730
- reason: "timeout",
1731
- });
1732
- });
1733
- it("never asks for a non-company vault or a client without the check", async () => {
1734
- const { vault, calls } = admissionVault(async () => ({ allowed: true }));
1735
- await expect(new PresignObjectIO(vault, "prs_me").checkFilesAdmission()).resolves.toMatchObject({
1736
- state: "unknown",
1737
- });
1738
- expect(calls).toEqual([]);
1739
- const { checkFilesAdmission: _drop, ...withoutCheck } = vault;
1740
- await expect(new PresignObjectIO(withoutCheck, "cmp_acme").checkFilesAdmission()).resolves.toMatchObject({ state: "unknown" });
1741
- });
1742
- });
1743
- describe("PresignObjectIO.prime — batch URL cache", () => {
1744
- let fetchMock;
1745
- beforeEach(() => {
1746
- fetchMock = vi.fn(async () => new Response(Buffer.from("x"), { status: 200 }));
1747
- vi.stubGlobal("fetch", fetchMock);
1748
- });
1749
- afterEach(() => vi.unstubAllGlobals());
1750
- it("chunks at 1000 keys per presign call (the server batch cap)", async () => {
1751
- const { vault, calls, totalKeys } = makeEchoVault();
1752
- const io = new PresignObjectIO(vault, COMPANY);
1753
- const keys = Array.from({ length: 2500 }, (_, i) => ({ key: `k${i}` }));
1754
- await io.prime("get", keys);
1755
- expect(calls()).toBe(3); // 1000 + 1000 + 500
1756
- expect(totalKeys()).toBe(2500);
1757
- });
1758
- it("primed getObject reuses the cache — zero extra presign calls", async () => {
1759
- const { vault, calls } = makeEchoVault();
1760
- const io = new PresignObjectIO(vault, COMPANY);
1761
- const keys = Array.from({ length: 5 }, (_, i) => ({ key: `f${i}` }));
1762
- await io.prime("get", keys);
1763
- const afterPrime = calls(); // 1 chunk
1764
- for (const k of keys)
1765
- await io.getObject(k.key);
1766
- expect(calls()).toBe(afterPrime); // no new presign calls
1767
- expect(fetchMock).toHaveBeenCalledTimes(5); // but the bytes were fetched
1768
- // The fetched URL is the primed (cached) one.
1769
- expect(fetchMock.mock.calls[0][0]).toBe("https://signed/get/f0");
1770
- });
1771
- it("F21: consumes primed GET URLs instead of retaining them", async () => {
1772
- const { vault, calls } = makeEchoVault();
1773
- const io = new PresignObjectIO(vault, COMPANY);
1774
- await io.prime("get", [{ key: "single" }]);
1775
- const afterPrime = calls();
1776
- await io.getObject("single");
1777
- expect(calls()).toBe(afterPrime);
1778
- await io.getObject("single");
1779
- expect(calls()).toBe(afterPrime + 1);
1780
- expect(fetchMock).toHaveBeenCalledTimes(2);
1781
- });
1782
- it("R-F21: reuses a primed GET for later HEAD and clears stale primed PUT on fenced PUT", async () => {
1783
- fetchMock.mockImplementation(async () => (new Response(Buffer.from("body"), {
1784
- status: 200,
1785
- headers: {
1786
- etag: '"etag-1"',
1787
- "content-length": "4",
1788
- "last-modified": "Wed, 01 Jan 2026 00:00:00 GMT",
1789
- },
1790
- })));
1791
- const { vault, calls } = makeEchoVault();
1792
- const io = new PresignObjectIO(vault, COMPANY);
1793
- await io.prime("get", [{ key: "doc.md" }]);
1794
- const afterGetPrime = calls();
1795
- const got = await io.getObject("doc.md");
1796
- expect(got.body.toString("utf-8")).toBe("body");
1797
- const head = await io.headObject("doc.md");
1798
- expect(head?.etag).toBe("etag-1");
1799
- expect(calls()).toBe(afterGetPrime);
1800
- expect(fetchMock.mock.calls[0][0]).toBe("https://signed/get/doc.md");
1801
- expect(fetchMock.mock.calls[1][0]).toBe("https://signed/get/doc.md");
1802
- await io.prime("put", [
1803
- { key: "stale.md", op: "put", contentType: "text/plain" },
1804
- ]);
1805
- expect(io.hasPrimedPut("stale.md")).toBe(true);
1806
- const afterPutPrime = calls();
1807
- await io.putObject({
1808
- key: "stale.md",
1809
- body: Buffer.from("new body"),
1810
- contentType: "text/plain",
1811
- ifMatch: "old-etag",
1812
- });
1813
- expect(calls()).toBe(afterPutPrime + 1);
1814
- expect(io.hasPrimedPut("stale.md")).toBe(false);
1815
- });
1816
- it("primed GET cache also serves headObject (HEAD reuses GET URLs)", async () => {
1817
- const { vault, calls } = makeEchoVault();
1818
- fetchMock.mockResolvedValue(new Response(Buffer.from(""), {
1819
- status: 200,
1820
- headers: { etag: '"e"', "content-length": "0" },
1821
- }));
1822
- const io = new PresignObjectIO(vault, COMPANY);
1823
- await io.prime("get", [{ key: "doc.md" }]);
1824
- const afterPrime = calls();
1825
- const head = await io.headObject("doc.md");
1826
- expect(head).not.toBeNull();
1827
- expect(calls()).toBe(afterPrime); // head used the cached GET url, no presign
1828
- });
1829
- it("a key NOT primed falls back to a single presign", async () => {
1830
- const { vault, calls } = makeEchoVault();
1831
- const io = new PresignObjectIO(vault, COMPANY);
1832
- await io.prime("get", [{ key: "primed" }]);
1833
- const afterPrime = calls();
1834
- await io.getObject("not-primed");
1835
- expect(calls()).toBe(afterPrime + 1);
1836
- });
1837
- it("prime(put) sends checksumSha256 per key from hq-content-sha256 metadata", async () => {
1838
- const hexA = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
1839
- const hexB = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
1840
- const { vault, presignCalls } = makeVault();
1841
- const io = new PresignObjectIO(vault, COMPANY);
1842
- await io.prime("put", [
1843
- {
1844
- key: "a.md",
1845
- op: "put",
1846
- contentType: "text/plain",
1847
- metadata: { "hq-content-sha256": hexA },
1848
- },
1849
- {
1850
- key: "b.md",
1851
- op: "put",
1852
- contentType: "text/plain",
1853
- metadata: { "hq-content-sha256": hexB },
1854
- },
1855
- { key: "c.md", op: "put", contentType: "text/plain" },
1856
- ]);
1857
- expect(presignCalls).toHaveLength(1);
1858
- expect(presignCalls[0].op).toBe("put");
1859
- const keys = presignCalls[0].keys;
1860
- expect(keys[0]).toMatchObject({
1861
- key: "a.md",
1862
- checksumSha256: Buffer.from(hexA, "hex").toString("base64"),
1863
- });
1864
- expect(keys[1]).toMatchObject({
1865
- key: "b.md",
1866
- checksumSha256: Buffer.from(hexB, "hex").toString("base64"),
1867
- });
1868
- expect(keys[2].key).toBe("c.md");
1869
- expect(keys[2].checksumSha256).toBeUndefined();
1870
- });
1871
- it("empty prime is a no-op (no presign call)", async () => {
1872
- const { vault, calls } = makeEchoVault();
1873
- const io = new PresignObjectIO(vault, COMPANY);
1874
- await io.prime("get", []);
1875
- expect(calls()).toBe(0);
1876
- });
1877
- it("a chunk that throws doesn't fail prime; those keys fall back per-file", async () => {
1878
- let calls = 0;
1879
- const vault = {
1880
- presign: async (input) => {
1881
- calls++;
1882
- if (calls === 1)
1883
- throw new Error("transient");
1884
- return {
1885
- results: input.keys.map((k) => ({
1886
- key: k.key,
1887
- op: "get",
1888
- url: `https://signed/get/${k.key}`,
1889
- expiresIn: 900,
1890
- })),
1891
- expiresAt: "2099-01-01T00:00:00.000Z",
1892
- };
1893
- },
1894
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1895
- };
1896
- const io = new PresignObjectIO(vault, COMPANY);
1897
- // Single chunk that throws — prime resolves anyway, key stays uncached.
1898
- await expect(io.prime("get", [{ key: "k" }])).resolves.toBeUndefined();
1899
- // getObject then single-presigns (call #2 succeeds).
1900
- await io.getObject("k");
1901
- expect(calls).toBe(2);
1902
- });
1903
- });
1904
- describe("presignObjectIOFactory — memoization", () => {
1905
- afterEach(() => setObjectIOFactory(null));
1906
- it("returns the SAME instance per companyUid so prime + transfer share a cache", () => {
1907
- const { vault } = makeEchoVault();
1908
- const factory = presignObjectIOFactory(vault);
1909
- const a1 = factory(ctx());
1910
- const a2 = factory(ctx());
1911
- expect(a1).toBe(a2); // same company → same instance
1912
- const other = factory({ ...ctx(), uid: "cmp_other" });
1913
- expect(other).not.toBe(a1); // different company → different instance
1914
- });
1915
- });
1916
- describe("PresignObjectIO — transient-failure retry (SDK parity)", () => {
1917
- afterEach(() => {
1918
- vi.useRealTimers();
1919
- vi.unstubAllGlobals();
1920
- });
1921
- it("retries a transient 503 then succeeds", async () => {
1922
- vi.useFakeTimers();
1923
- const { vault, setPresign } = makeVault();
1924
- setPresign([{ key: "k", op: "get", url: "https://s3/get" }]);
1925
- const fetchMock = vi
1926
- .fn()
1927
- .mockResolvedValueOnce(new Response("SlowDown", { status: 503 }))
1928
- .mockResolvedValueOnce(new Response(Buffer.from("ok"), { status: 200 }));
1929
- vi.stubGlobal("fetch", fetchMock);
1930
- const io = new PresignObjectIO(vault, COMPANY);
1931
- const pr = io.getObject("k");
1932
- await vi.runAllTimersAsync();
1933
- const res = await pr;
1934
- expect(res.body.toString("utf-8")).toBe("ok");
1935
- expect(fetchMock).toHaveBeenCalledTimes(2);
1936
- });
1937
- it("retries a network error (fetch throws) then succeeds", async () => {
1938
- vi.useFakeTimers();
1939
- const { vault, setPresign } = makeVault();
1940
- setPresign([{ key: "k", op: "get", url: "https://s3/get" }]);
1941
- const fetchMock = vi
1942
- .fn()
1943
- .mockRejectedValueOnce(new Error("socket hang up"))
1944
- .mockResolvedValueOnce(new Response(Buffer.from("ok"), { status: 200 }));
1945
- vi.stubGlobal("fetch", fetchMock);
1946
- const io = new PresignObjectIO(vault, COMPANY);
1947
- const pr = io.getObject("k");
1948
- await vi.runAllTimersAsync();
1949
- const res = await pr;
1950
- expect(res.body.toString("utf-8")).toBe("ok");
1951
- expect(fetchMock).toHaveBeenCalledTimes(2);
1952
- });
1953
- it("throws after exhausting retries on persistent 503 (initial + 3 retries = 4)", async () => {
1954
- vi.useFakeTimers();
1955
- const { vault, setPresign } = makeVault();
1956
- setPresign([{ key: "k", op: "get", url: "https://s3/get" }]);
1957
- const fetchMock = vi.fn().mockResolvedValue(new Response("err", { status: 503 }));
1958
- vi.stubGlobal("fetch", fetchMock);
1959
- const io = new PresignObjectIO(vault, COMPANY);
1960
- const pr = io.getObject("k").catch((e) => e);
1961
- await vi.runAllTimersAsync();
1962
- const err = await pr;
1963
- expect(String(err)).toMatch(/503/);
1964
- expect(fetchMock).toHaveBeenCalledTimes(4);
1965
- });
1966
- it("does NOT retry a definitive 404", async () => {
1967
- const { vault, setPresign } = makeVault();
1968
- setPresign([{ key: "gone", op: "get", url: "https://s3/get" }]);
1969
- const fetchMock = vi.fn().mockResolvedValue(new Response("", { status: 404 }));
1970
- vi.stubGlobal("fetch", fetchMock);
1971
- const io = new PresignObjectIO(vault, COMPANY);
1972
- await expect(io.getObject("gone")).rejects.toMatchObject({ name: "NotFound" });
1973
- expect(fetchMock).toHaveBeenCalledTimes(1);
1974
- });
1975
- /**
1976
- * Pam Beesly (Indigo) 2026-08-13: bulk company pull left primed GETs past
1977
- * X-Amz-Expires=1800. Pre-fix, 403 "Request has expired" was definitive —
1978
- * no re-presign — so those keys permanently failed the pass (partial=true).
1979
- */
1980
- it("re-presigns and retries when a primed GET URL returns 403 Request has expired", async () => {
1981
- vi.useFakeTimers();
1982
- let presignCalls = 0;
1983
- const vault = {
1984
- presign: async (input) => {
1985
- presignCalls++;
1986
- const n = presignCalls;
1987
- return {
1988
- results: input.keys.map((k) => ({
1989
- key: k.key,
1990
- op: "get",
1991
- url: `https://s3/get-v${n}/${k.key}`,
1992
- expiresIn: 1800,
1993
- })),
1994
- expiresAt: "2099-01-01T00:00:00.000Z",
1995
- };
1996
- },
1997
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
1998
- };
1999
- const expiredXml = '<?xml version="1.0" encoding="UTF-8"?>\n' +
2000
- "<Error><Code>AccessDenied</Code><Message>Request has expired</Message>" +
2001
- "<X-Amz-Expires>1800</X-Amz-Expires></Error>";
2002
- const transportGet = vi
2003
- .fn()
2004
- .mockResolvedValueOnce({
2005
- status: 403,
2006
- headers: new Headers(),
2007
- body: (async function* () {
2008
- yield Buffer.from(expiredXml);
2009
- })(),
2010
- destroy: () => undefined,
2011
- })
2012
- .mockResolvedValueOnce({
2013
- status: 200,
2014
- headers: new Headers({ "x-amz-meta-hq-mode": "644" }),
2015
- body: (async function* () {
2016
- yield Buffer.from("fresh-bytes");
2017
- })(),
2018
- destroy: () => undefined,
2019
- });
2020
- setPresignedGetTransportForTesting({ get: transportGet });
2021
- const io = new PresignObjectIO(vault, COMPANY);
2022
- await io.prime("get", [{ key: "projects/hq-desktop-installer/prd.json" }]);
2023
- expect(presignCalls).toBe(1);
2024
- const pr = io.getObject("projects/hq-desktop-installer/prd.json");
2025
- await vi.runAllTimersAsync();
2026
- const res = await pr;
2027
- expect(res.body.toString("utf-8")).toBe("fresh-bytes");
2028
- expect(res.metadata).toEqual({ "hq-mode": "644" });
2029
- // prime + one re-presign after expiry
2030
- expect(presignCalls).toBe(2);
2031
- expect(transportGet).toHaveBeenCalledTimes(2);
2032
- expect(transportGet.mock.calls[0][0]).toContain("get-v1/");
2033
- expect(transportGet.mock.calls[1][0]).toContain("get-v2/");
2034
- });
2035
- /**
2036
- * Memory-regression guard: getObjectStream must hand back a live body, not
2037
- * drain the whole object first. Mid-stream EPIPE recovery is the
2038
- * downloadFile whole-temp seam (s3.test.ts), not a buffer-in-transport loop.
2039
- */
2040
- it("getObjectStream yields without buffering the full object first", async () => {
2041
- const { vault, setPresign } = makeVault();
2042
- setPresign([{ key: "projects/large.bin", op: "get", url: "https://s3/get" }]);
2043
- let secondChunkPulled = false;
2044
- let resolveSecondChunk;
2045
- const secondChunkGate = new Promise((resolve) => {
2046
- resolveSecondChunk = resolve;
2047
- });
2048
- const transportGet = vi.fn(async () => ({
2049
- status: 200,
2050
- headers: new Headers({ "x-amz-meta-hq-mode": "644" }),
2051
- body: (async function* () {
2052
- yield Buffer.from("first-chunk-");
2053
- // Stall the rest of the body until the test has already received the
2054
- // first yield — proves getObjectStream returned before full drain.
2055
- await secondChunkGate;
2056
- secondChunkPulled = true;
2057
- yield Buffer.from("second-chunk");
2058
- })(),
2059
- destroy: () => undefined,
2060
- }));
2061
- setPresignedGetTransportForTesting({ get: transportGet });
2062
- const io = new PresignObjectIO(vault, COMPANY);
2063
- // Must resolve without waiting for second-chunk (true streaming).
2064
- const streamed = await io.getObjectStream("projects/large.bin");
2065
- expect(streamed.metadata).toEqual({ "hq-mode": "644" });
2066
- expect(secondChunkPulled).toBe(false);
2067
- const iterator = streamed.body[Symbol.asyncIterator]();
2068
- const first = await iterator.next();
2069
- expect(Buffer.from(first.value).toString("utf-8")).toBe("first-chunk-");
2070
- expect(secondChunkPulled).toBe(false);
2071
- resolveSecondChunk();
2072
- const second = await iterator.next();
2073
- expect(Buffer.from(second.value).toString("utf-8")).toBe("second-chunk");
2074
- expect(secondChunkPulled).toBe(true);
2075
- expect((await iterator.next()).done).toBe(true);
2076
- expect(transportGet).toHaveBeenCalledTimes(1);
2077
- });
2078
- it("surfaces mid-stream EPIPE from getObject without buffering/retrying in the stream seam", async () => {
2079
- const { vault, setPresign } = makeVault();
2080
- setPresign([{ key: "projects/mission-control/prd.json", op: "get", url: "https://s3/get" }]);
2081
- const transportGet = vi.fn(async () => ({
2082
- status: 200,
2083
- headers: new Headers(),
2084
- body: (async function* () {
2085
- yield Buffer.from("partial-");
2086
- throw Object.assign(new Error("write EPIPE"), { code: "EPIPE" });
2087
- })(),
2088
- destroy: () => undefined,
2089
- }));
2090
- setPresignedGetTransportForTesting({ get: transportGet });
2091
- const io = new PresignObjectIO(vault, COMPANY);
2092
- // getObject drains the live stream; mid-stream cuts propagate so the
2093
- // downloadFile seam (not this layer) owns whole-attempt recovery.
2094
- await expect(io.getObject("projects/mission-control/prd.json")).rejects.toMatchObject({ code: "EPIPE" });
2095
- expect(transportGet).toHaveBeenCalledTimes(1);
2096
- });
2097
- it("does NOT re-presign or retry a non-expired 403 AccessDenied (auth denial)", async () => {
2098
- const { vault, presignCalls, setPresign } = makeVault();
2099
- setPresign([{ key: "secret/x", op: "get", url: "https://s3/get" }]);
2100
- const transportGet = vi.fn(async () => ({
2101
- status: 403,
2102
- headers: new Headers(),
2103
- body: (async function* () {
2104
- yield Buffer.from("<Error><Code>AccessDenied</Code><Message>Access Denied</Message></Error>");
2105
- })(),
2106
- destroy: () => undefined,
2107
- }));
2108
- setPresignedGetTransportForTesting({ get: transportGet });
2109
- const io = new PresignObjectIO(vault, COMPANY);
2110
- await expect(io.getObject("secret/x")).rejects.toMatchObject({
2111
- name: "Forbidden",
2112
- });
2113
- expect(presignCalls).toHaveLength(1);
2114
- expect(transportGet).toHaveBeenCalledTimes(1);
2115
- });
2116
- it("does NOT retry a non-transient 400 on presigned GET", async () => {
2117
- const { vault, setPresign } = makeVault();
2118
- setPresign([{ key: "k", op: "get", url: "https://s3/get" }]);
2119
- const transportGet = vi.fn(async () => ({
2120
- status: 400,
2121
- headers: new Headers(),
2122
- body: (async function* () {
2123
- yield Buffer.from("Bad Request");
2124
- })(),
2125
- destroy: () => undefined,
2126
- }));
2127
- setPresignedGetTransportForTesting({ get: transportGet });
2128
- const io = new PresignObjectIO(vault, COMPANY);
2129
- await expect(io.getObject("k")).rejects.toThrow(/presigned GET failed for k: 400/);
2130
- expect(transportGet).toHaveBeenCalledTimes(1);
2131
- });
2132
- it("headObject re-presigns on 403 Request has expired then returns metadata", async () => {
2133
- vi.useFakeTimers();
2134
- let presignCalls = 0;
2135
- const vault = {
2136
- presign: async (input) => {
2137
- presignCalls++;
2138
- return {
2139
- results: input.keys.map((k) => ({
2140
- key: k.key,
2141
- op: "get",
2142
- url: `https://s3/head-v${presignCalls}/${k.key}`,
2143
- })),
2144
- expiresAt: "2099-01-01T00:00:00.000Z",
2145
- };
2146
- },
2147
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
2148
- };
2149
- const expiredXml = "<Error><Code>AccessDenied</Code><Message>Request has expired</Message>" +
2150
- "<X-Amz-Expires>1800</X-Amz-Expires></Error>";
2151
- const transportGet = vi
2152
- .fn()
2153
- .mockResolvedValueOnce({
2154
- status: 403,
2155
- headers: new Headers(),
2156
- body: (async function* () {
2157
- yield Buffer.from(expiredXml);
2158
- })(),
2159
- destroy: () => undefined,
2160
- })
2161
- .mockResolvedValueOnce({
2162
- status: 200,
2163
- headers: new Headers({
2164
- etag: '"abc"',
2165
- "content-length": "9",
2166
- "last-modified": "Wed, 01 Jan 2026 00:00:00 GMT",
2167
- }),
2168
- body: (async function* () {
2169
- yield Buffer.from("not-used");
2170
- })(),
2171
- destroy: () => undefined,
2172
- });
2173
- setPresignedGetTransportForTesting({ get: transportGet });
2174
- const io = new PresignObjectIO(vault, COMPANY);
2175
- const pr = io.headObject("shared/a.md");
2176
- await vi.runAllTimersAsync();
2177
- const head = await pr;
2178
- expect(head).toMatchObject({ etag: "abc", size: 9 });
2179
- expect(presignCalls).toBe(2);
2180
- expect(transportGet).toHaveBeenCalledTimes(2);
2181
- });
2182
- });
2183
- import { VaultClientError } from "./vault-client.js";
2184
- describe("PresignObjectIO — 429 circuit breaker", () => {
2185
- afterEach(() => vi.unstubAllGlobals());
2186
- it("trips on a 429: uncached keys fail fast (no further presign), primed keys still work", async () => {
2187
- let presignCalls = 0;
2188
- const vault = {
2189
- presign: async (input) => {
2190
- presignCalls++;
2191
- if (input.keys.some((k) => k.key === "primed")) {
2192
- return {
2193
- results: input.keys.map((k) => ({
2194
- key: k.key,
2195
- op: k.op ?? "get",
2196
- url: `https://s/${k.key}`,
2197
- expiresIn: 900,
2198
- })),
2199
- expiresAt: "2099-01-01T00:00:00.000Z",
2200
- };
2201
- }
2202
- throw new VaultClientError("Rate limit exceeded: 100 requests per hour", 429);
2203
- },
2204
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
2205
- };
2206
- vi.stubGlobal("fetch", vi.fn(async () => new Response(Buffer.from("x"), { status: 200 })));
2207
- const io = new PresignObjectIO(vault, COMPANY);
2208
- await io.prime("get", [{ key: "primed" }]); // caches "primed"
2209
- await expect(io.getObject("uncached1")).rejects.toMatchObject({
2210
- name: "RateLimited",
2211
- });
2212
- const afterTrip = presignCalls;
2213
- // Second uncached key fails fast — NO new presign call.
2214
- await expect(io.getObject("uncached2")).rejects.toMatchObject({
2215
- name: "RateLimited",
2216
- });
2217
- expect(presignCalls).toBe(afterTrip);
2218
- // The primed key still resolves from cache (no presign needed).
2219
- const r = await io.getObject("primed");
2220
- expect(r.body.toString("utf-8")).toBe("x");
2221
- });
2222
- it("prime stops minting once a chunk 429s (breaker trips, remaining chunks skipped)", async () => {
2223
- let calls = 0;
2224
- const vault = {
2225
- presign: async () => {
2226
- calls++;
2227
- throw new VaultClientError("rate", 429);
2228
- },
2229
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
2230
- };
2231
- const io = new PresignObjectIO(vault, COMPANY);
2232
- // 10 chunks (10k keys @ 1000); concurrency is 4, so at most the first
2233
- // concurrent batch fires before the breaker trips — far fewer than 10.
2234
- await io.prime("get", Array.from({ length: 10_000 }, (_, i) => ({ key: `k${i}` })));
2235
- expect(calls).toBeLessThanOrEqual(4);
2236
- expect(calls).toBeLessThan(10);
2237
- });
2238
- });
2239
- describe("PresignObjectIO.hasPrimedPut", () => {
2240
- it("reflects a put prime", async () => {
2241
- const { vault } = makeEchoVault();
2242
- const io = new PresignObjectIO(vault, COMPANY);
2243
- expect(io.hasPrimedPut("k")).toBe(false);
2244
- await io.prime("put", [
2245
- { key: "k", op: "put", contentType: "text/plain", metadata: { "hq-mode": "644" } },
2246
- ]);
2247
- expect(io.hasPrimedPut("k")).toBe(true);
2248
- // A different, unprimed key stays false.
2249
- expect(io.hasPrimedPut("other")).toBe(false);
2250
- });
2251
- });
2252
- describe("presignObjectIOFactory — personal vaults route to S3 SDK", () => {
2253
- it("routes a person entity (prs_*) to S3SdkObjectIO, a company (cmp_*) to presign", () => {
2254
- const { vault } = makeEchoVault();
2255
- const factory = presignObjectIOFactory(vault);
2256
- const company = factory({ ...ctx(), uid: "cmp_real" });
2257
- const personal = factory({ ...ctx(), uid: "prs_real" });
2258
- expect(company).toBeInstanceOf(PresignObjectIO);
2259
- // Personal vaults use the membership-less vend-self model; the
2260
- // membership-gated presign endpoints 403 for them, so they stay on STS.
2261
- expect(personal).toBeInstanceOf(S3SdkObjectIO);
2262
- });
2263
- });
2264
- describe("PresignObjectIO — core HTTP presigned GET", () => {
2265
- function streamingVault(baseUrl) {
2266
- return {
2267
- presign: async (input) => ({
2268
- results: input.keys.map((key) => ({
2269
- key: key.key,
2270
- op: "get",
2271
- url: `${baseUrl}/${key.key}`,
2272
- })),
2273
- expiresAt: "2099-01-01T00:00:00.000Z",
2274
- }),
2275
- listFiles: async () => ({ objects: [], cursor: null, truncated: false }),
2276
- };
2277
- }
2278
- it("destroys an early-exit response and rejects a FIN-short body", async () => {
2279
- setPresignedGetTransportForTesting(null);
2280
- let responseClosed;
2281
- const server = http.createServer((req, res) => {
2282
- if (req.url === "/short") {
2283
- res.writeHead(200, { "content-length": "10" });
2284
- res.end("short");
2285
- return;
2286
- }
2287
- res.writeHead(200, { "content-length": "131072" });
2288
- res.write(Buffer.alloc(65_536, 1));
2289
- responseClosed = once(res, "close");
2290
- const finish = setTimeout(() => res.end(Buffer.alloc(65_536, 2)), 1_000);
2291
- res.once("close", () => clearTimeout(finish));
2292
- });
2293
- server.listen(0, "127.0.0.1");
2294
- await once(server, "listening");
2295
- const address = server.address();
2296
- if (!address || typeof address === "string") {
2297
- throw new Error("test server did not bind a TCP port");
2298
- }
2299
- try {
2300
- const io = new PresignObjectIO(streamingVault(`http://127.0.0.1:${address.port}`), COMPANY);
2301
- const streamed = await io.getObjectStream("large");
2302
- const iterator = streamed.body[Symbol.asyncIterator]();
2303
- expect((await iterator.next()).value).toBeInstanceOf(Uint8Array);
2304
- await iterator.return?.();
2305
- if (!responseClosed)
2306
- throw new Error("response did not start streaming");
2307
- await expect(responseClosed).resolves.toBeDefined();
2308
- await expect(io.getObject("short")).rejects.toMatchObject({
2309
- code: "ERR_HQ_TRUNCATED_RESPONSE",
2310
- message: expect.stringMatching(/content-length mismatch/),
2311
- });
2312
- }
2313
- finally {
2314
- server.close();
2315
- await once(server, "close");
2316
- }
2317
- });
2318
- it.runIf(process.platform === "win32" && /^(22|24)\./.test(process.versions.node))("keeps a child FIN safe after the consumer has applied backpressure", async () => {
2319
- setPresignedGetTransportForTesting(null);
2320
- const child = spawn(process.execPath, [
2321
- "-e",
2322
- [
2323
- "const http = require('node:http');",
2324
- "const body = Buffer.alloc(524288, 7);",
2325
- "const server = http.createServer((_req, res) => {",
2326
- " res.writeHead(200, { 'content-length': String(body.length) });",
2327
- " for (let offset = 0; offset < body.length; offset += 16384) res.write(body.subarray(offset, offset + 16384));",
2328
- " res.end();",
2329
- "});",
2330
- "server.listen(0, '127.0.0.1', () => process.stdout.write(String(server.address().port) + '\\n'));",
2331
- "process.on('SIGTERM', () => server.close(() => process.exit(0)));",
2332
- ].join("\n"),
2333
- ], { stdio: ["ignore", "pipe", "inherit"] });
2334
- const port = await new Promise((resolve, reject) => {
2335
- child.once("error", reject);
2336
- child.stdout?.once("data", (data) => resolve(Number(data.toString())));
2337
- child.once("exit", (code) => reject(new Error(`child server exited (${code})`)));
2338
- });
2339
- try {
2340
- const io = new PresignObjectIO(streamingVault(`http://127.0.0.1:${port}`), COMPANY);
2341
- const streamed = await io.getObjectStream("bulk");
2342
- await new Promise((resolve) => setTimeout(resolve, 40));
2343
- let bytes = 0;
2344
- for await (const chunk of streamed.body)
2345
- bytes += chunk.byteLength;
2346
- expect(bytes).toBe(524_288);
2347
- }
2348
- finally {
2349
- child.kill("SIGTERM");
2350
- }
2351
- });
2352
- });
2353
- describe("presigned GET node body wrapper", () => {
2354
- it("keeps a socket error raised before iteration for the reader instead of throwing it", async () => {
2355
- const { PassThrough } = await import("node:stream");
2356
- const { checkedNodeBodyForTesting } = await import("./object-io.js");
2357
- const message = new PassThrough();
2358
- const unhandled = [];
2359
- const onUncaught = (error) => { unhandled.push(error); };
2360
- process.on("uncaughtException", onUncaught);
2361
- try {
2362
- const body = checkedNodeBodyForTesting(message, undefined);
2363
- message.emit("error", new Error("socket reset before iteration"));
2364
- await new Promise((resolve) => setImmediate(resolve));
2365
- expect(unhandled).toEqual([]);
2366
- const read = (async () => {
2367
- for await (const _chunk of body) { /* drain */ }
2368
- })();
2369
- await expect(read).rejects.toThrow("socket reset before iteration");
2370
- }
2371
- finally {
2372
- process.off("uncaughtException", onUncaught);
2373
- }
2374
- });
2375
- });
2376
- //# sourceMappingURL=object-io.test.js.map