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,32 @@
|
|
|
1
|
+
import { RefStore } from "../../../core/types/refs.mjs";
|
|
2
|
+
import { ObjectDatabase } from "../../../core/types/odb.mjs";
|
|
3
|
+
import { PushOptions, PushResult, ReceivePackTransport, RefAdvertisement } from "../../protocol/types.mjs";
|
|
4
|
+
import { PushError } from "./push-error.mjs";
|
|
5
|
+
import { PushRefItem, determinePushRefs } from "./push-ref-plan.mjs";
|
|
6
|
+
import { checkFastForward } from "./push-policy.mjs";
|
|
7
|
+
|
|
8
|
+
//#region src/transport/client/receive-pack/push.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* 执行 push 操作
|
|
11
|
+
*
|
|
12
|
+
* 将本地对象推送到远程 Git 仓库。调用方需自行创建 ReceivePackTransport
|
|
13
|
+
* 并获取 advertisement。
|
|
14
|
+
*
|
|
15
|
+
* @param store - 本地对象存储
|
|
16
|
+
* @param refs - 本地引用存储
|
|
17
|
+
* @param transport - receive-pack 传输接口
|
|
18
|
+
* @param advertisement - 服务端 receive-pack 广告
|
|
19
|
+
* @param options - 可选配置(refSpecs、force、shallowBoundaries)
|
|
20
|
+
* @returns push 操作结果
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const transport = createReceivePackHttpClient("https://github.com/user/repo");
|
|
25
|
+
* const adv = await transport.advertise();
|
|
26
|
+
* const result = await push(store, refs, transport, adv, { refSpecs: [...] });
|
|
27
|
+
* console.log(`Pushed ${result.objectCount} objects`);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
declare function push(store: ObjectDatabase, refs: RefStore, transport: ReceivePackTransport, advertisement: RefAdvertisement, options?: PushOptions): Promise<PushResult>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { push };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getLocalRefs, remoteRefsToMap } from "../../protocol/ref-collection.mjs";
|
|
2
|
+
import { PushError } from "./push-error.mjs";
|
|
3
|
+
import { createPackWriter } from "../../../pack/pack-writer.mjs";
|
|
4
|
+
import { computeObjectsToSend, mergePushBoundaries } from "./push-pack-plan.mjs";
|
|
5
|
+
import { checkFastForward } from "./push-policy.mjs";
|
|
6
|
+
import { determinePushRefs } from "./push-ref-plan.mjs";
|
|
7
|
+
import { processPushReport } from "./push-report.mjs";
|
|
8
|
+
import { buildPushCommands, buildPushRequestBody, resolvePushParsedSpecs, validatePushCapabilities } from "./push-request-plan.mjs";
|
|
9
|
+
import { ReceivePackResultError } from "./result.mjs";
|
|
10
|
+
import { decodeReceivePackResponse } from "./response.mjs";
|
|
11
|
+
//#region src/transport/client/receive-pack/push.ts
|
|
12
|
+
/**
|
|
13
|
+
* 高层 push 编排
|
|
14
|
+
*
|
|
15
|
+
* 编排完整的 Smart HTTP push 流程:
|
|
16
|
+
* 1. 按 refspec 确定要推送的本地引用与远程目标
|
|
17
|
+
* 2. 收集需要发送的对象
|
|
18
|
+
* 3. 构建 packfile
|
|
19
|
+
* 4. 构造 receive-pack 请求并发送
|
|
20
|
+
* 5. 解析 report-status 响应
|
|
21
|
+
*
|
|
22
|
+
* push() 不再自行创建传输层或获取 advertisement ——
|
|
23
|
+
* 这两种依赖由调用方通过 ReceivePackTransport + RefAdvertisement 显式传入。
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { push } from "./transport/push.ts";
|
|
28
|
+
*
|
|
29
|
+
* const transport = createReceivePackHttpClient("https://github.com/user/repo");
|
|
30
|
+
* const adv = await transport.advertise();
|
|
31
|
+
* const result = await push(store, refs, transport, adv, { refSpecs: [...] });
|
|
32
|
+
* console.log(`Pushed ${result.objectCount} objects`);
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* 执行 push 操作
|
|
37
|
+
*
|
|
38
|
+
* 将本地对象推送到远程 Git 仓库。调用方需自行创建 ReceivePackTransport
|
|
39
|
+
* 并获取 advertisement。
|
|
40
|
+
*
|
|
41
|
+
* @param store - 本地对象存储
|
|
42
|
+
* @param refs - 本地引用存储
|
|
43
|
+
* @param transport - receive-pack 传输接口
|
|
44
|
+
* @param advertisement - 服务端 receive-pack 广告
|
|
45
|
+
* @param options - 可选配置(refSpecs、force、shallowBoundaries)
|
|
46
|
+
* @returns push 操作结果
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const transport = createReceivePackHttpClient("https://github.com/user/repo");
|
|
51
|
+
* const adv = await transport.advertise();
|
|
52
|
+
* const result = await push(store, refs, transport, adv, { refSpecs: [...] });
|
|
53
|
+
* console.log(`Pushed ${result.objectCount} objects`);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
async function push(store, refs, transport, advertisement, options) {
|
|
57
|
+
const effectiveSpecs = resolvePushParsedSpecs(refs, options);
|
|
58
|
+
const shallowSet = options?.shallowBoundaries ? new Set(options.shallowBoundaries) : void 0;
|
|
59
|
+
const localRefs = getLocalRefs(refs);
|
|
60
|
+
const remoteRefs = remoteRefsToMap(advertisement.refs);
|
|
61
|
+
const pushRefs = determinePushRefs(localRefs, remoteRefs, effectiveSpecs);
|
|
62
|
+
if (pushRefs.length === 0) return {
|
|
63
|
+
refUpdates: [],
|
|
64
|
+
objectCount: 0,
|
|
65
|
+
progress: []
|
|
66
|
+
};
|
|
67
|
+
const pushBoundaries = mergePushBoundaries(shallowSet, pushRefs);
|
|
68
|
+
checkFastForward(store, pushRefs, shallowSet);
|
|
69
|
+
const objectsToSend = computeObjectsToSend(store, pushRefs, remoteRefs, pushBoundaries);
|
|
70
|
+
const packWriter = createPackWriter();
|
|
71
|
+
for (const hash of objectsToSend) {
|
|
72
|
+
const raw = store.read(hash);
|
|
73
|
+
packWriter.addRaw(raw);
|
|
74
|
+
}
|
|
75
|
+
const packfile = packWriter.build();
|
|
76
|
+
const caps = validatePushCapabilities(advertisement, pushRefs);
|
|
77
|
+
const commands = buildPushCommands(pushRefs);
|
|
78
|
+
const body = buildPushRequestBody(commands, packfile, caps);
|
|
79
|
+
let progress;
|
|
80
|
+
let refUpdates;
|
|
81
|
+
try {
|
|
82
|
+
const decoded = decodeReceivePackResponse(await transport.request(body));
|
|
83
|
+
progress = decoded.progress;
|
|
84
|
+
refUpdates = decoded.refUpdates;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (err instanceof ReceivePackResultError) throw new PushError(`Remote server rejected the push: ${err.message}`);
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
const report = processPushReport(commands, refUpdates, pushRefs, progress);
|
|
90
|
+
return {
|
|
91
|
+
refUpdates: report.refUpdates,
|
|
92
|
+
objectCount: packWriter.objectCount,
|
|
93
|
+
progress: report.progress
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { push };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { SHA1 } from "../../../core/types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/transport/client/receive-pack/request.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Receive-pack 引用更新命令
|
|
6
|
+
*
|
|
7
|
+
* 表示一条 push 命令:将远程的 refName 从 oldHash 更新为 newHash。
|
|
8
|
+
*
|
|
9
|
+
* - oldHash 为 000...0 表示新建引用
|
|
10
|
+
* - newHash 为 000...0 表示删除引用
|
|
11
|
+
*/
|
|
12
|
+
interface ReceivePackCommand {
|
|
13
|
+
/** 引用当前指向的哈希(服务端原有值) */
|
|
14
|
+
oldHash: SHA1;
|
|
15
|
+
/** 要更新到的目标哈希 */
|
|
16
|
+
newHash: SHA1;
|
|
17
|
+
/** 引用完整名称,如 "refs/heads/main" */
|
|
18
|
+
refName: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 构建 receive-pack 请求 body
|
|
22
|
+
*
|
|
23
|
+
* @param commands - 引用更新命令列表(至少一个)
|
|
24
|
+
* @param packfile - 包含新对象的 packfile 数据(push 时必需,delete 时可传空 Buffer)
|
|
25
|
+
* @param capabilities - 能力列表(如 ["report-status", "side-band-64k", "ofs-delta"])
|
|
26
|
+
* @returns pkt-line 编码的请求 body Buffer
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const body = buildReceivePackRequest(
|
|
31
|
+
* [{ oldHash: sha1("..."), newHash: sha1("..."), refName: "refs/heads/main" }],
|
|
32
|
+
* packfile,
|
|
33
|
+
* ["report-status", "side-band-64k", "ofs-delta"],
|
|
34
|
+
* );
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function buildReceivePackRequest(commands: ReceivePackCommand[], packfile: Buffer, capabilities: string[]): Buffer;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { ReceivePackCommand, buildReceivePackRequest };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { encodeFlushPkt, encodePktLine } from "../../protocol/pkt-line.mjs";
|
|
2
|
+
//#region src/transport/client/receive-pack/request.ts
|
|
3
|
+
/**
|
|
4
|
+
* 请求生成(receive-pack)
|
|
5
|
+
*
|
|
6
|
+
* 构造 Git receive-pack 请求 body。
|
|
7
|
+
*
|
|
8
|
+
* 协议 v1 的请求格式:
|
|
9
|
+
* <old-hash> <new-hash> <ref-name>\0<capabilities>\n (首行带 capabilities)
|
|
10
|
+
* <old-hash> <new-hash> <ref-name>\n (后续命令)
|
|
11
|
+
* 0000 (flush)
|
|
12
|
+
* <packfile data> (包数据,不经过 pkt-line 编码)
|
|
13
|
+
*
|
|
14
|
+
* @see https://git-scm.com/docs/git-receive-pack#_request
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* 构建 receive-pack 请求 body
|
|
18
|
+
*
|
|
19
|
+
* @param commands - 引用更新命令列表(至少一个)
|
|
20
|
+
* @param packfile - 包含新对象的 packfile 数据(push 时必需,delete 时可传空 Buffer)
|
|
21
|
+
* @param capabilities - 能力列表(如 ["report-status", "side-band-64k", "ofs-delta"])
|
|
22
|
+
* @returns pkt-line 编码的请求 body Buffer
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const body = buildReceivePackRequest(
|
|
27
|
+
* [{ oldHash: sha1("..."), newHash: sha1("..."), refName: "refs/heads/main" }],
|
|
28
|
+
* packfile,
|
|
29
|
+
* ["report-status", "side-band-64k", "ofs-delta"],
|
|
30
|
+
* );
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
function buildReceivePackRequest(commands, packfile, capabilities) {
|
|
34
|
+
if (commands.length === 0) throw new Error("At least one command is required");
|
|
35
|
+
const chunks = [];
|
|
36
|
+
for (let i = 0; i < commands.length; i++) {
|
|
37
|
+
const cmd = commands[i];
|
|
38
|
+
if (i === 0 && capabilities.length > 0) chunks.push(encodePktLine(`${cmd.oldHash} ${cmd.newHash} ${cmd.refName}\0${capabilities.join(" ")}\n`));
|
|
39
|
+
else chunks.push(encodePktLine(`${cmd.oldHash} ${cmd.newHash} ${cmd.refName}\n`));
|
|
40
|
+
}
|
|
41
|
+
chunks.push(encodeFlushPkt());
|
|
42
|
+
if (packfile.length > 0) chunks.push(packfile);
|
|
43
|
+
return Buffer.concat(chunks);
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { buildReceivePackRequest };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { GitError } from "../../../core/errors.mjs";
|
|
2
|
+
import { PushRefUpdate } from "../../protocol/types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/transport/client/receive-pack/response.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* receive-pack 响应解码错误
|
|
7
|
+
*/
|
|
8
|
+
declare class ReceivePackResponseError extends GitError {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 解码后的 receive-pack 响应
|
|
13
|
+
*/
|
|
14
|
+
interface DecodedReceivePackResponse {
|
|
15
|
+
readonly data: Buffer;
|
|
16
|
+
readonly refUpdates: PushRefUpdate[];
|
|
17
|
+
readonly progress: string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 解码 receive-pack RPC 原始响应
|
|
21
|
+
*
|
|
22
|
+
* @param data - transport.request() 返回的原始 body
|
|
23
|
+
*/
|
|
24
|
+
declare function decodeReceivePackResponse(data: Buffer): DecodedReceivePackResponse;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { ReceivePackResponseError, decodeReceivePackResponse };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { GitError } from "../../../core/errors.mjs";
|
|
2
|
+
import { parsePktLines } from "../../protocol/pkt-line.mjs";
|
|
3
|
+
import { parseReceivePackResult } from "./result.mjs";
|
|
4
|
+
//#region src/transport/client/receive-pack/response.ts
|
|
5
|
+
/**
|
|
6
|
+
* receive-pack 响应协议解码
|
|
7
|
+
*
|
|
8
|
+
* 从 transport 返回的原始 HTTP body 中解析 side-band 与 report-status。
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* receive-pack 响应解码错误
|
|
12
|
+
*/
|
|
13
|
+
var ReceivePackResponseError = class extends GitError {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(`receive-pack response: ${message}`);
|
|
16
|
+
this.name = "ReceivePackResponseError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 解码 receive-pack RPC 原始响应
|
|
21
|
+
*
|
|
22
|
+
* @param data - transport.request() 返回的原始 body
|
|
23
|
+
*/
|
|
24
|
+
function decodeReceivePackResponse(data) {
|
|
25
|
+
let progress = [];
|
|
26
|
+
let refUpdates = [];
|
|
27
|
+
const pktLines = parsePktLines(data);
|
|
28
|
+
if (pktLines.length > 0 && pktLines[0].type === "data") {
|
|
29
|
+
const firstPayload = pktLines[0].payload;
|
|
30
|
+
if (firstPayload.length > 0 && (firstPayload[0] === 1 || firstPayload[0] === 2 || firstPayload[0] === 3)) {
|
|
31
|
+
const reportStatusChunks = [];
|
|
32
|
+
for (const line of pktLines) {
|
|
33
|
+
if (line.type !== "data") continue;
|
|
34
|
+
const payload = line.payload;
|
|
35
|
+
if (payload.length < 2) continue;
|
|
36
|
+
const channel = payload[0];
|
|
37
|
+
const frameData = payload.subarray(1);
|
|
38
|
+
if (channel === 1) reportStatusChunks.push(frameData);
|
|
39
|
+
else if (channel === 2) progress.push(frameData.toString("utf-8").trimEnd());
|
|
40
|
+
else if (channel === 3) throw new ReceivePackResponseError(`Server reported error: ${frameData.toString("utf-8").trimEnd()}`);
|
|
41
|
+
}
|
|
42
|
+
if (reportStatusChunks.length > 0) refUpdates = parseReceivePackResult(Buffer.concat(reportStatusChunks));
|
|
43
|
+
} else refUpdates = parseReceivePackResult(data);
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
data,
|
|
47
|
+
refUpdates,
|
|
48
|
+
progress
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { ReceivePackResponseError, decodeReceivePackResponse };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { GitError } from "../../../core/errors.mjs";
|
|
2
|
+
import { PushRefUpdate } from "../../protocol/types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/transport/client/receive-pack/result.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Receive-pack 结果解析错误
|
|
7
|
+
*
|
|
8
|
+
* 当 report-status 数据格式不符合 Git 协议规范时抛出。
|
|
9
|
+
*/
|
|
10
|
+
declare class ReceivePackResultError extends GitError {
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 从 receive-pack 响应中解析 report-status
|
|
15
|
+
*
|
|
16
|
+
* 输入数据应为已经过 side-band 解复用后的纯 pkt-line report-status 数据。
|
|
17
|
+
* 如果响应是 side-band 编码的,需要先调用 `extractPackfile`/`extractProgress`
|
|
18
|
+
* 再使用本函数解析 report-status 部分(在 side-band 中 report-status 位于
|
|
19
|
+
* channel 1 的 packfile 数据末尾的 pkt-line 中)。
|
|
20
|
+
*
|
|
21
|
+
* @param data - 来自服务端的 report-status 响应体(pkt-line 编码)
|
|
22
|
+
* @returns 解析后的引用更新状态列表,每个条目标明成功/失败及错误消息
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const result = parseReceivePackResult(responseBody);
|
|
27
|
+
* for (const update of result) {
|
|
28
|
+
* console.log(`${update.refName}: ${update.success ? "OK" : "FAIL: " + update.error}`);
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
declare function parseReceivePackResult(data: Buffer): PushRefUpdate[];
|
|
33
|
+
//#endregion
|
|
34
|
+
export { ReceivePackResultError, parseReceivePackResult };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { GitError } from "../../../core/errors.mjs";
|
|
2
|
+
import { parsePktLines } from "../../protocol/pkt-line.mjs";
|
|
3
|
+
//#region src/transport/client/receive-pack/result.ts
|
|
4
|
+
/**
|
|
5
|
+
* 响应解析(receive-pack)
|
|
6
|
+
*
|
|
7
|
+
* 解析 `git-receive-pack` 返回的 report-status 响应。
|
|
8
|
+
*
|
|
9
|
+
* report-status 格式:
|
|
10
|
+
* <status-line>\n
|
|
11
|
+
* ...
|
|
12
|
+
* 0000
|
|
13
|
+
*
|
|
14
|
+
* 每条 status-line:
|
|
15
|
+
* ok <ref-name>
|
|
16
|
+
* ng <ref-name> <error-msg>
|
|
17
|
+
*
|
|
18
|
+
* @see https://git-scm.com/docs/pack-protocol#_report_status
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Receive-pack 结果解析错误
|
|
22
|
+
*
|
|
23
|
+
* 当 report-status 数据格式不符合 Git 协议规范时抛出。
|
|
24
|
+
*/
|
|
25
|
+
var ReceivePackResultError = class extends GitError {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(`receive-pack result error: ${message}`);
|
|
28
|
+
this.name = "ReceivePackResultError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/** report-status 中的 "ok" 前缀 */
|
|
32
|
+
const OK_PREFIX = "ok ";
|
|
33
|
+
/** report-status 中的 "ng" 前缀 */
|
|
34
|
+
const NG_PREFIX = "ng ";
|
|
35
|
+
/**
|
|
36
|
+
* 从 receive-pack 响应中解析 report-status
|
|
37
|
+
*
|
|
38
|
+
* 输入数据应为已经过 side-band 解复用后的纯 pkt-line report-status 数据。
|
|
39
|
+
* 如果响应是 side-band 编码的,需要先调用 `extractPackfile`/`extractProgress`
|
|
40
|
+
* 再使用本函数解析 report-status 部分(在 side-band 中 report-status 位于
|
|
41
|
+
* channel 1 的 packfile 数据末尾的 pkt-line 中)。
|
|
42
|
+
*
|
|
43
|
+
* @param data - 来自服务端的 report-status 响应体(pkt-line 编码)
|
|
44
|
+
* @returns 解析后的引用更新状态列表,每个条目标明成功/失败及错误消息
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const result = parseReceivePackResult(responseBody);
|
|
49
|
+
* for (const update of result) {
|
|
50
|
+
* console.log(`${update.refName}: ${update.success ? "OK" : "FAIL: " + update.error}`);
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
function parseReceivePackResult(data) {
|
|
55
|
+
const pktLines = parsePktLines(data);
|
|
56
|
+
const updates = [];
|
|
57
|
+
let seenUnpack = false;
|
|
58
|
+
for (const line of pktLines) {
|
|
59
|
+
if (line.type !== "data") continue;
|
|
60
|
+
const payload = line.payload.toString("utf-8").trimEnd();
|
|
61
|
+
if (payload.length === 0) continue;
|
|
62
|
+
if (!seenUnpack) {
|
|
63
|
+
if (payload.startsWith("unpack ")) {
|
|
64
|
+
seenUnpack = true;
|
|
65
|
+
const unpackResult = payload.slice(7);
|
|
66
|
+
if (unpackResult !== "ok") throw new ReceivePackResultError(`Server failed to unpack packfile: ${unpackResult}`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
throw new ReceivePackResultError(`Missing unpack status line: first status line is "${payload}", expected "unpack <result>"`);
|
|
70
|
+
}
|
|
71
|
+
if (payload.startsWith(OK_PREFIX)) {
|
|
72
|
+
const refName = payload.slice(3);
|
|
73
|
+
updates.push({
|
|
74
|
+
refName,
|
|
75
|
+
oldHash: null,
|
|
76
|
+
newHash: null,
|
|
77
|
+
success: true,
|
|
78
|
+
forced: false
|
|
79
|
+
});
|
|
80
|
+
} else if (payload.startsWith(NG_PREFIX)) {
|
|
81
|
+
const rest = payload.slice(3);
|
|
82
|
+
const spaceIndex = rest.indexOf(" ");
|
|
83
|
+
if (spaceIndex === -1) throw new ReceivePackResultError(`Invalid ng status line: "${payload}" (missing error message)`);
|
|
84
|
+
const refName = rest.substring(0, spaceIndex);
|
|
85
|
+
const errorMsg = rest.substring(spaceIndex + 1);
|
|
86
|
+
updates.push({
|
|
87
|
+
refName,
|
|
88
|
+
oldHash: null,
|
|
89
|
+
newHash: null,
|
|
90
|
+
success: false,
|
|
91
|
+
error: errorMsg,
|
|
92
|
+
forced: false
|
|
93
|
+
});
|
|
94
|
+
} else if (payload.startsWith("unpack ")) throw new ReceivePackResultError(`Unexpected duplicate unpack status line: "${payload}"`);
|
|
95
|
+
else throw new ReceivePackResultError(`Unexpected status line: "${payload}" (expected "ok " or "ng ")`);
|
|
96
|
+
}
|
|
97
|
+
return updates;
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
export { ReceivePackResultError, parseReceivePackResult };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { GitError } from "../../../core/errors.mjs";
|
|
2
|
+
import { V2CapabilityAdvertisement } from "./types.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/transport/client/upload-pack/capability-advertisement.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* v2 能力广告解析错误
|
|
7
|
+
*/
|
|
8
|
+
declare class V2CapabilityError extends GitError {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 解析 v2 能力广告响应
|
|
13
|
+
*
|
|
14
|
+
* @param data - 服务端返回的原始响应数据
|
|
15
|
+
* @returns 结构化的能力广告
|
|
16
|
+
* @throws {V2CapabilityError} 当格式不符合 v2 规范时
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const adv = parseV2CapabilityAdvertisement(response);
|
|
21
|
+
* console.log(adv.commands); // [{ name: "ls-refs", features: [] }, ...]
|
|
22
|
+
* console.log(adv.agent); // "nano-git/0.1"
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare function parseV2CapabilityAdvertisement(data: Buffer): V2CapabilityAdvertisement;
|
|
26
|
+
/**
|
|
27
|
+
* 检测 v2 能力广告中是否包含指定命令
|
|
28
|
+
*
|
|
29
|
+
* @param advertisement - v2 能力广告
|
|
30
|
+
* @param command - 命令名称
|
|
31
|
+
* @returns 是否支持
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* if (hasCommand(adv, "ls-refs")) {
|
|
36
|
+
* // 使用 ls-refs 获取 refs
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function hasCommand(advertisement: V2CapabilityAdvertisement, command: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* 获取命令的附加特性列表
|
|
43
|
+
*
|
|
44
|
+
* @param advertisement - v2 能力广告
|
|
45
|
+
* @param command - 命令名称
|
|
46
|
+
* @returns 特性列表,命令不存在时返回空数组
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const fetchFeatures = getCommandFeatures(adv, "fetch");
|
|
51
|
+
* // ["shallow", "ref-in-want"]
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare function getCommandFeatures(advertisement: V2CapabilityAdvertisement, command: string): string[];
|
|
55
|
+
//#endregion
|
|
56
|
+
export { V2CapabilityError, getCommandFeatures, hasCommand, parseV2CapabilityAdvertisement };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { GitError } from "../../../core/errors.mjs";
|
|
2
|
+
import { parsePktLines } from "../../protocol/pkt-line.mjs";
|
|
3
|
+
//#region src/transport/client/upload-pack/capability-advertisement.ts
|
|
4
|
+
/**
|
|
5
|
+
* v2 能力广告解析
|
|
6
|
+
*
|
|
7
|
+
* 解析 Git Wire 协议 v2 的 capability advertisement 响应。
|
|
8
|
+
*
|
|
9
|
+
* v2 能力广告格式:
|
|
10
|
+
* ```
|
|
11
|
+
* 000eversion 2\n
|
|
12
|
+
* ls-refs\n
|
|
13
|
+
* fetch=shallow ref-in-want\n
|
|
14
|
+
* push\n
|
|
15
|
+
* object-info\n
|
|
16
|
+
* agent=nano-git/0.1\n
|
|
17
|
+
* 0000
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @see https://git-scm.com/docs/protocol-v2#_capability_advertisement
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* v2 能力广告解析错误
|
|
24
|
+
*/
|
|
25
|
+
var V2CapabilityError = class extends GitError {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(`v2 capability error: ${message}`);
|
|
28
|
+
this.name = "V2CapabilityError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* 解析 v2 能力广告响应
|
|
33
|
+
*
|
|
34
|
+
* @param data - 服务端返回的原始响应数据
|
|
35
|
+
* @returns 结构化的能力广告
|
|
36
|
+
* @throws {V2CapabilityError} 当格式不符合 v2 规范时
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const adv = parseV2CapabilityAdvertisement(response);
|
|
41
|
+
* console.log(adv.commands); // [{ name: "ls-refs", features: [] }, ...]
|
|
42
|
+
* console.log(adv.agent); // "nano-git/0.1"
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function parseV2CapabilityAdvertisement(data) {
|
|
46
|
+
const lines = parsePktLines(data);
|
|
47
|
+
if (lines.length === 0) throw new V2CapabilityError("Empty capability advertisement");
|
|
48
|
+
const firstLine = lines[0];
|
|
49
|
+
if (!firstLine || firstLine.type !== "data") throw new V2CapabilityError(`Expected data line as first line, got ${firstLine?.type ?? "undefined"}`);
|
|
50
|
+
const versionStr = firstLine.payload.toString("utf-8").trim();
|
|
51
|
+
if (versionStr !== "version 2") throw new V2CapabilityError(`Expected "version 2", got "${versionStr}"`);
|
|
52
|
+
const capabilities = {};
|
|
53
|
+
const commands = [];
|
|
54
|
+
for (let i = 1; i < lines.length; i++) {
|
|
55
|
+
const line = lines[i];
|
|
56
|
+
if (!line || line.type !== "data") break;
|
|
57
|
+
const text = line.payload.toString("utf-8").trim();
|
|
58
|
+
if (text.length === 0) continue;
|
|
59
|
+
if ([
|
|
60
|
+
"ls-refs",
|
|
61
|
+
"fetch",
|
|
62
|
+
"object-info"
|
|
63
|
+
].some((cmd) => text.startsWith(cmd))) {
|
|
64
|
+
const eqIndex = text.indexOf("=");
|
|
65
|
+
if (eqIndex === -1) {
|
|
66
|
+
const name = text;
|
|
67
|
+
commands.push({
|
|
68
|
+
name,
|
|
69
|
+
features: []
|
|
70
|
+
});
|
|
71
|
+
capabilities[name] = true;
|
|
72
|
+
} else {
|
|
73
|
+
const name = text.substring(0, eqIndex);
|
|
74
|
+
const features = text.substring(eqIndex + 1).split(" ").filter((f) => f.length > 0);
|
|
75
|
+
commands.push({
|
|
76
|
+
name,
|
|
77
|
+
features
|
|
78
|
+
});
|
|
79
|
+
capabilities[name] = features.join(" ");
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
const eqIndex = text.indexOf("=");
|
|
83
|
+
if (eqIndex === -1) capabilities[text] = true;
|
|
84
|
+
else {
|
|
85
|
+
const key = text.substring(0, eqIndex);
|
|
86
|
+
capabilities[key] = text.substring(eqIndex + 1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
capabilities,
|
|
92
|
+
commands,
|
|
93
|
+
agent: typeof capabilities.agent === "string" ? capabilities.agent : void 0
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 检测 v2 能力广告中是否包含指定命令
|
|
98
|
+
*
|
|
99
|
+
* @param advertisement - v2 能力广告
|
|
100
|
+
* @param command - 命令名称
|
|
101
|
+
* @returns 是否支持
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```ts
|
|
105
|
+
* if (hasCommand(adv, "ls-refs")) {
|
|
106
|
+
* // 使用 ls-refs 获取 refs
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
function hasCommand(advertisement, command) {
|
|
111
|
+
return advertisement.commands.some((cmd) => cmd.name === command);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 获取命令的附加特性列表
|
|
115
|
+
*
|
|
116
|
+
* @param advertisement - v2 能力广告
|
|
117
|
+
* @param command - 命令名称
|
|
118
|
+
* @returns 特性列表,命令不存在时返回空数组
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const fetchFeatures = getCommandFeatures(adv, "fetch");
|
|
123
|
+
* // ["shallow", "ref-in-want"]
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
function getCommandFeatures(advertisement, command) {
|
|
127
|
+
return advertisement.commands.find((cmd) => cmd.name === command)?.features ?? [];
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { V2CapabilityError, getCommandFeatures, hasCommand, parseV2CapabilityAdvertisement };
|