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,826 +0,0 @@
1
- /**
2
- * @fileoverview Commit Creation Operations
3
- *
4
- * Provides functionality for creating, formatting, and amending git commits.
5
- * Supports author/committer info, parent handling, GPG signing, and message formatting.
6
- *
7
- * ## Features
8
- *
9
- * - Create new commits with full metadata
10
- * - Amend existing commits
11
- * - GPG signature support
12
- * - Message formatting and validation
13
- * - Empty commit detection
14
- * - Author/committer timestamp handling
15
- *
16
- * ## Usage Example
17
- *
18
- * ```typescript
19
- * import { createCommit, formatCommitMessage } from './ops/commit'
20
- *
21
- * // Create a commit
22
- * const result = await createCommit(store, {
23
- * message: 'Add new feature',
24
- * tree: treeHash,
25
- * parents: [parentHash],
26
- * author: { name: 'John Doe', email: 'john@example.com' }
27
- * })
28
- *
29
- * console.log('Created commit:', result.sha)
30
- * ```
31
- *
32
- * @module ops/commit
33
- */
34
- // ============================================================================
35
- // Author/Timestamp Utilities
36
- // ============================================================================
37
- /**
38
- * Gets the current timezone offset string.
39
- *
40
- * Returns the local timezone in Git's format (e.g., '+0000', '-0500').
41
- *
42
- * @returns Timezone offset string
43
- *
44
- * @example
45
- * ```typescript
46
- * const tz = getCurrentTimezone()
47
- * // Returns something like '-0800' for Pacific time
48
- * ```
49
- */
50
- export function getCurrentTimezone() {
51
- const offset = new Date().getTimezoneOffset();
52
- const sign = offset <= 0 ? '+' : '-';
53
- const absOffset = Math.abs(offset);
54
- const hours = Math.floor(absOffset / 60);
55
- const minutes = absOffset % 60;
56
- return `${sign}${String(hours).padStart(2, '0')}${String(minutes).padStart(2, '0')}`;
57
- }
58
- /**
59
- * Formats a timestamp and timezone as git author/committer format.
60
- *
61
- * @param timestamp - Unix timestamp in seconds
62
- * @param timezone - Timezone offset string (e.g., '+0000', '-0500')
63
- * @returns Formatted string like "1234567890 +0000"
64
- *
65
- * @example
66
- * ```typescript
67
- * const formatted = formatTimestamp(1609459200, '+0000')
68
- * // Returns "1609459200 +0000"
69
- * ```
70
- */
71
- export function formatTimestamp(timestamp, timezone) {
72
- return `${timestamp} ${timezone}`;
73
- }
74
- /**
75
- * Parses a git timestamp string.
76
- *
77
- * @param timestampStr - Timestamp string like "1234567890 +0000"
78
- * @returns Object with parsed timestamp and timezone
79
- *
80
- * @throws {Error} If the format is invalid
81
- *
82
- * @example
83
- * ```typescript
84
- * const { timestamp, timezone } = parseTimestamp("1609459200 -0500")
85
- * // timestamp = 1609459200, timezone = "-0500"
86
- * ```
87
- */
88
- export function parseTimestamp(timestampStr) {
89
- const match = timestampStr.match(/^(\d+) ([+-]\d{4})$/);
90
- if (!match) {
91
- throw new Error(`Invalid timestamp format: ${timestampStr}`);
92
- }
93
- return {
94
- timestamp: parseInt(match[1], 10),
95
- timezone: match[2]
96
- };
97
- }
98
- /**
99
- * Creates an Author object with current timestamp.
100
- *
101
- * Convenience function for creating author information with
102
- * the current time and local timezone.
103
- *
104
- * @param name - Author name
105
- * @param email - Author email
106
- * @param timezone - Optional timezone (defaults to local timezone)
107
- * @returns Author object with current timestamp
108
- *
109
- * @example
110
- * ```typescript
111
- * const author = createAuthor('John Doe', 'john@example.com')
112
- * // { name: 'John Doe', email: 'john@example.com', timestamp: <now>, timezone: <local> }
113
- * ```
114
- */
115
- export function createAuthor(name, email, timezone) {
116
- return {
117
- name,
118
- email,
119
- timestamp: Math.floor(Date.now() / 1000),
120
- timezone: timezone ?? getCurrentTimezone()
121
- };
122
- }
123
- // ============================================================================
124
- // Message Formatting
125
- // ============================================================================
126
- /**
127
- * Wraps text at a specified column width.
128
- * @internal
129
- */
130
- function wrapText(text, column) {
131
- if (column <= 0)
132
- return text;
133
- const words = text.split(/\s+/);
134
- const lines = [];
135
- let currentLine = '';
136
- for (const word of words) {
137
- if (currentLine.length === 0) {
138
- currentLine = word;
139
- }
140
- else if (currentLine.length + 1 + word.length <= column) {
141
- currentLine += ' ' + word;
142
- }
143
- else {
144
- lines.push(currentLine);
145
- currentLine = word;
146
- }
147
- }
148
- if (currentLine.length > 0) {
149
- lines.push(currentLine);
150
- }
151
- return lines.join('\n');
152
- }
153
- /**
154
- * Formats a commit message according to git conventions.
155
- *
156
- * Applies various transformations based on the cleanup mode:
157
- * - Strips comments
158
- * - Normalizes whitespace
159
- * - Wraps long lines
160
- * - Removes scissors markers
161
- *
162
- * @param message - The raw commit message
163
- * @param options - Formatting options
164
- * @returns The formatted commit message
165
- *
166
- * @example
167
- * ```typescript
168
- * // Clean up a message
169
- * const formatted = formatCommitMessage(`
170
- * Add feature
171
- *
172
- * # This is a comment
173
- * Long description here
174
- * `, { cleanup: 'strip' })
175
- * // Returns: "Add feature\n\nLong description here"
176
- * ```
177
- */
178
- export function formatCommitMessage(message, options = {}) {
179
- const { cleanup = 'default', commentChar = '#', wrapColumn = 0 } = options;
180
- // Verbatim mode: return message as-is
181
- if (cleanup === 'verbatim') {
182
- return message;
183
- }
184
- let result = message;
185
- // Scissors mode: remove everything after scissors line
186
- if (cleanup === 'scissors') {
187
- const scissorsPattern = new RegExp(`^${commentChar} -+ >8 -+`, 'm');
188
- const scissorsMatch = result.match(scissorsPattern);
189
- if (scissorsMatch && scissorsMatch.index !== undefined) {
190
- result = result.slice(0, scissorsMatch.index);
191
- }
192
- }
193
- // Strip comments if cleanup is 'strip' or 'scissors'
194
- if (cleanup === 'strip' || cleanup === 'scissors') {
195
- const lines = result.split('\n');
196
- result = lines.filter(line => !line.startsWith(commentChar)).join('\n');
197
- }
198
- // Strip whitespace (for 'whitespace', 'strip', 'scissors', 'default')
199
- // Note: verbatim check already handled above, so this always runs
200
- if (true) {
201
- // Strip leading/trailing whitespace from each line
202
- const lines = result.split('\n');
203
- const trimmedLines = lines.map(line => line.trim());
204
- // Collapse multiple blank lines into one
205
- const collapsedLines = [];
206
- let lastWasBlank = false;
207
- for (const line of trimmedLines) {
208
- if (line === '') {
209
- if (!lastWasBlank) {
210
- collapsedLines.push(line);
211
- }
212
- lastWasBlank = true;
213
- }
214
- else {
215
- collapsedLines.push(line);
216
- lastWasBlank = false;
217
- }
218
- }
219
- result = collapsedLines.join('\n');
220
- // Trim leading/trailing blank lines
221
- result = result.replace(/^\n+/, '').replace(/\n+$/, '');
222
- }
223
- // Wrap body (not subject) if wrapColumn is specified
224
- if (wrapColumn > 0 && result.length > 0) {
225
- const lines = result.split('\n');
226
- if (lines.length > 0) {
227
- const subject = lines[0];
228
- const rest = lines.slice(1);
229
- // Find where body starts (after blank line)
230
- let bodyStartIndex = 0;
231
- for (let i = 0; i < rest.length; i++) {
232
- if (rest[i] === '') {
233
- bodyStartIndex = i + 1;
234
- break;
235
- }
236
- }
237
- // Wrap body lines
238
- const wrappedRest = [];
239
- for (let i = 0; i < rest.length; i++) {
240
- if (i >= bodyStartIndex && rest[i] !== '') {
241
- wrappedRest.push(wrapText(rest[i], wrapColumn));
242
- }
243
- else {
244
- wrappedRest.push(rest[i]);
245
- }
246
- }
247
- result = [subject, ...wrappedRest].join('\n');
248
- }
249
- }
250
- return result;
251
- }
252
- /**
253
- * Parses a commit message into subject and body.
254
- *
255
- * The subject is the first line. The body starts after the first
256
- * blank line following the subject.
257
- *
258
- * @param message - The commit message
259
- * @returns Object with subject (first line) and body (rest)
260
- *
261
- * @example
262
- * ```typescript
263
- * const { subject, body } = parseCommitMessage(
264
- * 'Add feature\n\nThis adds the new feature'
265
- * )
266
- * // subject = 'Add feature'
267
- * // body = 'This adds the new feature'
268
- * ```
269
- */
270
- export function parseCommitMessage(message) {
271
- if (!message) {
272
- return { subject: '', body: '' };
273
- }
274
- const lines = message.split('\n');
275
- const subject = lines[0] || '';
276
- // Find the body - it starts after the first blank line (or second line if no blank)
277
- let bodyStartIndex = 1;
278
- for (let i = 1; i < lines.length; i++) {
279
- if (lines[i] === '') {
280
- bodyStartIndex = i + 1;
281
- break;
282
- }
283
- }
284
- const body = lines.slice(bodyStartIndex).join('\n');
285
- return { subject, body };
286
- }
287
- /**
288
- * Validates a commit message format.
289
- *
290
- * Checks for common issues and provides warnings for style violations.
291
- * Returns errors for critical issues that would prevent commit creation.
292
- *
293
- * @param message - The commit message to validate
294
- * @returns Object with valid flag and any error/warning messages
295
- *
296
- * @example
297
- * ```typescript
298
- * const result = validateCommitMessage('Fix bug.')
299
- * // {
300
- * // valid: true,
301
- * // errors: [],
302
- * // warnings: ['Subject line should not end with a period']
303
- * // }
304
- * ```
305
- */
306
- export function validateCommitMessage(message) {
307
- const errors = [];
308
- const warnings = [];
309
- if (!message || message.trim() === '') {
310
- errors.push('Commit message is empty');
311
- return { valid: false, errors, warnings };
312
- }
313
- const { subject, body: _body } = parseCommitMessage(message);
314
- // Check subject line length (72 chars is conventional max)
315
- if (subject.length > 72) {
316
- warnings.push('Subject line exceeds 72 characters');
317
- }
318
- // Check if subject ends with a period
319
- if (subject.endsWith('.')) {
320
- warnings.push('Subject line should not end with a period');
321
- }
322
- // Check for missing blank line between subject and body
323
- const lines = message.split('\n');
324
- if (lines.length > 1 && lines[1] !== '') {
325
- warnings.push('Missing blank line between subject and body');
326
- }
327
- return { valid: errors.length === 0, errors, warnings };
328
- }
329
- // ============================================================================
330
- // GPG Signing
331
- // ============================================================================
332
- /**
333
- * Checks if a commit is signed.
334
- *
335
- * @param commit - The commit object
336
- * @returns true if the commit has a GPG signature
337
- *
338
- * @example
339
- * ```typescript
340
- * if (isCommitSigned(commit)) {
341
- * const sig = extractCommitSignature(commit)
342
- * // Verify signature...
343
- * }
344
- * ```
345
- */
346
- export function isCommitSigned(commit) {
347
- const signedCommit = commit;
348
- return signedCommit.gpgsig !== undefined && signedCommit.gpgsig !== null;
349
- }
350
- /**
351
- * Extracts the GPG signature from a signed commit.
352
- *
353
- * @param commit - The commit object
354
- * @returns The signature string if present, null otherwise
355
- */
356
- export function extractCommitSignature(commit) {
357
- const signedCommit = commit;
358
- return signedCommit.gpgsig ?? null;
359
- }
360
- /**
361
- * Adds a GPG signature to a commit.
362
- *
363
- * Creates a new commit object with the signature attached.
364
- * Does not modify the original commit object.
365
- *
366
- * @param commit - The unsigned commit object
367
- * @param signature - The GPG signature string
368
- * @returns The signed commit object
369
- */
370
- export function addSignatureToCommit(commit, signature) {
371
- const signedCommit = {
372
- ...commit,
373
- gpgsig: signature
374
- };
375
- return signedCommit;
376
- }
377
- // ============================================================================
378
- // Empty Commit Detection
379
- // ============================================================================
380
- /**
381
- * Extracts tree SHA from raw commit data.
382
- * @internal
383
- */
384
- function extractTreeFromCommitData(data) {
385
- const decoder = new TextDecoder();
386
- const content = decoder.decode(data);
387
- const match = content.match(/tree ([0-9a-f]{40})/);
388
- return match ? match[1] : null;
389
- }
390
- /**
391
- * Checks if a commit would be empty (same tree as parent).
392
- *
393
- * A commit is considered empty if its tree SHA is identical to
394
- * its parent's tree SHA, meaning no files were changed.
395
- *
396
- * @param store - The object store for reading objects
397
- * @param tree - The tree SHA for the new commit
398
- * @param parent - The parent commit SHA (or null for initial commit)
399
- * @returns true if the commit would have no changes
400
- *
401
- * @example
402
- * ```typescript
403
- * const isEmpty = await isEmptyCommit(store, newTreeSha, parentSha)
404
- * if (isEmpty && !options.allowEmpty) {
405
- * throw new Error('Nothing to commit')
406
- * }
407
- * ```
408
- */
409
- export async function isEmptyCommit(store, tree, parent) {
410
- // Initial commits are never "empty"
411
- if (parent === null) {
412
- return false;
413
- }
414
- const parentObj = await store.getObject(parent);
415
- if (!parentObj) {
416
- return false;
417
- }
418
- // Extract tree from parent commit
419
- const parentTree = extractTreeFromCommitData(parentObj.data);
420
- return parentTree === tree;
421
- }
422
- // ============================================================================
423
- // Validation Helpers
424
- // ============================================================================
425
- const SHA_REGEX = /^[0-9a-f]{40}$/;
426
- /**
427
- * Validates a SHA format.
428
- * @internal
429
- */
430
- function isValidSha(sha) {
431
- return SHA_REGEX.test(sha);
432
- }
433
- /**
434
- * Validates an email format.
435
- * @internal
436
- */
437
- function isValidEmail(email) {
438
- // Basic email validation - must contain @ and have something before and after
439
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
440
- }
441
- /**
442
- * Validates an author name.
443
- * @internal
444
- */
445
- function validateAuthorName(name) {
446
- if (name.includes('<') || name.includes('>')) {
447
- throw new Error('Author name cannot contain angle brackets');
448
- }
449
- if (name.includes('\n')) {
450
- throw new Error('Author name cannot contain newlines');
451
- }
452
- }
453
- /**
454
- * Validates commit options.
455
- * @internal
456
- */
457
- function validateCommitOptions(options) {
458
- // Validate tree
459
- if (!options.tree) {
460
- throw new Error('Tree SHA is required');
461
- }
462
- if (!isValidSha(options.tree)) {
463
- throw new Error('Invalid tree SHA format');
464
- }
465
- // Validate author
466
- if (!options.author) {
467
- throw new Error('Author is required');
468
- }
469
- validateAuthorName(options.author.name);
470
- if (!isValidEmail(options.author.email)) {
471
- throw new Error('Invalid author email format');
472
- }
473
- // Validate committer if provided
474
- if (options.committer) {
475
- validateAuthorName(options.committer.name);
476
- if (!isValidEmail(options.committer.email)) {
477
- throw new Error('Invalid committer email format');
478
- }
479
- }
480
- // Validate message
481
- if (!options.message || options.message.trim() === '') {
482
- throw new Error('Commit message is required');
483
- }
484
- // Validate parents
485
- if (options.parents) {
486
- for (const parent of options.parents) {
487
- if (!isValidSha(parent)) {
488
- throw new Error('Invalid parent SHA format');
489
- }
490
- }
491
- }
492
- // Validate timestamp if provided
493
- if (options.author.timestamp !== undefined && options.author.timestamp < 0) {
494
- throw new Error('Timestamp cannot be negative');
495
- }
496
- if (options.committer?.timestamp !== undefined && options.committer.timestamp < 0) {
497
- throw new Error('Timestamp cannot be negative');
498
- }
499
- }
500
- // ============================================================================
501
- // Commit Creation
502
- // ============================================================================
503
- /**
504
- * Resolves a CommitAuthor to a full Author with timestamp and timezone.
505
- * @internal
506
- */
507
- function resolveAuthor(commitAuthor) {
508
- return {
509
- name: commitAuthor.name,
510
- email: commitAuthor.email,
511
- timestamp: commitAuthor.timestamp ?? Math.floor(Date.now() / 1000),
512
- timezone: commitAuthor.timezone ?? getCurrentTimezone()
513
- };
514
- }
515
- /**
516
- * Serializes commit content to bytes (without the header).
517
- * @internal
518
- */
519
- function serializeCommitContent(commit) {
520
- const encoder = new TextEncoder();
521
- const lines = [];
522
- lines.push(`tree ${commit.tree}`);
523
- for (const parent of commit.parents) {
524
- lines.push(`parent ${parent}`);
525
- }
526
- lines.push(`author ${commit.author.name} <${commit.author.email}> ${commit.author.timestamp} ${commit.author.timezone}`);
527
- lines.push(`committer ${commit.committer.name} <${commit.committer.email}> ${commit.committer.timestamp} ${commit.committer.timezone}`);
528
- // Add gpgsig if present
529
- if (commit.gpgsig) {
530
- const sigLines = commit.gpgsig.split('\n');
531
- lines.push(`gpgsig ${sigLines[0]}`);
532
- for (let i = 1; i < sigLines.length; i++) {
533
- lines.push(` ${sigLines[i]}`);
534
- }
535
- }
536
- lines.push('');
537
- lines.push(commit.message);
538
- return encoder.encode(lines.join('\n'));
539
- }
540
- /**
541
- * Builds a commit object from options without storing it.
542
- *
543
- * Useful for creating commit objects for inspection or testing
544
- * without actually persisting them to the object store.
545
- *
546
- * @param options - Commit creation options
547
- * @returns The commit object (not stored)
548
- *
549
- * @example
550
- * ```typescript
551
- * const commit = buildCommitObject({
552
- * message: 'Test commit',
553
- * tree: treeSha,
554
- * parents: [parentSha],
555
- * author: { name: 'Test', email: 'test@example.com' }
556
- * })
557
- *
558
- * console.log(commit.message) // 'Test commit'
559
- * ```
560
- */
561
- export function buildCommitObject(options) {
562
- const author = resolveAuthor(options.author);
563
- const committer = options.committer ? resolveAuthor(options.committer) : author;
564
- const parents = options.parents ?? [];
565
- const commit = {
566
- type: 'commit',
567
- data: new Uint8Array(),
568
- tree: options.tree,
569
- parents,
570
- author,
571
- committer,
572
- message: options.message
573
- };
574
- // Set the data field
575
- commit.data = serializeCommitContent({
576
- tree: commit.tree,
577
- parents: commit.parents,
578
- author: commit.author,
579
- committer: commit.committer,
580
- message: commit.message
581
- });
582
- return commit;
583
- }
584
- /**
585
- * Creates a new commit.
586
- *
587
- * Creates a commit object with the specified options and stores it
588
- * in the object store. Handles validation, empty commit detection,
589
- * and optional GPG signing.
590
- *
591
- * @param store - The object store for reading/writing objects
592
- * @param options - Commit creation options
593
- * @returns The created commit result with SHA and commit object
594
- *
595
- * @throws {Error} If tree SHA is missing or invalid
596
- * @throws {Error} If author is missing or invalid
597
- * @throws {Error} If commit message is empty
598
- * @throws {Error} If commit would be empty and allowEmpty is false
599
- *
600
- * @example
601
- * ```typescript
602
- * // Basic commit
603
- * const result = await createCommit(store, {
604
- * message: 'Add new feature',
605
- * tree: treeSha,
606
- * parents: [headSha],
607
- * author: { name: 'John', email: 'john@example.com' }
608
- * })
609
- *
610
- * // Signed commit
611
- * const signedResult = await createCommit(store, {
612
- * message: 'Signed commit',
613
- * tree: treeSha,
614
- * parents: [headSha],
615
- * author: { name: 'John', email: 'john@example.com' },
616
- * signing: {
617
- * sign: true,
618
- * signer: async (data) => myGpgSign(data)
619
- * }
620
- * })
621
- *
622
- * // Initial commit (no parents)
623
- * const initialResult = await createCommit(store, {
624
- * message: 'Initial commit',
625
- * tree: treeSha,
626
- * parents: [],
627
- * author: { name: 'John', email: 'john@example.com' }
628
- * })
629
- * ```
630
- */
631
- export async function createCommit(store, options) {
632
- // Validate options
633
- validateCommitOptions(options);
634
- const parents = options.parents ?? [];
635
- // Check for empty commit
636
- if (options.allowEmpty === false && parents.length > 0) {
637
- const isEmpty = await isEmptyCommit(store, options.tree, parents[0]);
638
- if (isEmpty) {
639
- throw new Error('Nothing to commit (empty commit not allowed)');
640
- }
641
- }
642
- // Build the commit object
643
- let commit = buildCommitObject(options);
644
- // Sign if requested
645
- if (options.signing?.sign && options.signing.signer) {
646
- const commitData = serializeCommitContent({
647
- tree: commit.tree,
648
- parents: commit.parents,
649
- author: commit.author,
650
- committer: commit.committer,
651
- message: commit.message
652
- });
653
- const signature = await options.signing.signer(commitData);
654
- commit = addSignatureToCommit(commit, signature);
655
- // Update commit data with signature
656
- const signedCommit = commit;
657
- commit.data = serializeCommitContent({
658
- tree: commit.tree,
659
- parents: commit.parents,
660
- author: commit.author,
661
- committer: commit.committer,
662
- message: commit.message,
663
- gpgsig: signedCommit.gpgsig
664
- });
665
- }
666
- // Store the commit
667
- const sha = await store.storeObject('commit', commit.data);
668
- return {
669
- sha,
670
- commit,
671
- created: true
672
- };
673
- }
674
- // ============================================================================
675
- // Commit Amendment
676
- // ============================================================================
677
- /**
678
- * Parses a stored commit object from raw data.
679
- * Supports both git text format and JSON format (for testing).
680
- * @internal
681
- */
682
- function parseStoredCommit(data) {
683
- const decoder = new TextDecoder();
684
- const content = decoder.decode(data);
685
- // Try to parse as JSON first (for test compatibility)
686
- if (content.startsWith('{')) {
687
- try {
688
- const parsed = JSON.parse(content);
689
- return {
690
- tree: parsed.tree,
691
- parents: parsed.parents || [],
692
- author: parsed.author,
693
- committer: parsed.committer || parsed.author,
694
- message: parsed.message
695
- };
696
- }
697
- catch {
698
- // Not JSON, fall through to git format parsing
699
- }
700
- }
701
- // Parse git text format
702
- const lines = content.split('\n');
703
- let tree = '';
704
- const parents = [];
705
- let author = null;
706
- let committer = null;
707
- let messageStartIndex = 0;
708
- for (let i = 0; i < lines.length; i++) {
709
- const line = lines[i];
710
- if (line === '') {
711
- messageStartIndex = i + 1;
712
- break;
713
- }
714
- if (line.startsWith('tree ')) {
715
- tree = line.slice(5);
716
- }
717
- else if (line.startsWith('parent ')) {
718
- parents.push(line.slice(7));
719
- }
720
- else if (line.startsWith('author ')) {
721
- const match = line.match(/^author (.+) <(.+)> (\d+) ([+-]\d{4})$/);
722
- if (match) {
723
- author = {
724
- name: match[1],
725
- email: match[2],
726
- timestamp: parseInt(match[3], 10),
727
- timezone: match[4]
728
- };
729
- }
730
- }
731
- else if (line.startsWith('committer ')) {
732
- const match = line.match(/^committer (.+) <(.+)> (\d+) ([+-]\d{4})$/);
733
- if (match) {
734
- committer = {
735
- name: match[1],
736
- email: match[2],
737
- timestamp: parseInt(match[3], 10),
738
- timezone: match[4]
739
- };
740
- }
741
- }
742
- }
743
- const message = lines.slice(messageStartIndex).join('\n');
744
- if (!author) {
745
- author = { name: 'Unknown', email: 'unknown@example.com', timestamp: 0, timezone: '+0000' };
746
- }
747
- if (!committer) {
748
- committer = author;
749
- }
750
- return { tree, parents, author, committer, message };
751
- }
752
- /**
753
- * Amends an existing commit.
754
- *
755
- * Creates a new commit that replaces the specified commit.
756
- * The original commit is not modified. Only specified fields
757
- * in options will be changed from the original.
758
- *
759
- * Note: This does not update any refs. The caller is responsible
760
- * for updating HEAD or branch refs to point to the new commit.
761
- *
762
- * @param store - The object store for reading/writing objects
763
- * @param commitSha - SHA of the commit to amend
764
- * @param options - Amendment options (only specified fields are changed)
765
- * @returns The new commit result (original commit is not modified)
766
- *
767
- * @throws {Error} If the commit doesn't exist
768
- *
769
- * @example
770
- * ```typescript
771
- * // Change just the message
772
- * const newCommit = await amendCommit(store, headSha, {
773
- * message: 'Better commit message'
774
- * })
775
- *
776
- * // Update tree and committer
777
- * const newCommit = await amendCommit(store, headSha, {
778
- * tree: newTreeSha,
779
- * committer: { name: 'New Name', email: 'new@example.com' }
780
- * })
781
- * ```
782
- */
783
- export async function amendCommit(store, commitSha, options) {
784
- // Get the original commit
785
- const originalObj = await store.getObject(commitSha);
786
- if (!originalObj) {
787
- throw new Error(`Commit not found: ${commitSha}`);
788
- }
789
- // Parse the original commit
790
- const original = parseStoredCommit(originalObj.data);
791
- // Build new author
792
- let newAuthor = original.author;
793
- if (options.author) {
794
- newAuthor = resolveAuthor(options.author);
795
- }
796
- else if (options.resetAuthorDate) {
797
- newAuthor = {
798
- ...original.author,
799
- timestamp: Math.floor(Date.now() / 1000)
800
- };
801
- }
802
- // Build new committer (defaults to current time)
803
- let newCommitter;
804
- if (options.committer) {
805
- newCommitter = resolveAuthor(options.committer);
806
- }
807
- else {
808
- newCommitter = {
809
- ...original.committer,
810
- timestamp: Math.floor(Date.now() / 1000),
811
- timezone: getCurrentTimezone()
812
- };
813
- }
814
- // Build new commit
815
- const newCommitOptions = {
816
- message: options.message ?? original.message,
817
- tree: options.tree ?? original.tree,
818
- parents: original.parents,
819
- author: newAuthor,
820
- committer: newCommitter,
821
- signing: options.signing,
822
- allowEmpty: true
823
- };
824
- return createCommit(store, newCommitOptions);
825
- }
826
- //# sourceMappingURL=commit.js.map