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,837 +0,0 @@
1
- /**
2
- * @fileoverview Git Object Types and Serialization
3
- *
4
- * This module defines the core Git object types (blob, tree, commit, tag) and provides
5
- * functions for serializing and deserializing these objects in the Git format.
6
- *
7
- * Git uses a content-addressable storage model where each object is identified by
8
- * its SHA-1 hash. The format for each object type is:
9
- * - Header: "{type} {size}\0"
10
- * - Content: type-specific binary data
11
- *
12
- * @module types/objects
13
- *
14
- * @example
15
- * ```typescript
16
- * import { serializeBlob, parseBlob, isBlob } from './types/objects'
17
- *
18
- * // Create and serialize a blob
19
- * const content = new TextEncoder().encode('Hello, World!')
20
- * const serialized = serializeBlob(content)
21
- *
22
- * // Parse it back
23
- * const blob = parseBlob(serialized)
24
- * console.log(blob.type) // 'blob'
25
- * ```
26
- */
27
- // ============================================================================
28
- // Validation Helpers
29
- // ============================================================================
30
- /**
31
- * Valid SHA-1 hash pattern (40 lowercase hexadecimal characters).
32
- *
33
- * @description
34
- * Regular expression for validating SHA-1 hashes used in Git.
35
- * Matches exactly 40 lowercase hexadecimal characters.
36
- *
37
- * @example
38
- * ```typescript
39
- * if (SHA_PATTERN.test(input)) {
40
- * // Valid SHA-1 format
41
- * }
42
- * ```
43
- */
44
- export const SHA_PATTERN = /^[0-9a-f]{40}$/;
45
- /**
46
- * Valid file modes in Git.
47
- *
48
- * @description
49
- * The set of valid mode strings for tree entries:
50
- * - '100644': Regular file (non-executable)
51
- * - '100755': Executable file
52
- * - '040000': Directory (tree)
53
- * - '120000': Symbolic link
54
- * - '160000': Git submodule (gitlink)
55
- */
56
- export const VALID_MODES = new Set(['100644', '100755', '040000', '120000', '160000']);
57
- /**
58
- * Validate a SHA-1 hash string.
59
- *
60
- * @description
61
- * Checks if a string is a valid Git SHA-1 hash (40 lowercase hex characters).
62
- * Use this to validate user input or data from external sources.
63
- *
64
- * @param sha - The string to validate
65
- * @returns True if the string is a valid SHA-1 hash
66
- *
67
- * @example
68
- * ```typescript
69
- * if (isValidSha('abc123')) {
70
- * console.log('Invalid SHA') // Too short
71
- * }
72
- *
73
- * if (isValidSha('da39a3ee5e6b4b0d3255bfef95601890afd80709')) {
74
- * console.log('Valid SHA')
75
- * }
76
- * ```
77
- */
78
- export function isValidSha(sha) {
79
- return typeof sha === 'string' && SHA_PATTERN.test(sha);
80
- }
81
- /**
82
- * Validate a Git object type string.
83
- *
84
- * @description
85
- * Checks if a string is one of the four valid Git object types.
86
- *
87
- * @param type - The string to validate
88
- * @returns True if the string is a valid object type
89
- *
90
- * @example
91
- * ```typescript
92
- * if (isValidObjectType(input)) {
93
- * // input is 'blob' | 'tree' | 'commit' | 'tag'
94
- * }
95
- * ```
96
- */
97
- export function isValidObjectType(type) {
98
- return type === 'blob' || type === 'tree' || type === 'commit' || type === 'tag';
99
- }
100
- /**
101
- * Validate a tree entry mode string.
102
- *
103
- * @description
104
- * Checks if a string is a valid Git tree entry mode.
105
- *
106
- * @param mode - The mode string to validate
107
- * @returns True if the mode is valid
108
- *
109
- * @example
110
- * ```typescript
111
- * if (isValidMode('100644')) {
112
- * console.log('Valid regular file mode')
113
- * }
114
- * ```
115
- */
116
- export function isValidMode(mode) {
117
- return VALID_MODES.has(mode);
118
- }
119
- /**
120
- * Validate a tree entry object.
121
- *
122
- * @description
123
- * Validates all fields of a tree entry including mode, name, and SHA.
124
- * Returns an object with validity status and optional error message.
125
- *
126
- * @param entry - The tree entry to validate
127
- * @returns Validation result with isValid boolean and optional error message
128
- *
129
- * @example
130
- * ```typescript
131
- * const result = validateTreeEntry({ mode: '100644', name: 'file.txt', sha: 'abc...' })
132
- * if (!result.isValid) {
133
- * console.error(result.error)
134
- * }
135
- * ```
136
- */
137
- export function validateTreeEntry(entry) {
138
- if (!isValidMode(entry.mode)) {
139
- return { isValid: false, error: `Invalid mode: ${entry.mode}. Valid modes: ${Array.from(VALID_MODES).join(', ')}` };
140
- }
141
- if (!entry.name || typeof entry.name !== 'string') {
142
- return { isValid: false, error: 'Entry name is required and must be a string' };
143
- }
144
- if (entry.name.includes('/') || entry.name.includes('\0')) {
145
- return { isValid: false, error: 'Entry name cannot contain "/" or null characters' };
146
- }
147
- if (!isValidSha(entry.sha)) {
148
- return { isValid: false, error: `Invalid SHA: ${entry.sha}. Must be 40 lowercase hex characters` };
149
- }
150
- return { isValid: true };
151
- }
152
- /**
153
- * Validate an author object.
154
- *
155
- * @description
156
- * Validates all fields of an Author object including name, email,
157
- * timestamp, and timezone format.
158
- *
159
- * @param author - The author object to validate
160
- * @returns Validation result with isValid boolean and optional error message
161
- *
162
- * @example
163
- * ```typescript
164
- * const result = validateAuthor({
165
- * name: 'Alice',
166
- * email: 'alice@example.com',
167
- * timestamp: 1704067200,
168
- * timezone: '+0000'
169
- * })
170
- * if (!result.isValid) {
171
- * console.error(result.error)
172
- * }
173
- * ```
174
- */
175
- export function validateAuthor(author) {
176
- if (!author.name || typeof author.name !== 'string') {
177
- return { isValid: false, error: 'Author name is required and must be a string' };
178
- }
179
- if (!author.email || typeof author.email !== 'string') {
180
- return { isValid: false, error: 'Author email is required and must be a string' };
181
- }
182
- if (typeof author.timestamp !== 'number' || !Number.isInteger(author.timestamp) || author.timestamp < 0) {
183
- return { isValid: false, error: 'Timestamp must be a non-negative integer (Unix seconds)' };
184
- }
185
- if (!/^[+-]\d{4}$/.test(author.timezone)) {
186
- return { isValid: false, error: `Invalid timezone format: ${author.timezone}. Expected +/-HHMM (e.g., +0530, -0800)` };
187
- }
188
- return { isValid: true };
189
- }
190
- /**
191
- * Validate a commit object (excluding type and data fields).
192
- *
193
- * @description
194
- * Validates the structure and content of commit fields.
195
- * Checks tree SHA, parent SHAs, author, committer, and message.
196
- *
197
- * @param commit - The commit data to validate
198
- * @returns Validation result with isValid boolean and optional error message
199
- *
200
- * @example
201
- * ```typescript
202
- * const result = validateCommit({
203
- * tree: 'abc123...',
204
- * parents: ['parent1...'],
205
- * author: { ... },
206
- * committer: { ... },
207
- * message: 'Initial commit'
208
- * })
209
- * ```
210
- */
211
- export function validateCommit(commit) {
212
- if (!isValidSha(commit.tree)) {
213
- return { isValid: false, error: `Invalid tree SHA: ${commit.tree}` };
214
- }
215
- for (let i = 0; i < commit.parents.length; i++) {
216
- if (!isValidSha(commit.parents[i])) {
217
- return { isValid: false, error: `Invalid parent SHA at index ${i}: ${commit.parents[i]}` };
218
- }
219
- }
220
- const authorResult = validateAuthor(commit.author);
221
- if (!authorResult.isValid) {
222
- return { isValid: false, error: `Invalid author: ${authorResult.error}` };
223
- }
224
- const committerResult = validateAuthor(commit.committer);
225
- if (!committerResult.isValid) {
226
- return { isValid: false, error: `Invalid committer: ${committerResult.error}` };
227
- }
228
- if (typeof commit.message !== 'string') {
229
- return { isValid: false, error: 'Commit message must be a string' };
230
- }
231
- return { isValid: true };
232
- }
233
- /**
234
- * Validate a tag object (excluding type and data fields).
235
- *
236
- * @description
237
- * Validates the structure and content of tag fields.
238
- * Checks object SHA, object type, name, tagger, and message.
239
- *
240
- * @param tag - The tag data to validate
241
- * @returns Validation result with isValid boolean and optional error message
242
- *
243
- * @example
244
- * ```typescript
245
- * const result = validateTag({
246
- * object: 'commitsha...',
247
- * objectType: 'commit',
248
- * name: 'v1.0.0',
249
- * tagger: { ... },
250
- * message: 'Release v1.0.0'
251
- * })
252
- * ```
253
- */
254
- export function validateTag(tag) {
255
- if (!isValidSha(tag.object)) {
256
- return { isValid: false, error: `Invalid object SHA: ${tag.object}` };
257
- }
258
- if (!isValidObjectType(tag.objectType)) {
259
- return { isValid: false, error: `Invalid object type: ${tag.objectType}` };
260
- }
261
- if (!tag.name || typeof tag.name !== 'string') {
262
- return { isValid: false, error: 'Tag name is required and must be a string' };
263
- }
264
- if (tag.tagger) {
265
- const taggerResult = validateAuthor(tag.tagger);
266
- if (!taggerResult.isValid) {
267
- return { isValid: false, error: `Invalid tagger: ${taggerResult.error}` };
268
- }
269
- }
270
- if (typeof tag.message !== 'string') {
271
- return { isValid: false, error: 'Tag message must be a string' };
272
- }
273
- return { isValid: true };
274
- }
275
- // ============================================================================
276
- // Type Guards
277
- // ============================================================================
278
- /**
279
- * Type guard to check if a GitObject is a BlobObject.
280
- *
281
- * @description
282
- * Narrows the type of a GitObject to BlobObject based on the type field.
283
- *
284
- * @param obj - The Git object to check
285
- * @returns True if the object is a blob, false otherwise
286
- *
287
- * @example
288
- * ```typescript
289
- * const obj: GitObject = getObject(sha)
290
- * if (isBlob(obj)) {
291
- * // obj is now typed as BlobObject
292
- * const content = new TextDecoder().decode(obj.data)
293
- * }
294
- * ```
295
- */
296
- export function isBlob(obj) {
297
- return obj.type === 'blob';
298
- }
299
- /**
300
- * Type guard to check if a GitObject is a TreeObject.
301
- *
302
- * @description
303
- * Narrows the type of a GitObject to TreeObject based on the type field.
304
- *
305
- * @param obj - The Git object to check
306
- * @returns True if the object is a tree, false otherwise
307
- *
308
- * @example
309
- * ```typescript
310
- * const obj: GitObject = getObject(sha)
311
- * if (isTree(obj)) {
312
- * // obj is now typed as TreeObject
313
- * for (const entry of obj.entries) {
314
- * console.log(entry.name, entry.mode)
315
- * }
316
- * }
317
- * ```
318
- */
319
- export function isTree(obj) {
320
- return obj.type === 'tree';
321
- }
322
- /**
323
- * Type guard to check if a GitObject is a CommitObject.
324
- *
325
- * @description
326
- * Narrows the type of a GitObject to CommitObject based on the type field.
327
- *
328
- * @param obj - The Git object to check
329
- * @returns True if the object is a commit, false otherwise
330
- *
331
- * @example
332
- * ```typescript
333
- * const obj: GitObject = getObject(sha)
334
- * if (isCommit(obj)) {
335
- * // obj is now typed as CommitObject
336
- * console.log(obj.message, obj.author.name)
337
- * }
338
- * ```
339
- */
340
- export function isCommit(obj) {
341
- return obj.type === 'commit';
342
- }
343
- /**
344
- * Type guard to check if a GitObject is a TagObject.
345
- *
346
- * @description
347
- * Narrows the type of a GitObject to TagObject based on the type field.
348
- *
349
- * @param obj - The Git object to check
350
- * @returns True if the object is a tag, false otherwise
351
- *
352
- * @example
353
- * ```typescript
354
- * const obj: GitObject = getObject(sha)
355
- * if (isTag(obj)) {
356
- * // obj is now typed as TagObject
357
- * console.log(obj.name, obj.message)
358
- * }
359
- * ```
360
- */
361
- export function isTag(obj) {
362
- return obj.type === 'tag';
363
- }
364
- // ============================================================================
365
- // Helper Functions (internal)
366
- // ============================================================================
367
- const encoder = new TextEncoder();
368
- const decoder = new TextDecoder();
369
- /**
370
- * Convert a hexadecimal string to a Uint8Array.
371
- *
372
- * @param hex - Hexadecimal string (must have even length)
373
- * @returns Binary representation as Uint8Array
374
- * @internal
375
- */
376
- function hexToBytes(hex) {
377
- const bytes = new Uint8Array(hex.length / 2);
378
- for (let i = 0; i < hex.length; i += 2) {
379
- bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
380
- }
381
- return bytes;
382
- }
383
- /**
384
- * Convert a Uint8Array to a lowercase hexadecimal string.
385
- *
386
- * @param bytes - Binary data to convert
387
- * @returns Lowercase hexadecimal string
388
- * @internal
389
- */
390
- function bytesToHex(bytes) {
391
- return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
392
- }
393
- /**
394
- * Format an Author object as a Git author/committer/tagger line.
395
- *
396
- * @param prefix - Line prefix ('author', 'committer', or 'tagger')
397
- * @param author - Author information
398
- * @returns Formatted line string
399
- * @internal
400
- */
401
- function formatAuthor(prefix, author) {
402
- return `${prefix} ${author.name} <${author.email}> ${author.timestamp} ${author.timezone}`;
403
- }
404
- /**
405
- * Parse a Git author/committer/tagger line into an Author object.
406
- *
407
- * @param line - The full line including prefix
408
- * @returns Parsed Author object
409
- * @throws Error if the line format is invalid
410
- * @internal
411
- */
412
- function parseAuthorLine(line) {
413
- // Format: "author Name <email> timestamp timezone"
414
- // or "committer Name <email> timestamp timezone"
415
- // or "tagger Name <email> timestamp timezone"
416
- const match = line.match(/^(?:author|committer|tagger) (.+) <(.+)> (\d+) ([+-]\d{4})$/);
417
- if (!match) {
418
- throw new Error(`Invalid author line: ${line}`);
419
- }
420
- return {
421
- name: match[1],
422
- email: match[2],
423
- timestamp: parseInt(match[3], 10),
424
- timezone: match[4]
425
- };
426
- }
427
- // ============================================================================
428
- // Serialization Functions
429
- // ============================================================================
430
- /**
431
- * Serialize raw blob data into Git blob object format.
432
- *
433
- * @description
434
- * Creates a complete Git blob object with header: "blob {size}\0{content}"
435
- * This format is used for hashing and storage.
436
- *
437
- * @param data - Raw file content as binary data
438
- * @returns Complete blob object with Git header
439
- *
440
- * @example
441
- * ```typescript
442
- * const content = new TextEncoder().encode('Hello, World!')
443
- * const blob = serializeBlob(content)
444
- * // blob contains: "blob 13\0Hello, World!"
445
- *
446
- * // Hash it to get the SHA
447
- * const sha = await sha1(blob)
448
- * ```
449
- */
450
- export function serializeBlob(data) {
451
- // Git format: "blob <size>\0<content>"
452
- const header = encoder.encode(`blob ${data.length}\0`);
453
- const result = new Uint8Array(header.length + data.length);
454
- result.set(header);
455
- result.set(data, header.length);
456
- return result;
457
- }
458
- /**
459
- * Serialize tree entries into Git tree object format.
460
- *
461
- * @description
462
- * Creates a complete Git tree object with header and sorted entries.
463
- * Each entry format: "{mode} {name}\0{20-byte-sha}"
464
- * Entries are sorted by name with directories treated as having trailing slashes.
465
- *
466
- * @param entries - Array of tree entries to serialize
467
- * @returns Complete tree object with Git header
468
- *
469
- * @example
470
- * ```typescript
471
- * const entries: TreeEntry[] = [
472
- * { mode: '100644', name: 'file.txt', sha: 'abc...' },
473
- * { mode: '040000', name: 'src', sha: 'def...' }
474
- * ]
475
- * const tree = serializeTree(entries)
476
- * const sha = await sha1(tree)
477
- * ```
478
- */
479
- export function serializeTree(entries) {
480
- // Git format: "tree <size>\0<entries>"
481
- // Each entry: "<mode> <name>\0<20-byte-sha>"
482
- // Sort entries by name (Git sorts directories as if they have trailing /)
483
- const sortedEntries = [...entries].sort((a, b) => {
484
- const aName = a.mode === '040000' ? a.name + '/' : a.name;
485
- const bName = b.mode === '040000' ? b.name + '/' : b.name;
486
- return aName.localeCompare(bName);
487
- });
488
- // Build entry content
489
- const entryParts = [];
490
- for (const entry of sortedEntries) {
491
- const modeName = encoder.encode(`${entry.mode} ${entry.name}\0`);
492
- const sha20 = hexToBytes(entry.sha);
493
- const entryData = new Uint8Array(modeName.length + 20);
494
- entryData.set(modeName);
495
- entryData.set(sha20, modeName.length);
496
- entryParts.push(entryData);
497
- }
498
- // Calculate total content length
499
- const contentLength = entryParts.reduce((sum, part) => sum + part.length, 0);
500
- const content = new Uint8Array(contentLength);
501
- let offset = 0;
502
- for (const part of entryParts) {
503
- content.set(part, offset);
504
- offset += part.length;
505
- }
506
- // Add header
507
- const header = encoder.encode(`tree ${contentLength}\0`);
508
- const result = new Uint8Array(header.length + contentLength);
509
- result.set(header);
510
- result.set(content, header.length);
511
- return result;
512
- }
513
- /**
514
- * Serialize commit data into Git commit object format.
515
- *
516
- * @description
517
- * Creates a complete Git commit object with header and formatted content.
518
- * The content includes tree SHA, parent SHAs, author, committer, and message.
519
- *
520
- * @param commit - Commit data (without 'type' and 'data' fields)
521
- * @returns Complete commit object with Git header
522
- *
523
- * @example
524
- * ```typescript
525
- * const commit = serializeCommit({
526
- * tree: 'abc123...',
527
- * parents: ['parent1...'],
528
- * author: { name: 'Alice', email: 'alice@example.com', timestamp: 1704067200, timezone: '+0000' },
529
- * committer: { name: 'Alice', email: 'alice@example.com', timestamp: 1704067200, timezone: '+0000' },
530
- * message: 'Initial commit'
531
- * })
532
- * const sha = await sha1(commit)
533
- * ```
534
- */
535
- export function serializeCommit(commit) {
536
- // Git format: "commit <size>\0<content>"
537
- // Content:
538
- // tree <sha>\n
539
- // parent <sha>\n (for each parent)
540
- // author <name> <email> <timestamp> <timezone>\n
541
- // committer <name> <email> <timestamp> <timezone>\n
542
- // \n
543
- // <message>
544
- const lines = [];
545
- lines.push(`tree ${commit.tree}`);
546
- for (const parent of commit.parents) {
547
- lines.push(`parent ${parent}`);
548
- }
549
- lines.push(formatAuthor('author', commit.author));
550
- lines.push(formatAuthor('committer', commit.committer));
551
- lines.push('');
552
- lines.push(commit.message);
553
- const content = lines.join('\n');
554
- const header = `commit ${encoder.encode(content).length}\0`;
555
- return encoder.encode(header + content);
556
- }
557
- /**
558
- * Serialize tag data into Git tag object format.
559
- *
560
- * @description
561
- * Creates a complete Git tag object with header and formatted content.
562
- * The content includes object SHA, object type, tag name, tagger (optional), and message.
563
- *
564
- * @param tag - Tag data (without 'type' and 'data' fields)
565
- * @returns Complete tag object with Git header
566
- *
567
- * @example
568
- * ```typescript
569
- * const tag = serializeTag({
570
- * object: 'commitsha...',
571
- * objectType: 'commit',
572
- * name: 'v1.0.0',
573
- * tagger: { name: 'Bob', email: 'bob@example.com', timestamp: 1704067200, timezone: '+0000' },
574
- * message: 'Release v1.0.0'
575
- * })
576
- * const sha = await sha1(tag)
577
- * ```
578
- */
579
- export function serializeTag(tag) {
580
- // Git format: "tag <size>\0<content>"
581
- // Content:
582
- // object <sha>\n
583
- // type <objecttype>\n
584
- // tag <name>\n
585
- // tagger <name> <email> <timestamp> <timezone>\n
586
- // \n
587
- // <message>
588
- const lines = [];
589
- lines.push(`object ${tag.object}`);
590
- lines.push(`type ${tag.objectType}`);
591
- lines.push(`tag ${tag.name}`);
592
- if (tag.tagger) {
593
- lines.push(formatAuthor('tagger', tag.tagger));
594
- }
595
- lines.push('');
596
- lines.push(tag.message);
597
- const content = lines.join('\n');
598
- const header = `tag ${encoder.encode(content).length}\0`;
599
- return encoder.encode(header + content);
600
- }
601
- // ============================================================================
602
- // Deserialization (Parsing) Functions
603
- // ============================================================================
604
- /**
605
- * Parse a Git blob object from its serialized format.
606
- *
607
- * @description
608
- * Parses a complete Git blob object (with header) back into a BlobObject.
609
- * Validates the header format and extracts the content.
610
- *
611
- * @param data - Complete blob object data including Git header
612
- * @returns Parsed BlobObject
613
- * @throws Error if the data is not a valid blob object (missing null byte or invalid header)
614
- *
615
- * @example
616
- * ```typescript
617
- * const rawBlob = await storage.getObject(sha)
618
- * const blob = parseBlob(rawBlob)
619
- * const content = new TextDecoder().decode(blob.data)
620
- * ```
621
- */
622
- export function parseBlob(data) {
623
- // Git format: "blob <size>\0<content>"
624
- // Find the null byte to separate header from content
625
- const nullIndex = data.indexOf(0);
626
- if (nullIndex === -1) {
627
- throw new Error('Invalid blob: no null byte found');
628
- }
629
- const header = decoder.decode(data.slice(0, nullIndex));
630
- const match = header.match(/^blob (\d+)$/);
631
- if (!match) {
632
- throw new Error(`Invalid blob header: ${header}`);
633
- }
634
- const content = data.slice(nullIndex + 1);
635
- return {
636
- type: 'blob',
637
- data: content
638
- };
639
- }
640
- /**
641
- * Parse a Git tree object from its serialized format.
642
- *
643
- * @description
644
- * Parses a complete Git tree object (with header) back into a TreeObject.
645
- * Extracts all tree entries with their modes, names, and SHA references.
646
- *
647
- * @param data - Complete tree object data including Git header
648
- * @returns Parsed TreeObject with entries array
649
- * @throws Error if the data is not a valid tree object (missing null byte or invalid header)
650
- *
651
- * @example
652
- * ```typescript
653
- * const rawTree = await storage.getObject(sha)
654
- * const tree = parseTree(rawTree)
655
- * for (const entry of tree.entries) {
656
- * console.log(`${entry.mode} ${entry.name} ${entry.sha}`)
657
- * }
658
- * ```
659
- */
660
- export function parseTree(data) {
661
- // Git format: "tree <size>\0<entries>"
662
- // Each entry: "<mode> <name>\0<20-byte-sha>"
663
- const nullIndex = data.indexOf(0);
664
- if (nullIndex === -1) {
665
- throw new Error('Invalid tree: no null byte found');
666
- }
667
- const header = decoder.decode(data.slice(0, nullIndex));
668
- const match = header.match(/^tree (\d+)$/);
669
- if (!match) {
670
- throw new Error(`Invalid tree header: ${header}`);
671
- }
672
- const entries = [];
673
- let offset = nullIndex + 1;
674
- while (offset < data.length) {
675
- // Find the null byte after mode+name
676
- let entryNullIndex = offset;
677
- while (entryNullIndex < data.length && data[entryNullIndex] !== 0) {
678
- entryNullIndex++;
679
- }
680
- const modeNameStr = decoder.decode(data.slice(offset, entryNullIndex));
681
- const spaceIndex = modeNameStr.indexOf(' ');
682
- const mode = modeNameStr.slice(0, spaceIndex);
683
- const name = modeNameStr.slice(spaceIndex + 1);
684
- // Read 20-byte SHA
685
- const sha20 = data.slice(entryNullIndex + 1, entryNullIndex + 21);
686
- const sha = bytesToHex(sha20);
687
- entries.push({ mode, name, sha });
688
- offset = entryNullIndex + 21;
689
- }
690
- return {
691
- type: 'tree',
692
- data: data.slice(nullIndex + 1),
693
- entries
694
- };
695
- }
696
- /**
697
- * Parse a Git commit object from its serialized format.
698
- *
699
- * @description
700
- * Parses a complete Git commit object (with header) back into a CommitObject.
701
- * Extracts tree SHA, parent SHAs, author, committer, and message.
702
- *
703
- * @param data - Complete commit object data including Git header
704
- * @returns Parsed CommitObject
705
- * @throws Error if the data is not a valid commit object (missing null byte, invalid header, or missing author/committer)
706
- *
707
- * @example
708
- * ```typescript
709
- * const rawCommit = await storage.getObject(sha)
710
- * const commit = parseCommit(rawCommit)
711
- * console.log(`Author: ${commit.author.name}`)
712
- * console.log(`Message: ${commit.message}`)
713
- * console.log(`Parents: ${commit.parents.length}`)
714
- * ```
715
- */
716
- export function parseCommit(data) {
717
- // Git format: "commit <size>\0<content>"
718
- const nullIndex = data.indexOf(0);
719
- if (nullIndex === -1) {
720
- throw new Error('Invalid commit: no null byte found');
721
- }
722
- const header = decoder.decode(data.slice(0, nullIndex));
723
- const match = header.match(/^commit (\d+)$/);
724
- if (!match) {
725
- throw new Error(`Invalid commit header: ${header}`);
726
- }
727
- const content = decoder.decode(data.slice(nullIndex + 1));
728
- const lines = content.split('\n');
729
- let tree = '';
730
- const parents = [];
731
- let author = null;
732
- let committer = null;
733
- let messageStartIndex = 0;
734
- for (let i = 0; i < lines.length; i++) {
735
- const line = lines[i];
736
- if (line === '') {
737
- // Empty line separates headers from message
738
- messageStartIndex = i + 1;
739
- break;
740
- }
741
- if (line.startsWith('tree ')) {
742
- tree = line.slice(5);
743
- }
744
- else if (line.startsWith('parent ')) {
745
- parents.push(line.slice(7));
746
- }
747
- else if (line.startsWith('author ')) {
748
- author = parseAuthorLine(line);
749
- }
750
- else if (line.startsWith('committer ')) {
751
- committer = parseAuthorLine(line);
752
- }
753
- }
754
- if (!author || !committer) {
755
- throw new Error('Invalid commit: missing author or committer');
756
- }
757
- const message = lines.slice(messageStartIndex).join('\n');
758
- return {
759
- type: 'commit',
760
- data: data.slice(nullIndex + 1),
761
- tree,
762
- parents,
763
- author,
764
- committer,
765
- message
766
- };
767
- }
768
- /**
769
- * Parse a Git tag object from its serialized format.
770
- *
771
- * @description
772
- * Parses a complete Git tag object (with header) back into a TagObject.
773
- * Extracts object SHA, object type, tag name, tagger, and message.
774
- *
775
- * @param data - Complete tag object data including Git header
776
- * @returns Parsed TagObject
777
- * @throws Error if the data is not a valid tag object (missing null byte, invalid header, or missing tagger)
778
- *
779
- * @example
780
- * ```typescript
781
- * const rawTag = await storage.getObject(sha)
782
- * const tag = parseTag(rawTag)
783
- * console.log(`Tag: ${tag.name}`)
784
- * console.log(`Points to: ${tag.object} (${tag.objectType})`)
785
- * console.log(`Message: ${tag.message}`)
786
- * ```
787
- */
788
- export function parseTag(data) {
789
- // Git format: "tag <size>\0<content>"
790
- const nullIndex = data.indexOf(0);
791
- if (nullIndex === -1) {
792
- throw new Error('Invalid tag: no null byte found');
793
- }
794
- const header = decoder.decode(data.slice(0, nullIndex));
795
- const match = header.match(/^tag (\d+)$/);
796
- if (!match) {
797
- throw new Error(`Invalid tag header: ${header}`);
798
- }
799
- const content = decoder.decode(data.slice(nullIndex + 1));
800
- const lines = content.split('\n');
801
- let object = '';
802
- let objectType = 'commit';
803
- let name = '';
804
- let tagger = null;
805
- let messageStartIndex = 0;
806
- for (let i = 0; i < lines.length; i++) {
807
- const line = lines[i];
808
- if (line === '') {
809
- // Empty line separates headers from message
810
- messageStartIndex = i + 1;
811
- break;
812
- }
813
- if (line.startsWith('object ')) {
814
- object = line.slice(7);
815
- }
816
- else if (line.startsWith('type ')) {
817
- objectType = line.slice(5);
818
- }
819
- else if (line.startsWith('tag ')) {
820
- name = line.slice(4);
821
- }
822
- else if (line.startsWith('tagger ')) {
823
- tagger = parseAuthorLine(line);
824
- }
825
- }
826
- const message = lines.slice(messageStartIndex).join('\n');
827
- return {
828
- type: 'tag',
829
- data: data.slice(nullIndex + 1),
830
- object,
831
- objectType,
832
- name,
833
- tagger: tagger ?? undefined,
834
- message
835
- };
836
- }
837
- //# sourceMappingURL=objects.js.map