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,57 @@
1
+ import { SHA1 } from "../core/types.mjs";
2
+ import { PackIndexEntry } from "./pack-index-types.mjs";
3
+
4
+ //#region src/pack/pack-index-reader.d.ts
5
+ /**
6
+ * Packfile 索引读取器接口
7
+ */
8
+ interface PackIndexReader {
9
+ /** 对象数量 */
10
+ readonly objectCount: number;
11
+ /**
12
+ * 查找对象的索引条目
13
+ *
14
+ * @param hash - 对象的 SHA-1 哈希
15
+ * @returns 索引条目,如果不存在则返回 undefined
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const entry = index.lookup(hash);
20
+ * console.log(entry?.offset);
21
+ * ```
22
+ */
23
+ lookup(hash: SHA1): PackIndexEntry | undefined;
24
+ /**
25
+ * 检查对象是否存在
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * const exists = index.has(hash);
30
+ * ```
31
+ */
32
+ has(hash: SHA1): boolean;
33
+ /**
34
+ * 获取所有对象的哈希列表
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const hashes = index.listHashes();
39
+ * ```
40
+ */
41
+ listHashes(): SHA1[];
42
+ }
43
+ /**
44
+ * 创建 Packfile 索引读取器
45
+ *
46
+ * @param data - 完整的 .idx 文件数据
47
+ * @returns 索引读取器实例
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const index = createPackIndexReader(idxData);
52
+ * console.log(index.objectCount);
53
+ * ```
54
+ */
55
+ declare function createPackIndexReader(data: Buffer): PackIndexReader;
56
+ //#endregion
57
+ export { PackIndexReader, createPackIndexReader };
@@ -0,0 +1,90 @@
1
+ import { PackIndexError } from "../core/errors.mjs";
2
+ import { sha1 } from "../core/types.mjs";
3
+ import { IDX_V2_FANOUT_SIZE, IDX_V2_SIGNATURE } from "./constants.mjs";
4
+ //#region src/pack/pack-index-reader.ts
5
+ /**
6
+ * Packfile 索引读取
7
+ */
8
+ /**
9
+ * 创建 Packfile 索引读取器
10
+ *
11
+ * @param data - 完整的 .idx 文件数据
12
+ * @returns 索引读取器实例
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const index = createPackIndexReader(idxData);
17
+ * console.log(index.objectCount);
18
+ * ```
19
+ */
20
+ function createPackIndexReader(data) {
21
+ if (data.length < 8) throw new PackIndexError("Index file too small");
22
+ const signature = data.subarray(0, 4);
23
+ if (!signature.equals(IDX_V2_SIGNATURE)) throw new PackIndexError(`Invalid signature: ${signature.toString("hex")}`);
24
+ const version = data.readUInt32BE(4);
25
+ if (version !== 2) throw new PackIndexError(`Unsupported version: ${version}`);
26
+ const fanout = [];
27
+ const fanoutStart = 8;
28
+ for (let i = 0; i < 256; i++) fanout.push(data.readUInt32BE(fanoutStart + i * 4));
29
+ const _objectCount = fanout[255];
30
+ const sha1TableOffset = 8 + IDX_V2_FANOUT_SIZE;
31
+ const crc32TableOffset = sha1TableOffset + _objectCount * 20;
32
+ const offsetTableOffset = crc32TableOffset + _objectCount * 4;
33
+ const largeOffsetTable = offsetTableOffset + _objectCount * 4;
34
+ /**
35
+ * 获取指定索引位置的哈希
36
+ */
37
+ function getHashAt(index) {
38
+ const offset = sha1TableOffset + index * 20;
39
+ return data.subarray(offset, offset + 20).toString("hex");
40
+ }
41
+ /**
42
+ * 获取指定索引位置的条目
43
+ */
44
+ function getEntryAt(index) {
45
+ const hash = sha1(getHashAt(index));
46
+ const crc32 = data.readUInt32BE(crc32TableOffset + index * 4);
47
+ let offset = data.readUInt32BE(offsetTableOffset + index * 4);
48
+ if (offset & 2147483648) {
49
+ const largeIndex = offset & 2147483647;
50
+ offset = Number(data.readBigUInt64BE(largeOffsetTable + largeIndex * 8));
51
+ }
52
+ return {
53
+ hash,
54
+ offset,
55
+ crc32
56
+ };
57
+ }
58
+ function lookup(hash) {
59
+ const firstByte = parseInt(hash.slice(0, 2), 16);
60
+ const start = firstByte > 0 ? fanout[firstByte - 1] : 0;
61
+ const end = fanout[firstByte];
62
+ let low = start;
63
+ let high = end;
64
+ while (low < high) {
65
+ const mid = Math.floor((low + high) / 2);
66
+ const cmp = getHashAt(mid).localeCompare(hash);
67
+ if (cmp < 0) low = mid + 1;
68
+ else if (cmp > 0) high = mid;
69
+ else return getEntryAt(mid);
70
+ }
71
+ }
72
+ function has(hash) {
73
+ return lookup(hash) !== void 0;
74
+ }
75
+ function listHashes() {
76
+ const hashes = [];
77
+ for (let i = 0; i < _objectCount; i++) hashes.push(sha1(getHashAt(i)));
78
+ return hashes;
79
+ }
80
+ return {
81
+ get objectCount() {
82
+ return _objectCount;
83
+ },
84
+ lookup,
85
+ has,
86
+ listHashes
87
+ };
88
+ }
89
+ //#endregion
90
+ export { createPackIndexReader };
@@ -0,0 +1,16 @@
1
+ import { SHA1 } from "../core/types.mjs";
2
+
3
+ //#region src/pack/pack-index-types.d.ts
4
+ /**
5
+ * 索引中的对象条目
6
+ */
7
+ interface PackIndexEntry {
8
+ /** 对象的 SHA-1 哈希 */
9
+ hash: SHA1;
10
+ /** 对象在 packfile 中的偏移量 */
11
+ offset: number;
12
+ /** 压缩数据的 CRC32 校验和 */
13
+ crc32: number;
14
+ }
15
+ //#endregion
16
+ export { PackIndexEntry };
@@ -0,0 +1,45 @@
1
+ import { PackIndexEntry } from "./pack-index-types.mjs";
2
+
3
+ //#region src/pack/pack-index-writer.d.ts
4
+ /**
5
+ * Packfile 索引写入器接口
6
+ */
7
+ interface PackIndexWriter {
8
+ /**
9
+ * 添加一个索引条目
10
+ *
11
+ * @param entry - 索引条目
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * writer.addEntry({ hash, offset: 12, crc32: 0x12345678 });
16
+ * ```
17
+ */
18
+ addEntry(entry: PackIndexEntry): void;
19
+ /**
20
+ * 构建索引文件数据
21
+ *
22
+ * @param packChecksum - packfile 的 SHA-1 校验和(20 字节)
23
+ * @returns 完整的 .idx 文件数据
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const idxData = writer.build(packChecksum);
28
+ * ```
29
+ */
30
+ build(packChecksum: Buffer): Buffer;
31
+ }
32
+ /**
33
+ * 创建 Packfile 索引写入器
34
+ *
35
+ * @returns 索引写入器实例
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const writer = createPackIndexWriter();
40
+ * writer.addEntry({ hash, offset: 12, crc32: 0x12345678 });
41
+ * ```
42
+ */
43
+ declare function createPackIndexWriter(): PackIndexWriter;
44
+ //#endregion
45
+ export { PackIndexWriter, createPackIndexWriter };
@@ -0,0 +1,106 @@
1
+ import { IDX_V2_FANOUT_SIZE, IDX_V2_SIGNATURE } from "./constants.mjs";
2
+ import { createHash } from "node:crypto";
3
+ //#region src/pack/pack-index-writer.ts
4
+ /**
5
+ * Packfile 索引写入
6
+ */
7
+ /**
8
+ * 创建 Packfile 索引写入器
9
+ *
10
+ * @returns 索引写入器实例
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const writer = createPackIndexWriter();
15
+ * writer.addEntry({ hash, offset: 12, crc32: 0x12345678 });
16
+ * ```
17
+ */
18
+ function createPackIndexWriter() {
19
+ const entries = [];
20
+ /**
21
+ * 构建头部
22
+ */
23
+ function createHeader() {
24
+ const header = Buffer.alloc(8);
25
+ IDX_V2_SIGNATURE.copy(header, 0);
26
+ header.writeUInt32BE(2, 4);
27
+ return header;
28
+ }
29
+ /**
30
+ * 构建扇出表
31
+ */
32
+ function createFanoutTable(entries) {
33
+ const fanout = Buffer.alloc(IDX_V2_FANOUT_SIZE);
34
+ let count = 0;
35
+ for (let i = 0; i < 256; i++) {
36
+ while (count < entries.length && parseInt(entries[count].hash.slice(0, 2), 16) <= i) count++;
37
+ fanout.writeUInt32BE(count, i * 4);
38
+ }
39
+ return fanout;
40
+ }
41
+ /**
42
+ * 构建 SHA-1 表
43
+ */
44
+ function createSha1Table(entries) {
45
+ const sha1Table = Buffer.alloc(entries.length * 20);
46
+ for (let i = 0; i < entries.length; i++) Buffer.from(entries[i].hash, "hex").copy(sha1Table, i * 20);
47
+ return sha1Table;
48
+ }
49
+ /**
50
+ * 构建 CRC32 表
51
+ */
52
+ function createCrc32Table(entries) {
53
+ const crc32Table = Buffer.alloc(entries.length * 4);
54
+ for (let i = 0; i < entries.length; i++) crc32Table.writeUInt32BE(entries[i].crc32 >>> 0, i * 4);
55
+ return crc32Table;
56
+ }
57
+ /**
58
+ * 构建偏移量表与大偏移量列表
59
+ */
60
+ function createOffsetTables(entries) {
61
+ const offsetTable = Buffer.alloc(entries.length * 4);
62
+ const largeOffsets = [];
63
+ for (let i = 0; i < entries.length; i++) {
64
+ const offset = entries[i].offset;
65
+ if (offset >= 2147483648) {
66
+ const largeIndex = largeOffsets.length;
67
+ largeOffsets.push(offset);
68
+ offsetTable.writeUInt32BE(2147483648 | largeIndex, i * 4);
69
+ } else offsetTable.writeUInt32BE(offset, i * 4);
70
+ }
71
+ return {
72
+ offsetTable,
73
+ largeOffsets
74
+ };
75
+ }
76
+ /**
77
+ * 构建大偏移量表
78
+ */
79
+ function createLargeOffsetTable(largeOffsets) {
80
+ const largeOffsetTable = Buffer.alloc(largeOffsets.length * 8);
81
+ for (let i = 0; i < largeOffsets.length; i++) largeOffsetTable.writeBigUInt64BE(BigInt(largeOffsets[i]), i * 8);
82
+ return largeOffsetTable;
83
+ }
84
+ return {
85
+ addEntry(entry) {
86
+ entries.push(entry);
87
+ },
88
+ build(packChecksum) {
89
+ const sorted = [...entries].sort((a, b) => a.hash.localeCompare(b.hash));
90
+ const parts = [];
91
+ parts.push(createHeader());
92
+ parts.push(createFanoutTable(sorted));
93
+ parts.push(createSha1Table(sorted));
94
+ parts.push(createCrc32Table(sorted));
95
+ const { offsetTable, largeOffsets } = createOffsetTables(sorted);
96
+ parts.push(offsetTable);
97
+ if (largeOffsets.length > 0) parts.push(createLargeOffsetTable(largeOffsets));
98
+ parts.push(packChecksum);
99
+ const idxWithoutChecksum = Buffer.concat(parts);
100
+ const idxChecksum = createHash("sha1").update(idxWithoutChecksum).digest();
101
+ return Buffer.concat([idxWithoutChecksum, idxChecksum]);
102
+ }
103
+ };
104
+ }
105
+ //#endregion
106
+ export { createPackIndexWriter };
@@ -0,0 +1,104 @@
1
+ import { InvalidPackError } from "../core/errors.mjs";
2
+ import { hashObject } from "../core/hash-digest.mjs";
3
+ import { numberToObjectType } from "./constants.mjs";
4
+ import { applyDelta } from "./delta-apply.mjs";
5
+ import { readCompressedData } from "./pack-reader-utils.mjs";
6
+ //#region src/pack/pack-reader-resolver.ts
7
+ /**
8
+ * Packfile 对象解析辅助函数
9
+ */
10
+ /**
11
+ * 解析普通对象
12
+ *
13
+ * @param data - 完整的 packfile 数据
14
+ * @param offset - 压缩数据起始偏移量
15
+ * @param objOffset - 当前对象头起始偏移量
16
+ * @param typeNum - pack 中的对象类型编号
17
+ * @returns 解析结果和新的偏移量
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const result = resolvePlainPackObject(packData, offset, objOffset, 3);
22
+ * ```
23
+ */
24
+ function resolvePlainPackObject(data, offset, objOffset, typeNum) {
25
+ const type = numberToObjectType(typeNum);
26
+ const [compressedData, compressedBytes] = readCompressedData(data, offset);
27
+ const nextOffset = offset + compressedBytes;
28
+ return {
29
+ object: {
30
+ type,
31
+ hash: hashObject(type, compressedData),
32
+ offset: objOffset,
33
+ data: compressedData
34
+ },
35
+ nextOffset
36
+ };
37
+ }
38
+ /**
39
+ * 解析基于偏移量的 delta 对象
40
+ *
41
+ * @param data - 完整的 packfile 数据
42
+ * @param offset - delta 数据起始偏移量
43
+ * @param objOffset - 当前对象头起始偏移量
44
+ * @param negOffset - 相对 base object 的负偏移量
45
+ * @param objectsByOffset - 已解析对象缓存
46
+ * @returns 解析结果和新的偏移量
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const result = resolveOfsDeltaPackObject(data, offset, objOffset, 32, cache);
51
+ * ```
52
+ */
53
+ function resolveOfsDeltaPackObject(data, offset, objOffset, negOffset, objectsByOffset) {
54
+ const [deltaData, compressedBytes] = readCompressedData(data, offset);
55
+ const nextOffset = offset + compressedBytes;
56
+ const baseOffset = objOffset - negOffset;
57
+ const baseObj = objectsByOffset.get(baseOffset);
58
+ if (!baseObj) throw new InvalidPackError(`Base object not found at offset ${baseOffset}`);
59
+ const resolvedData = applyDelta(baseObj.data, deltaData);
60
+ const hash = hashObject(baseObj.type, resolvedData);
61
+ return {
62
+ object: {
63
+ type: baseObj.type,
64
+ hash,
65
+ offset: objOffset,
66
+ data: resolvedData
67
+ },
68
+ nextOffset
69
+ };
70
+ }
71
+ /**
72
+ * 解析基于哈希的 delta 对象
73
+ *
74
+ * @param data - 完整的 packfile 数据
75
+ * @param offset - delta 数据起始偏移量
76
+ * @param objOffset - 当前对象头起始偏移量
77
+ * @param baseHash - base object 的 SHA-1 十六进制字符串
78
+ * @param objectsByHash - 已解析对象缓存
79
+ * @returns 解析结果和新的偏移量
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const result = resolveRefDeltaPackObject(data, offset, objOffset, hash, cache);
84
+ * ```
85
+ */
86
+ function resolveRefDeltaPackObject(data, offset, objOffset, baseHash, objectsByHash) {
87
+ const [deltaData, compressedBytes] = readCompressedData(data, offset);
88
+ const nextOffset = offset + compressedBytes;
89
+ const baseObj = objectsByHash.get(baseHash);
90
+ if (!baseObj) throw new InvalidPackError(`Base object not found: ${baseHash}`);
91
+ const resolvedData = applyDelta(baseObj.data, deltaData);
92
+ const hash = hashObject(baseObj.type, resolvedData);
93
+ return {
94
+ object: {
95
+ type: baseObj.type,
96
+ hash,
97
+ offset: objOffset,
98
+ data: resolvedData
99
+ },
100
+ nextOffset
101
+ };
102
+ }
103
+ //#endregion
104
+ export { resolveOfsDeltaPackObject, resolvePlainPackObject, resolveRefDeltaPackObject };
@@ -0,0 +1,31 @@
1
+ import { ObjectType, RawGitObject, SHA1 } from "../core/types.mjs";
2
+
3
+ //#region src/pack/pack-reader-types.d.ts
4
+ /**
5
+ * Packfile 中的对象信息
6
+ */
7
+ interface PackObject {
8
+ /** 对象类型 */
9
+ type: ObjectType;
10
+ /** 对象的 SHA-1 哈希 */
11
+ hash: SHA1;
12
+ /** 对象在 packfile 中的偏移量 */
13
+ offset: number;
14
+ /** 解压后的原始数据(对应 RawGitObject.content) */
15
+ data: Buffer;
16
+ }
17
+ /**
18
+ * 将 PackObject 转换为 RawGitObject
19
+ *
20
+ * @param packObj - packfile 中的对象
21
+ * @returns 可用于 ODB 的原始对象
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const raw = packObjectToRaw(packObj);
26
+ * db.ingest(raw);
27
+ * ```
28
+ */
29
+ declare function packObjectToRaw(packObj: PackObject): RawGitObject;
30
+ //#endregion
31
+ export { PackObject, packObjectToRaw };
@@ -0,0 +1,22 @@
1
+ //#region src/pack/pack-reader-types.ts
2
+ /**
3
+ * 将 PackObject 转换为 RawGitObject
4
+ *
5
+ * @param packObj - packfile 中的对象
6
+ * @returns 可用于 ODB 的原始对象
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const raw = packObjectToRaw(packObj);
11
+ * db.ingest(raw);
12
+ * ```
13
+ */
14
+ function packObjectToRaw(packObj) {
15
+ return {
16
+ hash: packObj.hash,
17
+ type: packObj.type,
18
+ content: packObj.data
19
+ };
20
+ }
21
+ //#endregion
22
+ export { packObjectToRaw };
@@ -0,0 +1,61 @@
1
+ import { InvalidPackError } from "../core/errors.mjs";
2
+ import { PACK_SIGNATURE } from "./constants.mjs";
3
+ import { createHash } from "node:crypto";
4
+ import { inflateSync } from "node:zlib";
5
+ //#region src/pack/pack-reader-utils.ts
6
+ /**
7
+ * Packfile 读取底层辅助函数
8
+ */
9
+ /**
10
+ * 解析并校验 packfile 头部
11
+ *
12
+ * @param data - 完整的 packfile 数据
13
+ * @returns 对象数量
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const objectCount = parsePackHeader(packData);
18
+ * ```
19
+ */
20
+ function parsePackHeader(data) {
21
+ if (data.length < 32) throw new InvalidPackError("Packfile too small");
22
+ const signature = data.subarray(0, 4);
23
+ if (!signature.equals(PACK_SIGNATURE)) throw new InvalidPackError(`Invalid signature: ${signature.toString("hex")}`);
24
+ const version = data.readUInt32BE(4);
25
+ if (version !== 2) throw new InvalidPackError(`Unsupported version: ${version}`);
26
+ const objectCount = data.readUInt32BE(8);
27
+ const expectedChecksum = data.subarray(data.length - 20);
28
+ const actualChecksum = createHash("sha1").update(data.subarray(0, data.length - 20)).digest();
29
+ if (!expectedChecksum.equals(actualChecksum)) throw new InvalidPackError("Checksum mismatch");
30
+ return objectCount;
31
+ }
32
+ /**
33
+ * 读取 zlib 压缩的数据
34
+ *
35
+ * 使用 zlib 的 `info` 选项获取实际消耗的输入字节数,
36
+ * 从而精确定位下一个对象的起始偏移量。
37
+ *
38
+ * @param data - 完整的 packfile 数据
39
+ * @param offset - 压缩数据的起始偏移量
40
+ * @returns 解压结果和消耗的字节数
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const [buffer, bytesRead] = readCompressedData(packData, 12);
45
+ * ```
46
+ */
47
+ function readCompressedData(data, offset) {
48
+ const remaining = data.subarray(offset);
49
+ try {
50
+ const inflated = inflateSync(remaining, { info: true });
51
+ if (!inflated || typeof inflated !== "object" || !("buffer" in inflated) || !("engine" in inflated)) throw new InvalidPackError("Unexpected inflate result shape");
52
+ const result = inflated;
53
+ const consumed = result.engine?.bytesWritten;
54
+ if (typeof consumed !== "number" || consumed <= 0) throw new InvalidPackError("Failed to determine compressed stream length");
55
+ return [Buffer.from(result.buffer), consumed];
56
+ } catch (err) {
57
+ throw new InvalidPackError(`Failed to decompress data at offset ${offset}: ${String(err)}`);
58
+ }
59
+ }
60
+ //#endregion
61
+ export { parsePackHeader, readCompressedData };
@@ -0,0 +1,81 @@
1
+ import { SHA1 } from "../core/types.mjs";
2
+ import { PackObject, packObjectToRaw } from "./pack-reader-types.mjs";
3
+
4
+ //#region src/pack/pack-reader.d.ts
5
+ /**
6
+ * 创建 Packfile 读取器
7
+ *
8
+ * @param data - 完整的 packfile 数据
9
+ * @returns Packfile 读取器实例
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const reader = createPackReader(packData);
14
+ * console.log(`Packfile 包含 ${reader.objectCount} 个对象`);
15
+ *
16
+ * // 遍历所有对象
17
+ * for (const obj of reader.objects()) {
18
+ * console.log(`${obj.type} ${obj.hash}`);
19
+ * }
20
+ * ```
21
+ */
22
+ declare function createPackReader(data: Buffer): PackReader;
23
+ /**
24
+ * Packfile 读取器类
25
+ *
26
+ * 使用惰性解析策略:对象在首次被访问时才解析。
27
+ * - getByHash() / getByOffset() / has() 只解析到找到目标为止
28
+ * - objects() / listHashes() 解析全部对象
29
+ */
30
+ declare class PackReader {
31
+ private readonly data;
32
+ private readonly _objectCount;
33
+ private readonly objectsByOffset;
34
+ private readonly objectsByHash;
35
+ private parseOffset;
36
+ private parsedCount;
37
+ private fullyParsed;
38
+ constructor(data: Buffer);
39
+ /** 对象数量 */
40
+ get objectCount(): number;
41
+ /**
42
+ * 解析下一个对象
43
+ */
44
+ private parseNext;
45
+ /**
46
+ * 按需解析直到条件满足或解析全部
47
+ */
48
+ private parseUntil;
49
+ /**
50
+ * 解析所有剩余对象
51
+ */
52
+ private parseAllRemaining;
53
+ /**
54
+ * 遍历所有对象
55
+ *
56
+ * 按 offset 顺序解析并迭代所有对象。
57
+ */
58
+ objects(): Generator<PackObject>;
59
+ /**
60
+ * 根据哈希获取对象(惰性解析)
61
+ *
62
+ * 只解析到找到目标对象为止,不解析全部。
63
+ */
64
+ getByHash(hash: SHA1): PackObject | undefined;
65
+ /**
66
+ * 根据偏移量获取对象(惰性解析)
67
+ *
68
+ * 只解析到目标偏移位置为止。
69
+ */
70
+ getByOffset(offset: number): PackObject | undefined;
71
+ /**
72
+ * 检查对象是否存在(惰性查找)
73
+ */
74
+ has(hash: SHA1): boolean;
75
+ /**
76
+ * 获取所有对象的哈希列表(解析全部)
77
+ */
78
+ listHashes(): SHA1[];
79
+ }
80
+ //#endregion
81
+ export { createPackReader };