nano-git 0.1.0

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 (227) hide show
  1. package/README.md +407 -0
  2. package/dist/backend/file-backend.d.mts +26 -0
  3. package/dist/backend/file-backend.mjs +83 -0
  4. package/dist/backend/file.d.mts +2 -0
  5. package/dist/backend/file.mjs +2 -0
  6. package/dist/backend/index.d.mts +2 -0
  7. package/dist/backend/index.mjs +1 -0
  8. package/dist/backend/memory-backend.d.mts +25 -0
  9. package/dist/backend/memory-backend.mjs +33 -0
  10. package/dist/backend/memory.d.mts +2 -0
  11. package/dist/backend/memory.mjs +2 -0
  12. package/dist/backend/types.d.mts +90 -0
  13. package/dist/core/errors.d.mts +124 -0
  14. package/dist/core/errors.mjs +168 -0
  15. package/dist/core/hash-digest.d.mts +35 -0
  16. package/dist/core/hash-digest.mjs +50 -0
  17. package/dist/core/hash-file.d.mts +20 -0
  18. package/dist/core/hash-file.mjs +27 -0
  19. package/dist/core/hash-path.d.mts +17 -0
  20. package/dist/core/hash-path.mjs +35 -0
  21. package/dist/core/types/odb.d.mts +63 -0
  22. package/dist/core/types/refs.d.mts +140 -0
  23. package/dist/core/types/refs.mjs +19 -0
  24. package/dist/core/types/shallow.d.mts +52 -0
  25. package/dist/core/types.d.mts +154 -0
  26. package/dist/core/types.mjs +43 -0
  27. package/dist/errors.d.mts +2 -0
  28. package/dist/errors.mjs +2 -0
  29. package/dist/hash-file.d.mts +2 -0
  30. package/dist/hash-file.mjs +2 -0
  31. package/dist/index.d.mts +16 -0
  32. package/dist/index.mjs +14 -0
  33. package/dist/objects/author.d.mts +25 -0
  34. package/dist/objects/author.mjs +45 -0
  35. package/dist/objects/blob.d.mts +15 -0
  36. package/dist/objects/blob.mjs +20 -0
  37. package/dist/objects/codec.d.mts +46 -0
  38. package/dist/objects/codec.mjs +84 -0
  39. package/dist/objects/commit.d.mts +26 -0
  40. package/dist/objects/commit.mjs +160 -0
  41. package/dist/objects/index.d.mts +8 -0
  42. package/dist/objects/index.mjs +8 -0
  43. package/dist/objects/raw.d.mts +85 -0
  44. package/dist/objects/raw.mjs +111 -0
  45. package/dist/objects/tag.d.mts +26 -0
  46. package/dist/objects/tag.mjs +148 -0
  47. package/dist/objects/tree.d.mts +22 -0
  48. package/dist/objects/tree.mjs +66 -0
  49. package/dist/odb/file-utils.mjs +136 -0
  50. package/dist/odb/file.d.mts +22 -0
  51. package/dist/odb/file.mjs +66 -0
  52. package/dist/odb/memory.d.mts +22 -0
  53. package/dist/odb/memory.mjs +54 -0
  54. package/dist/pack/composite-store.d.mts +76 -0
  55. package/dist/pack/composite-store.mjs +133 -0
  56. package/dist/pack/constants.mjs +48 -0
  57. package/dist/pack/crc32.mjs +38 -0
  58. package/dist/pack/delta-apply.d.mts +21 -0
  59. package/dist/pack/delta-apply.mjs +71 -0
  60. package/dist/pack/delta-create.d.mts +28 -0
  61. package/dist/pack/delta-create.mjs +151 -0
  62. package/dist/pack/index.d.mts +16 -0
  63. package/dist/pack/index.mjs +13 -0
  64. package/dist/pack/object-header.d.mts +36 -0
  65. package/dist/pack/object-header.mjs +72 -0
  66. package/dist/pack/ofs-delta-offset.d.mts +35 -0
  67. package/dist/pack/ofs-delta-offset.mjs +59 -0
  68. package/dist/pack/pack-builder-types.d.mts +19 -0
  69. package/dist/pack/pack-builder.d.mts +52 -0
  70. package/dist/pack/pack-builder.mjs +80 -0
  71. package/dist/pack/pack-encoding.mjs +76 -0
  72. package/dist/pack/pack-index-reader.d.mts +57 -0
  73. package/dist/pack/pack-index-reader.mjs +90 -0
  74. package/dist/pack/pack-index-types.d.mts +16 -0
  75. package/dist/pack/pack-index-writer.d.mts +45 -0
  76. package/dist/pack/pack-index-writer.mjs +106 -0
  77. package/dist/pack/pack-reader-resolver.mjs +104 -0
  78. package/dist/pack/pack-reader-types.d.mts +31 -0
  79. package/dist/pack/pack-reader-types.mjs +22 -0
  80. package/dist/pack/pack-reader-utils.mjs +61 -0
  81. package/dist/pack/pack-reader.d.mts +81 -0
  82. package/dist/pack/pack-reader.mjs +171 -0
  83. package/dist/pack/pack-store-loader.mjs +79 -0
  84. package/dist/pack/pack-store-types.d.mts +16 -0
  85. package/dist/pack/pack-store.d.mts +55 -0
  86. package/dist/pack/pack-store.mjs +114 -0
  87. package/dist/pack/pack-writer.mjs +96 -0
  88. package/dist/pack/varint.d.mts +34 -0
  89. package/dist/pack/varint.mjs +57 -0
  90. package/dist/refs/file.d.mts +6 -0
  91. package/dist/refs/file.mjs +222 -0
  92. package/dist/refs/fs-utils.mjs +30 -0
  93. package/dist/refs/memory.d.mts +14 -0
  94. package/dist/refs/memory.mjs +104 -0
  95. package/dist/refs/names.d.mts +57 -0
  96. package/dist/refs/names.mjs +80 -0
  97. package/dist/refs/resolve.d.mts +33 -0
  98. package/dist/refs/resolve.mjs +55 -0
  99. package/dist/refs/shallow/file.d.mts +17 -0
  100. package/dist/refs/shallow/file.mjs +61 -0
  101. package/dist/refs/shallow/memory.d.mts +18 -0
  102. package/dist/refs/shallow/memory.mjs +33 -0
  103. package/dist/repository/core.d.mts +3 -0
  104. package/dist/repository/core.mjs +2 -0
  105. package/dist/repository/create.d.mts +22 -0
  106. package/dist/repository/create.mjs +43 -0
  107. package/dist/repository/file.d.mts +43 -0
  108. package/dist/repository/file.mjs +82 -0
  109. package/dist/repository/import/import-glob.mjs +44 -0
  110. package/dist/repository/import/import-plan-builder.mjs +625 -0
  111. package/dist/repository/import/import-session-types.d.mts +280 -0
  112. package/dist/repository/import/import-session.mjs +96 -0
  113. package/dist/repository/import/import-view.mjs +133 -0
  114. package/dist/repository/memory.d.mts +17 -0
  115. package/dist/repository/memory.mjs +33 -0
  116. package/dist/repository/ops/fetch-operations.mjs +20 -0
  117. package/dist/repository/ops/fetch-types.d.mts +82 -0
  118. package/dist/repository/ops/fetch-url.mjs +82 -0
  119. package/dist/repository/ops/fs-object-operations.mjs +31 -0
  120. package/dist/repository/ops/maintenance-operations.mjs +62 -0
  121. package/dist/repository/ops/maintenance-types.d.mts +42 -0
  122. package/dist/repository/ops/object-operations.mjs +65 -0
  123. package/dist/repository/ops/object-types.d.mts +109 -0
  124. package/dist/repository/ops/push-operations.mjs +16 -0
  125. package/dist/repository/ops/push-resolution.mjs +17 -0
  126. package/dist/repository/ops/push-types.d.mts +72 -0
  127. package/dist/repository/ops/push-url.mjs +45 -0
  128. package/dist/repository/ops/reachability.mjs +60 -0
  129. package/dist/repository/ops/ref-operations.mjs +86 -0
  130. package/dist/repository/ops/ref-types.d.mts +70 -0
  131. package/dist/repository/tree/tree-patch.d.mts +64 -0
  132. package/dist/repository/tree/tree-patch.mjs +268 -0
  133. package/dist/repository/tree/tree-walk.d.mts +46 -0
  134. package/dist/repository/tree/tree-walk.mjs +65 -0
  135. package/dist/repository/tree/tree-writer.mjs +68 -0
  136. package/dist/repository/types.d.mts +36 -0
  137. package/dist/sha1.d.mts +4 -0
  138. package/dist/sha1.mjs +4 -0
  139. package/dist/transport/client/receive-pack/http.d.mts +33 -0
  140. package/dist/transport/client/receive-pack/http.mjs +99 -0
  141. package/dist/transport/client/receive-pack/push-error.d.mts +23 -0
  142. package/dist/transport/client/receive-pack/push-error.mjs +32 -0
  143. package/dist/transport/client/receive-pack/push-pack-plan.d.mts +28 -0
  144. package/dist/transport/client/receive-pack/push-pack-plan.mjs +60 -0
  145. package/dist/transport/client/receive-pack/push-policy.d.mts +19 -0
  146. package/dist/transport/client/receive-pack/push-policy.mjs +64 -0
  147. package/dist/transport/client/receive-pack/push-ref-plan.d.mts +45 -0
  148. package/dist/transport/client/receive-pack/push-ref-plan.mjs +108 -0
  149. package/dist/transport/client/receive-pack/push-report.d.mts +28 -0
  150. package/dist/transport/client/receive-pack/push-report.mjs +84 -0
  151. package/dist/transport/client/receive-pack/push-request-plan.mjs +52 -0
  152. package/dist/transport/client/receive-pack/push.d.mts +32 -0
  153. package/dist/transport/client/receive-pack/push.mjs +97 -0
  154. package/dist/transport/client/receive-pack/request.d.mts +39 -0
  155. package/dist/transport/client/receive-pack/request.mjs +46 -0
  156. package/dist/transport/client/receive-pack/response.d.mts +26 -0
  157. package/dist/transport/client/receive-pack/response.mjs +52 -0
  158. package/dist/transport/client/receive-pack/result.d.mts +34 -0
  159. package/dist/transport/client/receive-pack/result.mjs +100 -0
  160. package/dist/transport/client/upload-pack/capability-advertisement.d.mts +56 -0
  161. package/dist/transport/client/upload-pack/capability-advertisement.mjs +130 -0
  162. package/dist/transport/client/upload-pack/fetch.d.mts +109 -0
  163. package/dist/transport/client/upload-pack/fetch.mjs +392 -0
  164. package/dist/transport/client/upload-pack/http.d.mts +29 -0
  165. package/dist/transport/client/upload-pack/http.mjs +79 -0
  166. package/dist/transport/client/upload-pack/ls-refs.d.mts +75 -0
  167. package/dist/transport/client/upload-pack/ls-refs.mjs +150 -0
  168. package/dist/transport/client/upload-pack/object-info.d.mts +65 -0
  169. package/dist/transport/client/upload-pack/object-info.mjs +111 -0
  170. package/dist/transport/client/upload-pack/types.d.mts +153 -0
  171. package/dist/transport/http/index.d.mts +3 -0
  172. package/dist/transport/http/index.mjs +2 -0
  173. package/dist/transport/http/smart-http.d.mts +46 -0
  174. package/dist/transport/http/smart-http.mjs +176 -0
  175. package/dist/transport/http/types.d.mts +27 -0
  176. package/dist/transport/index.d.mts +9 -0
  177. package/dist/transport/index.mjs +8 -0
  178. package/dist/transport/protocol/object-graph.d.mts +63 -0
  179. package/dist/transport/protocol/object-graph.mjs +149 -0
  180. package/dist/transport/protocol/pkt-line.d.mts +109 -0
  181. package/dist/transport/protocol/pkt-line.mjs +195 -0
  182. package/dist/transport/protocol/ref-advertisement.mjs +185 -0
  183. package/dist/transport/protocol/ref-collection.d.mts +38 -0
  184. package/dist/transport/protocol/ref-collection.mjs +63 -0
  185. package/dist/transport/protocol/ref-match.d.mts +39 -0
  186. package/dist/transport/protocol/ref-match.mjs +42 -0
  187. package/dist/transport/protocol/refspec.d.mts +44 -0
  188. package/dist/transport/protocol/refspec.mjs +79 -0
  189. package/dist/transport/protocol/side-band.d.mts +65 -0
  190. package/dist/transport/protocol/side-band.mjs +142 -0
  191. package/dist/transport/protocol/transport-capabilities.mjs +46 -0
  192. package/dist/transport/protocol/types.d.mts +148 -0
  193. package/dist/transport/protocol/update-refs.d.mts +68 -0
  194. package/dist/transport/protocol/update-refs.mjs +126 -0
  195. package/dist/transport/receive-pack.d.mts +11 -0
  196. package/dist/transport/receive-pack.mjs +11 -0
  197. package/dist/transport/server/receive-pack/advertise.d.mts +28 -0
  198. package/dist/transport/server/receive-pack/advertise.mjs +88 -0
  199. package/dist/transport/server/receive-pack/handler.d.mts +30 -0
  200. package/dist/transport/server/receive-pack/handler.mjs +156 -0
  201. package/dist/transport/server/receive-pack/index.d.mts +6 -0
  202. package/dist/transport/server/receive-pack/index.mjs +6 -0
  203. package/dist/transport/server/receive-pack/parse.d.mts +17 -0
  204. package/dist/transport/server/receive-pack/parse.mjs +80 -0
  205. package/dist/transport/server/receive-pack/report-status.mjs +64 -0
  206. package/dist/transport/server/receive-pack/service.d.mts +41 -0
  207. package/dist/transport/server/receive-pack/service.mjs +39 -0
  208. package/dist/transport/server/receive-pack/types.d.mts +56 -0
  209. package/dist/transport/server/receive-pack/types.mjs +25 -0
  210. package/dist/transport/server/receive-pack/unpack.mjs +119 -0
  211. package/dist/transport/server/upload-pack/advertise.d.mts +20 -0
  212. package/dist/transport/server/upload-pack/advertise.mjs +30 -0
  213. package/dist/transport/server/upload-pack/command.d.mts +43 -0
  214. package/dist/transport/server/upload-pack/command.mjs +56 -0
  215. package/dist/transport/server/upload-pack/fetch.d.mts +43 -0
  216. package/dist/transport/server/upload-pack/fetch.mjs +217 -0
  217. package/dist/transport/server/upload-pack/index.d.mts +7 -0
  218. package/dist/transport/server/upload-pack/index.mjs +7 -0
  219. package/dist/transport/server/upload-pack/ls-refs.d.mts +38 -0
  220. package/dist/transport/server/upload-pack/ls-refs.mjs +113 -0
  221. package/dist/transport/server/upload-pack/service.d.mts +40 -0
  222. package/dist/transport/server/upload-pack/service.mjs +51 -0
  223. package/dist/transport/server/upload-pack/types.d.mts +11 -0
  224. package/dist/transport/server/upload-pack/types.mjs +21 -0
  225. package/dist/transport/upload-pack.d.mts +7 -0
  226. package/dist/transport/upload-pack.mjs +6 -0
  227. package/package.json +98 -0
@@ -0,0 +1,32 @@
1
+ import { RefStore } from "../../../core/types/refs.mjs";
2
+ import { ObjectDatabase } from "../../../core/types/odb.mjs";
3
+ import { PushOptions, PushResult, ReceivePackTransport, RefAdvertisement } from "../../protocol/types.mjs";
4
+ import { PushError } from "./push-error.mjs";
5
+ import { PushRefItem, determinePushRefs } from "./push-ref-plan.mjs";
6
+ import { checkFastForward } from "./push-policy.mjs";
7
+
8
+ //#region src/transport/client/receive-pack/push.d.ts
9
+ /**
10
+ * 执行 push 操作
11
+ *
12
+ * 将本地对象推送到远程 Git 仓库。调用方需自行创建 ReceivePackTransport
13
+ * 并获取 advertisement。
14
+ *
15
+ * @param store - 本地对象存储
16
+ * @param refs - 本地引用存储
17
+ * @param transport - receive-pack 传输接口
18
+ * @param advertisement - 服务端 receive-pack 广告
19
+ * @param options - 可选配置(refSpecs、force、shallowBoundaries)
20
+ * @returns push 操作结果
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const transport = createReceivePackHttpClient("https://github.com/user/repo");
25
+ * const adv = await transport.advertise();
26
+ * const result = await push(store, refs, transport, adv, { refSpecs: [...] });
27
+ * console.log(`Pushed ${result.objectCount} objects`);
28
+ * ```
29
+ */
30
+ declare function push(store: ObjectDatabase, refs: RefStore, transport: ReceivePackTransport, advertisement: RefAdvertisement, options?: PushOptions): Promise<PushResult>;
31
+ //#endregion
32
+ export { push };
@@ -0,0 +1,97 @@
1
+ import { getLocalRefs, remoteRefsToMap } from "../../protocol/ref-collection.mjs";
2
+ import { PushError } from "./push-error.mjs";
3
+ import { createPackWriter } from "../../../pack/pack-writer.mjs";
4
+ import { computeObjectsToSend, mergePushBoundaries } from "./push-pack-plan.mjs";
5
+ import { checkFastForward } from "./push-policy.mjs";
6
+ import { determinePushRefs } from "./push-ref-plan.mjs";
7
+ import { processPushReport } from "./push-report.mjs";
8
+ import { buildPushCommands, buildPushRequestBody, resolvePushParsedSpecs, validatePushCapabilities } from "./push-request-plan.mjs";
9
+ import { ReceivePackResultError } from "./result.mjs";
10
+ import { decodeReceivePackResponse } from "./response.mjs";
11
+ //#region src/transport/client/receive-pack/push.ts
12
+ /**
13
+ * 高层 push 编排
14
+ *
15
+ * 编排完整的 Smart HTTP push 流程:
16
+ * 1. 按 refspec 确定要推送的本地引用与远程目标
17
+ * 2. 收集需要发送的对象
18
+ * 3. 构建 packfile
19
+ * 4. 构造 receive-pack 请求并发送
20
+ * 5. 解析 report-status 响应
21
+ *
22
+ * push() 不再自行创建传输层或获取 advertisement ——
23
+ * 这两种依赖由调用方通过 ReceivePackTransport + RefAdvertisement 显式传入。
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { push } from "./transport/push.ts";
28
+ *
29
+ * const transport = createReceivePackHttpClient("https://github.com/user/repo");
30
+ * const adv = await transport.advertise();
31
+ * const result = await push(store, refs, transport, adv, { refSpecs: [...] });
32
+ * console.log(`Pushed ${result.objectCount} objects`);
33
+ * ```
34
+ */
35
+ /**
36
+ * 执行 push 操作
37
+ *
38
+ * 将本地对象推送到远程 Git 仓库。调用方需自行创建 ReceivePackTransport
39
+ * 并获取 advertisement。
40
+ *
41
+ * @param store - 本地对象存储
42
+ * @param refs - 本地引用存储
43
+ * @param transport - receive-pack 传输接口
44
+ * @param advertisement - 服务端 receive-pack 广告
45
+ * @param options - 可选配置(refSpecs、force、shallowBoundaries)
46
+ * @returns push 操作结果
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const transport = createReceivePackHttpClient("https://github.com/user/repo");
51
+ * const adv = await transport.advertise();
52
+ * const result = await push(store, refs, transport, adv, { refSpecs: [...] });
53
+ * console.log(`Pushed ${result.objectCount} objects`);
54
+ * ```
55
+ */
56
+ async function push(store, refs, transport, advertisement, options) {
57
+ const effectiveSpecs = resolvePushParsedSpecs(refs, options);
58
+ const shallowSet = options?.shallowBoundaries ? new Set(options.shallowBoundaries) : void 0;
59
+ const localRefs = getLocalRefs(refs);
60
+ const remoteRefs = remoteRefsToMap(advertisement.refs);
61
+ const pushRefs = determinePushRefs(localRefs, remoteRefs, effectiveSpecs);
62
+ if (pushRefs.length === 0) return {
63
+ refUpdates: [],
64
+ objectCount: 0,
65
+ progress: []
66
+ };
67
+ const pushBoundaries = mergePushBoundaries(shallowSet, pushRefs);
68
+ checkFastForward(store, pushRefs, shallowSet);
69
+ const objectsToSend = computeObjectsToSend(store, pushRefs, remoteRefs, pushBoundaries);
70
+ const packWriter = createPackWriter();
71
+ for (const hash of objectsToSend) {
72
+ const raw = store.read(hash);
73
+ packWriter.addRaw(raw);
74
+ }
75
+ const packfile = packWriter.build();
76
+ const caps = validatePushCapabilities(advertisement, pushRefs);
77
+ const commands = buildPushCommands(pushRefs);
78
+ const body = buildPushRequestBody(commands, packfile, caps);
79
+ let progress;
80
+ let refUpdates;
81
+ try {
82
+ const decoded = decodeReceivePackResponse(await transport.request(body));
83
+ progress = decoded.progress;
84
+ refUpdates = decoded.refUpdates;
85
+ } catch (err) {
86
+ if (err instanceof ReceivePackResultError) throw new PushError(`Remote server rejected the push: ${err.message}`);
87
+ throw err;
88
+ }
89
+ const report = processPushReport(commands, refUpdates, pushRefs, progress);
90
+ return {
91
+ refUpdates: report.refUpdates,
92
+ objectCount: packWriter.objectCount,
93
+ progress: report.progress
94
+ };
95
+ }
96
+ //#endregion
97
+ export { push };
@@ -0,0 +1,39 @@
1
+ import { SHA1 } from "../../../core/types.mjs";
2
+
3
+ //#region src/transport/client/receive-pack/request.d.ts
4
+ /**
5
+ * Receive-pack 引用更新命令
6
+ *
7
+ * 表示一条 push 命令:将远程的 refName 从 oldHash 更新为 newHash。
8
+ *
9
+ * - oldHash 为 000...0 表示新建引用
10
+ * - newHash 为 000...0 表示删除引用
11
+ */
12
+ interface ReceivePackCommand {
13
+ /** 引用当前指向的哈希(服务端原有值) */
14
+ oldHash: SHA1;
15
+ /** 要更新到的目标哈希 */
16
+ newHash: SHA1;
17
+ /** 引用完整名称,如 "refs/heads/main" */
18
+ refName: string;
19
+ }
20
+ /**
21
+ * 构建 receive-pack 请求 body
22
+ *
23
+ * @param commands - 引用更新命令列表(至少一个)
24
+ * @param packfile - 包含新对象的 packfile 数据(push 时必需,delete 时可传空 Buffer)
25
+ * @param capabilities - 能力列表(如 ["report-status", "side-band-64k", "ofs-delta"])
26
+ * @returns pkt-line 编码的请求 body Buffer
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const body = buildReceivePackRequest(
31
+ * [{ oldHash: sha1("..."), newHash: sha1("..."), refName: "refs/heads/main" }],
32
+ * packfile,
33
+ * ["report-status", "side-band-64k", "ofs-delta"],
34
+ * );
35
+ * ```
36
+ */
37
+ declare function buildReceivePackRequest(commands: ReceivePackCommand[], packfile: Buffer, capabilities: string[]): Buffer;
38
+ //#endregion
39
+ export { ReceivePackCommand, buildReceivePackRequest };
@@ -0,0 +1,46 @@
1
+ import { encodeFlushPkt, encodePktLine } from "../../protocol/pkt-line.mjs";
2
+ //#region src/transport/client/receive-pack/request.ts
3
+ /**
4
+ * 请求生成(receive-pack)
5
+ *
6
+ * 构造 Git receive-pack 请求 body。
7
+ *
8
+ * 协议 v1 的请求格式:
9
+ * <old-hash> <new-hash> <ref-name>\0<capabilities>\n (首行带 capabilities)
10
+ * <old-hash> <new-hash> <ref-name>\n (后续命令)
11
+ * 0000 (flush)
12
+ * <packfile data> (包数据,不经过 pkt-line 编码)
13
+ *
14
+ * @see https://git-scm.com/docs/git-receive-pack#_request
15
+ */
16
+ /**
17
+ * 构建 receive-pack 请求 body
18
+ *
19
+ * @param commands - 引用更新命令列表(至少一个)
20
+ * @param packfile - 包含新对象的 packfile 数据(push 时必需,delete 时可传空 Buffer)
21
+ * @param capabilities - 能力列表(如 ["report-status", "side-band-64k", "ofs-delta"])
22
+ * @returns pkt-line 编码的请求 body Buffer
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const body = buildReceivePackRequest(
27
+ * [{ oldHash: sha1("..."), newHash: sha1("..."), refName: "refs/heads/main" }],
28
+ * packfile,
29
+ * ["report-status", "side-band-64k", "ofs-delta"],
30
+ * );
31
+ * ```
32
+ */
33
+ function buildReceivePackRequest(commands, packfile, capabilities) {
34
+ if (commands.length === 0) throw new Error("At least one command is required");
35
+ const chunks = [];
36
+ for (let i = 0; i < commands.length; i++) {
37
+ const cmd = commands[i];
38
+ if (i === 0 && capabilities.length > 0) chunks.push(encodePktLine(`${cmd.oldHash} ${cmd.newHash} ${cmd.refName}\0${capabilities.join(" ")}\n`));
39
+ else chunks.push(encodePktLine(`${cmd.oldHash} ${cmd.newHash} ${cmd.refName}\n`));
40
+ }
41
+ chunks.push(encodeFlushPkt());
42
+ if (packfile.length > 0) chunks.push(packfile);
43
+ return Buffer.concat(chunks);
44
+ }
45
+ //#endregion
46
+ export { buildReceivePackRequest };
@@ -0,0 +1,26 @@
1
+ import { GitError } from "../../../core/errors.mjs";
2
+ import { PushRefUpdate } from "../../protocol/types.mjs";
3
+
4
+ //#region src/transport/client/receive-pack/response.d.ts
5
+ /**
6
+ * receive-pack 响应解码错误
7
+ */
8
+ declare class ReceivePackResponseError extends GitError {
9
+ constructor(message: string);
10
+ }
11
+ /**
12
+ * 解码后的 receive-pack 响应
13
+ */
14
+ interface DecodedReceivePackResponse {
15
+ readonly data: Buffer;
16
+ readonly refUpdates: PushRefUpdate[];
17
+ readonly progress: string[];
18
+ }
19
+ /**
20
+ * 解码 receive-pack RPC 原始响应
21
+ *
22
+ * @param data - transport.request() 返回的原始 body
23
+ */
24
+ declare function decodeReceivePackResponse(data: Buffer): DecodedReceivePackResponse;
25
+ //#endregion
26
+ export { ReceivePackResponseError, decodeReceivePackResponse };
@@ -0,0 +1,52 @@
1
+ import { GitError } from "../../../core/errors.mjs";
2
+ import { parsePktLines } from "../../protocol/pkt-line.mjs";
3
+ import { parseReceivePackResult } from "./result.mjs";
4
+ //#region src/transport/client/receive-pack/response.ts
5
+ /**
6
+ * receive-pack 响应协议解码
7
+ *
8
+ * 从 transport 返回的原始 HTTP body 中解析 side-band 与 report-status。
9
+ */
10
+ /**
11
+ * receive-pack 响应解码错误
12
+ */
13
+ var ReceivePackResponseError = class extends GitError {
14
+ constructor(message) {
15
+ super(`receive-pack response: ${message}`);
16
+ this.name = "ReceivePackResponseError";
17
+ }
18
+ };
19
+ /**
20
+ * 解码 receive-pack RPC 原始响应
21
+ *
22
+ * @param data - transport.request() 返回的原始 body
23
+ */
24
+ function decodeReceivePackResponse(data) {
25
+ let progress = [];
26
+ let refUpdates = [];
27
+ const pktLines = parsePktLines(data);
28
+ if (pktLines.length > 0 && pktLines[0].type === "data") {
29
+ const firstPayload = pktLines[0].payload;
30
+ if (firstPayload.length > 0 && (firstPayload[0] === 1 || firstPayload[0] === 2 || firstPayload[0] === 3)) {
31
+ const reportStatusChunks = [];
32
+ for (const line of pktLines) {
33
+ if (line.type !== "data") continue;
34
+ const payload = line.payload;
35
+ if (payload.length < 2) continue;
36
+ const channel = payload[0];
37
+ const frameData = payload.subarray(1);
38
+ if (channel === 1) reportStatusChunks.push(frameData);
39
+ else if (channel === 2) progress.push(frameData.toString("utf-8").trimEnd());
40
+ else if (channel === 3) throw new ReceivePackResponseError(`Server reported error: ${frameData.toString("utf-8").trimEnd()}`);
41
+ }
42
+ if (reportStatusChunks.length > 0) refUpdates = parseReceivePackResult(Buffer.concat(reportStatusChunks));
43
+ } else refUpdates = parseReceivePackResult(data);
44
+ }
45
+ return {
46
+ data,
47
+ refUpdates,
48
+ progress
49
+ };
50
+ }
51
+ //#endregion
52
+ export { ReceivePackResponseError, decodeReceivePackResponse };
@@ -0,0 +1,34 @@
1
+ import { GitError } from "../../../core/errors.mjs";
2
+ import { PushRefUpdate } from "../../protocol/types.mjs";
3
+
4
+ //#region src/transport/client/receive-pack/result.d.ts
5
+ /**
6
+ * Receive-pack 结果解析错误
7
+ *
8
+ * 当 report-status 数据格式不符合 Git 协议规范时抛出。
9
+ */
10
+ declare class ReceivePackResultError extends GitError {
11
+ constructor(message: string);
12
+ }
13
+ /**
14
+ * 从 receive-pack 响应中解析 report-status
15
+ *
16
+ * 输入数据应为已经过 side-band 解复用后的纯 pkt-line report-status 数据。
17
+ * 如果响应是 side-band 编码的,需要先调用 `extractPackfile`/`extractProgress`
18
+ * 再使用本函数解析 report-status 部分(在 side-band 中 report-status 位于
19
+ * channel 1 的 packfile 数据末尾的 pkt-line 中)。
20
+ *
21
+ * @param data - 来自服务端的 report-status 响应体(pkt-line 编码)
22
+ * @returns 解析后的引用更新状态列表,每个条目标明成功/失败及错误消息
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const result = parseReceivePackResult(responseBody);
27
+ * for (const update of result) {
28
+ * console.log(`${update.refName}: ${update.success ? "OK" : "FAIL: " + update.error}`);
29
+ * }
30
+ * ```
31
+ */
32
+ declare function parseReceivePackResult(data: Buffer): PushRefUpdate[];
33
+ //#endregion
34
+ export { ReceivePackResultError, parseReceivePackResult };
@@ -0,0 +1,100 @@
1
+ import { GitError } from "../../../core/errors.mjs";
2
+ import { parsePktLines } from "../../protocol/pkt-line.mjs";
3
+ //#region src/transport/client/receive-pack/result.ts
4
+ /**
5
+ * 响应解析(receive-pack)
6
+ *
7
+ * 解析 `git-receive-pack` 返回的 report-status 响应。
8
+ *
9
+ * report-status 格式:
10
+ * <status-line>\n
11
+ * ...
12
+ * 0000
13
+ *
14
+ * 每条 status-line:
15
+ * ok <ref-name>
16
+ * ng <ref-name> <error-msg>
17
+ *
18
+ * @see https://git-scm.com/docs/pack-protocol#_report_status
19
+ */
20
+ /**
21
+ * Receive-pack 结果解析错误
22
+ *
23
+ * 当 report-status 数据格式不符合 Git 协议规范时抛出。
24
+ */
25
+ var ReceivePackResultError = class extends GitError {
26
+ constructor(message) {
27
+ super(`receive-pack result error: ${message}`);
28
+ this.name = "ReceivePackResultError";
29
+ }
30
+ };
31
+ /** report-status 中的 "ok" 前缀 */
32
+ const OK_PREFIX = "ok ";
33
+ /** report-status 中的 "ng" 前缀 */
34
+ const NG_PREFIX = "ng ";
35
+ /**
36
+ * 从 receive-pack 响应中解析 report-status
37
+ *
38
+ * 输入数据应为已经过 side-band 解复用后的纯 pkt-line report-status 数据。
39
+ * 如果响应是 side-band 编码的,需要先调用 `extractPackfile`/`extractProgress`
40
+ * 再使用本函数解析 report-status 部分(在 side-band 中 report-status 位于
41
+ * channel 1 的 packfile 数据末尾的 pkt-line 中)。
42
+ *
43
+ * @param data - 来自服务端的 report-status 响应体(pkt-line 编码)
44
+ * @returns 解析后的引用更新状态列表,每个条目标明成功/失败及错误消息
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const result = parseReceivePackResult(responseBody);
49
+ * for (const update of result) {
50
+ * console.log(`${update.refName}: ${update.success ? "OK" : "FAIL: " + update.error}`);
51
+ * }
52
+ * ```
53
+ */
54
+ function parseReceivePackResult(data) {
55
+ const pktLines = parsePktLines(data);
56
+ const updates = [];
57
+ let seenUnpack = false;
58
+ for (const line of pktLines) {
59
+ if (line.type !== "data") continue;
60
+ const payload = line.payload.toString("utf-8").trimEnd();
61
+ if (payload.length === 0) continue;
62
+ if (!seenUnpack) {
63
+ if (payload.startsWith("unpack ")) {
64
+ seenUnpack = true;
65
+ const unpackResult = payload.slice(7);
66
+ if (unpackResult !== "ok") throw new ReceivePackResultError(`Server failed to unpack packfile: ${unpackResult}`);
67
+ continue;
68
+ }
69
+ throw new ReceivePackResultError(`Missing unpack status line: first status line is "${payload}", expected "unpack <result>"`);
70
+ }
71
+ if (payload.startsWith(OK_PREFIX)) {
72
+ const refName = payload.slice(3);
73
+ updates.push({
74
+ refName,
75
+ oldHash: null,
76
+ newHash: null,
77
+ success: true,
78
+ forced: false
79
+ });
80
+ } else if (payload.startsWith(NG_PREFIX)) {
81
+ const rest = payload.slice(3);
82
+ const spaceIndex = rest.indexOf(" ");
83
+ if (spaceIndex === -1) throw new ReceivePackResultError(`Invalid ng status line: "${payload}" (missing error message)`);
84
+ const refName = rest.substring(0, spaceIndex);
85
+ const errorMsg = rest.substring(spaceIndex + 1);
86
+ updates.push({
87
+ refName,
88
+ oldHash: null,
89
+ newHash: null,
90
+ success: false,
91
+ error: errorMsg,
92
+ forced: false
93
+ });
94
+ } else if (payload.startsWith("unpack ")) throw new ReceivePackResultError(`Unexpected duplicate unpack status line: "${payload}"`);
95
+ else throw new ReceivePackResultError(`Unexpected status line: "${payload}" (expected "ok " or "ng ")`);
96
+ }
97
+ return updates;
98
+ }
99
+ //#endregion
100
+ export { ReceivePackResultError, parseReceivePackResult };
@@ -0,0 +1,56 @@
1
+ import { GitError } from "../../../core/errors.mjs";
2
+ import { V2CapabilityAdvertisement } from "./types.mjs";
3
+
4
+ //#region src/transport/client/upload-pack/capability-advertisement.d.ts
5
+ /**
6
+ * v2 能力广告解析错误
7
+ */
8
+ declare class V2CapabilityError extends GitError {
9
+ constructor(message: string);
10
+ }
11
+ /**
12
+ * 解析 v2 能力广告响应
13
+ *
14
+ * @param data - 服务端返回的原始响应数据
15
+ * @returns 结构化的能力广告
16
+ * @throws {V2CapabilityError} 当格式不符合 v2 规范时
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const adv = parseV2CapabilityAdvertisement(response);
21
+ * console.log(adv.commands); // [{ name: "ls-refs", features: [] }, ...]
22
+ * console.log(adv.agent); // "nano-git/0.1"
23
+ * ```
24
+ */
25
+ declare function parseV2CapabilityAdvertisement(data: Buffer): V2CapabilityAdvertisement;
26
+ /**
27
+ * 检测 v2 能力广告中是否包含指定命令
28
+ *
29
+ * @param advertisement - v2 能力广告
30
+ * @param command - 命令名称
31
+ * @returns 是否支持
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * if (hasCommand(adv, "ls-refs")) {
36
+ * // 使用 ls-refs 获取 refs
37
+ * }
38
+ * ```
39
+ */
40
+ declare function hasCommand(advertisement: V2CapabilityAdvertisement, command: string): boolean;
41
+ /**
42
+ * 获取命令的附加特性列表
43
+ *
44
+ * @param advertisement - v2 能力广告
45
+ * @param command - 命令名称
46
+ * @returns 特性列表,命令不存在时返回空数组
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const fetchFeatures = getCommandFeatures(adv, "fetch");
51
+ * // ["shallow", "ref-in-want"]
52
+ * ```
53
+ */
54
+ declare function getCommandFeatures(advertisement: V2CapabilityAdvertisement, command: string): string[];
55
+ //#endregion
56
+ export { V2CapabilityError, getCommandFeatures, hasCommand, parseV2CapabilityAdvertisement };
@@ -0,0 +1,130 @@
1
+ import { GitError } from "../../../core/errors.mjs";
2
+ import { parsePktLines } from "../../protocol/pkt-line.mjs";
3
+ //#region src/transport/client/upload-pack/capability-advertisement.ts
4
+ /**
5
+ * v2 能力广告解析
6
+ *
7
+ * 解析 Git Wire 协议 v2 的 capability advertisement 响应。
8
+ *
9
+ * v2 能力广告格式:
10
+ * ```
11
+ * 000eversion 2\n
12
+ * ls-refs\n
13
+ * fetch=shallow ref-in-want\n
14
+ * push\n
15
+ * object-info\n
16
+ * agent=nano-git/0.1\n
17
+ * 0000
18
+ * ```
19
+ *
20
+ * @see https://git-scm.com/docs/protocol-v2#_capability_advertisement
21
+ */
22
+ /**
23
+ * v2 能力广告解析错误
24
+ */
25
+ var V2CapabilityError = class extends GitError {
26
+ constructor(message) {
27
+ super(`v2 capability error: ${message}`);
28
+ this.name = "V2CapabilityError";
29
+ }
30
+ };
31
+ /**
32
+ * 解析 v2 能力广告响应
33
+ *
34
+ * @param data - 服务端返回的原始响应数据
35
+ * @returns 结构化的能力广告
36
+ * @throws {V2CapabilityError} 当格式不符合 v2 规范时
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * const adv = parseV2CapabilityAdvertisement(response);
41
+ * console.log(adv.commands); // [{ name: "ls-refs", features: [] }, ...]
42
+ * console.log(adv.agent); // "nano-git/0.1"
43
+ * ```
44
+ */
45
+ function parseV2CapabilityAdvertisement(data) {
46
+ const lines = parsePktLines(data);
47
+ if (lines.length === 0) throw new V2CapabilityError("Empty capability advertisement");
48
+ const firstLine = lines[0];
49
+ if (!firstLine || firstLine.type !== "data") throw new V2CapabilityError(`Expected data line as first line, got ${firstLine?.type ?? "undefined"}`);
50
+ const versionStr = firstLine.payload.toString("utf-8").trim();
51
+ if (versionStr !== "version 2") throw new V2CapabilityError(`Expected "version 2", got "${versionStr}"`);
52
+ const capabilities = {};
53
+ const commands = [];
54
+ for (let i = 1; i < lines.length; i++) {
55
+ const line = lines[i];
56
+ if (!line || line.type !== "data") break;
57
+ const text = line.payload.toString("utf-8").trim();
58
+ if (text.length === 0) continue;
59
+ if ([
60
+ "ls-refs",
61
+ "fetch",
62
+ "object-info"
63
+ ].some((cmd) => text.startsWith(cmd))) {
64
+ const eqIndex = text.indexOf("=");
65
+ if (eqIndex === -1) {
66
+ const name = text;
67
+ commands.push({
68
+ name,
69
+ features: []
70
+ });
71
+ capabilities[name] = true;
72
+ } else {
73
+ const name = text.substring(0, eqIndex);
74
+ const features = text.substring(eqIndex + 1).split(" ").filter((f) => f.length > 0);
75
+ commands.push({
76
+ name,
77
+ features
78
+ });
79
+ capabilities[name] = features.join(" ");
80
+ }
81
+ } else {
82
+ const eqIndex = text.indexOf("=");
83
+ if (eqIndex === -1) capabilities[text] = true;
84
+ else {
85
+ const key = text.substring(0, eqIndex);
86
+ capabilities[key] = text.substring(eqIndex + 1);
87
+ }
88
+ }
89
+ }
90
+ return {
91
+ capabilities,
92
+ commands,
93
+ agent: typeof capabilities.agent === "string" ? capabilities.agent : void 0
94
+ };
95
+ }
96
+ /**
97
+ * 检测 v2 能力广告中是否包含指定命令
98
+ *
99
+ * @param advertisement - v2 能力广告
100
+ * @param command - 命令名称
101
+ * @returns 是否支持
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * if (hasCommand(adv, "ls-refs")) {
106
+ * // 使用 ls-refs 获取 refs
107
+ * }
108
+ * ```
109
+ */
110
+ function hasCommand(advertisement, command) {
111
+ return advertisement.commands.some((cmd) => cmd.name === command);
112
+ }
113
+ /**
114
+ * 获取命令的附加特性列表
115
+ *
116
+ * @param advertisement - v2 能力广告
117
+ * @param command - 命令名称
118
+ * @returns 特性列表,命令不存在时返回空数组
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * const fetchFeatures = getCommandFeatures(adv, "fetch");
123
+ * // ["shallow", "ref-in-want"]
124
+ * ```
125
+ */
126
+ function getCommandFeatures(advertisement, command) {
127
+ return advertisement.commands.find((cmd) => cmd.name === command)?.features ?? [];
128
+ }
129
+ //#endregion
130
+ export { V2CapabilityError, getCommandFeatures, hasCommand, parseV2CapabilityAdvertisement };