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.
- package/README.md +407 -0
- package/dist/backend/file-backend.d.mts +26 -0
- package/dist/backend/file-backend.mjs +83 -0
- package/dist/backend/file.d.mts +2 -0
- package/dist/backend/file.mjs +2 -0
- package/dist/backend/index.d.mts +2 -0
- package/dist/backend/index.mjs +1 -0
- package/dist/backend/memory-backend.d.mts +25 -0
- package/dist/backend/memory-backend.mjs +33 -0
- package/dist/backend/memory.d.mts +2 -0
- package/dist/backend/memory.mjs +2 -0
- package/dist/backend/types.d.mts +90 -0
- package/dist/core/errors.d.mts +124 -0
- package/dist/core/errors.mjs +168 -0
- package/dist/core/hash-digest.d.mts +35 -0
- package/dist/core/hash-digest.mjs +50 -0
- package/dist/core/hash-file.d.mts +20 -0
- package/dist/core/hash-file.mjs +27 -0
- package/dist/core/hash-path.d.mts +17 -0
- package/dist/core/hash-path.mjs +35 -0
- package/dist/core/types/odb.d.mts +63 -0
- package/dist/core/types/refs.d.mts +140 -0
- package/dist/core/types/refs.mjs +19 -0
- package/dist/core/types/shallow.d.mts +52 -0
- package/dist/core/types.d.mts +154 -0
- package/dist/core/types.mjs +43 -0
- package/dist/errors.d.mts +2 -0
- package/dist/errors.mjs +2 -0
- package/dist/hash-file.d.mts +2 -0
- package/dist/hash-file.mjs +2 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.mjs +14 -0
- package/dist/objects/author.d.mts +25 -0
- package/dist/objects/author.mjs +45 -0
- package/dist/objects/blob.d.mts +15 -0
- package/dist/objects/blob.mjs +20 -0
- package/dist/objects/codec.d.mts +46 -0
- package/dist/objects/codec.mjs +84 -0
- package/dist/objects/commit.d.mts +26 -0
- package/dist/objects/commit.mjs +160 -0
- package/dist/objects/index.d.mts +8 -0
- package/dist/objects/index.mjs +8 -0
- package/dist/objects/raw.d.mts +85 -0
- package/dist/objects/raw.mjs +111 -0
- package/dist/objects/tag.d.mts +26 -0
- package/dist/objects/tag.mjs +148 -0
- package/dist/objects/tree.d.mts +22 -0
- package/dist/objects/tree.mjs +66 -0
- package/dist/odb/file-utils.mjs +136 -0
- package/dist/odb/file.d.mts +22 -0
- package/dist/odb/file.mjs +66 -0
- package/dist/odb/memory.d.mts +22 -0
- package/dist/odb/memory.mjs +54 -0
- package/dist/pack/composite-store.d.mts +76 -0
- package/dist/pack/composite-store.mjs +133 -0
- package/dist/pack/constants.mjs +48 -0
- package/dist/pack/crc32.mjs +38 -0
- package/dist/pack/delta-apply.d.mts +21 -0
- package/dist/pack/delta-apply.mjs +71 -0
- package/dist/pack/delta-create.d.mts +28 -0
- package/dist/pack/delta-create.mjs +151 -0
- package/dist/pack/index.d.mts +16 -0
- package/dist/pack/index.mjs +13 -0
- package/dist/pack/object-header.d.mts +36 -0
- package/dist/pack/object-header.mjs +72 -0
- package/dist/pack/ofs-delta-offset.d.mts +35 -0
- package/dist/pack/ofs-delta-offset.mjs +59 -0
- package/dist/pack/pack-builder-types.d.mts +19 -0
- package/dist/pack/pack-builder.d.mts +52 -0
- package/dist/pack/pack-builder.mjs +80 -0
- package/dist/pack/pack-encoding.mjs +76 -0
- package/dist/pack/pack-index-reader.d.mts +57 -0
- package/dist/pack/pack-index-reader.mjs +90 -0
- package/dist/pack/pack-index-types.d.mts +16 -0
- package/dist/pack/pack-index-writer.d.mts +45 -0
- package/dist/pack/pack-index-writer.mjs +106 -0
- package/dist/pack/pack-reader-resolver.mjs +104 -0
- package/dist/pack/pack-reader-types.d.mts +31 -0
- package/dist/pack/pack-reader-types.mjs +22 -0
- package/dist/pack/pack-reader-utils.mjs +61 -0
- package/dist/pack/pack-reader.d.mts +81 -0
- package/dist/pack/pack-reader.mjs +171 -0
- package/dist/pack/pack-store-loader.mjs +79 -0
- package/dist/pack/pack-store-types.d.mts +16 -0
- package/dist/pack/pack-store.d.mts +55 -0
- package/dist/pack/pack-store.mjs +114 -0
- package/dist/pack/pack-writer.mjs +96 -0
- package/dist/pack/varint.d.mts +34 -0
- package/dist/pack/varint.mjs +57 -0
- package/dist/refs/file.d.mts +6 -0
- package/dist/refs/file.mjs +222 -0
- package/dist/refs/fs-utils.mjs +30 -0
- package/dist/refs/memory.d.mts +14 -0
- package/dist/refs/memory.mjs +104 -0
- package/dist/refs/names.d.mts +57 -0
- package/dist/refs/names.mjs +80 -0
- package/dist/refs/resolve.d.mts +33 -0
- package/dist/refs/resolve.mjs +55 -0
- package/dist/refs/shallow/file.d.mts +17 -0
- package/dist/refs/shallow/file.mjs +61 -0
- package/dist/refs/shallow/memory.d.mts +18 -0
- package/dist/refs/shallow/memory.mjs +33 -0
- package/dist/repository/core.d.mts +3 -0
- package/dist/repository/core.mjs +2 -0
- package/dist/repository/create.d.mts +22 -0
- package/dist/repository/create.mjs +43 -0
- package/dist/repository/file.d.mts +43 -0
- package/dist/repository/file.mjs +82 -0
- package/dist/repository/import/import-glob.mjs +44 -0
- package/dist/repository/import/import-plan-builder.mjs +625 -0
- package/dist/repository/import/import-session-types.d.mts +280 -0
- package/dist/repository/import/import-session.mjs +96 -0
- package/dist/repository/import/import-view.mjs +133 -0
- package/dist/repository/memory.d.mts +17 -0
- package/dist/repository/memory.mjs +33 -0
- package/dist/repository/ops/fetch-operations.mjs +20 -0
- package/dist/repository/ops/fetch-types.d.mts +82 -0
- package/dist/repository/ops/fetch-url.mjs +82 -0
- package/dist/repository/ops/fs-object-operations.mjs +31 -0
- package/dist/repository/ops/maintenance-operations.mjs +62 -0
- package/dist/repository/ops/maintenance-types.d.mts +42 -0
- package/dist/repository/ops/object-operations.mjs +65 -0
- package/dist/repository/ops/object-types.d.mts +109 -0
- package/dist/repository/ops/push-operations.mjs +16 -0
- package/dist/repository/ops/push-resolution.mjs +17 -0
- package/dist/repository/ops/push-types.d.mts +72 -0
- package/dist/repository/ops/push-url.mjs +45 -0
- package/dist/repository/ops/reachability.mjs +60 -0
- package/dist/repository/ops/ref-operations.mjs +86 -0
- package/dist/repository/ops/ref-types.d.mts +70 -0
- package/dist/repository/tree/tree-patch.d.mts +64 -0
- package/dist/repository/tree/tree-patch.mjs +268 -0
- package/dist/repository/tree/tree-walk.d.mts +46 -0
- package/dist/repository/tree/tree-walk.mjs +65 -0
- package/dist/repository/tree/tree-writer.mjs +68 -0
- package/dist/repository/types.d.mts +36 -0
- package/dist/sha1.d.mts +4 -0
- package/dist/sha1.mjs +4 -0
- package/dist/transport/client/receive-pack/http.d.mts +33 -0
- package/dist/transport/client/receive-pack/http.mjs +99 -0
- package/dist/transport/client/receive-pack/push-error.d.mts +23 -0
- package/dist/transport/client/receive-pack/push-error.mjs +32 -0
- package/dist/transport/client/receive-pack/push-pack-plan.d.mts +28 -0
- package/dist/transport/client/receive-pack/push-pack-plan.mjs +60 -0
- package/dist/transport/client/receive-pack/push-policy.d.mts +19 -0
- package/dist/transport/client/receive-pack/push-policy.mjs +64 -0
- package/dist/transport/client/receive-pack/push-ref-plan.d.mts +45 -0
- package/dist/transport/client/receive-pack/push-ref-plan.mjs +108 -0
- package/dist/transport/client/receive-pack/push-report.d.mts +28 -0
- package/dist/transport/client/receive-pack/push-report.mjs +84 -0
- package/dist/transport/client/receive-pack/push-request-plan.mjs +52 -0
- package/dist/transport/client/receive-pack/push.d.mts +32 -0
- package/dist/transport/client/receive-pack/push.mjs +97 -0
- package/dist/transport/client/receive-pack/request.d.mts +39 -0
- package/dist/transport/client/receive-pack/request.mjs +46 -0
- package/dist/transport/client/receive-pack/response.d.mts +26 -0
- package/dist/transport/client/receive-pack/response.mjs +52 -0
- package/dist/transport/client/receive-pack/result.d.mts +34 -0
- package/dist/transport/client/receive-pack/result.mjs +100 -0
- package/dist/transport/client/upload-pack/capability-advertisement.d.mts +56 -0
- package/dist/transport/client/upload-pack/capability-advertisement.mjs +130 -0
- package/dist/transport/client/upload-pack/fetch.d.mts +109 -0
- package/dist/transport/client/upload-pack/fetch.mjs +392 -0
- package/dist/transport/client/upload-pack/http.d.mts +29 -0
- package/dist/transport/client/upload-pack/http.mjs +79 -0
- package/dist/transport/client/upload-pack/ls-refs.d.mts +75 -0
- package/dist/transport/client/upload-pack/ls-refs.mjs +150 -0
- package/dist/transport/client/upload-pack/object-info.d.mts +65 -0
- package/dist/transport/client/upload-pack/object-info.mjs +111 -0
- package/dist/transport/client/upload-pack/types.d.mts +153 -0
- package/dist/transport/http/index.d.mts +3 -0
- package/dist/transport/http/index.mjs +2 -0
- package/dist/transport/http/smart-http.d.mts +46 -0
- package/dist/transport/http/smart-http.mjs +176 -0
- package/dist/transport/http/types.d.mts +27 -0
- package/dist/transport/index.d.mts +9 -0
- package/dist/transport/index.mjs +8 -0
- package/dist/transport/protocol/object-graph.d.mts +63 -0
- package/dist/transport/protocol/object-graph.mjs +149 -0
- package/dist/transport/protocol/pkt-line.d.mts +109 -0
- package/dist/transport/protocol/pkt-line.mjs +195 -0
- package/dist/transport/protocol/ref-advertisement.mjs +185 -0
- package/dist/transport/protocol/ref-collection.d.mts +38 -0
- package/dist/transport/protocol/ref-collection.mjs +63 -0
- package/dist/transport/protocol/ref-match.d.mts +39 -0
- package/dist/transport/protocol/ref-match.mjs +42 -0
- package/dist/transport/protocol/refspec.d.mts +44 -0
- package/dist/transport/protocol/refspec.mjs +79 -0
- package/dist/transport/protocol/side-band.d.mts +65 -0
- package/dist/transport/protocol/side-band.mjs +142 -0
- package/dist/transport/protocol/transport-capabilities.mjs +46 -0
- package/dist/transport/protocol/types.d.mts +148 -0
- package/dist/transport/protocol/update-refs.d.mts +68 -0
- package/dist/transport/protocol/update-refs.mjs +126 -0
- package/dist/transport/receive-pack.d.mts +11 -0
- package/dist/transport/receive-pack.mjs +11 -0
- package/dist/transport/server/receive-pack/advertise.d.mts +28 -0
- package/dist/transport/server/receive-pack/advertise.mjs +88 -0
- package/dist/transport/server/receive-pack/handler.d.mts +30 -0
- package/dist/transport/server/receive-pack/handler.mjs +156 -0
- package/dist/transport/server/receive-pack/index.d.mts +6 -0
- package/dist/transport/server/receive-pack/index.mjs +6 -0
- package/dist/transport/server/receive-pack/parse.d.mts +17 -0
- package/dist/transport/server/receive-pack/parse.mjs +80 -0
- package/dist/transport/server/receive-pack/report-status.mjs +64 -0
- package/dist/transport/server/receive-pack/service.d.mts +41 -0
- package/dist/transport/server/receive-pack/service.mjs +39 -0
- package/dist/transport/server/receive-pack/types.d.mts +56 -0
- package/dist/transport/server/receive-pack/types.mjs +25 -0
- package/dist/transport/server/receive-pack/unpack.mjs +119 -0
- package/dist/transport/server/upload-pack/advertise.d.mts +20 -0
- package/dist/transport/server/upload-pack/advertise.mjs +30 -0
- package/dist/transport/server/upload-pack/command.d.mts +43 -0
- package/dist/transport/server/upload-pack/command.mjs +56 -0
- package/dist/transport/server/upload-pack/fetch.d.mts +43 -0
- package/dist/transport/server/upload-pack/fetch.mjs +217 -0
- package/dist/transport/server/upload-pack/index.d.mts +7 -0
- package/dist/transport/server/upload-pack/index.mjs +7 -0
- package/dist/transport/server/upload-pack/ls-refs.d.mts +38 -0
- package/dist/transport/server/upload-pack/ls-refs.mjs +113 -0
- package/dist/transport/server/upload-pack/service.d.mts +40 -0
- package/dist/transport/server/upload-pack/service.mjs +51 -0
- package/dist/transport/server/upload-pack/types.d.mts +11 -0
- package/dist/transport/server/upload-pack/types.mjs +21 -0
- package/dist/transport/upload-pack.d.mts +7 -0
- package/dist/transport/upload-pack.mjs +6 -0
- 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 };
|