gitx.do 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/README.md +40 -353
  2. package/dist/do/logger.d.ts +50 -0
  3. package/dist/do/logger.d.ts.map +1 -0
  4. package/dist/do/logger.js +122 -0
  5. package/dist/do/logger.js.map +1 -0
  6. package/dist/{durable-object → do}/schema.d.ts +3 -3
  7. package/dist/do/schema.d.ts.map +1 -0
  8. package/dist/{durable-object → do}/schema.js +4 -3
  9. package/dist/do/schema.js.map +1 -0
  10. package/dist/do/types.d.ts +267 -0
  11. package/dist/do/types.d.ts.map +1 -0
  12. package/dist/do/types.js +62 -0
  13. package/dist/do/types.js.map +1 -0
  14. package/dist/index.d.ts +15 -415
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +31 -483
  17. package/dist/index.js.map +1 -1
  18. package/package.json +13 -21
  19. package/dist/cli/commands/add.d.ts +0 -174
  20. package/dist/cli/commands/add.d.ts.map +0 -1
  21. package/dist/cli/commands/add.js +0 -131
  22. package/dist/cli/commands/add.js.map +0 -1
  23. package/dist/cli/commands/blame.d.ts +0 -259
  24. package/dist/cli/commands/blame.d.ts.map +0 -1
  25. package/dist/cli/commands/blame.js +0 -609
  26. package/dist/cli/commands/blame.js.map +0 -1
  27. package/dist/cli/commands/branch.d.ts +0 -249
  28. package/dist/cli/commands/branch.d.ts.map +0 -1
  29. package/dist/cli/commands/branch.js +0 -693
  30. package/dist/cli/commands/branch.js.map +0 -1
  31. package/dist/cli/commands/commit.d.ts +0 -182
  32. package/dist/cli/commands/commit.d.ts.map +0 -1
  33. package/dist/cli/commands/commit.js +0 -437
  34. package/dist/cli/commands/commit.js.map +0 -1
  35. package/dist/cli/commands/diff.d.ts +0 -464
  36. package/dist/cli/commands/diff.d.ts.map +0 -1
  37. package/dist/cli/commands/diff.js +0 -958
  38. package/dist/cli/commands/diff.js.map +0 -1
  39. package/dist/cli/commands/log.d.ts +0 -239
  40. package/dist/cli/commands/log.d.ts.map +0 -1
  41. package/dist/cli/commands/log.js +0 -535
  42. package/dist/cli/commands/log.js.map +0 -1
  43. package/dist/cli/commands/merge.d.ts +0 -106
  44. package/dist/cli/commands/merge.d.ts.map +0 -1
  45. package/dist/cli/commands/merge.js +0 -55
  46. package/dist/cli/commands/merge.js.map +0 -1
  47. package/dist/cli/commands/review.d.ts +0 -457
  48. package/dist/cli/commands/review.d.ts.map +0 -1
  49. package/dist/cli/commands/review.js +0 -533
  50. package/dist/cli/commands/review.js.map +0 -1
  51. package/dist/cli/commands/status.d.ts +0 -269
  52. package/dist/cli/commands/status.d.ts.map +0 -1
  53. package/dist/cli/commands/status.js +0 -493
  54. package/dist/cli/commands/status.js.map +0 -1
  55. package/dist/cli/commands/web.d.ts +0 -199
  56. package/dist/cli/commands/web.d.ts.map +0 -1
  57. package/dist/cli/commands/web.js +0 -696
  58. package/dist/cli/commands/web.js.map +0 -1
  59. package/dist/cli/fs-adapter.d.ts +0 -656
  60. package/dist/cli/fs-adapter.d.ts.map +0 -1
  61. package/dist/cli/fs-adapter.js +0 -1179
  62. package/dist/cli/fs-adapter.js.map +0 -1
  63. package/dist/cli/fsx-cli-adapter.d.ts +0 -359
  64. package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
  65. package/dist/cli/fsx-cli-adapter.js +0 -619
  66. package/dist/cli/fsx-cli-adapter.js.map +0 -1
  67. package/dist/cli/index.d.ts +0 -387
  68. package/dist/cli/index.d.ts.map +0 -1
  69. package/dist/cli/index.js +0 -523
  70. package/dist/cli/index.js.map +0 -1
  71. package/dist/cli/ui/components/DiffView.d.ts +0 -7
  72. package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
  73. package/dist/cli/ui/components/DiffView.js +0 -11
  74. package/dist/cli/ui/components/DiffView.js.map +0 -1
  75. package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -6
  76. package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
  77. package/dist/cli/ui/components/ErrorDisplay.js +0 -11
  78. package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
  79. package/dist/cli/ui/components/FuzzySearch.d.ts +0 -9
  80. package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
  81. package/dist/cli/ui/components/FuzzySearch.js +0 -12
  82. package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
  83. package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -6
  84. package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
  85. package/dist/cli/ui/components/LoadingSpinner.js +0 -10
  86. package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
  87. package/dist/cli/ui/components/NavigationList.d.ts +0 -9
  88. package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
  89. package/dist/cli/ui/components/NavigationList.js +0 -11
  90. package/dist/cli/ui/components/NavigationList.js.map +0 -1
  91. package/dist/cli/ui/components/ScrollableContent.d.ts +0 -8
  92. package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
  93. package/dist/cli/ui/components/ScrollableContent.js +0 -11
  94. package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
  95. package/dist/cli/ui/components/index.d.ts +0 -7
  96. package/dist/cli/ui/components/index.d.ts.map +0 -1
  97. package/dist/cli/ui/components/index.js +0 -9
  98. package/dist/cli/ui/components/index.js.map +0 -1
  99. package/dist/cli/ui/terminal-ui.d.ts +0 -52
  100. package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
  101. package/dist/cli/ui/terminal-ui.js +0 -121
  102. package/dist/cli/ui/terminal-ui.js.map +0 -1
  103. package/dist/do/BashModule.d.ts +0 -871
  104. package/dist/do/BashModule.d.ts.map +0 -1
  105. package/dist/do/BashModule.js +0 -1143
  106. package/dist/do/BashModule.js.map +0 -1
  107. package/dist/do/FsModule.d.ts +0 -601
  108. package/dist/do/FsModule.d.ts.map +0 -1
  109. package/dist/do/FsModule.js +0 -1120
  110. package/dist/do/FsModule.js.map +0 -1
  111. package/dist/do/GitModule.d.ts +0 -635
  112. package/dist/do/GitModule.d.ts.map +0 -1
  113. package/dist/do/GitModule.js +0 -781
  114. package/dist/do/GitModule.js.map +0 -1
  115. package/dist/do/GitRepoDO.d.ts +0 -281
  116. package/dist/do/GitRepoDO.d.ts.map +0 -1
  117. package/dist/do/GitRepoDO.js +0 -479
  118. package/dist/do/GitRepoDO.js.map +0 -1
  119. package/dist/do/bash-ast.d.ts +0 -246
  120. package/dist/do/bash-ast.d.ts.map +0 -1
  121. package/dist/do/bash-ast.js +0 -888
  122. package/dist/do/bash-ast.js.map +0 -1
  123. package/dist/do/container-executor.d.ts +0 -491
  124. package/dist/do/container-executor.d.ts.map +0 -1
  125. package/dist/do/container-executor.js +0 -730
  126. package/dist/do/container-executor.js.map +0 -1
  127. package/dist/do/index.d.ts +0 -53
  128. package/dist/do/index.d.ts.map +0 -1
  129. package/dist/do/index.js +0 -91
  130. package/dist/do/index.js.map +0 -1
  131. package/dist/do/tiered-storage.d.ts +0 -403
  132. package/dist/do/tiered-storage.d.ts.map +0 -1
  133. package/dist/do/tiered-storage.js +0 -689
  134. package/dist/do/tiered-storage.js.map +0 -1
  135. package/dist/do/withBash.d.ts +0 -231
  136. package/dist/do/withBash.d.ts.map +0 -1
  137. package/dist/do/withBash.js +0 -244
  138. package/dist/do/withBash.js.map +0 -1
  139. package/dist/do/withFs.d.ts +0 -237
  140. package/dist/do/withFs.d.ts.map +0 -1
  141. package/dist/do/withFs.js +0 -387
  142. package/dist/do/withFs.js.map +0 -1
  143. package/dist/do/withGit.d.ts +0 -180
  144. package/dist/do/withGit.d.ts.map +0 -1
  145. package/dist/do/withGit.js +0 -271
  146. package/dist/do/withGit.js.map +0 -1
  147. package/dist/durable-object/object-store.d.ts +0 -633
  148. package/dist/durable-object/object-store.d.ts.map +0 -1
  149. package/dist/durable-object/object-store.js +0 -1161
  150. package/dist/durable-object/object-store.js.map +0 -1
  151. package/dist/durable-object/schema.d.ts.map +0 -1
  152. package/dist/durable-object/schema.js.map +0 -1
  153. package/dist/durable-object/wal.d.ts +0 -416
  154. package/dist/durable-object/wal.d.ts.map +0 -1
  155. package/dist/durable-object/wal.js +0 -445
  156. package/dist/durable-object/wal.js.map +0 -1
  157. package/dist/mcp/adapter.d.ts +0 -772
  158. package/dist/mcp/adapter.d.ts.map +0 -1
  159. package/dist/mcp/adapter.js +0 -895
  160. package/dist/mcp/adapter.js.map +0 -1
  161. package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
  162. package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
  163. package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
  164. package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
  165. package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
  166. package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
  167. package/dist/mcp/sandbox/object-store-proxy.js +0 -30
  168. package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
  169. package/dist/mcp/sandbox/template.d.ts +0 -17
  170. package/dist/mcp/sandbox/template.d.ts.map +0 -1
  171. package/dist/mcp/sandbox/template.js +0 -71
  172. package/dist/mcp/sandbox/template.js.map +0 -1
  173. package/dist/mcp/sandbox.d.ts +0 -764
  174. package/dist/mcp/sandbox.d.ts.map +0 -1
  175. package/dist/mcp/sandbox.js +0 -1362
  176. package/dist/mcp/sandbox.js.map +0 -1
  177. package/dist/mcp/sdk-adapter.d.ts +0 -835
  178. package/dist/mcp/sdk-adapter.d.ts.map +0 -1
  179. package/dist/mcp/sdk-adapter.js +0 -974
  180. package/dist/mcp/sdk-adapter.js.map +0 -1
  181. package/dist/mcp/tools/do.d.ts +0 -32
  182. package/dist/mcp/tools/do.d.ts.map +0 -1
  183. package/dist/mcp/tools/do.js +0 -115
  184. package/dist/mcp/tools/do.js.map +0 -1
  185. package/dist/mcp/tools.d.ts +0 -548
  186. package/dist/mcp/tools.d.ts.map +0 -1
  187. package/dist/mcp/tools.js +0 -1934
  188. package/dist/mcp/tools.js.map +0 -1
  189. package/dist/ops/blame.d.ts +0 -551
  190. package/dist/ops/blame.d.ts.map +0 -1
  191. package/dist/ops/blame.js +0 -1037
  192. package/dist/ops/blame.js.map +0 -1
  193. package/dist/ops/branch.d.ts +0 -766
  194. package/dist/ops/branch.d.ts.map +0 -1
  195. package/dist/ops/branch.js +0 -950
  196. package/dist/ops/branch.js.map +0 -1
  197. package/dist/ops/commit-traversal.d.ts +0 -349
  198. package/dist/ops/commit-traversal.d.ts.map +0 -1
  199. package/dist/ops/commit-traversal.js +0 -821
  200. package/dist/ops/commit-traversal.js.map +0 -1
  201. package/dist/ops/commit.d.ts +0 -555
  202. package/dist/ops/commit.d.ts.map +0 -1
  203. package/dist/ops/commit.js +0 -826
  204. package/dist/ops/commit.js.map +0 -1
  205. package/dist/ops/merge-base.d.ts +0 -397
  206. package/dist/ops/merge-base.d.ts.map +0 -1
  207. package/dist/ops/merge-base.js +0 -691
  208. package/dist/ops/merge-base.js.map +0 -1
  209. package/dist/ops/merge.d.ts +0 -855
  210. package/dist/ops/merge.d.ts.map +0 -1
  211. package/dist/ops/merge.js +0 -1551
  212. package/dist/ops/merge.js.map +0 -1
  213. package/dist/ops/tag.d.ts +0 -247
  214. package/dist/ops/tag.d.ts.map +0 -1
  215. package/dist/ops/tag.js +0 -649
  216. package/dist/ops/tag.js.map +0 -1
  217. package/dist/ops/tree-builder.d.ts +0 -178
  218. package/dist/ops/tree-builder.d.ts.map +0 -1
  219. package/dist/ops/tree-builder.js +0 -271
  220. package/dist/ops/tree-builder.js.map +0 -1
  221. package/dist/ops/tree-diff.d.ts +0 -291
  222. package/dist/ops/tree-diff.d.ts.map +0 -1
  223. package/dist/ops/tree-diff.js +0 -705
  224. package/dist/ops/tree-diff.js.map +0 -1
  225. package/dist/pack/delta.d.ts +0 -248
  226. package/dist/pack/delta.d.ts.map +0 -1
  227. package/dist/pack/delta.js +0 -736
  228. package/dist/pack/delta.js.map +0 -1
  229. package/dist/pack/format.d.ts +0 -446
  230. package/dist/pack/format.d.ts.map +0 -1
  231. package/dist/pack/format.js +0 -572
  232. package/dist/pack/format.js.map +0 -1
  233. package/dist/pack/full-generation.d.ts +0 -612
  234. package/dist/pack/full-generation.d.ts.map +0 -1
  235. package/dist/pack/full-generation.js +0 -1378
  236. package/dist/pack/full-generation.js.map +0 -1
  237. package/dist/pack/generation.d.ts +0 -441
  238. package/dist/pack/generation.d.ts.map +0 -1
  239. package/dist/pack/generation.js +0 -707
  240. package/dist/pack/generation.js.map +0 -1
  241. package/dist/pack/index.d.ts +0 -502
  242. package/dist/pack/index.d.ts.map +0 -1
  243. package/dist/pack/index.js +0 -833
  244. package/dist/pack/index.js.map +0 -1
  245. package/dist/refs/branch.d.ts +0 -668
  246. package/dist/refs/branch.d.ts.map +0 -1
  247. package/dist/refs/branch.js +0 -897
  248. package/dist/refs/branch.js.map +0 -1
  249. package/dist/refs/storage.d.ts +0 -833
  250. package/dist/refs/storage.d.ts.map +0 -1
  251. package/dist/refs/storage.js +0 -1023
  252. package/dist/refs/storage.js.map +0 -1
  253. package/dist/refs/tag.d.ts +0 -860
  254. package/dist/refs/tag.d.ts.map +0 -1
  255. package/dist/refs/tag.js +0 -996
  256. package/dist/refs/tag.js.map +0 -1
  257. package/dist/storage/backend.d.ts +0 -425
  258. package/dist/storage/backend.d.ts.map +0 -1
  259. package/dist/storage/backend.js +0 -41
  260. package/dist/storage/backend.js.map +0 -1
  261. package/dist/storage/fsx-adapter.d.ts +0 -204
  262. package/dist/storage/fsx-adapter.d.ts.map +0 -1
  263. package/dist/storage/fsx-adapter.js +0 -470
  264. package/dist/storage/fsx-adapter.js.map +0 -1
  265. package/dist/storage/lru-cache.d.ts +0 -691
  266. package/dist/storage/lru-cache.d.ts.map +0 -1
  267. package/dist/storage/lru-cache.js +0 -813
  268. package/dist/storage/lru-cache.js.map +0 -1
  269. package/dist/storage/object-index.d.ts +0 -585
  270. package/dist/storage/object-index.d.ts.map +0 -1
  271. package/dist/storage/object-index.js +0 -532
  272. package/dist/storage/object-index.js.map +0 -1
  273. package/dist/storage/r2-pack.d.ts +0 -1257
  274. package/dist/storage/r2-pack.d.ts.map +0 -1
  275. package/dist/storage/r2-pack.js +0 -1770
  276. package/dist/storage/r2-pack.js.map +0 -1
  277. package/dist/tiered/cdc-pipeline.d.ts +0 -1888
  278. package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
  279. package/dist/tiered/cdc-pipeline.js +0 -1880
  280. package/dist/tiered/cdc-pipeline.js.map +0 -1
  281. package/dist/tiered/migration.d.ts +0 -1104
  282. package/dist/tiered/migration.d.ts.map +0 -1
  283. package/dist/tiered/migration.js +0 -1214
  284. package/dist/tiered/migration.js.map +0 -1
  285. package/dist/tiered/parquet-writer.d.ts +0 -1145
  286. package/dist/tiered/parquet-writer.d.ts.map +0 -1
  287. package/dist/tiered/parquet-writer.js +0 -1183
  288. package/dist/tiered/parquet-writer.js.map +0 -1
  289. package/dist/tiered/read-path.d.ts +0 -835
  290. package/dist/tiered/read-path.d.ts.map +0 -1
  291. package/dist/tiered/read-path.js +0 -487
  292. package/dist/tiered/read-path.js.map +0 -1
  293. package/dist/types/capability.d.ts +0 -1385
  294. package/dist/types/capability.d.ts.map +0 -1
  295. package/dist/types/capability.js +0 -36
  296. package/dist/types/capability.js.map +0 -1
  297. package/dist/types/index.d.ts +0 -13
  298. package/dist/types/index.d.ts.map +0 -1
  299. package/dist/types/index.js +0 -18
  300. package/dist/types/index.js.map +0 -1
  301. package/dist/types/objects.d.ts +0 -692
  302. package/dist/types/objects.d.ts.map +0 -1
  303. package/dist/types/objects.js +0 -837
  304. package/dist/types/objects.js.map +0 -1
  305. package/dist/types/storage.d.ts +0 -603
  306. package/dist/types/storage.d.ts.map +0 -1
  307. package/dist/types/storage.js +0 -191
  308. package/dist/types/storage.js.map +0 -1
  309. package/dist/types/worker-loader.d.ts +0 -60
  310. package/dist/types/worker-loader.d.ts.map +0 -1
  311. package/dist/types/worker-loader.js +0 -62
  312. package/dist/types/worker-loader.js.map +0 -1
  313. package/dist/utils/hash.d.ts +0 -197
  314. package/dist/utils/hash.d.ts.map +0 -1
  315. package/dist/utils/hash.js +0 -268
  316. package/dist/utils/hash.js.map +0 -1
  317. package/dist/utils/sha1.d.ts +0 -290
  318. package/dist/utils/sha1.d.ts.map +0 -1
  319. package/dist/utils/sha1.js +0 -582
  320. package/dist/utils/sha1.js.map +0 -1
  321. package/dist/wire/capabilities.d.ts +0 -1044
  322. package/dist/wire/capabilities.d.ts.map +0 -1
  323. package/dist/wire/capabilities.js +0 -941
  324. package/dist/wire/capabilities.js.map +0 -1
  325. package/dist/wire/path-security.d.ts +0 -157
  326. package/dist/wire/path-security.d.ts.map +0 -1
  327. package/dist/wire/path-security.js +0 -307
  328. package/dist/wire/path-security.js.map +0 -1
  329. package/dist/wire/pkt-line.d.ts +0 -345
  330. package/dist/wire/pkt-line.d.ts.map +0 -1
  331. package/dist/wire/pkt-line.js +0 -381
  332. package/dist/wire/pkt-line.js.map +0 -1
  333. package/dist/wire/receive-pack.d.ts +0 -1059
  334. package/dist/wire/receive-pack.d.ts.map +0 -1
  335. package/dist/wire/receive-pack.js +0 -1414
  336. package/dist/wire/receive-pack.js.map +0 -1
  337. package/dist/wire/smart-http.d.ts +0 -799
  338. package/dist/wire/smart-http.d.ts.map +0 -1
  339. package/dist/wire/smart-http.js +0 -945
  340. package/dist/wire/smart-http.js.map +0 -1
  341. package/dist/wire/upload-pack.d.ts +0 -727
  342. package/dist/wire/upload-pack.d.ts.map +0 -1
  343. package/dist/wire/upload-pack.js +0 -1138
  344. package/dist/wire/upload-pack.js.map +0 -1
@@ -1,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