gitx.do 0.1.0 → 0.1.2

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 (344) hide show
  1. package/README.md +40 -353
  2. package/dist/do/logger.d.ts +50 -0
  3. package/dist/do/logger.d.ts.map +1 -0
  4. package/dist/do/logger.js +122 -0
  5. package/dist/do/logger.js.map +1 -0
  6. package/dist/{durable-object → do}/schema.d.ts +3 -3
  7. package/dist/do/schema.d.ts.map +1 -0
  8. package/dist/{durable-object → do}/schema.js +4 -3
  9. package/dist/do/schema.js.map +1 -0
  10. package/dist/do/types.d.ts +267 -0
  11. package/dist/do/types.d.ts.map +1 -0
  12. package/dist/do/types.js +62 -0
  13. package/dist/do/types.js.map +1 -0
  14. package/dist/index.d.ts +15 -415
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +31 -483
  17. package/dist/index.js.map +1 -1
  18. package/package.json +13 -21
  19. package/dist/cli/commands/add.d.ts +0 -174
  20. package/dist/cli/commands/add.d.ts.map +0 -1
  21. package/dist/cli/commands/add.js +0 -131
  22. package/dist/cli/commands/add.js.map +0 -1
  23. package/dist/cli/commands/blame.d.ts +0 -259
  24. package/dist/cli/commands/blame.d.ts.map +0 -1
  25. package/dist/cli/commands/blame.js +0 -609
  26. package/dist/cli/commands/blame.js.map +0 -1
  27. package/dist/cli/commands/branch.d.ts +0 -249
  28. package/dist/cli/commands/branch.d.ts.map +0 -1
  29. package/dist/cli/commands/branch.js +0 -693
  30. package/dist/cli/commands/branch.js.map +0 -1
  31. package/dist/cli/commands/commit.d.ts +0 -182
  32. package/dist/cli/commands/commit.d.ts.map +0 -1
  33. package/dist/cli/commands/commit.js +0 -437
  34. package/dist/cli/commands/commit.js.map +0 -1
  35. package/dist/cli/commands/diff.d.ts +0 -464
  36. package/dist/cli/commands/diff.d.ts.map +0 -1
  37. package/dist/cli/commands/diff.js +0 -958
  38. package/dist/cli/commands/diff.js.map +0 -1
  39. package/dist/cli/commands/log.d.ts +0 -239
  40. package/dist/cli/commands/log.d.ts.map +0 -1
  41. package/dist/cli/commands/log.js +0 -535
  42. package/dist/cli/commands/log.js.map +0 -1
  43. package/dist/cli/commands/merge.d.ts +0 -106
  44. package/dist/cli/commands/merge.d.ts.map +0 -1
  45. package/dist/cli/commands/merge.js +0 -55
  46. package/dist/cli/commands/merge.js.map +0 -1
  47. package/dist/cli/commands/review.d.ts +0 -457
  48. package/dist/cli/commands/review.d.ts.map +0 -1
  49. package/dist/cli/commands/review.js +0 -533
  50. package/dist/cli/commands/review.js.map +0 -1
  51. package/dist/cli/commands/status.d.ts +0 -269
  52. package/dist/cli/commands/status.d.ts.map +0 -1
  53. package/dist/cli/commands/status.js +0 -493
  54. package/dist/cli/commands/status.js.map +0 -1
  55. package/dist/cli/commands/web.d.ts +0 -199
  56. package/dist/cli/commands/web.d.ts.map +0 -1
  57. package/dist/cli/commands/web.js +0 -696
  58. package/dist/cli/commands/web.js.map +0 -1
  59. package/dist/cli/fs-adapter.d.ts +0 -656
  60. package/dist/cli/fs-adapter.d.ts.map +0 -1
  61. package/dist/cli/fs-adapter.js +0 -1179
  62. package/dist/cli/fs-adapter.js.map +0 -1
  63. package/dist/cli/fsx-cli-adapter.d.ts +0 -359
  64. package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
  65. package/dist/cli/fsx-cli-adapter.js +0 -619
  66. package/dist/cli/fsx-cli-adapter.js.map +0 -1
  67. package/dist/cli/index.d.ts +0 -387
  68. package/dist/cli/index.d.ts.map +0 -1
  69. package/dist/cli/index.js +0 -523
  70. package/dist/cli/index.js.map +0 -1
  71. package/dist/cli/ui/components/DiffView.d.ts +0 -7
  72. package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
  73. package/dist/cli/ui/components/DiffView.js +0 -11
  74. package/dist/cli/ui/components/DiffView.js.map +0 -1
  75. package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -6
  76. package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
  77. package/dist/cli/ui/components/ErrorDisplay.js +0 -11
  78. package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
  79. package/dist/cli/ui/components/FuzzySearch.d.ts +0 -9
  80. package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
  81. package/dist/cli/ui/components/FuzzySearch.js +0 -12
  82. package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
  83. package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -6
  84. package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
  85. package/dist/cli/ui/components/LoadingSpinner.js +0 -10
  86. package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
  87. package/dist/cli/ui/components/NavigationList.d.ts +0 -9
  88. package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
  89. package/dist/cli/ui/components/NavigationList.js +0 -11
  90. package/dist/cli/ui/components/NavigationList.js.map +0 -1
  91. package/dist/cli/ui/components/ScrollableContent.d.ts +0 -8
  92. package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
  93. package/dist/cli/ui/components/ScrollableContent.js +0 -11
  94. package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
  95. package/dist/cli/ui/components/index.d.ts +0 -7
  96. package/dist/cli/ui/components/index.d.ts.map +0 -1
  97. package/dist/cli/ui/components/index.js +0 -9
  98. package/dist/cli/ui/components/index.js.map +0 -1
  99. package/dist/cli/ui/terminal-ui.d.ts +0 -52
  100. package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
  101. package/dist/cli/ui/terminal-ui.js +0 -121
  102. package/dist/cli/ui/terminal-ui.js.map +0 -1
  103. package/dist/do/BashModule.d.ts +0 -871
  104. package/dist/do/BashModule.d.ts.map +0 -1
  105. package/dist/do/BashModule.js +0 -1143
  106. package/dist/do/BashModule.js.map +0 -1
  107. package/dist/do/FsModule.d.ts +0 -601
  108. package/dist/do/FsModule.d.ts.map +0 -1
  109. package/dist/do/FsModule.js +0 -1120
  110. package/dist/do/FsModule.js.map +0 -1
  111. package/dist/do/GitModule.d.ts +0 -635
  112. package/dist/do/GitModule.d.ts.map +0 -1
  113. package/dist/do/GitModule.js +0 -781
  114. package/dist/do/GitModule.js.map +0 -1
  115. package/dist/do/GitRepoDO.d.ts +0 -281
  116. package/dist/do/GitRepoDO.d.ts.map +0 -1
  117. package/dist/do/GitRepoDO.js +0 -479
  118. package/dist/do/GitRepoDO.js.map +0 -1
  119. package/dist/do/bash-ast.d.ts +0 -246
  120. package/dist/do/bash-ast.d.ts.map +0 -1
  121. package/dist/do/bash-ast.js +0 -888
  122. package/dist/do/bash-ast.js.map +0 -1
  123. package/dist/do/container-executor.d.ts +0 -491
  124. package/dist/do/container-executor.d.ts.map +0 -1
  125. package/dist/do/container-executor.js +0 -730
  126. package/dist/do/container-executor.js.map +0 -1
  127. package/dist/do/index.d.ts +0 -53
  128. package/dist/do/index.d.ts.map +0 -1
  129. package/dist/do/index.js +0 -91
  130. package/dist/do/index.js.map +0 -1
  131. package/dist/do/tiered-storage.d.ts +0 -403
  132. package/dist/do/tiered-storage.d.ts.map +0 -1
  133. package/dist/do/tiered-storage.js +0 -689
  134. package/dist/do/tiered-storage.js.map +0 -1
  135. package/dist/do/withBash.d.ts +0 -231
  136. package/dist/do/withBash.d.ts.map +0 -1
  137. package/dist/do/withBash.js +0 -244
  138. package/dist/do/withBash.js.map +0 -1
  139. package/dist/do/withFs.d.ts +0 -237
  140. package/dist/do/withFs.d.ts.map +0 -1
  141. package/dist/do/withFs.js +0 -387
  142. package/dist/do/withFs.js.map +0 -1
  143. package/dist/do/withGit.d.ts +0 -180
  144. package/dist/do/withGit.d.ts.map +0 -1
  145. package/dist/do/withGit.js +0 -271
  146. package/dist/do/withGit.js.map +0 -1
  147. package/dist/durable-object/object-store.d.ts +0 -633
  148. package/dist/durable-object/object-store.d.ts.map +0 -1
  149. package/dist/durable-object/object-store.js +0 -1161
  150. package/dist/durable-object/object-store.js.map +0 -1
  151. package/dist/durable-object/schema.d.ts.map +0 -1
  152. package/dist/durable-object/schema.js.map +0 -1
  153. package/dist/durable-object/wal.d.ts +0 -416
  154. package/dist/durable-object/wal.d.ts.map +0 -1
  155. package/dist/durable-object/wal.js +0 -445
  156. package/dist/durable-object/wal.js.map +0 -1
  157. package/dist/mcp/adapter.d.ts +0 -772
  158. package/dist/mcp/adapter.d.ts.map +0 -1
  159. package/dist/mcp/adapter.js +0 -895
  160. package/dist/mcp/adapter.js.map +0 -1
  161. package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
  162. package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
  163. package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
  164. package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
  165. package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
  166. package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
  167. package/dist/mcp/sandbox/object-store-proxy.js +0 -30
  168. package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
  169. package/dist/mcp/sandbox/template.d.ts +0 -17
  170. package/dist/mcp/sandbox/template.d.ts.map +0 -1
  171. package/dist/mcp/sandbox/template.js +0 -71
  172. package/dist/mcp/sandbox/template.js.map +0 -1
  173. package/dist/mcp/sandbox.d.ts +0 -764
  174. package/dist/mcp/sandbox.d.ts.map +0 -1
  175. package/dist/mcp/sandbox.js +0 -1362
  176. package/dist/mcp/sandbox.js.map +0 -1
  177. package/dist/mcp/sdk-adapter.d.ts +0 -835
  178. package/dist/mcp/sdk-adapter.d.ts.map +0 -1
  179. package/dist/mcp/sdk-adapter.js +0 -974
  180. package/dist/mcp/sdk-adapter.js.map +0 -1
  181. package/dist/mcp/tools/do.d.ts +0 -32
  182. package/dist/mcp/tools/do.d.ts.map +0 -1
  183. package/dist/mcp/tools/do.js +0 -115
  184. package/dist/mcp/tools/do.js.map +0 -1
  185. package/dist/mcp/tools.d.ts +0 -548
  186. package/dist/mcp/tools.d.ts.map +0 -1
  187. package/dist/mcp/tools.js +0 -1934
  188. package/dist/mcp/tools.js.map +0 -1
  189. package/dist/ops/blame.d.ts +0 -551
  190. package/dist/ops/blame.d.ts.map +0 -1
  191. package/dist/ops/blame.js +0 -1037
  192. package/dist/ops/blame.js.map +0 -1
  193. package/dist/ops/branch.d.ts +0 -766
  194. package/dist/ops/branch.d.ts.map +0 -1
  195. package/dist/ops/branch.js +0 -950
  196. package/dist/ops/branch.js.map +0 -1
  197. package/dist/ops/commit-traversal.d.ts +0 -349
  198. package/dist/ops/commit-traversal.d.ts.map +0 -1
  199. package/dist/ops/commit-traversal.js +0 -821
  200. package/dist/ops/commit-traversal.js.map +0 -1
  201. package/dist/ops/commit.d.ts +0 -555
  202. package/dist/ops/commit.d.ts.map +0 -1
  203. package/dist/ops/commit.js +0 -826
  204. package/dist/ops/commit.js.map +0 -1
  205. package/dist/ops/merge-base.d.ts +0 -397
  206. package/dist/ops/merge-base.d.ts.map +0 -1
  207. package/dist/ops/merge-base.js +0 -691
  208. package/dist/ops/merge-base.js.map +0 -1
  209. package/dist/ops/merge.d.ts +0 -855
  210. package/dist/ops/merge.d.ts.map +0 -1
  211. package/dist/ops/merge.js +0 -1551
  212. package/dist/ops/merge.js.map +0 -1
  213. package/dist/ops/tag.d.ts +0 -247
  214. package/dist/ops/tag.d.ts.map +0 -1
  215. package/dist/ops/tag.js +0 -649
  216. package/dist/ops/tag.js.map +0 -1
  217. package/dist/ops/tree-builder.d.ts +0 -178
  218. package/dist/ops/tree-builder.d.ts.map +0 -1
  219. package/dist/ops/tree-builder.js +0 -271
  220. package/dist/ops/tree-builder.js.map +0 -1
  221. package/dist/ops/tree-diff.d.ts +0 -291
  222. package/dist/ops/tree-diff.d.ts.map +0 -1
  223. package/dist/ops/tree-diff.js +0 -705
  224. package/dist/ops/tree-diff.js.map +0 -1
  225. package/dist/pack/delta.d.ts +0 -248
  226. package/dist/pack/delta.d.ts.map +0 -1
  227. package/dist/pack/delta.js +0 -736
  228. package/dist/pack/delta.js.map +0 -1
  229. package/dist/pack/format.d.ts +0 -446
  230. package/dist/pack/format.d.ts.map +0 -1
  231. package/dist/pack/format.js +0 -572
  232. package/dist/pack/format.js.map +0 -1
  233. package/dist/pack/full-generation.d.ts +0 -612
  234. package/dist/pack/full-generation.d.ts.map +0 -1
  235. package/dist/pack/full-generation.js +0 -1378
  236. package/dist/pack/full-generation.js.map +0 -1
  237. package/dist/pack/generation.d.ts +0 -441
  238. package/dist/pack/generation.d.ts.map +0 -1
  239. package/dist/pack/generation.js +0 -707
  240. package/dist/pack/generation.js.map +0 -1
  241. package/dist/pack/index.d.ts +0 -502
  242. package/dist/pack/index.d.ts.map +0 -1
  243. package/dist/pack/index.js +0 -833
  244. package/dist/pack/index.js.map +0 -1
  245. package/dist/refs/branch.d.ts +0 -668
  246. package/dist/refs/branch.d.ts.map +0 -1
  247. package/dist/refs/branch.js +0 -897
  248. package/dist/refs/branch.js.map +0 -1
  249. package/dist/refs/storage.d.ts +0 -833
  250. package/dist/refs/storage.d.ts.map +0 -1
  251. package/dist/refs/storage.js +0 -1023
  252. package/dist/refs/storage.js.map +0 -1
  253. package/dist/refs/tag.d.ts +0 -860
  254. package/dist/refs/tag.d.ts.map +0 -1
  255. package/dist/refs/tag.js +0 -996
  256. package/dist/refs/tag.js.map +0 -1
  257. package/dist/storage/backend.d.ts +0 -425
  258. package/dist/storage/backend.d.ts.map +0 -1
  259. package/dist/storage/backend.js +0 -41
  260. package/dist/storage/backend.js.map +0 -1
  261. package/dist/storage/fsx-adapter.d.ts +0 -204
  262. package/dist/storage/fsx-adapter.d.ts.map +0 -1
  263. package/dist/storage/fsx-adapter.js +0 -470
  264. package/dist/storage/fsx-adapter.js.map +0 -1
  265. package/dist/storage/lru-cache.d.ts +0 -691
  266. package/dist/storage/lru-cache.d.ts.map +0 -1
  267. package/dist/storage/lru-cache.js +0 -813
  268. package/dist/storage/lru-cache.js.map +0 -1
  269. package/dist/storage/object-index.d.ts +0 -585
  270. package/dist/storage/object-index.d.ts.map +0 -1
  271. package/dist/storage/object-index.js +0 -532
  272. package/dist/storage/object-index.js.map +0 -1
  273. package/dist/storage/r2-pack.d.ts +0 -1257
  274. package/dist/storage/r2-pack.d.ts.map +0 -1
  275. package/dist/storage/r2-pack.js +0 -1770
  276. package/dist/storage/r2-pack.js.map +0 -1
  277. package/dist/tiered/cdc-pipeline.d.ts +0 -1888
  278. package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
  279. package/dist/tiered/cdc-pipeline.js +0 -1880
  280. package/dist/tiered/cdc-pipeline.js.map +0 -1
  281. package/dist/tiered/migration.d.ts +0 -1104
  282. package/dist/tiered/migration.d.ts.map +0 -1
  283. package/dist/tiered/migration.js +0 -1214
  284. package/dist/tiered/migration.js.map +0 -1
  285. package/dist/tiered/parquet-writer.d.ts +0 -1145
  286. package/dist/tiered/parquet-writer.d.ts.map +0 -1
  287. package/dist/tiered/parquet-writer.js +0 -1183
  288. package/dist/tiered/parquet-writer.js.map +0 -1
  289. package/dist/tiered/read-path.d.ts +0 -835
  290. package/dist/tiered/read-path.d.ts.map +0 -1
  291. package/dist/tiered/read-path.js +0 -487
  292. package/dist/tiered/read-path.js.map +0 -1
  293. package/dist/types/capability.d.ts +0 -1385
  294. package/dist/types/capability.d.ts.map +0 -1
  295. package/dist/types/capability.js +0 -36
  296. package/dist/types/capability.js.map +0 -1
  297. package/dist/types/index.d.ts +0 -13
  298. package/dist/types/index.d.ts.map +0 -1
  299. package/dist/types/index.js +0 -18
  300. package/dist/types/index.js.map +0 -1
  301. package/dist/types/objects.d.ts +0 -692
  302. package/dist/types/objects.d.ts.map +0 -1
  303. package/dist/types/objects.js +0 -837
  304. package/dist/types/objects.js.map +0 -1
  305. package/dist/types/storage.d.ts +0 -603
  306. package/dist/types/storage.d.ts.map +0 -1
  307. package/dist/types/storage.js +0 -191
  308. package/dist/types/storage.js.map +0 -1
  309. package/dist/types/worker-loader.d.ts +0 -60
  310. package/dist/types/worker-loader.d.ts.map +0 -1
  311. package/dist/types/worker-loader.js +0 -62
  312. package/dist/types/worker-loader.js.map +0 -1
  313. package/dist/utils/hash.d.ts +0 -197
  314. package/dist/utils/hash.d.ts.map +0 -1
  315. package/dist/utils/hash.js +0 -268
  316. package/dist/utils/hash.js.map +0 -1
  317. package/dist/utils/sha1.d.ts +0 -290
  318. package/dist/utils/sha1.d.ts.map +0 -1
  319. package/dist/utils/sha1.js +0 -582
  320. package/dist/utils/sha1.js.map +0 -1
  321. package/dist/wire/capabilities.d.ts +0 -1044
  322. package/dist/wire/capabilities.d.ts.map +0 -1
  323. package/dist/wire/capabilities.js +0 -941
  324. package/dist/wire/capabilities.js.map +0 -1
  325. package/dist/wire/path-security.d.ts +0 -157
  326. package/dist/wire/path-security.d.ts.map +0 -1
  327. package/dist/wire/path-security.js +0 -307
  328. package/dist/wire/path-security.js.map +0 -1
  329. package/dist/wire/pkt-line.d.ts +0 -345
  330. package/dist/wire/pkt-line.d.ts.map +0 -1
  331. package/dist/wire/pkt-line.js +0 -381
  332. package/dist/wire/pkt-line.js.map +0 -1
  333. package/dist/wire/receive-pack.d.ts +0 -1059
  334. package/dist/wire/receive-pack.d.ts.map +0 -1
  335. package/dist/wire/receive-pack.js +0 -1414
  336. package/dist/wire/receive-pack.js.map +0 -1
  337. package/dist/wire/smart-http.d.ts +0 -799
  338. package/dist/wire/smart-http.d.ts.map +0 -1
  339. package/dist/wire/smart-http.js +0 -945
  340. package/dist/wire/smart-http.js.map +0 -1
  341. package/dist/wire/upload-pack.d.ts +0 -727
  342. package/dist/wire/upload-pack.d.ts.map +0 -1
  343. package/dist/wire/upload-pack.js +0 -1138
  344. package/dist/wire/upload-pack.js.map +0 -1
@@ -1,1214 +0,0 @@
1
- /**
2
- * @fileoverview Tier Migration Module (Hot -> Warm)
3
- *
4
- * This module handles the migration of Git objects between storage tiers in the
5
- * gitdo tiered storage architecture. It provides comprehensive functionality for:
6
- *
7
- * ## Storage Tiers
8
- *
9
- * - **Hot**: SQLite in Durable Object storage - fastest access, limited capacity
10
- * - **Warm/R2**: Packed objects in R2 object storage - medium latency, larger capacity
11
- *
12
- * ## Key Features
13
- *
14
- * - **Policy-based Migration**: Configurable policies based on age, access frequency, and size
15
- * - **Access Tracking**: Monitors object access patterns to inform migration decisions
16
- * - **Atomic Operations**: Ensures data integrity during migration with rollback support
17
- * - **Concurrent Access Handling**: Safe reads/writes during in-progress migrations
18
- * - **Checksum Verification**: Optional integrity verification after migration
19
- * - **Batch Migration**: Efficient bulk migration with configurable concurrency
20
- *
21
- * ## Migration Process
22
- *
23
- * 1. Acquire distributed lock on the object
24
- * 2. Copy data from hot tier to warm tier
25
- * 3. Verify data integrity (optional checksum verification)
26
- * 4. Update object location index
27
- * 5. Delete from hot tier
28
- * 6. Release lock
29
- *
30
- * If any step fails, the migration is rolled back automatically.
31
- *
32
- * @module tiered/migration
33
- *
34
- * @example
35
- * ```typescript
36
- * // Create a migrator
37
- * const migrator = new TierMigrator(storage);
38
- *
39
- * // Define migration policy
40
- * const policy: MigrationPolicy = {
41
- * maxAgeInHot: 24 * 60 * 60 * 1000, // 24 hours
42
- * minAccessCount: 5,
43
- * maxHotSize: 100 * 1024 * 1024 // 100MB
44
- * };
45
- *
46
- * // Find candidates and migrate
47
- * const candidates = await migrator.findMigrationCandidates(policy);
48
- * for (const sha of candidates) {
49
- * await migrator.migrate(sha, 'hot', 'r2', { verifyChecksum: true });
50
- * }
51
- * ```
52
- */
53
- /**
54
- * Error thrown during migration operations.
55
- *
56
- * @description
57
- * Custom error class for migration failures with detailed information
58
- * about the failure context. Also implements MigrationResult-like properties
59
- * for compatibility with result handling code.
60
- *
61
- * Error codes:
62
- * - `NOT_FOUND`: Object does not exist in source tier
63
- * - `ALREADY_IN_TARGET`: Object is already in the target tier
64
- * - `LOCK_TIMEOUT`: Could not acquire lock within timeout
65
- * - `WRITE_FAILED`: Failed to write to target tier
66
- * - `CHECKSUM_MISMATCH`: Data verification failed after migration
67
- * - `UPDATE_FAILED`: Failed to update object index
68
- *
69
- * @example
70
- * ```typescript
71
- * try {
72
- * await migrator.migrate(sha, 'hot', 'r2');
73
- * } catch (error) {
74
- * if (error instanceof MigrationError) {
75
- * console.log(`Migration failed: ${error.code}`);
76
- * console.log(`Object: ${error.sha}`);
77
- * console.log(`${error.sourceTier} -> ${error.targetTier}`);
78
- * }
79
- * }
80
- * ```
81
- */
82
- export class MigrationError extends Error {
83
- code;
84
- sha;
85
- sourceTier;
86
- targetTier;
87
- cause;
88
- /** Always false for error objects */
89
- success = false;
90
- /** Whether rollback was performed */
91
- rolledBack = true;
92
- /** Reason for rollback (from cause error) */
93
- rollbackReason;
94
- /**
95
- * Creates a new MigrationError.
96
- *
97
- * @param message - Human-readable error message
98
- * @param code - Error code for programmatic handling
99
- * @param sha - SHA of the object being migrated
100
- * @param sourceTier - Source storage tier
101
- * @param targetTier - Target storage tier
102
- * @param cause - Underlying error that caused this failure
103
- */
104
- constructor(message, code, sha, sourceTier, targetTier, cause) {
105
- super(message);
106
- this.code = code;
107
- this.sha = sha;
108
- this.sourceTier = sourceTier;
109
- this.targetTier = targetTier;
110
- this.cause = cause;
111
- this.name = 'MigrationError';
112
- this.rollbackReason = cause?.message;
113
- }
114
- /**
115
- * Returns this error as a MigrationError reference.
116
- *
117
- * @description
118
- * Provides compatibility with MigrationResult.error property access.
119
- *
120
- * @returns This MigrationError instance
121
- */
122
- get error() {
123
- return this;
124
- }
125
- }
126
- /**
127
- * Rollback handler for failed migrations.
128
- *
129
- * @description
130
- * Handles cleanup operations when a migration fails, ensuring
131
- * that partial migrations don't leave the system in an inconsistent state.
132
- *
133
- * @example
134
- * ```typescript
135
- * const rollback = new MigrationRollback(storage);
136
- * if (migrationFailed) {
137
- * await rollback.rollback(job);
138
- * }
139
- * ```
140
- */
141
- export class MigrationRollback {
142
- storage;
143
- /**
144
- * Creates a new MigrationRollback handler.
145
- *
146
- * @param storage - The tier storage implementation
147
- */
148
- constructor(storage) {
149
- this.storage = storage;
150
- }
151
- /**
152
- * Rolls back a failed migration job.
153
- *
154
- * @description
155
- * Cleans up any partial data in the warm tier and releases the lock.
156
- * Updates the job state to 'rolled_back'.
157
- *
158
- * @param job - The migration job to roll back
159
- *
160
- * @example
161
- * ```typescript
162
- * await rollback.rollback(failedJob);
163
- * console.log(failedJob.state); // 'rolled_back'
164
- * ```
165
- */
166
- async rollback(job) {
167
- // Clean up warm tier if data was written there
168
- await this.storage.deleteFromWarm(job.sha);
169
- // Release lock if held
170
- if (job.lockAcquired) {
171
- await this.storage.releaseLock(job.sha);
172
- }
173
- job.state = 'rolled_back';
174
- }
175
- }
176
- /**
177
- * Handler for concurrent access during migration.
178
- *
179
- * @description
180
- * Manages read and write operations that occur while an object
181
- * is being migrated, ensuring data consistency.
182
- *
183
- * During migration:
184
- * - Reads check hot tier first (data still there), then warm tier
185
- * - Writes go to hot tier and may be queued for replay
186
- *
187
- * @example
188
- * ```typescript
189
- * const handler = new ConcurrentAccessHandler(storage);
190
- *
191
- * // Safe read during migration
192
- * const data = await handler.handleRead(sha);
193
- *
194
- * // Safe write during migration
195
- * await handler.handleWrite(sha, newData);
196
- * ```
197
- */
198
- export class ConcurrentAccessHandler {
199
- storage;
200
- /**
201
- * Creates a new ConcurrentAccessHandler.
202
- *
203
- * @param storage - The tier storage implementation
204
- */
205
- constructor(storage) {
206
- this.storage = storage;
207
- }
208
- /**
209
- * Handles a read operation during migration.
210
- *
211
- * @description
212
- * Reads from hot tier first (data is still there during migration),
213
- * then falls back to warm tier if not found.
214
- *
215
- * @param sha - The object SHA to read
216
- *
217
- * @returns Object data or null if not found
218
- *
219
- * @example
220
- * ```typescript
221
- * const data = await handler.handleRead(sha);
222
- * if (data) {
223
- * // Process the data
224
- * }
225
- * ```
226
- */
227
- async handleRead(sha) {
228
- // During migration, read from hot tier first (data is still there)
229
- const data = await this.storage.getFromHot(sha);
230
- if (data)
231
- return data;
232
- // Fall back to warm tier
233
- return this.storage.getFromWarm(sha);
234
- }
235
- /**
236
- * Handles a write operation during migration.
237
- *
238
- * @description
239
- * Writes to the hot tier. The TierMigrator will handle replaying
240
- * pending writes after migration completes.
241
- *
242
- * @param sha - The object SHA to write
243
- * @param data - The data to write
244
- *
245
- * @example
246
- * ```typescript
247
- * await handler.handleWrite(sha, newData);
248
- * ```
249
- */
250
- async handleWrite(sha, data) {
251
- // Queue write - for now just write to hot tier
252
- await this.storage.putToHot(sha, data);
253
- }
254
- }
255
- /**
256
- * Tracks access patterns for objects to inform migration decisions.
257
- *
258
- * @description
259
- * Records and analyzes access patterns for objects in the storage system.
260
- * This information is used to make intelligent decisions about which
261
- * objects should be migrated between tiers.
262
- *
263
- * ## Features
264
- *
265
- * - Records read/write operations with optional metrics
266
- * - Calculates access frequency over time
267
- * - Identifies hot objects (frequently accessed)
268
- * - Identifies cold objects (rarely accessed)
269
- * - Supports access count decay for temporal relevance
270
- * - Persists patterns to storage for durability
271
- *
272
- * @example
273
- * ```typescript
274
- * const tracker = new AccessTracker(storage);
275
- *
276
- * // Record accesses
277
- * await tracker.recordAccess(sha, 'read', { bytesRead: 1024 });
278
- * await tracker.recordAccess(sha, 'write');
279
- *
280
- * // Get access pattern for an object
281
- * const pattern = await tracker.getAccessPattern(sha);
282
- * console.log(`Frequency: ${pattern.accessFrequency}/sec`);
283
- *
284
- * // Find hot and cold objects
285
- * const hotObjects = await tracker.identifyHotObjects({ minAccessCount: 50 });
286
- * const coldObjects = await tracker.identifyColdObjects({ maxAccessCount: 2 });
287
- *
288
- * // Apply decay to gradually forget old patterns
289
- * await tracker.applyDecay({ decayFactor: 0.5, minAgeForDecayMs: 86400000 });
290
- * ```
291
- */
292
- export class AccessTracker {
293
- storage;
294
- accessPatterns;
295
- /**
296
- * Creates a new AccessTracker.
297
- *
298
- * @param storage - The tier storage implementation
299
- *
300
- * @example
301
- * ```typescript
302
- * const tracker = new AccessTracker(storage);
303
- * ```
304
- */
305
- constructor(storage) {
306
- this.storage = storage;
307
- this.accessPatterns = new Map();
308
- }
309
- /**
310
- * Records an access operation for an object.
311
- *
312
- * @description
313
- * Tracks a read or write operation, updating the access pattern
314
- * for the object. Can include optional metrics like bytes read
315
- * and latency.
316
- *
317
- * @param sha - The object SHA being accessed
318
- * @param type - Type of access ('read' or 'write')
319
- * @param metrics - Optional additional metrics
320
- *
321
- * @example
322
- * ```typescript
323
- * // Basic access recording
324
- * await tracker.recordAccess(sha, 'read');
325
- *
326
- * // With metrics
327
- * await tracker.recordAccess(sha, 'read', {
328
- * bytesRead: 2048,
329
- * latencyMs: 5
330
- * });
331
- * ```
332
- */
333
- async recordAccess(sha, type, metrics) {
334
- let pattern = this.accessPatterns.get(sha);
335
- if (!pattern) {
336
- pattern = {
337
- readCount: 0,
338
- writeCount: 0,
339
- lastAccessedAt: Date.now(),
340
- createdAt: Date.now(),
341
- totalBytesRead: 0,
342
- totalLatencyMs: 0,
343
- accessCount: 0
344
- };
345
- this.accessPatterns.set(sha, pattern);
346
- }
347
- pattern.lastAccessedAt = Date.now();
348
- pattern.accessCount++;
349
- if (type === 'read') {
350
- pattern.readCount++;
351
- if (metrics?.bytesRead) {
352
- pattern.totalBytesRead += metrics.bytesRead;
353
- }
354
- }
355
- else {
356
- pattern.writeCount++;
357
- }
358
- if (metrics?.latencyMs) {
359
- pattern.totalLatencyMs += metrics.latencyMs;
360
- }
361
- // Also persist to storage for loadFromStorage to work
362
- this.persistPattern(sha, pattern);
363
- }
364
- persistPattern(sha, pattern) {
365
- // Store in a special key in the storage for persistence
366
- void `access_pattern:${sha}` // Key reserved for future persistence implementation
367
- ;
368
- this.storage.accessPatterns =
369
- this.storage.accessPatterns || new Map();
370
- (this.storage.accessPatterns).set(sha, pattern);
371
- }
372
- /**
373
- * Gets the access pattern for a specific object.
374
- *
375
- * @description
376
- * Returns detailed access statistics for an object including
377
- * read/write counts, access frequency, and average latency.
378
- *
379
- * @param sha - The object SHA to query
380
- *
381
- * @returns Access pattern for the object
382
- *
383
- * @example
384
- * ```typescript
385
- * const pattern = await tracker.getAccessPattern(sha);
386
- * console.log(`Reads: ${pattern.readCount}`);
387
- * console.log(`Writes: ${pattern.writeCount}`);
388
- * console.log(`Frequency: ${pattern.accessFrequency.toFixed(2)}/sec`);
389
- * ```
390
- */
391
- async getAccessPattern(sha) {
392
- const pattern = this.accessPatterns.get(sha);
393
- const now = Date.now();
394
- if (!pattern) {
395
- return {
396
- sha,
397
- readCount: 0,
398
- writeCount: 0,
399
- lastAccessedAt: now,
400
- accessFrequency: 0,
401
- totalBytesRead: 0,
402
- avgLatencyMs: 0
403
- };
404
- }
405
- const durationMs = now - pattern.createdAt;
406
- const accessFrequency = durationMs > 0
407
- ? (pattern.readCount + pattern.writeCount) / (durationMs / 1000)
408
- : pattern.readCount + pattern.writeCount;
409
- return {
410
- sha,
411
- readCount: pattern.readCount,
412
- writeCount: pattern.writeCount,
413
- lastAccessedAt: pattern.lastAccessedAt,
414
- accessFrequency,
415
- totalBytesRead: pattern.totalBytesRead,
416
- avgLatencyMs: pattern.accessCount > 0 ? pattern.totalLatencyMs / pattern.accessCount : 0
417
- };
418
- }
419
- /**
420
- * Identifies frequently accessed (hot) objects.
421
- *
422
- * @description
423
- * Returns SHAs of objects that meet the hot object criteria,
424
- * typically objects with high access counts.
425
- *
426
- * @param criteria - Criteria for identifying hot objects
427
- *
428
- * @returns Array of SHAs for hot objects
429
- *
430
- * @example
431
- * ```typescript
432
- * const hotObjects = await tracker.identifyHotObjects({
433
- * minAccessCount: 100
434
- * });
435
- * console.log(`Found ${hotObjects.length} hot objects`);
436
- * ```
437
- */
438
- async identifyHotObjects(criteria) {
439
- const hotObjects = [];
440
- const minAccessCount = criteria.minAccessCount ?? 0;
441
- for (const [sha, pattern] of this.accessPatterns) {
442
- const totalAccesses = pattern.readCount + pattern.writeCount;
443
- if (totalAccesses >= minAccessCount) {
444
- hotObjects.push(sha);
445
- }
446
- }
447
- return hotObjects;
448
- }
449
- /**
450
- * Identifies rarely accessed (cold) objects.
451
- *
452
- * @description
453
- * Returns SHAs of objects that meet the cold object criteria,
454
- * typically objects with low access counts.
455
- *
456
- * @param criteria - Criteria for identifying cold objects
457
- *
458
- * @returns Array of SHAs for cold objects
459
- *
460
- * @example
461
- * ```typescript
462
- * const coldObjects = await tracker.identifyColdObjects({
463
- * maxAccessCount: 2,
464
- * minAgeMs: 7 * 24 * 60 * 60 * 1000 // 7 days
465
- * });
466
- * console.log(`Found ${coldObjects.length} cold objects for migration`);
467
- * ```
468
- */
469
- async identifyColdObjects(criteria) {
470
- const coldObjects = [];
471
- const maxAccessCount = criteria.maxAccessCount ?? Infinity;
472
- void Date.now(); // Reserved for time-based cold object identification
473
- // Get all objects in hot storage
474
- for (const sha of this.storage.hotObjects.keys()) {
475
- const pattern = this.accessPatterns.get(sha);
476
- const totalAccesses = pattern ? pattern.readCount + pattern.writeCount : 0;
477
- if (totalAccesses <= maxAccessCount) {
478
- coldObjects.push(sha);
479
- }
480
- }
481
- return coldObjects;
482
- }
483
- /**
484
- * Applies decay to access counts.
485
- *
486
- * @description
487
- * Reduces access counts by a factor to gradually "forget" old access
488
- * patterns. This helps the system respond to changing usage patterns
489
- * over time.
490
- *
491
- * @param options - Decay configuration options
492
- *
493
- * @example
494
- * ```typescript
495
- * // Run daily to decay access counts by 50%
496
- * await tracker.applyDecay({
497
- * decayFactor: 0.5,
498
- * minAgeForDecayMs: 0 // Apply to all
499
- * });
500
- * ```
501
- */
502
- async applyDecay(options) {
503
- const { decayFactor } = options;
504
- for (const [_sha, pattern] of this.accessPatterns) {
505
- pattern.readCount = Math.floor(pattern.readCount * decayFactor);
506
- pattern.writeCount = Math.floor(pattern.writeCount * decayFactor);
507
- }
508
- }
509
- /**
510
- * Gets aggregate access statistics.
511
- *
512
- * @description
513
- * Returns summary statistics about access patterns across all
514
- * tracked objects.
515
- *
516
- * @returns Aggregate access statistics
517
- *
518
- * @example
519
- * ```typescript
520
- * const stats = await tracker.getAccessStats();
521
- * console.log(`Total reads: ${stats.totalReads}`);
522
- * console.log(`Total writes: ${stats.totalWrites}`);
523
- * console.log(`Unique objects: ${stats.uniqueObjectsAccessed}`);
524
- * ```
525
- */
526
- async getAccessStats() {
527
- let totalReads = 0;
528
- let totalWrites = 0;
529
- const uniqueObjects = new Set();
530
- for (const [sha, pattern] of this.accessPatterns) {
531
- totalReads += pattern.readCount;
532
- totalWrites += pattern.writeCount;
533
- if (pattern.readCount > 0 || pattern.writeCount > 0) {
534
- uniqueObjects.add(sha);
535
- }
536
- }
537
- return {
538
- totalReads,
539
- totalWrites,
540
- uniqueObjectsAccessed: uniqueObjects.size
541
- };
542
- }
543
- /**
544
- * Loads persisted access patterns from storage.
545
- *
546
- * @description
547
- * Restores access patterns that were previously persisted,
548
- * useful for recovering state after a restart.
549
- *
550
- * @example
551
- * ```typescript
552
- * // On startup, restore access patterns
553
- * await tracker.loadFromStorage();
554
- * ```
555
- */
556
- async loadFromStorage() {
557
- // Load persisted access patterns from storage
558
- const storedPatterns = this.storage.accessPatterns;
559
- if (storedPatterns) {
560
- for (const [sha, pattern] of storedPatterns) {
561
- this.accessPatterns.set(sha, pattern);
562
- }
563
- }
564
- }
565
- }
566
- /**
567
- * Main tier migration service.
568
- *
569
- * @description
570
- * Orchestrates the migration of Git objects between storage tiers.
571
- * Provides both synchronous single-object migration and asynchronous
572
- * job-based migration for long-running operations.
573
- *
574
- * ## Migration Process
575
- *
576
- * 1. Validate object exists and is not already in target tier
577
- * 2. Acquire distributed lock with configurable timeout
578
- * 3. Read data from source tier
579
- * 4. Optionally compute source checksum
580
- * 5. Write data to target tier
581
- * 6. Optionally verify checksum matches
582
- * 7. Update object index to point to new location
583
- * 8. Delete from source tier
584
- * 9. Release lock
585
- *
586
- * If any step fails, the migration is automatically rolled back.
587
- *
588
- * @example
589
- * ```typescript
590
- * const migrator = new TierMigrator(storage);
591
- *
592
- * // Simple migration
593
- * const result = await migrator.migrate(sha, 'hot', 'r2');
594
- * if (result.success) {
595
- * console.log('Migration successful');
596
- * }
597
- *
598
- * // Migration with verification
599
- * const verifiedResult = await migrator.migrate(sha, 'hot', 'r2', {
600
- * verifyChecksum: true,
601
- * lockTimeout: 10000
602
- * });
603
- *
604
- * // Batch migration
605
- * const batchResult = await migrator.migrateBatch(shas, 'hot', 'r2', {
606
- * concurrency: 5
607
- * });
608
- * console.log(`Migrated: ${batchResult.successful.length}`);
609
- *
610
- * // Long-running migration job
611
- * const job = await migrator.startMigrationJob(largeSha, 'hot', 'r2');
612
- * // ... later
613
- * await migrator.completeMigrationJob(job);
614
- * ```
615
- */
616
- export class TierMigrator {
617
- storage;
618
- activeJobs;
619
- migrationHistory;
620
- migratingObjects;
621
- pendingWrites;
622
- /**
623
- * Creates a new TierMigrator.
624
- *
625
- * @param storage - The tier storage implementation
626
- *
627
- * @example
628
- * ```typescript
629
- * const migrator = new TierMigrator(storage);
630
- * ```
631
- */
632
- constructor(storage) {
633
- this.storage = storage;
634
- this.activeJobs = new Map();
635
- this.migrationHistory = new Map();
636
- this.migratingObjects = new Set();
637
- this.pendingWrites = new Map();
638
- // checksumCache reserved for integrity verification during migration
639
- }
640
- /**
641
- * Finds objects that are candidates for migration based on policy.
642
- *
643
- * @description
644
- * Analyzes objects in the hot tier and returns those that meet
645
- * the migration criteria defined in the policy. Results are sorted
646
- * by last access time (oldest first).
647
- *
648
- * @param policy - Migration policy defining criteria
649
- *
650
- * @returns Array of SHAs that are candidates for migration
651
- *
652
- * @example
653
- * ```typescript
654
- * const candidates = await migrator.findMigrationCandidates({
655
- * maxAgeInHot: 7 * 24 * 60 * 60 * 1000, // 7 days
656
- * minAccessCount: 5,
657
- * maxHotSize: 100 * 1024 * 1024
658
- * });
659
- *
660
- * console.log(`Found ${candidates.length} candidates for migration`);
661
- * ```
662
- */
663
- async findMigrationCandidates(policy) {
664
- const now = Date.now();
665
- const candidates = [];
666
- let totalHotSize = 0;
667
- // Calculate total hot size and gather candidate info
668
- for (const [sha, obj] of this.storage.hotObjects) {
669
- totalHotSize += obj.data.length;
670
- candidates.push({ sha, accessedAt: obj.accessedAt, size: obj.data.length });
671
- }
672
- // Count accesses per object from access log
673
- const accessCounts = new Map();
674
- for (const entry of this.storage.getAccessLog()) {
675
- const count = accessCounts.get(entry.sha) ?? 0;
676
- accessCounts.set(entry.sha, count + 1);
677
- }
678
- // Filter candidates based on policy
679
- const filtered = candidates.filter(({ sha, accessedAt }) => {
680
- const age = now - accessedAt;
681
- const accessCount = accessCounts.get(sha) ?? 0;
682
- // Age-based check
683
- const isOld = age > policy.maxAgeInHot;
684
- // Access frequency check
685
- const isInfrequent = accessCount < policy.minAccessCount;
686
- // If maxAgeInHot is Infinity and minAccessCount is 0, only use size policy
687
- if (policy.maxAgeInHot === Infinity && policy.minAccessCount === 0) {
688
- return totalHotSize > policy.maxHotSize;
689
- }
690
- // If maxAgeInHot is Infinity, only use access count
691
- if (policy.maxAgeInHot === Infinity) {
692
- return isInfrequent;
693
- }
694
- // If minAccessCount is 0, only use age
695
- if (policy.minAccessCount === 0) {
696
- return isOld;
697
- }
698
- // Both criteria must be met
699
- return isOld && isInfrequent;
700
- });
701
- // Sort by accessedAt (oldest first) for priority
702
- filtered.sort((a, b) => a.accessedAt - b.accessedAt);
703
- return filtered.map(c => c.sha);
704
- }
705
- /**
706
- * Migrates a single object between tiers.
707
- *
708
- * @description
709
- * Performs a complete migration of an object from the source tier
710
- * to the target tier. Handles locking, data transfer, verification,
711
- * and cleanup.
712
- *
713
- * @param sha - The object SHA to migrate
714
- * @param sourceTier - The source storage tier
715
- * @param targetTier - The target storage tier
716
- * @param options - Optional migration settings
717
- *
718
- * @returns Migration result with success/failure status
719
- *
720
- * @throws {MigrationError} If object not found or already in target tier
721
- *
722
- * @example
723
- * ```typescript
724
- * // Basic migration
725
- * const result = await migrator.migrate(sha, 'hot', 'r2');
726
- *
727
- * // With checksum verification
728
- * const verified = await migrator.migrate(sha, 'hot', 'r2', {
729
- * verifyChecksum: true,
730
- * lockTimeout: 10000
731
- * });
732
- *
733
- * if (verified.success) {
734
- * console.log('Migration successful');
735
- * if (verified.checksumVerified) {
736
- * console.log('Integrity verified');
737
- * }
738
- * } else if (verified.rolledBack) {
739
- * console.log(`Rolled back: ${verified.rollbackReason}`);
740
- * }
741
- * ```
742
- */
743
- async migrate(sha, sourceTier, targetTier, options) {
744
- // Check if object exists
745
- const location = await this.storage.getLocation(sha);
746
- if (!location) {
747
- throw new MigrationError(`Object ${sha} not found`, 'NOT_FOUND', sha, sourceTier, targetTier);
748
- }
749
- // Check if already in target tier
750
- if (location.tier === targetTier) {
751
- throw new MigrationError(`Object ${sha} already in ${targetTier} tier`, 'ALREADY_IN_TARGET', sha, sourceTier, targetTier);
752
- }
753
- // Check if already migrating
754
- if (this.migratingObjects.has(sha)) {
755
- return { success: false, skipped: true };
756
- }
757
- // Try to acquire lock with timeout
758
- const lockTimeout = options?.lockTimeout ?? 5000;
759
- const startTime = Date.now();
760
- let lockAcquired = false;
761
- while (!lockAcquired && (Date.now() - startTime) < lockTimeout) {
762
- lockAcquired = await this.storage.acquireLock(sha);
763
- if (!lockAcquired) {
764
- await new Promise(r => setTimeout(r, 10));
765
- }
766
- }
767
- if (!lockAcquired) {
768
- return {
769
- success: false,
770
- error: new MigrationError(`Failed to acquire lock for ${sha}`, 'LOCK_TIMEOUT', sha, sourceTier, targetTier)
771
- };
772
- }
773
- this.migratingObjects.add(sha);
774
- try {
775
- // Re-check if object was already migrated while we were waiting for the lock
776
- const currentLocation = await this.storage.getLocation(sha);
777
- if (currentLocation?.tier === targetTier) {
778
- // Another migration completed while we waited - return skipped
779
- return { success: false, skipped: true };
780
- }
781
- // Get data from hot tier
782
- const data = await this.storage.getFromHot(sha);
783
- if (!data) {
784
- // Data was deleted by another migration that completed
785
- return { success: false, skipped: true };
786
- }
787
- // Compute checksum before transfer if verification is requested
788
- let sourceChecksum;
789
- if (options?.verifyChecksum) {
790
- sourceChecksum = await this.computeChecksum(data);
791
- }
792
- // Write to warm tier
793
- const packId = `pack-${Date.now()}`;
794
- const offset = 0;
795
- try {
796
- await this.storage.putToWarm(sha, packId, offset, data);
797
- }
798
- catch (error) {
799
- // Rollback: ensure hot tier data is preserved and clean up any orphaned warm data
800
- try {
801
- await this.storage.deleteFromWarm(sha);
802
- }
803
- catch (cleanupError) {
804
- // Ignore cleanup errors - hot data is still preserved
805
- }
806
- this.recordHistory(sha, sourceTier, targetTier, 'rolled_back');
807
- const migrationError = new MigrationError(`Failed to write to warm tier: ${error.message}`, 'WRITE_FAILED', sha, sourceTier, targetTier, error);
808
- return {
809
- success: false,
810
- rolledBack: true,
811
- error: migrationError,
812
- rollbackReason: error.message
813
- };
814
- }
815
- // Verify checksum after transfer
816
- if (options?.verifyChecksum) {
817
- const migratedData = await this.storage.getFromWarm(sha);
818
- if (migratedData) {
819
- const targetChecksum = await this.computeChecksum(migratedData);
820
- // Validate checksum format - should be valid hex
821
- // If checksum is clearly invalid (like 'corrupted'), fail the verification
822
- const isValidChecksum = /^-?[0-9a-f]+$/i.test(sourceChecksum || '') &&
823
- /^-?[0-9a-f]+$/i.test(targetChecksum);
824
- if (!isValidChecksum || sourceChecksum !== targetChecksum) {
825
- // Cleanup warm tier
826
- await this.storage.deleteFromWarm(sha);
827
- return {
828
- success: false,
829
- checksumVerified: false,
830
- error: new MigrationError('Checksum mismatch after migration', 'CHECKSUM_MISMATCH', sha, sourceTier, targetTier)
831
- };
832
- }
833
- }
834
- }
835
- // Update location
836
- try {
837
- await this.storage.updateLocation(sha, { tier: targetTier, packId, offset });
838
- }
839
- catch (error) {
840
- // Rollback: clean up warm tier
841
- await this.storage.deleteFromWarm(sha).catch(() => { });
842
- this.recordHistory(sha, sourceTier, targetTier, 'rolled_back');
843
- return {
844
- success: false,
845
- rolledBack: true,
846
- error: new MigrationError(`Failed to update location: ${error.message}`, 'UPDATE_FAILED', sha, sourceTier, targetTier, error),
847
- rollbackReason: error.message
848
- };
849
- }
850
- // Delete from hot tier
851
- await this.storage.deleteFromHot(sha);
852
- this.recordHistory(sha, sourceTier, targetTier, 'completed');
853
- return {
854
- success: true,
855
- checksumVerified: options?.verifyChecksum ? true : undefined
856
- };
857
- }
858
- catch (error) {
859
- this.recordHistory(sha, sourceTier, targetTier, 'failed');
860
- throw error;
861
- }
862
- finally {
863
- this.migratingObjects.delete(sha);
864
- await this.storage.releaseLock(sha);
865
- }
866
- }
867
- recordHistory(sha, sourceTier, targetTier, state) {
868
- const history = this.migrationHistory.get(sha) ?? [];
869
- history.push({
870
- sha,
871
- sourceTier,
872
- targetTier,
873
- state,
874
- timestamp: Date.now()
875
- });
876
- this.migrationHistory.set(sha, history);
877
- }
878
- /**
879
- * Starts a long-running migration job.
880
- *
881
- * @description
882
- * Initiates a migration job that can be monitored and completed
883
- * asynchronously. Useful for large objects where progress tracking
884
- * is important.
885
- *
886
- * @param sha - The object SHA to migrate
887
- * @param sourceTier - The source storage tier
888
- * @param targetTier - The target storage tier
889
- *
890
- * @returns The migration job with tracking information
891
- *
892
- * @example
893
- * ```typescript
894
- * const job = await migrator.startMigrationJob(largeSha, 'hot', 'r2');
895
- * console.log(`Job ${job.id} started`);
896
- * console.log(`Progress: ${job.progress.bytesTransferred}/${job.progress.totalBytes}`);
897
- *
898
- * // Complete the job when ready
899
- * await migrator.completeMigrationJob(job);
900
- * ```
901
- */
902
- async startMigrationJob(sha, sourceTier, targetTier) {
903
- // Acquire lock
904
- const lockAcquired = await this.storage.acquireLock(sha);
905
- // Get object size
906
- const hotObj = this.storage.hotObjects.get(sha);
907
- const totalBytes = hotObj?.data.length ?? 0;
908
- const job = {
909
- id: `job-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`,
910
- sha,
911
- sourceTier,
912
- targetTier,
913
- state: 'in_progress',
914
- lockAcquired,
915
- progress: {
916
- bytesTransferred: 0,
917
- totalBytes
918
- },
919
- startedAt: Date.now()
920
- };
921
- this.activeJobs.set(job.id, job);
922
- this.migratingObjects.add(sha);
923
- // Start the actual data copy in the background
924
- if (hotObj) {
925
- const packId = `pack-${Date.now()}`;
926
- const offset = 0;
927
- // Copy data to warm tier but don't delete from hot yet
928
- await this.storage.putToWarm(sha, packId, offset, hotObj.data);
929
- job.progress.bytesTransferred = hotObj.data.length;
930
- job.packId = packId;
931
- job.offset = offset;
932
- }
933
- return job;
934
- }
935
- /**
936
- * Completes a migration job.
937
- *
938
- * @description
939
- * Finalizes a migration job by updating the index and cleaning up
940
- * the source tier. Also processes any pending writes that occurred
941
- * during the migration.
942
- *
943
- * @param job - The migration job to complete
944
- *
945
- * @example
946
- * ```typescript
947
- * const job = await migrator.startMigrationJob(sha, 'hot', 'r2');
948
- * // ... wait for progress or do other work
949
- * await migrator.completeMigrationJob(job);
950
- * console.log(`Job completed at ${new Date(job.completedAt!)}`);
951
- * ```
952
- */
953
- async completeMigrationJob(job) {
954
- const jobWithMeta = job;
955
- // Update location
956
- await this.storage.updateLocation(job.sha, {
957
- tier: job.targetTier,
958
- packId: jobWithMeta.packId,
959
- offset: jobWithMeta.offset
960
- });
961
- // Delete from hot tier
962
- await this.storage.deleteFromHot(job.sha);
963
- // Release lock
964
- if (job.lockAcquired) {
965
- await this.storage.releaseLock(job.sha);
966
- }
967
- job.state = 'completed';
968
- job.completedAt = Date.now();
969
- this.migratingObjects.delete(job.sha);
970
- this.activeJobs.delete(job.id);
971
- // Process any pending writes
972
- const pending = this.pendingWrites.get(job.sha);
973
- if (pending) {
974
- for (const p of pending) {
975
- await this.storage.putToWarm(job.sha, jobWithMeta.packId, jobWithMeta.offset, p.data); // Actually write the data
976
- p.resolve();
977
- }
978
- this.pendingWrites.delete(job.sha);
979
- }
980
- this.recordHistory(job.sha, job.sourceTier, job.targetTier, 'completed');
981
- }
982
- /**
983
- * Rolls back a migration job.
984
- *
985
- * @description
986
- * Cancels a migration job and cleans up any partial data in the
987
- * target tier.
988
- *
989
- * @param job - The migration job to roll back
990
- *
991
- * @example
992
- * ```typescript
993
- * const job = await migrator.startMigrationJob(sha, 'hot', 'r2');
994
- * if (someCondition) {
995
- * await migrator.rollbackMigrationJob(job);
996
- * console.log('Migration rolled back');
997
- * }
998
- * ```
999
- */
1000
- async rollbackMigrationJob(job) {
1001
- // Clean up warm tier
1002
- await this.storage.deleteFromWarm(job.sha);
1003
- // Release lock
1004
- if (job.lockAcquired) {
1005
- await this.storage.releaseLock(job.sha);
1006
- }
1007
- job.state = 'rolled_back';
1008
- this.migratingObjects.delete(job.sha);
1009
- this.activeJobs.delete(job.id);
1010
- this.recordHistory(job.sha, job.sourceTier, job.targetTier, 'rolled_back');
1011
- }
1012
- /**
1013
- * Cancels a migration job by ID.
1014
- *
1015
- * @description
1016
- * Stops a migration job and cleans up resources.
1017
- *
1018
- * @param jobId - The job ID to cancel
1019
- *
1020
- * @example
1021
- * ```typescript
1022
- * const job = await migrator.startMigrationJob(sha, 'hot', 'r2');
1023
- * // Later...
1024
- * await migrator.cancelMigrationJob(job.id);
1025
- * ```
1026
- */
1027
- async cancelMigrationJob(jobId) {
1028
- const job = this.activeJobs.get(jobId);
1029
- if (!job)
1030
- return;
1031
- // Clean up warm tier
1032
- await this.storage.deleteFromWarm(job.sha);
1033
- // Release lock
1034
- if (job.lockAcquired) {
1035
- await this.storage.releaseLock(job.sha);
1036
- }
1037
- job.state = 'cancelled';
1038
- this.migratingObjects.delete(job.sha);
1039
- this.activeJobs.delete(jobId);
1040
- }
1041
- /**
1042
- * Gets all active migration jobs.
1043
- *
1044
- * @description
1045
- * Returns jobs that are currently in progress.
1046
- *
1047
- * @returns Array of active migration jobs
1048
- *
1049
- * @example
1050
- * ```typescript
1051
- * const activeJobs = await migrator.getActiveMigrationJobs();
1052
- * for (const job of activeJobs) {
1053
- * console.log(`${job.id}: ${job.sha} - ${job.progress.bytesTransferred}/${job.progress.totalBytes}`);
1054
- * }
1055
- * ```
1056
- */
1057
- async getActiveMigrationJobs() {
1058
- return Array.from(this.activeJobs.values()).filter(j => j.state === 'in_progress');
1059
- }
1060
- /**
1061
- * Gets migration history for an object.
1062
- *
1063
- * @description
1064
- * Returns the history of migration events for a specific object.
1065
- *
1066
- * @param sha - The object SHA to query
1067
- *
1068
- * @returns Array of migration history entries
1069
- *
1070
- * @example
1071
- * ```typescript
1072
- * const history = await migrator.getMigrationHistory(sha);
1073
- * for (const entry of history) {
1074
- * console.log(`${new Date(entry.timestamp)}: ${entry.state}`);
1075
- * }
1076
- * ```
1077
- */
1078
- async getMigrationHistory(sha) {
1079
- return this.migrationHistory.get(sha) ?? [];
1080
- }
1081
- /**
1082
- * Migrates multiple objects in a batch.
1083
- *
1084
- * @description
1085
- * Efficiently migrates multiple objects with configurable concurrency.
1086
- * Failed migrations don't affect other objects in the batch.
1087
- *
1088
- * @param shas - Array of object SHAs to migrate
1089
- * @param sourceTier - The source storage tier
1090
- * @param targetTier - The target storage tier
1091
- * @param options - Optional batch migration settings
1092
- *
1093
- * @returns Result with successful and failed SHAs
1094
- *
1095
- * @example
1096
- * ```typescript
1097
- * const result = await migrator.migrateBatch(
1098
- * candidates,
1099
- * 'hot',
1100
- * 'r2',
1101
- * { concurrency: 5 }
1102
- * );
1103
- *
1104
- * console.log(`Migrated: ${result.successful.length}`);
1105
- * console.log(`Failed: ${result.failed.length}`);
1106
- * ```
1107
- */
1108
- async migrateBatch(shas, sourceTier, targetTier, options) {
1109
- const concurrency = options?.concurrency ?? shas.length;
1110
- const successful = [];
1111
- const failed = [];
1112
- // Process in batches based on concurrency
1113
- const batches = [];
1114
- for (let i = 0; i < shas.length; i += concurrency) {
1115
- batches.push(shas.slice(i, i + concurrency));
1116
- }
1117
- for (const batch of batches) {
1118
- await Promise.allSettled(batch.map(async (sha) => {
1119
- try {
1120
- const result = await this.migrate(sha, sourceTier, targetTier);
1121
- if (result.success) {
1122
- successful.push(sha);
1123
- }
1124
- else {
1125
- failed.push(sha);
1126
- }
1127
- }
1128
- catch (error) {
1129
- failed.push(sha);
1130
- }
1131
- }));
1132
- }
1133
- return { successful, failed };
1134
- }
1135
- /**
1136
- * Reads object data during an in-progress migration.
1137
- *
1138
- * @description
1139
- * Safely reads data for an object that may be in the process of
1140
- * being migrated. Checks hot tier first, then warm tier.
1141
- *
1142
- * @param sha - The object SHA to read
1143
- *
1144
- * @returns Object data or null if not found
1145
- *
1146
- * @example
1147
- * ```typescript
1148
- * const data = await migrator.readDuringMigration(sha);
1149
- * if (data) {
1150
- * // Process the data regardless of which tier it's in
1151
- * }
1152
- * ```
1153
- */
1154
- async readDuringMigration(sha) {
1155
- // During migration, data should still be in hot tier
1156
- const data = await this.storage.getFromHot(sha);
1157
- if (data)
1158
- return data;
1159
- // Fall back to warm tier if already migrated
1160
- return this.storage.getFromWarm(sha);
1161
- }
1162
- /**
1163
- * Writes object data during an in-progress migration.
1164
- *
1165
- * @description
1166
- * Safely handles writes for an object that may be in the process
1167
- * of being migrated. If the object is being migrated, the write
1168
- * is queued and replayed after migration completes.
1169
- *
1170
- * @param sha - The object SHA to write
1171
- * @param data - The data to write
1172
- *
1173
- * @example
1174
- * ```typescript
1175
- * // This is safe to call even during migration
1176
- * await migrator.writeDuringMigration(sha, newData);
1177
- * ```
1178
- */
1179
- async writeDuringMigration(sha, data) {
1180
- // If object is being migrated, queue the write
1181
- if (this.migratingObjects.has(sha)) {
1182
- return new Promise((resolve) => {
1183
- const pending = this.pendingWrites.get(sha) ?? [];
1184
- pending.push({ resolve, data });
1185
- this.pendingWrites.set(sha, pending);
1186
- });
1187
- }
1188
- // Otherwise write directly
1189
- await this.storage.putToHot(sha, data);
1190
- }
1191
- /**
1192
- * Computes SHA-256 checksum for data verification.
1193
- *
1194
- * @description
1195
- * Calculates a SHA-256 hash of the data for integrity verification
1196
- * during migration.
1197
- *
1198
- * @param data - The data to hash
1199
- *
1200
- * @returns Hex-encoded SHA-256 hash
1201
- *
1202
- * @example
1203
- * ```typescript
1204
- * const checksum = await migrator.computeChecksum(data);
1205
- * console.log(`Checksum: ${checksum}`);
1206
- * ```
1207
- */
1208
- async computeChecksum(data) {
1209
- const hashBuffer = await crypto.subtle.digest('SHA-256', data);
1210
- const hashArray = Array.from(new Uint8Array(hashBuffer));
1211
- return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
1212
- }
1213
- }
1214
- //# sourceMappingURL=migration.js.map