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,119 @@
1
+ import { sha1 } from "../../../core/types.mjs";
2
+ import { hashObject } from "../../../core/hash-digest.mjs";
3
+ import { numberToObjectType } from "../../../pack/constants.mjs";
4
+ import { decodeObjectHeader } from "../../../pack/object-header.mjs";
5
+ import { decodeOfsDeltaOffset } from "../../../pack/ofs-delta-offset.mjs";
6
+ import { applyDelta } from "../../../pack/delta-apply.mjs";
7
+ import { parsePackHeader, readCompressedData } from "../../../pack/pack-reader-utils.mjs";
8
+ import { ReceivePackServiceError } from "./types.mjs";
9
+ //#region src/transport/server/receive-pack/unpack.ts
10
+ /**
11
+ * Push packfile 解包(raw-first)
12
+ *
13
+ * 将 receive-pack push 请求中的 packfile 解包,
14
+ * 将对象以 RawGitObject 形式摄入对象数据库,处理 ofs_delta 和 ref_delta。
15
+ *
16
+ * 所有对象按 canonical raw object 直接写入,不经过语义序列化/反序列化。
17
+ */
18
+ /**
19
+ * 将 push packfile 中的对象解包到对象数据库中
20
+ *
21
+ * 处理非 delta、ofs_delta 和 ref_delta 三种对象类型。
22
+ * 对于 ref_delta,如果 base 不在当前 packfile 中,则从已存在的数据库查找。
23
+ * 所有对象通过 db.ingest() 以 RawGitObject 形式直接写入。
24
+ *
25
+ * @param db - 对象数据库
26
+ * @param packfile - push 请求中的 packfile 数据
27
+ * @throws {ReceivePackServiceError} 当解包失败时
28
+ */
29
+ function unpackPackfile(db, packfile) {
30
+ if (packfile.length < 32) throw new ReceivePackServiceError("Packfile too small to contain any objects");
31
+ const objectCount = parsePackHeader(packfile);
32
+ if (objectCount === 0) return;
33
+ const resolvedByOffset = /* @__PURE__ */ new Map();
34
+ const resolvedByHash = /* @__PURE__ */ new Map();
35
+ let offset = 12;
36
+ for (let i = 0; i < objectCount; i++) {
37
+ const objOffset = offset;
38
+ const [typeNum, , headerBytes] = decodeObjectHeader(packfile, offset);
39
+ offset += headerBytes;
40
+ if (typeNum === 6) {
41
+ const [negOffset, offsetBytes] = decodeOfsDeltaOffset(packfile, offset);
42
+ offset += offsetBytes;
43
+ const [deltaData, compressedBytes] = readCompressedData(packfile, offset);
44
+ offset += compressedBytes;
45
+ const baseOffset = objOffset - negOffset;
46
+ const base = resolvedByOffset.get(baseOffset);
47
+ if (!base) throw new ReceivePackServiceError(`ofs_delta base not found at offset ${baseOffset}`);
48
+ const resolvedData = applyDelta(base.data, deltaData);
49
+ const hash = hashObject(base.type, resolvedData);
50
+ const raw = {
51
+ hash,
52
+ type: base.type,
53
+ content: resolvedData
54
+ };
55
+ db.ingest(raw);
56
+ resolvedByOffset.set(objOffset, {
57
+ type: base.type,
58
+ data: resolvedData
59
+ });
60
+ resolvedByHash.set(hash, {
61
+ type: base.type,
62
+ data: resolvedData
63
+ });
64
+ } else if (typeNum === 7) {
65
+ const baseHash = packfile.subarray(offset, offset + 20).toString("hex");
66
+ offset += 20;
67
+ const [deltaData, compressedBytes] = readCompressedData(packfile, offset);
68
+ offset += compressedBytes;
69
+ let base;
70
+ const cachedBase = resolvedByHash.get(baseHash);
71
+ if (cachedBase) base = cachedBase;
72
+ else if (db.exists(sha1(baseHash))) {
73
+ const raw = db.read(sha1(baseHash));
74
+ base = {
75
+ type: raw.type,
76
+ data: raw.content
77
+ };
78
+ }
79
+ if (!base) throw new ReceivePackServiceError(`ref_delta base not found: ${baseHash}`);
80
+ const resolvedData = applyDelta(base.data, deltaData);
81
+ const resolvedHash = hashObject(base.type, resolvedData);
82
+ const raw = {
83
+ hash: resolvedHash,
84
+ type: base.type,
85
+ content: resolvedData
86
+ };
87
+ db.ingest(raw);
88
+ resolvedByOffset.set(objOffset, {
89
+ type: base.type,
90
+ data: resolvedData
91
+ });
92
+ resolvedByHash.set(resolvedHash, {
93
+ type: base.type,
94
+ data: resolvedData
95
+ });
96
+ } else {
97
+ const [compressedData, compressedBytes] = readCompressedData(packfile, offset);
98
+ offset += compressedBytes;
99
+ const type = numberToObjectType(typeNum);
100
+ const hash = hashObject(type, compressedData);
101
+ const raw = {
102
+ hash,
103
+ type,
104
+ content: compressedData
105
+ };
106
+ db.ingest(raw);
107
+ resolvedByOffset.set(objOffset, {
108
+ type,
109
+ data: compressedData
110
+ });
111
+ resolvedByHash.set(hash, {
112
+ type,
113
+ data: compressedData
114
+ });
115
+ }
116
+ }
117
+ }
118
+ //#endregion
119
+ export { unpackPackfile };
@@ -0,0 +1,20 @@
1
+ //#region src/transport/server/upload-pack/advertise.d.ts
2
+ /**
3
+ * upload-pack 能力广告生成
4
+ *
5
+ * 生成 upload-pack 的 v2 能力广告。
6
+ */
7
+ /**
8
+ * 生成 v2 能力广告
9
+ *
10
+ * @returns 完整的 pkt-line 编码能力广告
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const response = advertiseUploadPack();
15
+ * // "000eversion 2\n000bls-refs\n...0000"
16
+ * ```
17
+ */
18
+ declare function advertiseUploadPack(): Buffer;
19
+ //#endregion
20
+ export { advertiseUploadPack };
@@ -0,0 +1,30 @@
1
+ import { encodeFlushPkt, encodePktLine } from "../../protocol/pkt-line.mjs";
2
+ import { SERVER_AGENT } from "./types.mjs";
3
+ //#region src/transport/server/upload-pack/advertise.ts
4
+ /**
5
+ * upload-pack 能力广告生成
6
+ *
7
+ * 生成 upload-pack 的 v2 能力广告。
8
+ */
9
+ /**
10
+ * 生成 v2 能力广告
11
+ *
12
+ * @returns 完整的 pkt-line 编码能力广告
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const response = advertiseUploadPack();
17
+ * // "000eversion 2\n000bls-refs\n...0000"
18
+ * ```
19
+ */
20
+ function advertiseUploadPack() {
21
+ const parts = [];
22
+ parts.push(encodePktLine("version 2\n"));
23
+ parts.push(encodePktLine("ls-refs\n"));
24
+ parts.push(encodePktLine("fetch=shallow ref-in-want filter\n"));
25
+ parts.push(encodePktLine(`agent=${SERVER_AGENT}\n`));
26
+ parts.push(encodeFlushPkt());
27
+ return Buffer.concat(parts);
28
+ }
29
+ //#endregion
30
+ export { advertiseUploadPack };
@@ -0,0 +1,43 @@
1
+ //#region src/transport/server/upload-pack/command.d.ts
2
+ /**
3
+ * v2 命令请求解析
4
+ *
5
+ * 解析 v2 协议中 command/capability/args 三段式请求体。
6
+ *
7
+ * 请求格式:
8
+ * ```
9
+ * command=<name>\n
10
+ * <capability-list>
11
+ * 0001
12
+ * <command-args>
13
+ * 0000
14
+ * ```
15
+ *
16
+ * @see https://git-scm.com/docs/protocol-v2#_request
17
+ */
18
+ /**
19
+ * 解析后的命令请求
20
+ */
21
+ interface ParsedCommandRequest {
22
+ /** 命令名称(如 "ls-refs"、"fetch") */
23
+ readonly command: string;
24
+ /** capability-list(command 行后的 pkt-line,至 delimiter 前) */
25
+ readonly capabilities: string[];
26
+ /** command-args(delimiter 后的 pkt-line,至 flush 前) */
27
+ readonly args: string[];
28
+ }
29
+ /**
30
+ * 解析 v2 命令请求体
31
+ *
32
+ * @param body - 完整的请求体(pkt-line 编码)
33
+ * @returns 解析后的命令信息
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const cmd = parseCommandRequest(body);
38
+ * console.log(cmd.command); // "ls-refs"
39
+ * ```
40
+ */
41
+ declare function parseCommandRequest(body: Buffer): ParsedCommandRequest;
42
+ //#endregion
43
+ export { ParsedCommandRequest, parseCommandRequest };
@@ -0,0 +1,56 @@
1
+ import { parsePktLines } from "../../protocol/pkt-line.mjs";
2
+ //#region src/transport/server/upload-pack/command.ts
3
+ /**
4
+ * v2 命令请求解析
5
+ *
6
+ * 解析 v2 协议中 command/capability/args 三段式请求体。
7
+ *
8
+ * 请求格式:
9
+ * ```
10
+ * command=<name>\n
11
+ * <capability-list>
12
+ * 0001
13
+ * <command-args>
14
+ * 0000
15
+ * ```
16
+ *
17
+ * @see https://git-scm.com/docs/protocol-v2#_request
18
+ */
19
+ /**
20
+ * 解析 v2 命令请求体
21
+ *
22
+ * @param body - 完整的请求体(pkt-line 编码)
23
+ * @returns 解析后的命令信息
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const cmd = parseCommandRequest(body);
28
+ * console.log(cmd.command); // "ls-refs"
29
+ * ```
30
+ */
31
+ function parseCommandRequest(body) {
32
+ const pktLines = parsePktLines(body);
33
+ let command = "";
34
+ const capabilities = [];
35
+ const args = [];
36
+ let afterDelimiter = false;
37
+ for (const pkt of pktLines) {
38
+ if (pkt.type === "flush") break;
39
+ if (pkt.type === "delimiter") {
40
+ afterDelimiter = true;
41
+ continue;
42
+ }
43
+ if (pkt.type !== "data") continue;
44
+ const text = pkt.payload.toString("utf-8").replace(/\n$/, "");
45
+ if (!afterDelimiter) if (text.startsWith("command=")) command = text.slice(8);
46
+ else capabilities.push(text);
47
+ else args.push(text);
48
+ }
49
+ return {
50
+ command,
51
+ capabilities,
52
+ args
53
+ };
54
+ }
55
+ //#endregion
56
+ export { parseCommandRequest };
@@ -0,0 +1,43 @@
1
+ import { SHA1 } from "../../../core/types.mjs";
2
+ import { RepositoryBackend } from "../../../backend/types.mjs";
3
+
4
+ //#region src/transport/server/upload-pack/fetch.d.ts
5
+ /** 从 fetch 请求 args 中解析的参数 */
6
+ interface FetchServerParams {
7
+ readonly wants: SHA1[];
8
+ readonly haves: SHA1[];
9
+ readonly wantRefs: string[];
10
+ readonly done: boolean;
11
+ readonly thinPack: boolean;
12
+ readonly noProgress: boolean;
13
+ readonly ofsDelta: boolean;
14
+ }
15
+ /**
16
+ * 从 args 中解析 fetch 参数
17
+ *
18
+ * @param args - fetch 命令的 args 列表
19
+ * @returns 结构化的 fetch 参数
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const params = parseFetchArgs(["want <oid>", "have <oid>", "done"]);
24
+ * // { wants: [<oid>], haves: [<oid>], done: true, ... }
25
+ * ```
26
+ */
27
+ declare function parseFetchArgs(args: string[]): FetchServerParams;
28
+ /**
29
+ * 生成 v2 fetch 响应
30
+ *
31
+ * @param backend - 仓库后端
32
+ * @param params - 解析后的 fetch 参数
33
+ * @returns 完整的 v2 fetch 响应(pkt-line 编码)
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const params = parseFetchArgs(["want <oid>", "done"]);
38
+ * const response = generateFetchResponse(backend, params);
39
+ * ```
40
+ */
41
+ declare function generateFetchResponse(backend: RepositoryBackend, params: FetchServerParams): Buffer;
42
+ //#endregion
43
+ export { FetchServerParams, generateFetchResponse, parseFetchArgs };
@@ -0,0 +1,217 @@
1
+ import { sha1 } from "../../../core/types.mjs";
2
+ import { resolveRefHash } from "../../../refs/resolve.mjs";
3
+ import { encodeDelimiterPkt, encodeFlushPkt, encodePktLine } from "../../protocol/pkt-line.mjs";
4
+ import { collectReachable } from "../../protocol/object-graph.mjs";
5
+ import { createPackWriter } from "../../../pack/pack-writer.mjs";
6
+ import { MAX_PKT_PAYLOAD, UploadPackServiceError } from "./types.mjs";
7
+ //#region src/transport/server/upload-pack/fetch.ts
8
+ /**
9
+ * v2 fetch 命令响应生成
10
+ *
11
+ * 处理 fetch 命令,计算对象集合、构建 packfile、生成 fetch 响应。
12
+ *
13
+ * @see https://git-scm.com/docs/protocol-v2#_fetch
14
+ */
15
+ /**
16
+ * 从 args 中解析 fetch 参数
17
+ *
18
+ * @param args - fetch 命令的 args 列表
19
+ * @returns 结构化的 fetch 参数
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const params = parseFetchArgs(["want <oid>", "have <oid>", "done"]);
24
+ * // { wants: [<oid>], haves: [<oid>], done: true, ... }
25
+ * ```
26
+ */
27
+ function parseFetchArgs(args) {
28
+ const wants = [];
29
+ const haves = [];
30
+ const wantRefs = [];
31
+ let done = false;
32
+ let thinPack = false;
33
+ let noProgress = false;
34
+ let ofsDelta = false;
35
+ for (const arg of args) if (arg === "done") done = true;
36
+ else if (arg === "thin-pack") thinPack = true;
37
+ else if (arg === "no-progress") noProgress = true;
38
+ else if (arg === "ofs-delta") ofsDelta = true;
39
+ else if (arg.startsWith("want ")) wants.push(sha1(arg.slice(5).trim()));
40
+ else if (arg.startsWith("have ")) haves.push(sha1(arg.slice(5).trim()));
41
+ else if (arg.startsWith("want-ref ")) wantRefs.push(arg.slice(9).trim());
42
+ return {
43
+ wants,
44
+ haves,
45
+ wantRefs,
46
+ done,
47
+ thinPack,
48
+ noProgress,
49
+ ofsDelta
50
+ };
51
+ }
52
+ /**
53
+ * 计算要打包的对象集合
54
+ *
55
+ * - 无 haves(clone):返回所有 want 对象及其可达对象
56
+ * - 有 haves(增量 fetch):返回 want 可达对象与 have 可达对象的差集
57
+ */
58
+ function computeObjectsToPack(backend, params) {
59
+ if (params.haves.length === 0) return collectReachable(backend.objects, params.wants, "skip-commit-parents");
60
+ const wantReachable = collectReachable(backend.objects, params.wants, "skip-commit-parents");
61
+ const haveReachable = collectReachable(backend.objects, params.haves, "skip-commit-parents");
62
+ const result = /* @__PURE__ */ new Set();
63
+ for (const hash of wantReachable) if (!haveReachable.has(hash)) result.add(hash);
64
+ return result;
65
+ }
66
+ /**
67
+ * 将 packfile 数据以 side-band channel 1 格式分帧
68
+ *
69
+ * 每个 pkt-line 帧:<4字节长度><1字节channel><数据>
70
+ */
71
+ function encodePackfileWithSideBand(packfile) {
72
+ const maxPayload = MAX_PKT_PAYLOAD - 1;
73
+ const frames = [];
74
+ let offset = 0;
75
+ while (offset < packfile.length) {
76
+ const chunkSize = Math.min(maxPayload, packfile.length - offset);
77
+ const frame = Buffer.alloc(1 + chunkSize);
78
+ frame[0] = 1;
79
+ packfile.copy(frame, 1, offset, offset + chunkSize);
80
+ frames.push(encodePktLine(frame));
81
+ offset += chunkSize;
82
+ }
83
+ return frames;
84
+ }
85
+ /**
86
+ * 生成带 packfile 的 fetch 响应
87
+ *
88
+ * 响应结构遵循 protocol-v2(节之间以 delim-pkt 分隔,最后以 flush-pkt 收尾):
89
+ * ```
90
+ * [acknowledgments\n ACK...\n ready\n 0001] ← 仅协商命中 ready 时(无 done)
91
+ * [wanted-refs\n <oid> <refname>\n ... 0001] ← 仅当客户端使用 want-ref
92
+ * packfile\n
93
+ * <side-band 编码的 packfile 数据>
94
+ * 0000
95
+ * ```
96
+ *
97
+ * 注意:
98
+ * - 当请求带 `done`(无 acknowledgments 节)时,section 不应以 delim-pkt 开头——
99
+ * 首节直接是 wanted-refs 或 packfile。早期实现错误地在 packfile 前加了 leading
100
+ * delim-pkt,导致 git CLI 报 `fatal: expected 'packfile'`。
101
+ * - 当协商阶段(无 done)服务端判定 ready 时,必须在 **同一响应** 中
102
+ * acknowledgments 节之后紧接 packfile,否则 git 报
103
+ * `fatal: expected packfile after 'ready'`。
104
+ *
105
+ * @param wantedRefs - want-ref 解析出的 refname→oid 映射(无则不发 wanted-refs 节)
106
+ * @param ackSection - 可选的 acknowledgments 节内容(不含分隔 delim);提供时会在其后补一个 delim-pkt
107
+ */
108
+ function generatePackfileResponse(backend, params, wantedRefs, ackSection) {
109
+ const parts = [];
110
+ if (ackSection !== void 0) {
111
+ parts.push(ackSection);
112
+ parts.push(encodeDelimiterPkt());
113
+ }
114
+ if (wantedRefs.length > 0) {
115
+ parts.push(encodePktLine("wanted-refs\n"));
116
+ for (const { refname, oid } of wantedRefs) parts.push(encodePktLine(`${oid} ${refname}\n`));
117
+ parts.push(encodeDelimiterPkt());
118
+ }
119
+ const toPack = computeObjectsToPack(backend, params);
120
+ const writer = createPackWriter();
121
+ for (const hash of toPack) {
122
+ const raw = backend.objects.tryRead(hash);
123
+ if (raw) writer.addRaw(raw);
124
+ }
125
+ const packfile = writer.build();
126
+ parts.push(encodePktLine("packfile\n"));
127
+ if (packfile.length > 0) parts.push(...encodePackfileWithSideBand(packfile));
128
+ parts.push(encodeFlushPkt());
129
+ return Buffer.concat(parts);
130
+ }
131
+ /**
132
+ * 查找 wants 与 haves 间的共同对象
133
+ *
134
+ * 当前简化实现:返回所有在本地仓库中存在且出现在 haves 中的哈希。
135
+ * 更完善的实现应通过 commit 图 BFS 验证祖先关系。
136
+ */
137
+ function findCommonObjects(backend, _wants, haves) {
138
+ const common = [];
139
+ for (const have of haves) if (backend.objects.exists(have)) common.push(have);
140
+ return {
141
+ common,
142
+ ready: common.length > 0
143
+ };
144
+ }
145
+ /**
146
+ * 构建 acknowledgments 节内容(不含尾部 flush/delim)
147
+ *
148
+ * ```
149
+ * acknowledgments\n
150
+ * NAK\n
151
+ * --- 或 ---
152
+ * acknowledgments\n
153
+ * ACK <oid>\n
154
+ * ready\n
155
+ * ```
156
+ *
157
+ * @returns section 内容及是否 ready
158
+ */
159
+ function buildAcknowledgmentsSection(backend, params) {
160
+ const parts = [];
161
+ parts.push(encodePktLine("acknowledgments\n"));
162
+ const { common, ready } = findCommonObjects(backend, params.wants, params.haves);
163
+ if (common.length > 0) {
164
+ for (const oid of common) parts.push(encodePktLine(`ACK ${oid}\n`));
165
+ if (ready) parts.push(encodePktLine("ready\n"));
166
+ } else parts.push(encodePktLine("NAK\n"));
167
+ return {
168
+ section: Buffer.concat(parts),
169
+ ready
170
+ };
171
+ }
172
+ /**
173
+ * 生成 v2 fetch 响应
174
+ *
175
+ * @param backend - 仓库后端
176
+ * @param params - 解析后的 fetch 参数
177
+ * @returns 完整的 v2 fetch 响应(pkt-line 编码)
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * const params = parseFetchArgs(["want <oid>", "done"]);
182
+ * const response = generateFetchResponse(backend, params);
183
+ * ```
184
+ */
185
+ function generateFetchResponse(backend, params) {
186
+ if (params.wants.length === 0 && params.wantRefs.length === 0) throw new UploadPackServiceError("fetch: no wants or want-refs specified");
187
+ for (const want of params.wants) if (!backend.objects.exists(want)) {
188
+ const parts = [];
189
+ parts.push(encodePktLine("packfile\n"));
190
+ parts.push(encodePktLine(Buffer.concat([Buffer.from([3]), Buffer.from(`want ${want} not found\n`)])));
191
+ parts.push(encodeFlushPkt());
192
+ return Buffer.concat(parts);
193
+ }
194
+ const effectiveWants = [...params.wants];
195
+ const wantedRefs = [];
196
+ for (const ref of params.wantRefs) {
197
+ const hash = resolveRefHash(backend.refs, ref);
198
+ if (hash !== null) {
199
+ effectiveWants.push(hash);
200
+ wantedRefs.push({
201
+ refname: ref,
202
+ oid: hash
203
+ });
204
+ }
205
+ }
206
+ const effectiveParams = {
207
+ ...params,
208
+ wants: effectiveWants
209
+ };
210
+ if (effectiveParams.wants.length === 0) throw new UploadPackServiceError("fetch: no wants resolved");
211
+ if (params.done) return generatePackfileResponse(backend, effectiveParams, wantedRefs);
212
+ const { section: ackSection, ready } = buildAcknowledgmentsSection(backend, effectiveParams);
213
+ if (ready) return generatePackfileResponse(backend, effectiveParams, wantedRefs, ackSection);
214
+ return Buffer.concat([ackSection, encodeFlushPkt()]);
215
+ }
216
+ //#endregion
217
+ export { generateFetchResponse, parseFetchArgs };
@@ -0,0 +1,7 @@
1
+ import { advertiseUploadPack } from "./advertise.mjs";
2
+ import { ParsedCommandRequest, parseCommandRequest } from "./command.mjs";
3
+ import { LsRefsServerOptions, generateLsRefsResponse, parseLsRefsArgs } from "./ls-refs.mjs";
4
+ import { FetchServerParams, generateFetchResponse, parseFetchArgs } from "./fetch.mjs";
5
+ import { UploadPackServiceError } from "./types.mjs";
6
+ import { UploadPackService, createUploadPackService } from "./service.mjs";
7
+ export { type FetchServerParams, type LsRefsServerOptions, type ParsedCommandRequest, type UploadPackService, UploadPackServiceError, advertiseUploadPack, createUploadPackService, generateFetchResponse, generateLsRefsResponse, parseCommandRequest, parseFetchArgs, parseLsRefsArgs };
@@ -0,0 +1,7 @@
1
+ import { UploadPackServiceError } from "./types.mjs";
2
+ import { advertiseUploadPack } from "./advertise.mjs";
3
+ import { parseCommandRequest } from "./command.mjs";
4
+ import { generateLsRefsResponse, parseLsRefsArgs } from "./ls-refs.mjs";
5
+ import { generateFetchResponse, parseFetchArgs } from "./fetch.mjs";
6
+ import { createUploadPackService } from "./service.mjs";
7
+ export { UploadPackServiceError, advertiseUploadPack, createUploadPackService, generateFetchResponse, generateLsRefsResponse, parseCommandRequest, parseFetchArgs, parseLsRefsArgs };
@@ -0,0 +1,38 @@
1
+ import { RepositoryBackend } from "../../../backend/types.mjs";
2
+
3
+ //#region src/transport/server/upload-pack/ls-refs.d.ts
4
+ /** ls-refs 请求中解析出的选项 */
5
+ interface LsRefsServerOptions {
6
+ readonly symrefs: boolean;
7
+ readonly peel: boolean;
8
+ readonly unborn: boolean;
9
+ readonly refPrefixes: string[];
10
+ }
11
+ /**
12
+ * 从 args 中解析 ls-refs 选项
13
+ *
14
+ * @param args - ls-refs 命令的 args 列表
15
+ * @returns 结构化的 ls-refs 选项
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const opts = parseLsRefsArgs(["symrefs", "peel", "ref-prefix refs/heads/"]);
20
+ * // { symrefs: true, peel: true, unborn: false, refPrefixes: ["refs/heads/"] }
21
+ * ```
22
+ */
23
+ declare function parseLsRefsArgs(args: string[]): LsRefsServerOptions;
24
+ /**
25
+ * 生成 ls-refs 响应
26
+ *
27
+ * @param backend - 仓库后端
28
+ * @param options - ls-refs 选项
29
+ * @returns pkt-line 编码的 ls-refs 响应
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const buf = generateLsRefsResponse(backend, { symrefs: true, peel: true, unborn: false, refPrefixes: [] });
34
+ * ```
35
+ */
36
+ declare function generateLsRefsResponse(backend: RepositoryBackend, options: LsRefsServerOptions): Buffer;
37
+ //#endregion
38
+ export { LsRefsServerOptions, generateLsRefsResponse, parseLsRefsArgs };