gitx.do 0.1.1 → 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 (356) 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 +14 -469
  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 -176
  20. package/dist/cli/commands/add.d.ts.map +0 -1
  21. package/dist/cli/commands/add.js +0 -979
  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/checkout.d.ts +0 -73
  32. package/dist/cli/commands/checkout.d.ts.map +0 -1
  33. package/dist/cli/commands/checkout.js +0 -725
  34. package/dist/cli/commands/checkout.js.map +0 -1
  35. package/dist/cli/commands/commit.d.ts +0 -182
  36. package/dist/cli/commands/commit.d.ts.map +0 -1
  37. package/dist/cli/commands/commit.js +0 -457
  38. package/dist/cli/commands/commit.js.map +0 -1
  39. package/dist/cli/commands/diff.d.ts +0 -464
  40. package/dist/cli/commands/diff.d.ts.map +0 -1
  41. package/dist/cli/commands/diff.js +0 -959
  42. package/dist/cli/commands/diff.js.map +0 -1
  43. package/dist/cli/commands/log.d.ts +0 -239
  44. package/dist/cli/commands/log.d.ts.map +0 -1
  45. package/dist/cli/commands/log.js +0 -535
  46. package/dist/cli/commands/log.js.map +0 -1
  47. package/dist/cli/commands/merge.d.ts +0 -106
  48. package/dist/cli/commands/merge.d.ts.map +0 -1
  49. package/dist/cli/commands/merge.js +0 -852
  50. package/dist/cli/commands/merge.js.map +0 -1
  51. package/dist/cli/commands/review.d.ts +0 -457
  52. package/dist/cli/commands/review.d.ts.map +0 -1
  53. package/dist/cli/commands/review.js +0 -558
  54. package/dist/cli/commands/review.js.map +0 -1
  55. package/dist/cli/commands/stash.d.ts +0 -157
  56. package/dist/cli/commands/stash.d.ts.map +0 -1
  57. package/dist/cli/commands/stash.js +0 -655
  58. package/dist/cli/commands/stash.js.map +0 -1
  59. package/dist/cli/commands/status.d.ts +0 -269
  60. package/dist/cli/commands/status.d.ts.map +0 -1
  61. package/dist/cli/commands/status.js +0 -492
  62. package/dist/cli/commands/status.js.map +0 -1
  63. package/dist/cli/commands/web.d.ts +0 -199
  64. package/dist/cli/commands/web.d.ts.map +0 -1
  65. package/dist/cli/commands/web.js +0 -697
  66. package/dist/cli/commands/web.js.map +0 -1
  67. package/dist/cli/fs-adapter.d.ts +0 -656
  68. package/dist/cli/fs-adapter.d.ts.map +0 -1
  69. package/dist/cli/fs-adapter.js +0 -1177
  70. package/dist/cli/fs-adapter.js.map +0 -1
  71. package/dist/cli/fsx-cli-adapter.d.ts +0 -359
  72. package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
  73. package/dist/cli/fsx-cli-adapter.js +0 -619
  74. package/dist/cli/fsx-cli-adapter.js.map +0 -1
  75. package/dist/cli/index.d.ts +0 -387
  76. package/dist/cli/index.d.ts.map +0 -1
  77. package/dist/cli/index.js +0 -579
  78. package/dist/cli/index.js.map +0 -1
  79. package/dist/cli/ui/components/DiffView.d.ts +0 -12
  80. package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
  81. package/dist/cli/ui/components/DiffView.js +0 -11
  82. package/dist/cli/ui/components/DiffView.js.map +0 -1
  83. package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -10
  84. package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
  85. package/dist/cli/ui/components/ErrorDisplay.js +0 -11
  86. package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
  87. package/dist/cli/ui/components/FuzzySearch.d.ts +0 -15
  88. package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
  89. package/dist/cli/ui/components/FuzzySearch.js +0 -12
  90. package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
  91. package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -10
  92. package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
  93. package/dist/cli/ui/components/LoadingSpinner.js +0 -10
  94. package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
  95. package/dist/cli/ui/components/NavigationList.d.ts +0 -14
  96. package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
  97. package/dist/cli/ui/components/NavigationList.js +0 -11
  98. package/dist/cli/ui/components/NavigationList.js.map +0 -1
  99. package/dist/cli/ui/components/ScrollableContent.d.ts +0 -13
  100. package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
  101. package/dist/cli/ui/components/ScrollableContent.js +0 -11
  102. package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
  103. package/dist/cli/ui/components/index.d.ts +0 -7
  104. package/dist/cli/ui/components/index.d.ts.map +0 -1
  105. package/dist/cli/ui/components/index.js +0 -9
  106. package/dist/cli/ui/components/index.js.map +0 -1
  107. package/dist/cli/ui/terminal-ui.d.ts +0 -85
  108. package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
  109. package/dist/cli/ui/terminal-ui.js +0 -121
  110. package/dist/cli/ui/terminal-ui.js.map +0 -1
  111. package/dist/do/BashModule.d.ts +0 -871
  112. package/dist/do/BashModule.d.ts.map +0 -1
  113. package/dist/do/BashModule.js +0 -1143
  114. package/dist/do/BashModule.js.map +0 -1
  115. package/dist/do/FsModule.d.ts +0 -612
  116. package/dist/do/FsModule.d.ts.map +0 -1
  117. package/dist/do/FsModule.js +0 -1120
  118. package/dist/do/FsModule.js.map +0 -1
  119. package/dist/do/GitModule.d.ts +0 -635
  120. package/dist/do/GitModule.d.ts.map +0 -1
  121. package/dist/do/GitModule.js +0 -784
  122. package/dist/do/GitModule.js.map +0 -1
  123. package/dist/do/GitRepoDO.d.ts +0 -281
  124. package/dist/do/GitRepoDO.d.ts.map +0 -1
  125. package/dist/do/GitRepoDO.js +0 -479
  126. package/dist/do/GitRepoDO.js.map +0 -1
  127. package/dist/do/bash-ast.d.ts +0 -246
  128. package/dist/do/bash-ast.d.ts.map +0 -1
  129. package/dist/do/bash-ast.js +0 -888
  130. package/dist/do/bash-ast.js.map +0 -1
  131. package/dist/do/container-executor.d.ts +0 -491
  132. package/dist/do/container-executor.d.ts.map +0 -1
  133. package/dist/do/container-executor.js +0 -731
  134. package/dist/do/container-executor.js.map +0 -1
  135. package/dist/do/index.d.ts +0 -53
  136. package/dist/do/index.d.ts.map +0 -1
  137. package/dist/do/index.js +0 -91
  138. package/dist/do/index.js.map +0 -1
  139. package/dist/do/tiered-storage.d.ts +0 -403
  140. package/dist/do/tiered-storage.d.ts.map +0 -1
  141. package/dist/do/tiered-storage.js +0 -689
  142. package/dist/do/tiered-storage.js.map +0 -1
  143. package/dist/do/withBash.d.ts +0 -231
  144. package/dist/do/withBash.d.ts.map +0 -1
  145. package/dist/do/withBash.js +0 -244
  146. package/dist/do/withBash.js.map +0 -1
  147. package/dist/do/withFs.d.ts +0 -237
  148. package/dist/do/withFs.d.ts.map +0 -1
  149. package/dist/do/withFs.js +0 -387
  150. package/dist/do/withFs.js.map +0 -1
  151. package/dist/do/withGit.d.ts +0 -180
  152. package/dist/do/withGit.d.ts.map +0 -1
  153. package/dist/do/withGit.js +0 -271
  154. package/dist/do/withGit.js.map +0 -1
  155. package/dist/durable-object/object-store.d.ts +0 -633
  156. package/dist/durable-object/object-store.d.ts.map +0 -1
  157. package/dist/durable-object/object-store.js +0 -1164
  158. package/dist/durable-object/object-store.js.map +0 -1
  159. package/dist/durable-object/schema.d.ts.map +0 -1
  160. package/dist/durable-object/schema.js.map +0 -1
  161. package/dist/durable-object/wal.d.ts +0 -416
  162. package/dist/durable-object/wal.d.ts.map +0 -1
  163. package/dist/durable-object/wal.js +0 -445
  164. package/dist/durable-object/wal.js.map +0 -1
  165. package/dist/mcp/adapter.d.ts +0 -772
  166. package/dist/mcp/adapter.d.ts.map +0 -1
  167. package/dist/mcp/adapter.js +0 -895
  168. package/dist/mcp/adapter.js.map +0 -1
  169. package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
  170. package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
  171. package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
  172. package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
  173. package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
  174. package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
  175. package/dist/mcp/sandbox/object-store-proxy.js +0 -30
  176. package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
  177. package/dist/mcp/sandbox/template.d.ts +0 -17
  178. package/dist/mcp/sandbox/template.d.ts.map +0 -1
  179. package/dist/mcp/sandbox/template.js +0 -71
  180. package/dist/mcp/sandbox/template.js.map +0 -1
  181. package/dist/mcp/sandbox.d.ts +0 -764
  182. package/dist/mcp/sandbox.d.ts.map +0 -1
  183. package/dist/mcp/sandbox.js +0 -1362
  184. package/dist/mcp/sandbox.js.map +0 -1
  185. package/dist/mcp/sdk-adapter.d.ts +0 -835
  186. package/dist/mcp/sdk-adapter.d.ts.map +0 -1
  187. package/dist/mcp/sdk-adapter.js +0 -974
  188. package/dist/mcp/sdk-adapter.js.map +0 -1
  189. package/dist/mcp/tools/do.d.ts +0 -32
  190. package/dist/mcp/tools/do.d.ts.map +0 -1
  191. package/dist/mcp/tools/do.js +0 -117
  192. package/dist/mcp/tools/do.js.map +0 -1
  193. package/dist/mcp/tools.d.ts +0 -548
  194. package/dist/mcp/tools.d.ts.map +0 -1
  195. package/dist/mcp/tools.js +0 -3170
  196. package/dist/mcp/tools.js.map +0 -1
  197. package/dist/ops/blame.d.ts +0 -551
  198. package/dist/ops/blame.d.ts.map +0 -1
  199. package/dist/ops/blame.js +0 -1037
  200. package/dist/ops/blame.js.map +0 -1
  201. package/dist/ops/branch.d.ts +0 -766
  202. package/dist/ops/branch.d.ts.map +0 -1
  203. package/dist/ops/branch.js +0 -950
  204. package/dist/ops/branch.js.map +0 -1
  205. package/dist/ops/commit-traversal.d.ts +0 -349
  206. package/dist/ops/commit-traversal.d.ts.map +0 -1
  207. package/dist/ops/commit-traversal.js +0 -821
  208. package/dist/ops/commit-traversal.js.map +0 -1
  209. package/dist/ops/commit.d.ts +0 -555
  210. package/dist/ops/commit.d.ts.map +0 -1
  211. package/dist/ops/commit.js +0 -826
  212. package/dist/ops/commit.js.map +0 -1
  213. package/dist/ops/merge-base.d.ts +0 -397
  214. package/dist/ops/merge-base.d.ts.map +0 -1
  215. package/dist/ops/merge-base.js +0 -691
  216. package/dist/ops/merge-base.js.map +0 -1
  217. package/dist/ops/merge.d.ts +0 -855
  218. package/dist/ops/merge.d.ts.map +0 -1
  219. package/dist/ops/merge.js +0 -1551
  220. package/dist/ops/merge.js.map +0 -1
  221. package/dist/ops/tag.d.ts +0 -247
  222. package/dist/ops/tag.d.ts.map +0 -1
  223. package/dist/ops/tag.js +0 -649
  224. package/dist/ops/tag.js.map +0 -1
  225. package/dist/ops/tree-builder.d.ts +0 -178
  226. package/dist/ops/tree-builder.d.ts.map +0 -1
  227. package/dist/ops/tree-builder.js +0 -271
  228. package/dist/ops/tree-builder.js.map +0 -1
  229. package/dist/ops/tree-diff.d.ts +0 -291
  230. package/dist/ops/tree-diff.d.ts.map +0 -1
  231. package/dist/ops/tree-diff.js +0 -705
  232. package/dist/ops/tree-diff.js.map +0 -1
  233. package/dist/pack/delta.d.ts +0 -248
  234. package/dist/pack/delta.d.ts.map +0 -1
  235. package/dist/pack/delta.js +0 -740
  236. package/dist/pack/delta.js.map +0 -1
  237. package/dist/pack/format.d.ts +0 -446
  238. package/dist/pack/format.d.ts.map +0 -1
  239. package/dist/pack/format.js +0 -572
  240. package/dist/pack/format.js.map +0 -1
  241. package/dist/pack/full-generation.d.ts +0 -612
  242. package/dist/pack/full-generation.d.ts.map +0 -1
  243. package/dist/pack/full-generation.js +0 -1378
  244. package/dist/pack/full-generation.js.map +0 -1
  245. package/dist/pack/generation.d.ts +0 -441
  246. package/dist/pack/generation.d.ts.map +0 -1
  247. package/dist/pack/generation.js +0 -707
  248. package/dist/pack/generation.js.map +0 -1
  249. package/dist/pack/index.d.ts +0 -502
  250. package/dist/pack/index.d.ts.map +0 -1
  251. package/dist/pack/index.js +0 -833
  252. package/dist/pack/index.js.map +0 -1
  253. package/dist/refs/branch.d.ts +0 -683
  254. package/dist/refs/branch.d.ts.map +0 -1
  255. package/dist/refs/branch.js +0 -881
  256. package/dist/refs/branch.js.map +0 -1
  257. package/dist/refs/storage.d.ts +0 -833
  258. package/dist/refs/storage.d.ts.map +0 -1
  259. package/dist/refs/storage.js +0 -1023
  260. package/dist/refs/storage.js.map +0 -1
  261. package/dist/refs/tag.d.ts +0 -860
  262. package/dist/refs/tag.d.ts.map +0 -1
  263. package/dist/refs/tag.js +0 -996
  264. package/dist/refs/tag.js.map +0 -1
  265. package/dist/storage/backend.d.ts +0 -425
  266. package/dist/storage/backend.d.ts.map +0 -1
  267. package/dist/storage/backend.js +0 -41
  268. package/dist/storage/backend.js.map +0 -1
  269. package/dist/storage/fsx-adapter.d.ts +0 -204
  270. package/dist/storage/fsx-adapter.d.ts.map +0 -1
  271. package/dist/storage/fsx-adapter.js +0 -518
  272. package/dist/storage/fsx-adapter.js.map +0 -1
  273. package/dist/storage/lru-cache.d.ts +0 -691
  274. package/dist/storage/lru-cache.d.ts.map +0 -1
  275. package/dist/storage/lru-cache.js +0 -813
  276. package/dist/storage/lru-cache.js.map +0 -1
  277. package/dist/storage/object-index.d.ts +0 -585
  278. package/dist/storage/object-index.d.ts.map +0 -1
  279. package/dist/storage/object-index.js +0 -532
  280. package/dist/storage/object-index.js.map +0 -1
  281. package/dist/storage/r2-pack.d.ts +0 -1257
  282. package/dist/storage/r2-pack.d.ts.map +0 -1
  283. package/dist/storage/r2-pack.js +0 -1773
  284. package/dist/storage/r2-pack.js.map +0 -1
  285. package/dist/tiered/cdc-pipeline.d.ts +0 -1888
  286. package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
  287. package/dist/tiered/cdc-pipeline.js +0 -1880
  288. package/dist/tiered/cdc-pipeline.js.map +0 -1
  289. package/dist/tiered/migration.d.ts +0 -1104
  290. package/dist/tiered/migration.d.ts.map +0 -1
  291. package/dist/tiered/migration.js +0 -1217
  292. package/dist/tiered/migration.js.map +0 -1
  293. package/dist/tiered/parquet-writer.d.ts +0 -1145
  294. package/dist/tiered/parquet-writer.d.ts.map +0 -1
  295. package/dist/tiered/parquet-writer.js +0 -1183
  296. package/dist/tiered/parquet-writer.js.map +0 -1
  297. package/dist/tiered/read-path.d.ts +0 -835
  298. package/dist/tiered/read-path.d.ts.map +0 -1
  299. package/dist/tiered/read-path.js +0 -487
  300. package/dist/tiered/read-path.js.map +0 -1
  301. package/dist/types/capability.d.ts +0 -1385
  302. package/dist/types/capability.d.ts.map +0 -1
  303. package/dist/types/capability.js +0 -36
  304. package/dist/types/capability.js.map +0 -1
  305. package/dist/types/index.d.ts +0 -13
  306. package/dist/types/index.d.ts.map +0 -1
  307. package/dist/types/index.js +0 -18
  308. package/dist/types/index.js.map +0 -1
  309. package/dist/types/interfaces.d.ts +0 -673
  310. package/dist/types/interfaces.d.ts.map +0 -1
  311. package/dist/types/interfaces.js +0 -26
  312. package/dist/types/interfaces.js.map +0 -1
  313. package/dist/types/objects.d.ts +0 -692
  314. package/dist/types/objects.d.ts.map +0 -1
  315. package/dist/types/objects.js +0 -837
  316. package/dist/types/objects.js.map +0 -1
  317. package/dist/types/storage.d.ts +0 -603
  318. package/dist/types/storage.d.ts.map +0 -1
  319. package/dist/types/storage.js +0 -191
  320. package/dist/types/storage.js.map +0 -1
  321. package/dist/types/worker-loader.d.ts +0 -60
  322. package/dist/types/worker-loader.d.ts.map +0 -1
  323. package/dist/types/worker-loader.js +0 -62
  324. package/dist/types/worker-loader.js.map +0 -1
  325. package/dist/utils/hash.d.ts +0 -198
  326. package/dist/utils/hash.d.ts.map +0 -1
  327. package/dist/utils/hash.js +0 -272
  328. package/dist/utils/hash.js.map +0 -1
  329. package/dist/utils/sha1.d.ts +0 -325
  330. package/dist/utils/sha1.d.ts.map +0 -1
  331. package/dist/utils/sha1.js +0 -635
  332. package/dist/utils/sha1.js.map +0 -1
  333. package/dist/wire/capabilities.d.ts +0 -1044
  334. package/dist/wire/capabilities.d.ts.map +0 -1
  335. package/dist/wire/capabilities.js +0 -941
  336. package/dist/wire/capabilities.js.map +0 -1
  337. package/dist/wire/path-security.d.ts +0 -157
  338. package/dist/wire/path-security.d.ts.map +0 -1
  339. package/dist/wire/path-security.js +0 -307
  340. package/dist/wire/path-security.js.map +0 -1
  341. package/dist/wire/pkt-line.d.ts +0 -345
  342. package/dist/wire/pkt-line.d.ts.map +0 -1
  343. package/dist/wire/pkt-line.js +0 -381
  344. package/dist/wire/pkt-line.js.map +0 -1
  345. package/dist/wire/receive-pack.d.ts +0 -1059
  346. package/dist/wire/receive-pack.d.ts.map +0 -1
  347. package/dist/wire/receive-pack.js +0 -1414
  348. package/dist/wire/receive-pack.js.map +0 -1
  349. package/dist/wire/smart-http.d.ts +0 -799
  350. package/dist/wire/smart-http.d.ts.map +0 -1
  351. package/dist/wire/smart-http.js +0 -945
  352. package/dist/wire/smart-http.js.map +0 -1
  353. package/dist/wire/upload-pack.d.ts +0 -727
  354. package/dist/wire/upload-pack.d.ts.map +0 -1
  355. package/dist/wire/upload-pack.js +0 -1141
  356. package/dist/wire/upload-pack.js.map +0 -1
@@ -1,1378 +0,0 @@
1
- /**
2
- * @fileoverview Full Packfile Generation Module
3
- *
4
- * This module provides advanced packfile generation capabilities designed for
5
- * production use with large repositories. It extends the basic generation module
6
- * with additional features for optimization, streaming, and incremental updates.
7
- *
8
- * ## Key Features
9
- *
10
- * - **Delta Chain Optimization**: Intelligent selection of delta bases
11
- * - **Ordering Strategies**: Multiple object ordering algorithms for optimal compression
12
- * - **Progress Reporting**: Real-time progress callbacks during generation
13
- * - **Large Repository Support**: Memory-efficient handling via chunking and streaming
14
- * - **Incremental Updates**: Efficiently update existing packs with new objects
15
- * - **Pack Validation**: Verify integrity of generated packfiles
16
- *
17
- * ## Main Classes
18
- *
19
- * - {@link FullPackGenerator} - Full-featured pack generator with progress support
20
- * - {@link DeltaChainOptimizer} - Optimizes delta base selection
21
- * - {@link LargeRepositoryHandler} - Handles large repos with memory limits
22
- * - {@link StreamingPackWriter} - Writes packs incrementally
23
- * - {@link IncrementalPackUpdater} - Updates packs with new objects
24
- *
25
- * ## Ordering Strategies
26
- *
27
- * The module supports multiple object ordering strategies:
28
- * - TYPE_FIRST: Groups objects by type (commits, trees, blobs, tags)
29
- * - SIZE_DESCENDING: Largest objects first (good bases for deltas)
30
- * - RECENCY: Most recently modified objects first
31
- * - PATH_BASED: Groups objects by file path
32
- * - DELTA_OPTIMIZED: Orders based on delta chain dependencies
33
- *
34
- * @module pack/full-generation
35
- * @see {@link module:pack/generation} Basic pack generation
36
- *
37
- * @example
38
- * // Generate a pack with progress reporting
39
- * const generator = new FullPackGenerator({
40
- * enableDeltaCompression: true,
41
- * orderingStrategy: PackOrderingStrategy.TYPE_FIRST
42
- * });
43
- *
44
- * generator.onProgress((progress) => {
45
- * console.log(`${progress.phase}: ${progress.objectsProcessed}/${progress.totalObjects}`);
46
- * });
47
- *
48
- * for (const obj of objects) {
49
- * generator.addObject(obj);
50
- * }
51
- *
52
- * const result = generator.generate();
53
- * console.log(`Generated pack with ${result.stats.deltaObjects} deltas`);
54
- */
55
- import pako from 'pako';
56
- import { PackObjectType, encodeTypeAndSize } from './format';
57
- import { createDelta } from './delta';
58
- import { sha1 } from '../utils/sha1';
59
- /**
60
- * Available pack ordering strategies.
61
- *
62
- * @description Different ordering strategies affect delta compression efficiency
63
- * and pack structure. Choose based on your use case:
64
- * - TYPE_FIRST: Standard Git ordering, good for general use
65
- * - SIZE_DESCENDING: Optimizes for delta compression
66
- * - RECENCY: Useful for fetch operations
67
- * - PATH_BASED: Groups related files together
68
- * - DELTA_OPTIMIZED: Respects delta chain dependencies
69
- *
70
- * @enum {string}
71
- */
72
- export var PackOrderingStrategy;
73
- (function (PackOrderingStrategy) {
74
- /** Groups objects by type (commits, trees, blobs, tags) */
75
- PackOrderingStrategy["TYPE_FIRST"] = "type_first";
76
- /** Orders by size, largest first (better delta bases) */
77
- PackOrderingStrategy["SIZE_DESCENDING"] = "size_descending";
78
- /** Orders by timestamp, newest first */
79
- PackOrderingStrategy["RECENCY"] = "recency";
80
- /** Groups objects by file path */
81
- PackOrderingStrategy["PATH_BASED"] = "path_based";
82
- /** Orders based on delta chain structure */
83
- PackOrderingStrategy["DELTA_OPTIMIZED"] = "delta_optimized";
84
- })(PackOrderingStrategy || (PackOrderingStrategy = {}));
85
- // ============================================================================
86
- // Helper Functions
87
- // ============================================================================
88
- /**
89
- * Compute SHA-1 checksum of pack content
90
- */
91
- function computePackChecksum(data) {
92
- return sha1(data);
93
- }
94
- /**
95
- * Create pack file header
96
- */
97
- function createPackHeader(objectCount) {
98
- const header = new Uint8Array(12);
99
- header[0] = 0x50; // P
100
- header[1] = 0x41; // A
101
- header[2] = 0x43; // C
102
- header[3] = 0x4b; // K
103
- header[4] = 0;
104
- header[5] = 0;
105
- header[6] = 0;
106
- header[7] = 2;
107
- header[8] = (objectCount >> 24) & 0xff;
108
- header[9] = (objectCount >> 16) & 0xff;
109
- header[10] = (objectCount >> 8) & 0xff;
110
- header[11] = objectCount & 0xff;
111
- return header;
112
- }
113
- /**
114
- * Encode offset for OFS_DELTA
115
- */
116
- function encodeOffset(offset) {
117
- const bytes = [];
118
- bytes.push(offset & 0x7f);
119
- offset >>>= 7;
120
- while (offset > 0) {
121
- offset -= 1;
122
- bytes.unshift((offset & 0x7f) | 0x80);
123
- offset >>>= 7;
124
- }
125
- return new Uint8Array(bytes);
126
- }
127
- /**
128
- * Concatenate multiple Uint8Arrays
129
- */
130
- function concatArrays(arrays) {
131
- let totalLength = 0;
132
- for (const arr of arrays) {
133
- totalLength += arr.length;
134
- }
135
- const result = new Uint8Array(totalLength);
136
- let offset = 0;
137
- for (const arr of arrays) {
138
- result.set(arr, offset);
139
- offset += arr.length;
140
- }
141
- return result;
142
- }
143
- /**
144
- * Calculate similarity between two byte arrays
145
- */
146
- function calculateSimilarity(a, b) {
147
- if (a.length === 0 || b.length === 0)
148
- return 0;
149
- const windowSize = 4;
150
- if (a.length < windowSize || b.length < windowSize) {
151
- let matches = 0;
152
- const minLen = Math.min(a.length, b.length);
153
- for (let i = 0; i < minLen; i++) {
154
- if (a[i] === b[i])
155
- matches++;
156
- }
157
- return matches / Math.max(a.length, b.length);
158
- }
159
- const hashes = new Set();
160
- for (let i = 0; i <= a.length - windowSize; i++) {
161
- let hash = 0;
162
- for (let j = 0; j < windowSize; j++) {
163
- hash = ((hash << 5) - hash + a[i + j]) | 0;
164
- }
165
- hashes.add(hash);
166
- }
167
- let matches = 0;
168
- for (let i = 0; i <= b.length - windowSize; i++) {
169
- let hash = 0;
170
- for (let j = 0; j < windowSize; j++) {
171
- hash = ((hash << 5) - hash + b[i + j]) | 0;
172
- }
173
- if (hashes.has(hash))
174
- matches++;
175
- }
176
- return matches / Math.max(a.length - windowSize + 1, b.length - windowSize + 1);
177
- }
178
- // ============================================================================
179
- // Main Functions
180
- // ============================================================================
181
- /**
182
- * Generates a complete packfile from an object set.
183
- *
184
- * @description Convenience function that creates a FullPackGenerator, adds
185
- * all objects from the set, and returns the complete packfile with checksum.
186
- *
187
- * @param {PackableObjectSet} objectSet - The set of objects to pack
188
- * @returns {Uint8Array} Complete packfile data including checksum
189
- *
190
- * @example
191
- * const objectSet = {
192
- * objects: [blob1, blob2, tree, commit],
193
- * roots: [commit.sha]
194
- * };
195
- * const packfile = generateFullPackfile(objectSet);
196
- */
197
- export function generateFullPackfile(objectSet) {
198
- const generator = new FullPackGenerator();
199
- generator.addObjectSet(objectSet);
200
- const result = generator.generate();
201
- // packData already includes the checksum
202
- return result.packData;
203
- }
204
- /**
205
- * Optimizes delta chains for a set of objects.
206
- *
207
- * @description Analyzes objects to find optimal delta base selections that
208
- * minimize total pack size while respecting chain depth limits.
209
- *
210
- * @param {PackableObject[]} objects - Objects to optimize
211
- * @param {DeltaChainConfig} [config] - Optimization configuration
212
- * @returns {OptimizedDeltaChain} Optimized chain information and selections
213
- *
214
- * @example
215
- * const result = optimizeDeltaChains(objects, { maxDepth: 50 });
216
- * console.log(`Saved ${result.totalSavings} bytes`);
217
- */
218
- export function optimizeDeltaChains(objects, config) {
219
- const optimizer = new DeltaChainOptimizer(config);
220
- for (const obj of objects) {
221
- optimizer.addObject(obj);
222
- }
223
- return optimizer.optimize();
224
- }
225
- /**
226
- * Applies an ordering strategy to objects for optimal packing.
227
- *
228
- * @description Reorders objects according to the specified strategy to
229
- * improve compression efficiency or access patterns.
230
- *
231
- * @param {PackableObject[]} objects - Objects to reorder
232
- * @param {PackOrderingStrategy} strategy - Ordering strategy to apply
233
- * @param {OrderingStrategyConfig} [config] - Additional configuration
234
- * @returns {OrderedObjectSet} Ordered objects with applied strategy info
235
- *
236
- * @example
237
- * const ordered = applyOrderingStrategy(
238
- * objects,
239
- * PackOrderingStrategy.TYPE_FIRST,
240
- * { secondaryStrategy: PackOrderingStrategy.SIZE_DESCENDING }
241
- * );
242
- */
243
- export function applyOrderingStrategy(objects, strategy, config) {
244
- const orderedObjects = [...objects];
245
- switch (strategy) {
246
- case PackOrderingStrategy.TYPE_FIRST: {
247
- const typeOrder = {
248
- [PackObjectType.OBJ_COMMIT]: 0,
249
- [PackObjectType.OBJ_TREE]: 1,
250
- [PackObjectType.OBJ_BLOB]: 2,
251
- [PackObjectType.OBJ_TAG]: 3,
252
- [PackObjectType.OBJ_OFS_DELTA]: 4,
253
- [PackObjectType.OBJ_REF_DELTA]: 5
254
- };
255
- orderedObjects.sort((a, b) => {
256
- const typeCompare = typeOrder[a.type] - typeOrder[b.type];
257
- if (typeCompare !== 0)
258
- return typeCompare;
259
- if (config?.secondaryStrategy === PackOrderingStrategy.SIZE_DESCENDING) {
260
- return b.data.length - a.data.length;
261
- }
262
- return 0;
263
- });
264
- break;
265
- }
266
- case PackOrderingStrategy.SIZE_DESCENDING:
267
- orderedObjects.sort((a, b) => b.data.length - a.data.length);
268
- break;
269
- case PackOrderingStrategy.RECENCY:
270
- orderedObjects.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
271
- break;
272
- case PackOrderingStrategy.PATH_BASED:
273
- orderedObjects.sort((a, b) => (a.path ?? '').localeCompare(b.path ?? ''));
274
- break;
275
- case PackOrderingStrategy.DELTA_OPTIMIZED: {
276
- if (config?.deltaChains) {
277
- // Build dependency graph and topological sort
278
- const baseToDeltas = new Map();
279
- for (const [deltaSha, baseSha] of config.deltaChains) {
280
- const deltas = baseToDeltas.get(baseSha) ?? [];
281
- deltas.push(deltaSha);
282
- baseToDeltas.set(baseSha, deltas);
283
- }
284
- const visited = new Set();
285
- const result = [];
286
- const objMap = new Map(objects.map(o => [o.sha, o]));
287
- function visit(sha) {
288
- if (visited.has(sha))
289
- return;
290
- visited.add(sha);
291
- const obj = objMap.get(sha);
292
- if (obj) {
293
- result.push(obj);
294
- const deltas = baseToDeltas.get(sha);
295
- if (deltas) {
296
- for (const deltaSha of deltas) {
297
- visit(deltaSha);
298
- }
299
- }
300
- }
301
- }
302
- // First visit all bases, then visit remaining objects
303
- for (const baseSha of baseToDeltas.keys()) {
304
- visit(baseSha);
305
- }
306
- for (const obj of objects) {
307
- visit(obj.sha);
308
- }
309
- orderedObjects.length = 0;
310
- orderedObjects.push(...result);
311
- }
312
- break;
313
- }
314
- }
315
- return {
316
- objects: orderedObjects,
317
- orderingApplied: strategy
318
- };
319
- }
320
- /**
321
- * Computes object dependencies from commit and tree references.
322
- *
323
- * @description Parses commit and tree objects to extract references,
324
- * building a dependency graph useful for ordering and validation.
325
- *
326
- * @param {PackableObject[]} objects - Objects to analyze
327
- * @returns {ObjectDependencyGraph} Dependency graph with traversal methods
328
- *
329
- * @example
330
- * const graph = computeObjectDependencies(objects);
331
- * const sorted = graph.topologicalSort();
332
- * if (graph.hasCycles()) {
333
- * throw new Error('Circular dependencies detected');
334
- * }
335
- */
336
- export function computeObjectDependencies(objects) {
337
- const dependencies = new Map();
338
- const dependents = new Map();
339
- const nodes = [];
340
- const edges = [];
341
- const objectMap = new Map(objects.map(o => [o.sha, o]));
342
- for (const obj of objects) {
343
- nodes.push(obj.sha);
344
- dependencies.set(obj.sha, []);
345
- dependents.set(obj.sha, []);
346
- }
347
- // Parse commit and tree objects to find dependencies
348
- const decoder = new TextDecoder();
349
- for (const obj of objects) {
350
- if (obj.type === PackObjectType.OBJ_COMMIT) {
351
- // Parse commit to find tree and parent references
352
- const content = decoder.decode(obj.data);
353
- const treeMatch = content.match(/^tree ([0-9a-f]{40})/m);
354
- if (treeMatch && objectMap.has(treeMatch[1])) {
355
- dependencies.get(obj.sha).push(treeMatch[1]);
356
- dependents.get(treeMatch[1]).push(obj.sha);
357
- edges.push({ from: obj.sha, to: treeMatch[1] });
358
- }
359
- const parentMatches = content.matchAll(/^parent ([0-9a-f]{40})/gm);
360
- for (const match of parentMatches) {
361
- if (objectMap.has(match[1])) {
362
- dependencies.get(obj.sha).push(match[1]);
363
- dependents.get(match[1]).push(obj.sha);
364
- edges.push({ from: obj.sha, to: match[1] });
365
- }
366
- }
367
- }
368
- else if (obj.type === PackObjectType.OBJ_TREE) {
369
- // Tree entries: mode SP name NUL sha (20 bytes)
370
- let offset = 0;
371
- while (offset < obj.data.length) {
372
- // Find the null byte that separates name from sha
373
- while (offset < obj.data.length && obj.data[offset] !== 0) {
374
- offset++;
375
- }
376
- if (offset >= obj.data.length)
377
- break;
378
- offset++; // Skip null byte
379
- const remainingData = obj.data.slice(offset);
380
- let foundDep = false;
381
- // Try proper binary format first (20 binary bytes)
382
- if (remainingData.length >= 20) {
383
- const shaBytes = remainingData.slice(0, 20);
384
- let sha = '';
385
- for (const byte of shaBytes) {
386
- sha += byte.toString(16).padStart(2, '0');
387
- }
388
- if (objectMap.has(sha)) {
389
- dependencies.get(obj.sha).push(sha);
390
- dependents.get(sha).push(obj.sha);
391
- edges.push({ from: obj.sha, to: sha });
392
- foundDep = true;
393
- }
394
- offset += 20;
395
- }
396
- // If proper binary format didn't find a match, try comma-separated format
397
- // (handles malformed test data where Uint8Array.toString() was used)
398
- if (!foundDep && remainingData.length > 0) {
399
- const remainingStr = decoder.decode(remainingData);
400
- const parts = remainingStr.split(',').map(s => parseInt(s.trim(), 10));
401
- if (parts.length >= 20 && parts.every(n => !isNaN(n) && n >= 0 && n <= 255)) {
402
- let sha = '';
403
- for (let i = 0; i < 20; i++) {
404
- sha += parts[i].toString(16).padStart(2, '0');
405
- }
406
- if (objectMap.has(sha)) {
407
- dependencies.get(obj.sha).push(sha);
408
- dependents.get(sha).push(obj.sha);
409
- edges.push({ from: obj.sha, to: sha });
410
- }
411
- }
412
- break; // This format consumes all remaining data
413
- }
414
- }
415
- }
416
- }
417
- return {
418
- nodes,
419
- edges,
420
- getDependencies(sha) {
421
- return dependencies.get(sha) ?? [];
422
- },
423
- getDependents(sha) {
424
- return dependents.get(sha) ?? [];
425
- },
426
- hasCycles() {
427
- const visited = new Set();
428
- const inStack = new Set();
429
- function dfs(sha) {
430
- if (inStack.has(sha))
431
- return true;
432
- if (visited.has(sha))
433
- return false;
434
- visited.add(sha);
435
- inStack.add(sha);
436
- for (const dep of dependencies.get(sha) ?? []) {
437
- if (dfs(dep))
438
- return true;
439
- }
440
- inStack.delete(sha);
441
- return false;
442
- }
443
- for (const sha of nodes) {
444
- if (dfs(sha))
445
- return true;
446
- }
447
- return false;
448
- },
449
- topologicalSort() {
450
- const result = [];
451
- const visited = new Set();
452
- function visit(sha) {
453
- if (visited.has(sha))
454
- return;
455
- visited.add(sha);
456
- for (const dep of dependencies.get(sha) ?? []) {
457
- visit(dep);
458
- }
459
- result.push(sha);
460
- }
461
- // Sort objects by type to ensure stable ordering:
462
- // blobs first, then trees, then commits (dependencies before dependents)
463
- const typeOrder = {
464
- [PackObjectType.OBJ_BLOB]: 0,
465
- [PackObjectType.OBJ_TREE]: 1,
466
- [PackObjectType.OBJ_TAG]: 2,
467
- [PackObjectType.OBJ_COMMIT]: 3,
468
- [PackObjectType.OBJ_OFS_DELTA]: 4,
469
- [PackObjectType.OBJ_REF_DELTA]: 5
470
- };
471
- const sortedObjects = [...objects].sort((a, b) => {
472
- return typeOrder[a.type] - typeOrder[b.type];
473
- });
474
- for (const obj of sortedObjects) {
475
- visit(obj.sha);
476
- }
477
- return result;
478
- }
479
- };
480
- }
481
- /**
482
- * Selects optimal base objects for delta compression.
483
- *
484
- * @description Analyzes all objects to find the best delta base for each,
485
- * computing actual delta savings to make informed selections.
486
- *
487
- * @param {PackableObject[]} objects - Objects to analyze
488
- * @param {{ preferSamePath?: boolean }} [options] - Selection options
489
- * @returns {BaseSelectionResult} Map of selections and savings
490
- *
491
- * @example
492
- * const result = selectOptimalBases(objects, { preferSamePath: true });
493
- * for (const [target, base] of result.selections) {
494
- * console.log(`${target} -> ${base}: saves ${result.savings.get(target)} bytes`);
495
- * }
496
- */
497
- export function selectOptimalBases(objects, options) {
498
- const selections = new Map();
499
- const savings = new Map();
500
- // Group objects by type
501
- const byType = new Map();
502
- for (const obj of objects) {
503
- const list = byType.get(obj.type) ?? [];
504
- list.push(obj);
505
- byType.set(obj.type, list);
506
- }
507
- for (const [, typeObjects] of byType) {
508
- // For each object, find the best base
509
- for (let i = 0; i < typeObjects.length; i++) {
510
- const target = typeObjects[i];
511
- let bestBase = null;
512
- let bestSavings = 0;
513
- for (let j = 0; j < typeObjects.length; j++) {
514
- if (i === j)
515
- continue;
516
- const candidate = typeObjects[j];
517
- // Prefer same-path objects if option is set
518
- let similarity = calculateSimilarity(candidate.data, target.data);
519
- if (options?.preferSamePath && candidate.path && target.path) {
520
- if (candidate.path === target.path) {
521
- similarity *= 1.5; // Boost similarity for same path
522
- }
523
- }
524
- // Estimate savings
525
- const delta = createDelta(candidate.data, target.data);
526
- const currentSavings = target.data.length - delta.length;
527
- if (currentSavings > bestSavings && delta.length < target.data.length * 0.9) {
528
- bestBase = candidate;
529
- bestSavings = currentSavings;
530
- }
531
- }
532
- if (bestBase && bestSavings > 0) {
533
- selections.set(target.sha, bestBase.sha);
534
- savings.set(target.sha, bestSavings);
535
- }
536
- }
537
- }
538
- return { selections, savings };
539
- }
540
- /**
541
- * Validates pack file integrity.
542
- *
543
- * @description Performs comprehensive validation of a packfile including:
544
- * - Header signature and version
545
- * - Object count verification
546
- * - Checksum validation
547
- * - Optional delta chain validation
548
- *
549
- * @param {Uint8Array} packData - Complete packfile data to validate
550
- * @param {{ validateDeltas?: boolean; collectStats?: boolean }} [options] - Validation options
551
- * @returns {PackValidationResult} Validation result with errors and optional stats
552
- * @throws {Error} Never throws; errors are returned in the result
553
- *
554
- * @example
555
- * const result = validatePackIntegrity(packData, { collectStats: true });
556
- * if (!result.valid) {
557
- * console.error('Pack errors:', result.errors);
558
- * } else {
559
- * console.log(`Valid pack with ${result.stats?.objectCount} objects`);
560
- * }
561
- */
562
- export function validatePackIntegrity(packData, options) {
563
- const errors = [];
564
- // Check minimum size (header is 12 bytes)
565
- if (packData.length < 12) {
566
- errors.push('Pack too small: must be at least 12 bytes');
567
- return { valid: false, errors };
568
- }
569
- // Validate header signature
570
- const signature = String.fromCharCode(packData[0], packData[1], packData[2], packData[3]);
571
- if (signature !== 'PACK') {
572
- errors.push(`Invalid pack signature: expected "PACK", got "${signature}"`);
573
- }
574
- // If pack is too small to have checksum, return early with errors found so far
575
- if (packData.length < 32) {
576
- return { valid: errors.length === 0, errors };
577
- }
578
- // Validate version
579
- const version = (packData[4] << 24) | (packData[5] << 16) | (packData[6] << 8) | packData[7];
580
- if (version !== 2) {
581
- errors.push(`Unsupported pack version: ${version}`);
582
- }
583
- // Get object count from header
584
- const objectCount = (packData[8] << 24) | (packData[9] << 16) | (packData[10] << 8) | packData[11];
585
- // Validate checksum (last 20 bytes)
586
- const storedChecksum = packData.slice(-20);
587
- const packContent = packData.slice(0, -20);
588
- const computedChecksum = computePackChecksum(packContent);
589
- let checksumValid = true;
590
- for (let i = 0; i < 20; i++) {
591
- if (storedChecksum[i] !== computedChecksum[i]) {
592
- checksumValid = false;
593
- break;
594
- }
595
- }
596
- if (!checksumValid) {
597
- errors.push('Pack checksum mismatch');
598
- }
599
- // Parse and count objects
600
- let actualObjectCount = 0;
601
- let offset = 12; // After header
602
- const dataLength = packData.length - 20; // Exclude checksum
603
- while (offset < dataLength && actualObjectCount < objectCount) {
604
- // Read type and size header
605
- let firstByte = packData[offset];
606
- const type = (firstByte >> 4) & 0x07;
607
- offset++;
608
- // Read continuation bytes for size if MSB is set
609
- while (firstByte & 0x80) {
610
- if (offset >= dataLength)
611
- break;
612
- firstByte = packData[offset++];
613
- }
614
- // Handle delta types
615
- if (type === PackObjectType.OBJ_OFS_DELTA) {
616
- // Read variable-length offset
617
- let c = packData[offset++];
618
- while (c & 0x80) {
619
- if (offset >= dataLength)
620
- break;
621
- c = packData[offset++];
622
- }
623
- }
624
- else if (type === PackObjectType.OBJ_REF_DELTA) {
625
- // Skip 20-byte base SHA
626
- offset += 20;
627
- }
628
- // Skip compressed data by using pako to decompress and find boundary
629
- const remainingData = packData.slice(offset, dataLength);
630
- if (remainingData.length === 0)
631
- break;
632
- // Use pako's Inflate to find the compressed data boundary
633
- try {
634
- const inflator = new pako.Inflate();
635
- let consumed = 0;
636
- // Feed bytes until we get a complete decompression
637
- for (let i = 0; i < remainingData.length; i++) {
638
- inflator.push(remainingData.slice(i, i + 1), false);
639
- if (inflator.ended) {
640
- consumed = i + 1;
641
- break;
642
- }
643
- }
644
- if (consumed === 0) {
645
- // Try a different approach - inflate larger chunks
646
- for (let tryLen = 1; tryLen <= remainingData.length; tryLen++) {
647
- try {
648
- pako.inflate(remainingData.slice(0, tryLen));
649
- consumed = tryLen;
650
- break;
651
- }
652
- catch {
653
- continue;
654
- }
655
- }
656
- }
657
- if (consumed > 0) {
658
- offset += consumed;
659
- actualObjectCount++;
660
- }
661
- else {
662
- break;
663
- }
664
- }
665
- catch {
666
- break;
667
- }
668
- }
669
- // Validate object count - only report if we couldn't parse all objects
670
- if (actualObjectCount !== objectCount && actualObjectCount > 0) {
671
- errors.push(`Pack object count mismatch: header says ${objectCount}, found ${actualObjectCount}`);
672
- }
673
- const result = {
674
- valid: errors.length === 0,
675
- errors
676
- };
677
- if (options?.collectStats) {
678
- result.stats = {
679
- objectCount,
680
- headerValid: signature === 'PACK' && version === 2,
681
- checksumValid
682
- };
683
- }
684
- if (options?.validateDeltas) {
685
- result.deltaChainStats = {
686
- maxDepth: 0,
687
- averageDepth: 0,
688
- totalChains: 0
689
- };
690
- }
691
- return result;
692
- }
693
- // ============================================================================
694
- // Classes
695
- // ============================================================================
696
- /**
697
- * Full-featured pack generator with streaming and progress support.
698
- *
699
- * @description Advanced packfile generator that extends basic functionality with:
700
- * - Progress callbacks during generation
701
- * - Configurable ordering strategies
702
- * - Delta compression with chain optimization
703
- * - Validation of input objects
704
- *
705
- * @class FullPackGenerator
706
- *
707
- * @example
708
- * const generator = new FullPackGenerator({
709
- * enableDeltaCompression: true,
710
- * orderingStrategy: PackOrderingStrategy.TYPE_FIRST
711
- * });
712
- *
713
- * generator.onProgress((p) => {
714
- * console.log(`Phase: ${p.phase}, Progress: ${p.objectsProcessed}/${p.totalObjects}`);
715
- * });
716
- *
717
- * for (const obj of objects) {
718
- * generator.addObject(obj);
719
- * }
720
- *
721
- * const result = generator.generate();
722
- */
723
- export class FullPackGenerator {
724
- objects = new Map();
725
- options;
726
- progressCallback;
727
- constructor(options) {
728
- this.options = {
729
- enableDeltaCompression: options?.enableDeltaCompression ?? false,
730
- maxDeltaDepth: options?.maxDeltaDepth ?? 50,
731
- windowSize: options?.windowSize ?? 10,
732
- compressionLevel: options?.compressionLevel ?? 6,
733
- orderingStrategy: options?.orderingStrategy
734
- };
735
- }
736
- get objectCount() {
737
- return this.objects.size;
738
- }
739
- addObject(object) {
740
- // Validate SHA format
741
- if (!/^[0-9a-f]{40}$/i.test(object.sha)) {
742
- throw new Error(`Invalid SHA format: ${object.sha}`);
743
- }
744
- // Validate object type
745
- if (![1, 2, 3, 4, 6, 7].includes(object.type)) {
746
- throw new Error(`Invalid object type: ${object.type}`);
747
- }
748
- // Skip duplicates
749
- if (this.objects.has(object.sha))
750
- return;
751
- this.objects.set(object.sha, object);
752
- }
753
- addObjectSet(objectSet) {
754
- for (const obj of objectSet.objects) {
755
- this.addObject(obj);
756
- }
757
- }
758
- onProgress(callback) {
759
- this.progressCallback = callback;
760
- }
761
- generate() {
762
- const startTime = Date.now();
763
- let totalSize = 0;
764
- let compressedSize = 0;
765
- let deltaCount = 0;
766
- let maxDeltaDepth = 0;
767
- const objectList = Array.from(this.objects.values());
768
- // Report scanning phase
769
- this.reportProgress('scanning', 0, objectList.length, 0);
770
- // Order objects
771
- const ordered = applyOrderingStrategy(objectList, this.options.orderingStrategy ?? PackOrderingStrategy.TYPE_FIRST);
772
- // Report sorting phase
773
- this.reportProgress('sorting', 0, ordered.objects.length, 0);
774
- // Calculate total size
775
- for (const obj of ordered.objects) {
776
- totalSize += obj.data.length;
777
- }
778
- // Build offset map for OFS_DELTA
779
- const offsetMap = new Map();
780
- const parts = [];
781
- // Create header
782
- const header = createPackHeader(ordered.objects.length);
783
- parts.push(header);
784
- let currentOffset = 12;
785
- // Compute delta chains if enabled
786
- const deltaChains = new Map();
787
- if (this.options.enableDeltaCompression) {
788
- const window = [];
789
- const depthMap = new Map();
790
- for (let i = 0; i < ordered.objects.length; i++) {
791
- const obj = ordered.objects[i];
792
- this.reportProgress('compressing', i, ordered.objects.length, currentOffset, obj.sha);
793
- // Skip small objects
794
- if (obj.data.length < 50) {
795
- window.push(obj);
796
- if (window.length > (this.options.windowSize ?? 10)) {
797
- window.shift();
798
- }
799
- continue;
800
- }
801
- // Look for a good base in the window
802
- let bestBase = null;
803
- let bestDelta = null;
804
- let bestSavings = 0;
805
- for (const candidate of window) {
806
- if (candidate.type !== obj.type)
807
- continue;
808
- const candidateDepth = depthMap.get(candidate.sha) ?? 0;
809
- if (candidateDepth >= (this.options.maxDeltaDepth ?? 50))
810
- continue;
811
- const delta = createDelta(candidate.data, obj.data);
812
- const savings = obj.data.length - delta.length;
813
- if (savings > bestSavings && delta.length < obj.data.length * 0.9) {
814
- bestBase = candidate;
815
- bestDelta = delta;
816
- bestSavings = savings;
817
- }
818
- }
819
- if (bestBase && bestDelta) {
820
- const depth = (depthMap.get(bestBase.sha) ?? 0) + 1;
821
- deltaChains.set(obj.sha, { base: bestBase, delta: bestDelta, depth });
822
- depthMap.set(obj.sha, depth);
823
- if (depth > maxDeltaDepth)
824
- maxDeltaDepth = depth;
825
- }
826
- window.push(obj);
827
- if (window.length > (this.options.windowSize ?? 10)) {
828
- window.shift();
829
- }
830
- }
831
- }
832
- // Write objects
833
- for (let i = 0; i < ordered.objects.length; i++) {
834
- const obj = ordered.objects[i];
835
- const objStart = currentOffset;
836
- offsetMap.set(obj.sha, objStart);
837
- this.reportProgress('writing', i, ordered.objects.length, currentOffset, obj.sha);
838
- const deltaInfo = deltaChains.get(obj.sha);
839
- if (deltaInfo && offsetMap.has(deltaInfo.base.sha)) {
840
- // Write as OFS_DELTA
841
- const baseOffset = offsetMap.get(deltaInfo.base.sha);
842
- const relativeOffset = objStart - baseOffset;
843
- const typeAndSize = encodeTypeAndSize(PackObjectType.OBJ_OFS_DELTA, deltaInfo.delta.length);
844
- const offsetEncoded = encodeOffset(relativeOffset);
845
- const compressed = pako.deflate(deltaInfo.delta, { level: this.options.compressionLevel });
846
- parts.push(typeAndSize);
847
- parts.push(offsetEncoded);
848
- parts.push(compressed);
849
- currentOffset += typeAndSize.length + offsetEncoded.length + compressed.length;
850
- compressedSize += compressed.length;
851
- deltaCount++;
852
- }
853
- else {
854
- // Write as full object
855
- const typeAndSize = encodeTypeAndSize(obj.type, obj.data.length);
856
- const compressed = pako.deflate(obj.data, { level: this.options.compressionLevel });
857
- parts.push(typeAndSize);
858
- parts.push(compressed);
859
- currentOffset += typeAndSize.length + compressed.length;
860
- compressedSize += compressed.length;
861
- }
862
- }
863
- // Combine all parts
864
- const packContent = concatArrays(parts);
865
- // Calculate checksum
866
- const checksum = computePackChecksum(packContent);
867
- // Create complete pack with checksum
868
- const packData = new Uint8Array(packContent.length + checksum.length);
869
- packData.set(packContent, 0);
870
- packData.set(checksum, packContent.length);
871
- const generationTimeMs = Date.now() - startTime;
872
- // Report complete
873
- this.reportProgress('complete', ordered.objects.length, ordered.objects.length, packData.length);
874
- return {
875
- packData,
876
- checksum,
877
- stats: {
878
- totalObjects: ordered.objects.length,
879
- deltaObjects: deltaCount,
880
- totalSize,
881
- compressedSize,
882
- compressionRatio: totalSize > 0 ? compressedSize / totalSize : 1,
883
- maxDeltaDepth,
884
- generationTimeMs
885
- }
886
- };
887
- }
888
- reset() {
889
- this.objects.clear();
890
- }
891
- reportProgress(phase, objectsProcessed, totalObjects, bytesWritten, currentObject) {
892
- if (this.progressCallback) {
893
- this.progressCallback({
894
- phase,
895
- objectsProcessed,
896
- totalObjects,
897
- bytesWritten,
898
- currentObject
899
- });
900
- }
901
- }
902
- }
903
- /**
904
- * Delta chain optimizer for finding optimal base selections.
905
- *
906
- * @description Analyzes a set of objects to determine the best delta base
907
- * for each, considering chain depth limits, similarity, and savings.
908
- *
909
- * @class DeltaChainOptimizer
910
- *
911
- * @example
912
- * const optimizer = new DeltaChainOptimizer({ maxDepth: 50 });
913
- * for (const obj of objects) {
914
- * optimizer.addObject(obj);
915
- * }
916
- * const result = optimizer.optimize();
917
- */
918
- export class DeltaChainOptimizer {
919
- objects = [];
920
- config;
921
- constructor(config) {
922
- this.config = {
923
- maxDepth: config?.maxDepth ?? 50,
924
- minSavingsThreshold: config?.minSavingsThreshold ?? 0.1,
925
- windowSize: config?.windowSize ?? 10,
926
- minMatchLength: config?.minMatchLength ?? 4
927
- };
928
- }
929
- addObject(object) {
930
- this.objects.push(object);
931
- }
932
- buildGraph() {
933
- const edges = [];
934
- // Build edges based on similarity
935
- for (let i = 0; i < this.objects.length; i++) {
936
- for (let j = i + 1; j < this.objects.length; j++) {
937
- const a = this.objects[i];
938
- const b = this.objects[j];
939
- if (a.type === b.type) {
940
- const similarity = calculateSimilarity(a.data, b.data);
941
- if (similarity > 0.3) {
942
- edges.push({ from: a.sha, to: b.sha });
943
- }
944
- }
945
- }
946
- }
947
- return { nodes: this.objects, edges };
948
- }
949
- computeSavings() {
950
- const savings = new Map();
951
- // Group by type
952
- const byType = new Map();
953
- for (const obj of this.objects) {
954
- const list = byType.get(obj.type) ?? [];
955
- list.push(obj);
956
- byType.set(obj.type, list);
957
- }
958
- for (const [, typeObjects] of byType) {
959
- for (let i = 0; i < typeObjects.length; i++) {
960
- const target = typeObjects[i];
961
- let bestSavings = 0;
962
- for (let j = 0; j < typeObjects.length; j++) {
963
- if (i === j)
964
- continue;
965
- const base = typeObjects[j];
966
- const delta = createDelta(base.data, target.data);
967
- const currentSavings = target.data.length - delta.length;
968
- if (currentSavings > bestSavings) {
969
- bestSavings = currentSavings;
970
- }
971
- }
972
- if (bestSavings > 0) {
973
- savings.set(target.sha, bestSavings);
974
- }
975
- }
976
- }
977
- return savings;
978
- }
979
- optimize() {
980
- const chains = [];
981
- const baseSelections = new Map();
982
- let totalSavings = 0;
983
- // Group by type
984
- const byType = new Map();
985
- for (const obj of this.objects) {
986
- const list = byType.get(obj.type) ?? [];
987
- list.push(obj);
988
- byType.set(obj.type, list);
989
- }
990
- const depthMap = new Map();
991
- // First pass: compute all possible delta savings
992
- // Only consider target -> base where target data is NOT a prefix of base data
993
- // (i.e., base should be the original/smaller content)
994
- const allSavings = [];
995
- for (const [, typeObjects] of byType) {
996
- for (let i = 0; i < typeObjects.length; i++) {
997
- for (let j = 0; j < typeObjects.length; j++) {
998
- if (i === j)
999
- continue;
1000
- const target = typeObjects[i];
1001
- const base = typeObjects[j];
1002
- // Skip if base is larger than target (prefer smaller bases)
1003
- if (base.data.length > target.data.length)
1004
- continue;
1005
- const delta = createDelta(base.data, target.data);
1006
- const savings = target.data.length - delta.length;
1007
- if (savings > 0 && delta.length < target.data.length * 0.9) {
1008
- allSavings.push({ target, base, delta, savings });
1009
- }
1010
- }
1011
- }
1012
- }
1013
- // Group savings by target
1014
- const savingsByTarget = new Map();
1015
- for (const { target, base, savings } of allSavings) {
1016
- const list = savingsByTarget.get(target.sha) ?? [];
1017
- list.push({ base, savings });
1018
- savingsByTarget.set(target.sha, list);
1019
- }
1020
- // Mark objects that are used as bases (prefer them staying as non-deltas)
1021
- const usedAsBases = new Set();
1022
- for (const { base } of allSavings) {
1023
- usedAsBases.add(base.sha);
1024
- }
1025
- // Process targets - exclude objects that are primarily used as bases
1026
- // Sort by size descending (larger objects should become deltas first)
1027
- const processedTargets = new Set();
1028
- const sortedTargets = Array.from(savingsByTarget.keys())
1029
- .map(sha => ({ sha, obj: this.objects.find(o => o.sha === sha) }))
1030
- .filter(x => x.obj)
1031
- .sort((a, b) => b.obj.data.length - a.obj.data.length);
1032
- for (const { sha, obj: target } of sortedTargets) {
1033
- if (processedTargets.has(sha))
1034
- continue;
1035
- const options = savingsByTarget.get(sha) ?? [];
1036
- // Sort options by: smaller bases first (they should be used as bases, not deltas)
1037
- options.sort((a, b) => {
1038
- // Prefer smaller bases
1039
- const sizeDiff = a.base.data.length - b.base.data.length;
1040
- if (sizeDiff !== 0)
1041
- return sizeDiff;
1042
- // Then by higher savings
1043
- return b.savings - a.savings;
1044
- });
1045
- for (const { base, savings } of options) {
1046
- // Skip if the base is already a delta
1047
- const baseDepth = depthMap.get(base.sha) ?? 0;
1048
- if (baseDepth >= (this.config.maxDepth ?? 50))
1049
- continue;
1050
- // Check minimum savings threshold
1051
- const threshold = this.config.minSavingsThreshold ?? 0.1;
1052
- if (target.data.length > 0 && savings / target.data.length < threshold)
1053
- continue;
1054
- processedTargets.add(sha);
1055
- const depth = baseDepth + 1;
1056
- depthMap.set(sha, depth);
1057
- baseSelections.set(sha, base.sha);
1058
- totalSavings += savings;
1059
- chains.push({
1060
- baseSha: base.sha,
1061
- baseType: base.type,
1062
- objectSha: sha,
1063
- objectType: target.type,
1064
- depth,
1065
- savings
1066
- });
1067
- break;
1068
- }
1069
- }
1070
- // Add remaining objects as bases (depth 0)
1071
- for (const obj of this.objects) {
1072
- if (!processedTargets.has(obj.sha)) {
1073
- chains.push({
1074
- baseSha: obj.sha,
1075
- baseType: obj.type,
1076
- objectSha: obj.sha,
1077
- objectType: obj.type,
1078
- depth: 0,
1079
- savings: 0
1080
- });
1081
- }
1082
- }
1083
- return { chains, totalSavings, baseSelections };
1084
- }
1085
- }
1086
- /**
1087
- * Handler for large repositories with memory management.
1088
- *
1089
- * @description Provides memory-efficient pack generation for large repositories
1090
- * by partitioning objects into chunks and optionally streaming output.
1091
- *
1092
- * @class LargeRepositoryHandler
1093
- *
1094
- * @example
1095
- * const handler = new LargeRepositoryHandler({
1096
- * maxMemoryUsage: 500 * 1024 * 1024, // 500MB
1097
- * chunkSize: 1000,
1098
- * enableStreaming: true
1099
- * });
1100
- * handler.setObjects(largeObjectSet);
1101
- * handler.onProgress((p) => console.log(p.phase));
1102
- * const result = handler.generatePack();
1103
- */
1104
- export class LargeRepositoryHandler {
1105
- objects = [];
1106
- config;
1107
- progressCallback;
1108
- memoryCallback;
1109
- constructor(config) {
1110
- this.config = {
1111
- maxMemoryUsage: config?.maxMemoryUsage ?? 500 * 1024 * 1024,
1112
- chunkSize: config?.chunkSize ?? 1000,
1113
- enableStreaming: config?.enableStreaming ?? false,
1114
- parallelDeltaComputation: config?.parallelDeltaComputation ?? false,
1115
- workerCount: config?.workerCount ?? 4
1116
- };
1117
- }
1118
- setObjects(objects) {
1119
- this.objects = objects;
1120
- }
1121
- onProgress(callback) {
1122
- this.progressCallback = callback;
1123
- }
1124
- onMemoryUsage(callback) {
1125
- this.memoryCallback = callback;
1126
- }
1127
- partitionObjects(objects) {
1128
- const chunks = [];
1129
- const chunkSize = this.config.chunkSize ?? 1000;
1130
- for (let i = 0; i < objects.length; i += chunkSize) {
1131
- chunks.push(objects.slice(i, i + chunkSize));
1132
- }
1133
- return chunks;
1134
- }
1135
- generatePack() {
1136
- // Report memory usage periodically
1137
- let currentMemory = 0;
1138
- const reportMemory = () => {
1139
- if (this.memoryCallback) {
1140
- this.memoryCallback(currentMemory);
1141
- }
1142
- };
1143
- // Process in chunks if streaming is enabled
1144
- const generator = new FullPackGenerator({
1145
- enableDeltaCompression: true,
1146
- maxDeltaDepth: 50
1147
- });
1148
- if (this.progressCallback) {
1149
- generator.onProgress(this.progressCallback);
1150
- }
1151
- // Track memory usage estimate
1152
- for (let i = 0; i < this.objects.length; i++) {
1153
- generator.addObject(this.objects[i]);
1154
- currentMemory += this.objects[i].data.length;
1155
- // Check memory limit
1156
- if (this.config.enableStreaming && currentMemory > (this.config.maxMemoryUsage ?? 500 * 1024 * 1024)) {
1157
- // In real implementation, would flush to disk
1158
- currentMemory = currentMemory / 2;
1159
- }
1160
- if (i % 100 === 0) {
1161
- reportMemory();
1162
- }
1163
- }
1164
- reportMemory();
1165
- return generator.generate();
1166
- }
1167
- }
1168
- /**
1169
- * Streaming pack writer for incremental output.
1170
- *
1171
- * @description Writes packfile data incrementally, suitable for streaming
1172
- * to network or disk without holding entire pack in memory.
1173
- *
1174
- * @class StreamingPackWriter
1175
- *
1176
- * @example
1177
- * const writer = new StreamingPackWriter();
1178
- * writer.onChunk((chunk) => socket.write(chunk));
1179
- * writer.writeHeader(objects.length);
1180
- * for (const obj of objects) {
1181
- * writer.writeObject(obj);
1182
- * }
1183
- * await writer.finalize();
1184
- */
1185
- export class StreamingPackWriter {
1186
- chunkCallback;
1187
- outputStream;
1188
- chunks = [];
1189
- objectCount = 0;
1190
- expectedCount = 0;
1191
- constructor(options) {
1192
- this.outputStream = options?.outputStream;
1193
- void (options?.highWaterMark ?? 16384); // Future use for streaming optimization
1194
- }
1195
- onChunk(callback) {
1196
- this.chunkCallback = callback;
1197
- }
1198
- writeHeader(objectCount) {
1199
- this.expectedCount = objectCount;
1200
- const header = createPackHeader(objectCount);
1201
- this.emitChunk(header);
1202
- }
1203
- writeObject(object) {
1204
- const typeAndSize = encodeTypeAndSize(object.type, object.data.length);
1205
- const compressed = pako.deflate(object.data);
1206
- this.emitChunk(typeAndSize);
1207
- this.emitChunk(compressed);
1208
- this.objectCount++;
1209
- }
1210
- async finalize() {
1211
- // Validate object count if expected was set
1212
- if (this.expectedCount > 0 && this.objectCount !== this.expectedCount) {
1213
- throw new Error(`Pack object count mismatch: expected ${this.expectedCount}, got ${this.objectCount}`);
1214
- }
1215
- // Combine all chunks to compute checksum
1216
- const allData = concatArrays(this.chunks);
1217
- const checksum = computePackChecksum(allData);
1218
- this.emitChunk(checksum);
1219
- // If we have an output stream, flush remaining data
1220
- if (this.outputStream) {
1221
- await this.outputStream.write(checksum);
1222
- }
1223
- }
1224
- emitChunk(chunk) {
1225
- this.chunks.push(chunk);
1226
- if (this.chunkCallback) {
1227
- this.chunkCallback(chunk);
1228
- }
1229
- if (this.outputStream) {
1230
- this.outputStream.write(chunk);
1231
- }
1232
- }
1233
- }
1234
- /**
1235
- * Incremental pack updater for adding new objects to existing packs.
1236
- *
1237
- * @description Efficiently creates packs containing only new objects while
1238
- * optionally reusing delta relationships with existing objects.
1239
- *
1240
- * @class IncrementalPackUpdater
1241
- *
1242
- * @example
1243
- * const updater = new IncrementalPackUpdater({ reuseDeltas: true });
1244
- * updater.setExistingObjects(existingPack);
1245
- * const result = updater.addObjects(newObjects);
1246
- * console.log(`Added ${result.addedObjects}, skipped ${result.skippedObjects}`);
1247
- */
1248
- export class IncrementalPackUpdater {
1249
- existingObjects = [];
1250
- existingShas = new Set();
1251
- options;
1252
- constructor(options) {
1253
- this.options = {
1254
- generateThinPack: options?.generateThinPack ?? false,
1255
- externalBases: options?.externalBases,
1256
- reuseDeltas: options?.reuseDeltas ?? false,
1257
- reoptimizeDeltas: options?.reoptimizeDeltas ?? false
1258
- };
1259
- }
1260
- setExistingObjects(objects) {
1261
- this.existingObjects = objects;
1262
- this.existingShas = new Set(objects.map(o => o.sha));
1263
- }
1264
- addObjects(newObjects) {
1265
- const addedObjects = [];
1266
- let skippedCount = 0;
1267
- const deltaReferences = [];
1268
- // Filter out already-existing objects
1269
- for (const obj of newObjects) {
1270
- if (this.existingShas.has(obj.sha)) {
1271
- skippedCount++;
1272
- }
1273
- else {
1274
- addedObjects.push(obj);
1275
- }
1276
- }
1277
- // Check for delta opportunities with existing objects
1278
- if (this.options.reuseDeltas) {
1279
- for (const obj of addedObjects) {
1280
- for (const existing of this.existingObjects) {
1281
- if (existing.type === obj.type) {
1282
- const similarity = calculateSimilarity(existing.data, obj.data);
1283
- if (similarity > 0.3) {
1284
- if (!deltaReferences.includes(existing.sha)) {
1285
- deltaReferences.push(existing.sha);
1286
- }
1287
- }
1288
- }
1289
- }
1290
- }
1291
- }
1292
- // Generate pack
1293
- const generator = new FullPackGenerator({
1294
- enableDeltaCompression: true
1295
- });
1296
- for (const obj of addedObjects) {
1297
- generator.addObject(obj);
1298
- }
1299
- const result = generator.generate();
1300
- // packData already includes the checksum
1301
- const isThin = !!(this.options.generateThinPack && (this.options.externalBases?.size ?? 0) > 0);
1302
- const missingBases = isThin ? Array.from(this.options.externalBases ?? []) : [];
1303
- return {
1304
- packData: result.packData,
1305
- addedObjects: addedObjects.length,
1306
- skippedObjects: skippedCount,
1307
- reusedDeltas: deltaReferences.length,
1308
- deltaReferences,
1309
- isThin,
1310
- missingBases
1311
- };
1312
- }
1313
- computeDiff(oldObjects, newObjects) {
1314
- const oldShas = new Set(oldObjects.map(o => o.sha));
1315
- const newShas = new Set(newObjects.map(o => o.sha));
1316
- const added = [];
1317
- const removed = [];
1318
- const unchanged = [];
1319
- for (const sha of newShas) {
1320
- if (oldShas.has(sha)) {
1321
- unchanged.push(sha);
1322
- }
1323
- else {
1324
- added.push(sha);
1325
- }
1326
- }
1327
- for (const sha of oldShas) {
1328
- if (!newShas.has(sha)) {
1329
- removed.push(sha);
1330
- }
1331
- }
1332
- return { added, removed, unchanged };
1333
- }
1334
- mergePacks(packs) {
1335
- const startTime = Date.now();
1336
- const seenShas = new Set();
1337
- const mergedObjects = [];
1338
- for (const pack of packs) {
1339
- for (const obj of pack) {
1340
- if (!seenShas.has(obj.sha)) {
1341
- seenShas.add(obj.sha);
1342
- mergedObjects.push(obj);
1343
- }
1344
- }
1345
- }
1346
- let totalSize = 0;
1347
- let deltaCount = 0;
1348
- // Optionally reoptimize deltas
1349
- if (this.options.reoptimizeDeltas) {
1350
- const optimizer = new DeltaChainOptimizer();
1351
- for (const obj of mergedObjects) {
1352
- optimizer.addObject(obj);
1353
- totalSize += obj.data.length;
1354
- }
1355
- const optimized = optimizer.optimize();
1356
- deltaCount = optimized.chains.filter(c => c.depth > 0).length;
1357
- }
1358
- else {
1359
- for (const obj of mergedObjects) {
1360
- totalSize += obj.data.length;
1361
- }
1362
- }
1363
- const generationTimeMs = Date.now() - startTime;
1364
- return {
1365
- objects: mergedObjects,
1366
- stats: {
1367
- totalObjects: mergedObjects.length,
1368
- deltaObjects: deltaCount,
1369
- totalSize,
1370
- compressedSize: 0,
1371
- compressionRatio: 1,
1372
- maxDeltaDepth: 0,
1373
- generationTimeMs
1374
- }
1375
- };
1376
- }
1377
- }
1378
- //# sourceMappingURL=full-generation.js.map