gitx.do 0.1.0 → 0.1.2
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 +40 -353
- package/dist/do/logger.d.ts +50 -0
- package/dist/do/logger.d.ts.map +1 -0
- package/dist/do/logger.js +122 -0
- package/dist/do/logger.js.map +1 -0
- package/dist/{durable-object → do}/schema.d.ts +3 -3
- package/dist/do/schema.d.ts.map +1 -0
- package/dist/{durable-object → do}/schema.js +4 -3
- package/dist/do/schema.js.map +1 -0
- package/dist/do/types.d.ts +267 -0
- package/dist/do/types.d.ts.map +1 -0
- package/dist/do/types.js +62 -0
- package/dist/do/types.js.map +1 -0
- package/dist/index.d.ts +15 -415
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -483
- package/dist/index.js.map +1 -1
- package/package.json +13 -21
- package/dist/cli/commands/add.d.ts +0 -174
- package/dist/cli/commands/add.d.ts.map +0 -1
- package/dist/cli/commands/add.js +0 -131
- package/dist/cli/commands/add.js.map +0 -1
- package/dist/cli/commands/blame.d.ts +0 -259
- package/dist/cli/commands/blame.d.ts.map +0 -1
- package/dist/cli/commands/blame.js +0 -609
- package/dist/cli/commands/blame.js.map +0 -1
- package/dist/cli/commands/branch.d.ts +0 -249
- package/dist/cli/commands/branch.d.ts.map +0 -1
- package/dist/cli/commands/branch.js +0 -693
- package/dist/cli/commands/branch.js.map +0 -1
- package/dist/cli/commands/commit.d.ts +0 -182
- package/dist/cli/commands/commit.d.ts.map +0 -1
- package/dist/cli/commands/commit.js +0 -437
- package/dist/cli/commands/commit.js.map +0 -1
- package/dist/cli/commands/diff.d.ts +0 -464
- package/dist/cli/commands/diff.d.ts.map +0 -1
- package/dist/cli/commands/diff.js +0 -958
- package/dist/cli/commands/diff.js.map +0 -1
- package/dist/cli/commands/log.d.ts +0 -239
- package/dist/cli/commands/log.d.ts.map +0 -1
- package/dist/cli/commands/log.js +0 -535
- package/dist/cli/commands/log.js.map +0 -1
- package/dist/cli/commands/merge.d.ts +0 -106
- package/dist/cli/commands/merge.d.ts.map +0 -1
- package/dist/cli/commands/merge.js +0 -55
- package/dist/cli/commands/merge.js.map +0 -1
- package/dist/cli/commands/review.d.ts +0 -457
- package/dist/cli/commands/review.d.ts.map +0 -1
- package/dist/cli/commands/review.js +0 -533
- package/dist/cli/commands/review.js.map +0 -1
- package/dist/cli/commands/status.d.ts +0 -269
- package/dist/cli/commands/status.d.ts.map +0 -1
- package/dist/cli/commands/status.js +0 -493
- package/dist/cli/commands/status.js.map +0 -1
- package/dist/cli/commands/web.d.ts +0 -199
- package/dist/cli/commands/web.d.ts.map +0 -1
- package/dist/cli/commands/web.js +0 -696
- package/dist/cli/commands/web.js.map +0 -1
- package/dist/cli/fs-adapter.d.ts +0 -656
- package/dist/cli/fs-adapter.d.ts.map +0 -1
- package/dist/cli/fs-adapter.js +0 -1179
- package/dist/cli/fs-adapter.js.map +0 -1
- package/dist/cli/fsx-cli-adapter.d.ts +0 -359
- package/dist/cli/fsx-cli-adapter.d.ts.map +0 -1
- package/dist/cli/fsx-cli-adapter.js +0 -619
- package/dist/cli/fsx-cli-adapter.js.map +0 -1
- package/dist/cli/index.d.ts +0 -387
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -523
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/ui/components/DiffView.d.ts +0 -7
- package/dist/cli/ui/components/DiffView.d.ts.map +0 -1
- package/dist/cli/ui/components/DiffView.js +0 -11
- package/dist/cli/ui/components/DiffView.js.map +0 -1
- package/dist/cli/ui/components/ErrorDisplay.d.ts +0 -6
- package/dist/cli/ui/components/ErrorDisplay.d.ts.map +0 -1
- package/dist/cli/ui/components/ErrorDisplay.js +0 -11
- package/dist/cli/ui/components/ErrorDisplay.js.map +0 -1
- package/dist/cli/ui/components/FuzzySearch.d.ts +0 -9
- package/dist/cli/ui/components/FuzzySearch.d.ts.map +0 -1
- package/dist/cli/ui/components/FuzzySearch.js +0 -12
- package/dist/cli/ui/components/FuzzySearch.js.map +0 -1
- package/dist/cli/ui/components/LoadingSpinner.d.ts +0 -6
- package/dist/cli/ui/components/LoadingSpinner.d.ts.map +0 -1
- package/dist/cli/ui/components/LoadingSpinner.js +0 -10
- package/dist/cli/ui/components/LoadingSpinner.js.map +0 -1
- package/dist/cli/ui/components/NavigationList.d.ts +0 -9
- package/dist/cli/ui/components/NavigationList.d.ts.map +0 -1
- package/dist/cli/ui/components/NavigationList.js +0 -11
- package/dist/cli/ui/components/NavigationList.js.map +0 -1
- package/dist/cli/ui/components/ScrollableContent.d.ts +0 -8
- package/dist/cli/ui/components/ScrollableContent.d.ts.map +0 -1
- package/dist/cli/ui/components/ScrollableContent.js +0 -11
- package/dist/cli/ui/components/ScrollableContent.js.map +0 -1
- package/dist/cli/ui/components/index.d.ts +0 -7
- package/dist/cli/ui/components/index.d.ts.map +0 -1
- package/dist/cli/ui/components/index.js +0 -9
- package/dist/cli/ui/components/index.js.map +0 -1
- package/dist/cli/ui/terminal-ui.d.ts +0 -52
- package/dist/cli/ui/terminal-ui.d.ts.map +0 -1
- package/dist/cli/ui/terminal-ui.js +0 -121
- package/dist/cli/ui/terminal-ui.js.map +0 -1
- package/dist/do/BashModule.d.ts +0 -871
- package/dist/do/BashModule.d.ts.map +0 -1
- package/dist/do/BashModule.js +0 -1143
- package/dist/do/BashModule.js.map +0 -1
- package/dist/do/FsModule.d.ts +0 -601
- package/dist/do/FsModule.d.ts.map +0 -1
- package/dist/do/FsModule.js +0 -1120
- package/dist/do/FsModule.js.map +0 -1
- package/dist/do/GitModule.d.ts +0 -635
- package/dist/do/GitModule.d.ts.map +0 -1
- package/dist/do/GitModule.js +0 -781
- package/dist/do/GitModule.js.map +0 -1
- package/dist/do/GitRepoDO.d.ts +0 -281
- package/dist/do/GitRepoDO.d.ts.map +0 -1
- package/dist/do/GitRepoDO.js +0 -479
- package/dist/do/GitRepoDO.js.map +0 -1
- package/dist/do/bash-ast.d.ts +0 -246
- package/dist/do/bash-ast.d.ts.map +0 -1
- package/dist/do/bash-ast.js +0 -888
- package/dist/do/bash-ast.js.map +0 -1
- package/dist/do/container-executor.d.ts +0 -491
- package/dist/do/container-executor.d.ts.map +0 -1
- package/dist/do/container-executor.js +0 -730
- package/dist/do/container-executor.js.map +0 -1
- package/dist/do/index.d.ts +0 -53
- package/dist/do/index.d.ts.map +0 -1
- package/dist/do/index.js +0 -91
- package/dist/do/index.js.map +0 -1
- package/dist/do/tiered-storage.d.ts +0 -403
- package/dist/do/tiered-storage.d.ts.map +0 -1
- package/dist/do/tiered-storage.js +0 -689
- package/dist/do/tiered-storage.js.map +0 -1
- package/dist/do/withBash.d.ts +0 -231
- package/dist/do/withBash.d.ts.map +0 -1
- package/dist/do/withBash.js +0 -244
- package/dist/do/withBash.js.map +0 -1
- package/dist/do/withFs.d.ts +0 -237
- package/dist/do/withFs.d.ts.map +0 -1
- package/dist/do/withFs.js +0 -387
- package/dist/do/withFs.js.map +0 -1
- package/dist/do/withGit.d.ts +0 -180
- package/dist/do/withGit.d.ts.map +0 -1
- package/dist/do/withGit.js +0 -271
- package/dist/do/withGit.js.map +0 -1
- package/dist/durable-object/object-store.d.ts +0 -633
- package/dist/durable-object/object-store.d.ts.map +0 -1
- package/dist/durable-object/object-store.js +0 -1161
- package/dist/durable-object/object-store.js.map +0 -1
- package/dist/durable-object/schema.d.ts.map +0 -1
- package/dist/durable-object/schema.js.map +0 -1
- package/dist/durable-object/wal.d.ts +0 -416
- package/dist/durable-object/wal.d.ts.map +0 -1
- package/dist/durable-object/wal.js +0 -445
- package/dist/durable-object/wal.js.map +0 -1
- package/dist/mcp/adapter.d.ts +0 -772
- package/dist/mcp/adapter.d.ts.map +0 -1
- package/dist/mcp/adapter.js +0 -895
- package/dist/mcp/adapter.js.map +0 -1
- package/dist/mcp/sandbox/miniflare-evaluator.d.ts +0 -22
- package/dist/mcp/sandbox/miniflare-evaluator.d.ts.map +0 -1
- package/dist/mcp/sandbox/miniflare-evaluator.js +0 -140
- package/dist/mcp/sandbox/miniflare-evaluator.js.map +0 -1
- package/dist/mcp/sandbox/object-store-proxy.d.ts +0 -32
- package/dist/mcp/sandbox/object-store-proxy.d.ts.map +0 -1
- package/dist/mcp/sandbox/object-store-proxy.js +0 -30
- package/dist/mcp/sandbox/object-store-proxy.js.map +0 -1
- package/dist/mcp/sandbox/template.d.ts +0 -17
- package/dist/mcp/sandbox/template.d.ts.map +0 -1
- package/dist/mcp/sandbox/template.js +0 -71
- package/dist/mcp/sandbox/template.js.map +0 -1
- package/dist/mcp/sandbox.d.ts +0 -764
- package/dist/mcp/sandbox.d.ts.map +0 -1
- package/dist/mcp/sandbox.js +0 -1362
- package/dist/mcp/sandbox.js.map +0 -1
- package/dist/mcp/sdk-adapter.d.ts +0 -835
- package/dist/mcp/sdk-adapter.d.ts.map +0 -1
- package/dist/mcp/sdk-adapter.js +0 -974
- package/dist/mcp/sdk-adapter.js.map +0 -1
- package/dist/mcp/tools/do.d.ts +0 -32
- package/dist/mcp/tools/do.d.ts.map +0 -1
- package/dist/mcp/tools/do.js +0 -115
- package/dist/mcp/tools/do.js.map +0 -1
- package/dist/mcp/tools.d.ts +0 -548
- package/dist/mcp/tools.d.ts.map +0 -1
- package/dist/mcp/tools.js +0 -1934
- package/dist/mcp/tools.js.map +0 -1
- package/dist/ops/blame.d.ts +0 -551
- package/dist/ops/blame.d.ts.map +0 -1
- package/dist/ops/blame.js +0 -1037
- package/dist/ops/blame.js.map +0 -1
- package/dist/ops/branch.d.ts +0 -766
- package/dist/ops/branch.d.ts.map +0 -1
- package/dist/ops/branch.js +0 -950
- package/dist/ops/branch.js.map +0 -1
- package/dist/ops/commit-traversal.d.ts +0 -349
- package/dist/ops/commit-traversal.d.ts.map +0 -1
- package/dist/ops/commit-traversal.js +0 -821
- package/dist/ops/commit-traversal.js.map +0 -1
- package/dist/ops/commit.d.ts +0 -555
- package/dist/ops/commit.d.ts.map +0 -1
- package/dist/ops/commit.js +0 -826
- package/dist/ops/commit.js.map +0 -1
- package/dist/ops/merge-base.d.ts +0 -397
- package/dist/ops/merge-base.d.ts.map +0 -1
- package/dist/ops/merge-base.js +0 -691
- package/dist/ops/merge-base.js.map +0 -1
- package/dist/ops/merge.d.ts +0 -855
- package/dist/ops/merge.d.ts.map +0 -1
- package/dist/ops/merge.js +0 -1551
- package/dist/ops/merge.js.map +0 -1
- package/dist/ops/tag.d.ts +0 -247
- package/dist/ops/tag.d.ts.map +0 -1
- package/dist/ops/tag.js +0 -649
- package/dist/ops/tag.js.map +0 -1
- package/dist/ops/tree-builder.d.ts +0 -178
- package/dist/ops/tree-builder.d.ts.map +0 -1
- package/dist/ops/tree-builder.js +0 -271
- package/dist/ops/tree-builder.js.map +0 -1
- package/dist/ops/tree-diff.d.ts +0 -291
- package/dist/ops/tree-diff.d.ts.map +0 -1
- package/dist/ops/tree-diff.js +0 -705
- package/dist/ops/tree-diff.js.map +0 -1
- package/dist/pack/delta.d.ts +0 -248
- package/dist/pack/delta.d.ts.map +0 -1
- package/dist/pack/delta.js +0 -736
- package/dist/pack/delta.js.map +0 -1
- package/dist/pack/format.d.ts +0 -446
- package/dist/pack/format.d.ts.map +0 -1
- package/dist/pack/format.js +0 -572
- package/dist/pack/format.js.map +0 -1
- package/dist/pack/full-generation.d.ts +0 -612
- package/dist/pack/full-generation.d.ts.map +0 -1
- package/dist/pack/full-generation.js +0 -1378
- package/dist/pack/full-generation.js.map +0 -1
- package/dist/pack/generation.d.ts +0 -441
- package/dist/pack/generation.d.ts.map +0 -1
- package/dist/pack/generation.js +0 -707
- package/dist/pack/generation.js.map +0 -1
- package/dist/pack/index.d.ts +0 -502
- package/dist/pack/index.d.ts.map +0 -1
- package/dist/pack/index.js +0 -833
- package/dist/pack/index.js.map +0 -1
- package/dist/refs/branch.d.ts +0 -668
- package/dist/refs/branch.d.ts.map +0 -1
- package/dist/refs/branch.js +0 -897
- package/dist/refs/branch.js.map +0 -1
- package/dist/refs/storage.d.ts +0 -833
- package/dist/refs/storage.d.ts.map +0 -1
- package/dist/refs/storage.js +0 -1023
- package/dist/refs/storage.js.map +0 -1
- package/dist/refs/tag.d.ts +0 -860
- package/dist/refs/tag.d.ts.map +0 -1
- package/dist/refs/tag.js +0 -996
- package/dist/refs/tag.js.map +0 -1
- package/dist/storage/backend.d.ts +0 -425
- package/dist/storage/backend.d.ts.map +0 -1
- package/dist/storage/backend.js +0 -41
- package/dist/storage/backend.js.map +0 -1
- package/dist/storage/fsx-adapter.d.ts +0 -204
- package/dist/storage/fsx-adapter.d.ts.map +0 -1
- package/dist/storage/fsx-adapter.js +0 -470
- package/dist/storage/fsx-adapter.js.map +0 -1
- package/dist/storage/lru-cache.d.ts +0 -691
- package/dist/storage/lru-cache.d.ts.map +0 -1
- package/dist/storage/lru-cache.js +0 -813
- package/dist/storage/lru-cache.js.map +0 -1
- package/dist/storage/object-index.d.ts +0 -585
- package/dist/storage/object-index.d.ts.map +0 -1
- package/dist/storage/object-index.js +0 -532
- package/dist/storage/object-index.js.map +0 -1
- package/dist/storage/r2-pack.d.ts +0 -1257
- package/dist/storage/r2-pack.d.ts.map +0 -1
- package/dist/storage/r2-pack.js +0 -1770
- package/dist/storage/r2-pack.js.map +0 -1
- package/dist/tiered/cdc-pipeline.d.ts +0 -1888
- package/dist/tiered/cdc-pipeline.d.ts.map +0 -1
- package/dist/tiered/cdc-pipeline.js +0 -1880
- package/dist/tiered/cdc-pipeline.js.map +0 -1
- package/dist/tiered/migration.d.ts +0 -1104
- package/dist/tiered/migration.d.ts.map +0 -1
- package/dist/tiered/migration.js +0 -1214
- package/dist/tiered/migration.js.map +0 -1
- package/dist/tiered/parquet-writer.d.ts +0 -1145
- package/dist/tiered/parquet-writer.d.ts.map +0 -1
- package/dist/tiered/parquet-writer.js +0 -1183
- package/dist/tiered/parquet-writer.js.map +0 -1
- package/dist/tiered/read-path.d.ts +0 -835
- package/dist/tiered/read-path.d.ts.map +0 -1
- package/dist/tiered/read-path.js +0 -487
- package/dist/tiered/read-path.js.map +0 -1
- package/dist/types/capability.d.ts +0 -1385
- package/dist/types/capability.d.ts.map +0 -1
- package/dist/types/capability.js +0 -36
- package/dist/types/capability.js.map +0 -1
- package/dist/types/index.d.ts +0 -13
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -18
- package/dist/types/index.js.map +0 -1
- package/dist/types/objects.d.ts +0 -692
- package/dist/types/objects.d.ts.map +0 -1
- package/dist/types/objects.js +0 -837
- package/dist/types/objects.js.map +0 -1
- package/dist/types/storage.d.ts +0 -603
- package/dist/types/storage.d.ts.map +0 -1
- package/dist/types/storage.js +0 -191
- package/dist/types/storage.js.map +0 -1
- package/dist/types/worker-loader.d.ts +0 -60
- package/dist/types/worker-loader.d.ts.map +0 -1
- package/dist/types/worker-loader.js +0 -62
- package/dist/types/worker-loader.js.map +0 -1
- package/dist/utils/hash.d.ts +0 -197
- package/dist/utils/hash.d.ts.map +0 -1
- package/dist/utils/hash.js +0 -268
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/sha1.d.ts +0 -290
- package/dist/utils/sha1.d.ts.map +0 -1
- package/dist/utils/sha1.js +0 -582
- package/dist/utils/sha1.js.map +0 -1
- package/dist/wire/capabilities.d.ts +0 -1044
- package/dist/wire/capabilities.d.ts.map +0 -1
- package/dist/wire/capabilities.js +0 -941
- package/dist/wire/capabilities.js.map +0 -1
- package/dist/wire/path-security.d.ts +0 -157
- package/dist/wire/path-security.d.ts.map +0 -1
- package/dist/wire/path-security.js +0 -307
- package/dist/wire/path-security.js.map +0 -1
- package/dist/wire/pkt-line.d.ts +0 -345
- package/dist/wire/pkt-line.d.ts.map +0 -1
- package/dist/wire/pkt-line.js +0 -381
- package/dist/wire/pkt-line.js.map +0 -1
- package/dist/wire/receive-pack.d.ts +0 -1059
- package/dist/wire/receive-pack.d.ts.map +0 -1
- package/dist/wire/receive-pack.js +0 -1414
- package/dist/wire/receive-pack.js.map +0 -1
- package/dist/wire/smart-http.d.ts +0 -799
- package/dist/wire/smart-http.d.ts.map +0 -1
- package/dist/wire/smart-http.js +0 -945
- package/dist/wire/smart-http.js.map +0 -1
- package/dist/wire/upload-pack.d.ts +0 -727
- package/dist/wire/upload-pack.d.ts.map +0 -1
- package/dist/wire/upload-pack.js +0 -1138
- package/dist/wire/upload-pack.js.map +0 -1
|
@@ -1,941 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Git wire protocol capability negotiation
|
|
3
|
-
*
|
|
4
|
-
* This module implements the capability negotiation mechanism used in Git's wire protocol.
|
|
5
|
-
* Capabilities are exchanged during the initial handshake between git client and server
|
|
6
|
-
* to determine what features are supported by both sides, enabling backward compatibility
|
|
7
|
-
* and feature detection.
|
|
8
|
-
*
|
|
9
|
-
* ## Protocol Versions
|
|
10
|
-
*
|
|
11
|
-
* **Protocol v1:**
|
|
12
|
-
* - Capabilities are sent as a space-separated list after the first ref line
|
|
13
|
-
* - Format: `<oid> <refname>\0<cap1> <cap2> cap3=value...`
|
|
14
|
-
* - The NUL byte (`\0`) separates ref information from capabilities
|
|
15
|
-
* - Only the first ref line contains capabilities
|
|
16
|
-
*
|
|
17
|
-
* **Protocol v2:**
|
|
18
|
-
* - Capabilities are advertised line by line in the initial handshake
|
|
19
|
-
* - Starts with `version 2` line
|
|
20
|
-
* - Each capability on its own line, with optional values after `=`
|
|
21
|
-
* - More structured and extensible than v1
|
|
22
|
-
*
|
|
23
|
-
* ## Common Capabilities
|
|
24
|
-
*
|
|
25
|
-
* **Fetch operations:**
|
|
26
|
-
* - `multi_ack`, `multi_ack_detailed`: Improved negotiation
|
|
27
|
-
* - `thin-pack`: Send thin packs requiring client to resolve deltas
|
|
28
|
-
* - `side-band`, `side-band-64k`: Multiplexed data channels
|
|
29
|
-
* - `ofs-delta`: Use offset-based delta encoding
|
|
30
|
-
* - `shallow`: Support shallow clone operations
|
|
31
|
-
*
|
|
32
|
-
* **Push operations:**
|
|
33
|
-
* - `report-status`, `report-status-v2`: Push result reporting
|
|
34
|
-
* - `atomic`: All-or-nothing ref updates
|
|
35
|
-
* - `delete-refs`: Allow ref deletion
|
|
36
|
-
* - `push-options`: Support push options
|
|
37
|
-
*
|
|
38
|
-
* ## Usage Example
|
|
39
|
-
*
|
|
40
|
-
* ```typescript
|
|
41
|
-
* import {
|
|
42
|
-
* parseCapabilityString,
|
|
43
|
-
* findCommonCapabilities,
|
|
44
|
-
* buildCapabilityString,
|
|
45
|
-
* DEFAULT_FETCH_CAPABILITIES_V1
|
|
46
|
-
* } from './capabilities';
|
|
47
|
-
*
|
|
48
|
-
* // Parse capabilities from server advertisement
|
|
49
|
-
* const serverCaps = parseCapabilityString('abc123... refs/heads/main\0multi_ack side-band-64k');
|
|
50
|
-
*
|
|
51
|
-
* // Find common capabilities
|
|
52
|
-
* const clientCaps = DEFAULT_FETCH_CAPABILITIES_V1.map(name => ({ name }));
|
|
53
|
-
* const common = findCommonCapabilities(clientCaps, serverCaps);
|
|
54
|
-
*
|
|
55
|
-
* // Build capability string for request
|
|
56
|
-
* const capString = buildCapabilityString([
|
|
57
|
-
* { name: 'multi_ack' },
|
|
58
|
-
* { name: 'agent', value: 'gitdo/1.0' }
|
|
59
|
-
* ]);
|
|
60
|
-
* ```
|
|
61
|
-
*
|
|
62
|
-
* @module wire/capabilities
|
|
63
|
-
* @see {@link https://git-scm.com/docs/protocol-capabilities} - Protocol capabilities reference
|
|
64
|
-
* @see {@link https://git-scm.com/docs/protocol-v2} - Protocol v2 specification
|
|
65
|
-
*/
|
|
66
|
-
// ============================================================================
|
|
67
|
-
// Constants
|
|
68
|
-
// ============================================================================
|
|
69
|
-
/**
|
|
70
|
-
* Default client capabilities for fetch operations (protocol v1).
|
|
71
|
-
*
|
|
72
|
-
* @description
|
|
73
|
-
* A sensible set of capabilities for fetch operations that provides
|
|
74
|
-
* good performance while maintaining compatibility. These are commonly
|
|
75
|
-
* supported by modern Git servers.
|
|
76
|
-
*
|
|
77
|
-
* - `multi_ack_detailed`: Efficient negotiation with detailed feedback
|
|
78
|
-
* - `side-band-64k`: Large multiplexed data channels for progress/data
|
|
79
|
-
* - `thin-pack`: Receive thin packs (smaller transfer size)
|
|
80
|
-
* - `ofs-delta`: Efficient delta encoding
|
|
81
|
-
* - `agent`: Identify the client
|
|
82
|
-
*
|
|
83
|
-
* @example
|
|
84
|
-
* ```typescript
|
|
85
|
-
* const clientCaps = DEFAULT_FETCH_CAPABILITIES_V1.map(name => ({ name }));
|
|
86
|
-
* // Add agent value
|
|
87
|
-
* clientCaps.find(c => c.name === 'agent')!.value = 'gitdo/1.0';
|
|
88
|
-
*
|
|
89
|
-
* const selected = selectFetchCapabilities(serverCaps, clientCaps);
|
|
90
|
-
* ```
|
|
91
|
-
*/
|
|
92
|
-
export const DEFAULT_FETCH_CAPABILITIES_V1 = [
|
|
93
|
-
'multi_ack_detailed',
|
|
94
|
-
'side-band-64k',
|
|
95
|
-
'thin-pack',
|
|
96
|
-
'ofs-delta',
|
|
97
|
-
'agent',
|
|
98
|
-
];
|
|
99
|
-
/**
|
|
100
|
-
* Default client capabilities for push operations (protocol v1).
|
|
101
|
-
*
|
|
102
|
-
* @description
|
|
103
|
-
* A sensible set of capabilities for push operations that provides
|
|
104
|
-
* detailed feedback and compatibility with modern Git servers.
|
|
105
|
-
*
|
|
106
|
-
* - `report-status`: Receive detailed push result status
|
|
107
|
-
* - `side-band-64k`: Multiplexed channels for status/errors
|
|
108
|
-
* - `agent`: Identify the client
|
|
109
|
-
* - `quiet`: Suppress unnecessary progress output
|
|
110
|
-
*
|
|
111
|
-
* @example
|
|
112
|
-
* ```typescript
|
|
113
|
-
* const pushCaps = DEFAULT_PUSH_CAPABILITIES_V1.map(name => ({ name }));
|
|
114
|
-
* pushCaps.find(c => c.name === 'agent')!.value = 'gitdo/1.0';
|
|
115
|
-
* ```
|
|
116
|
-
*/
|
|
117
|
-
export const DEFAULT_PUSH_CAPABILITIES_V1 = [
|
|
118
|
-
'report-status',
|
|
119
|
-
'side-band-64k',
|
|
120
|
-
'agent',
|
|
121
|
-
'quiet',
|
|
122
|
-
];
|
|
123
|
-
/**
|
|
124
|
-
* Minimum required capabilities for basic fetch.
|
|
125
|
-
*
|
|
126
|
-
* @description
|
|
127
|
-
* Capabilities that must be present for fetch to work correctly.
|
|
128
|
-
* Currently empty as Git is designed to work with minimal capabilities,
|
|
129
|
-
* but this can be populated if specific capabilities become required.
|
|
130
|
-
*
|
|
131
|
-
* @example
|
|
132
|
-
* ```typescript
|
|
133
|
-
* const missing = validateRequiredCapabilities(serverCaps, REQUIRED_FETCH_CAPABILITIES);
|
|
134
|
-
* if (missing.length > 0) {
|
|
135
|
-
* throw new Error(`Server missing required capabilities: ${missing.join(', ')}`);
|
|
136
|
-
* }
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
export const REQUIRED_FETCH_CAPABILITIES = [];
|
|
140
|
-
// ============================================================================
|
|
141
|
-
// Parsing Functions
|
|
142
|
-
// ============================================================================
|
|
143
|
-
/**
|
|
144
|
-
* Parse a capability string from ref advertisement (protocol v1).
|
|
145
|
-
*
|
|
146
|
-
* @description
|
|
147
|
-
* Extracts capabilities from a protocol v1 ref advertisement line.
|
|
148
|
-
* The capabilities appear after a NUL byte (`\0`) separator following
|
|
149
|
-
* the ref information. This is only present on the first ref line.
|
|
150
|
-
*
|
|
151
|
-
* Line format: `<oid> <refname>\0<cap1> <cap2> cap3=value...`
|
|
152
|
-
*
|
|
153
|
-
* @param line - The ref advertisement line containing capabilities
|
|
154
|
-
* @returns Parsed capability set with version 1
|
|
155
|
-
*
|
|
156
|
-
* @throws {Error} If the line doesn't contain a NUL byte separator
|
|
157
|
-
*
|
|
158
|
-
* @example
|
|
159
|
-
* ```typescript
|
|
160
|
-
* // Parse from first ref line
|
|
161
|
-
* const line = 'abc123def456789012345678901234567890abcd refs/heads/main\0multi_ack side-band-64k agent=git/2.30.0';
|
|
162
|
-
* const caps = parseCapabilityString(line);
|
|
163
|
-
*
|
|
164
|
-
* console.log(caps.version); // 1
|
|
165
|
-
* console.log(caps.capabilities.has('multi_ack')); // true
|
|
166
|
-
* console.log(caps.capabilities.get('agent')); // 'git/2.30.0'
|
|
167
|
-
* ```
|
|
168
|
-
*/
|
|
169
|
-
export function parseCapabilityString(line) {
|
|
170
|
-
// Find the NUL byte that separates ref info from capabilities
|
|
171
|
-
const nulIndex = line.indexOf('\0');
|
|
172
|
-
if (nulIndex === -1) {
|
|
173
|
-
throw new Error('Invalid capability string: missing NUL byte separator');
|
|
174
|
-
}
|
|
175
|
-
// Extract the capability portion after the NUL byte
|
|
176
|
-
const capString = line.slice(nulIndex + 1);
|
|
177
|
-
// Parse the capabilities
|
|
178
|
-
const entries = parseCapabilities(capString);
|
|
179
|
-
// Build the capability map
|
|
180
|
-
const capabilities = new Map();
|
|
181
|
-
for (const entry of entries) {
|
|
182
|
-
capabilities.set(entry.name, entry.value);
|
|
183
|
-
}
|
|
184
|
-
return {
|
|
185
|
-
version: 1,
|
|
186
|
-
capabilities,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Parse individual capability entries from a space-separated string.
|
|
191
|
-
*
|
|
192
|
-
* @description
|
|
193
|
-
* Parses a whitespace-separated capability string into individual entries.
|
|
194
|
-
* Handles both simple capabilities (`multi_ack`) and capabilities with
|
|
195
|
-
* values (`agent=git/2.30.0`).
|
|
196
|
-
*
|
|
197
|
-
* @param capString - Space-separated capability string
|
|
198
|
-
* @returns Array of capability entries
|
|
199
|
-
*
|
|
200
|
-
* @example
|
|
201
|
-
* ```typescript
|
|
202
|
-
* // Simple capabilities
|
|
203
|
-
* const caps1 = parseCapabilities('multi_ack thin-pack ofs-delta');
|
|
204
|
-
* // [{ name: 'multi_ack' }, { name: 'thin-pack' }, { name: 'ofs-delta' }]
|
|
205
|
-
*
|
|
206
|
-
* // Capabilities with values
|
|
207
|
-
* const caps2 = parseCapabilities('agent=git/2.30.0 symref=HEAD:refs/heads/main');
|
|
208
|
-
* // [{ name: 'agent', value: 'git/2.30.0' }, { name: 'symref', value: 'HEAD:refs/heads/main' }]
|
|
209
|
-
*
|
|
210
|
-
* // Empty string
|
|
211
|
-
* const caps3 = parseCapabilities('');
|
|
212
|
-
* // []
|
|
213
|
-
* ```
|
|
214
|
-
*/
|
|
215
|
-
export function parseCapabilities(capString) {
|
|
216
|
-
// Trim and split by whitespace
|
|
217
|
-
const trimmed = capString.trim();
|
|
218
|
-
if (trimmed === '') {
|
|
219
|
-
return [];
|
|
220
|
-
}
|
|
221
|
-
// Split by whitespace (handles multiple spaces)
|
|
222
|
-
const parts = trimmed.split(/\s+/);
|
|
223
|
-
return parts.map((part) => {
|
|
224
|
-
const eqIndex = part.indexOf('=');
|
|
225
|
-
if (eqIndex === -1) {
|
|
226
|
-
return { name: part };
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
return {
|
|
230
|
-
name: part.slice(0, eqIndex),
|
|
231
|
-
value: part.slice(eqIndex + 1),
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Parse a ref advertisement line (protocol v1).
|
|
238
|
-
*
|
|
239
|
-
* @description
|
|
240
|
-
* Parses a single line from the server's ref advertisement. The first
|
|
241
|
-
* line has a special format including capabilities after a NUL byte,
|
|
242
|
-
* while subsequent lines contain only the OID and ref name.
|
|
243
|
-
*
|
|
244
|
-
* First line format: `<oid> <refname>\0<capabilities>`
|
|
245
|
-
* Subsequent lines: `<oid> <refname>`
|
|
246
|
-
*
|
|
247
|
-
* @param line - The pkt-line data (without length prefix)
|
|
248
|
-
* @param isFirst - Whether this is the first line (contains capabilities)
|
|
249
|
-
* @returns Parsed ref advertisement
|
|
250
|
-
*
|
|
251
|
-
* @throws {Error} If the first line is missing the NUL byte
|
|
252
|
-
* @throws {Error} If the line is missing the space between OID and refname
|
|
253
|
-
* @throws {Error} If the OID is not 40 characters (SHA-1)
|
|
254
|
-
*
|
|
255
|
-
* @example
|
|
256
|
-
* ```typescript
|
|
257
|
-
* // Parse first line (with capabilities)
|
|
258
|
-
* const firstLine = 'abc123def456789012345678901234567890abcd refs/heads/main\0multi_ack side-band-64k\n';
|
|
259
|
-
* const firstRef = parseRefAdvertisement(firstLine, true);
|
|
260
|
-
* // {
|
|
261
|
-
* // oid: 'abc123def456789012345678901234567890abcd',
|
|
262
|
-
* // name: 'refs/heads/main',
|
|
263
|
-
* // capabilities: { version: 1, capabilities: Map {...} }
|
|
264
|
-
* // }
|
|
265
|
-
*
|
|
266
|
-
* // Parse subsequent line (no capabilities)
|
|
267
|
-
* const otherLine = 'def456789012345678901234567890abcdef12 refs/heads/feature\n';
|
|
268
|
-
* const otherRef = parseRefAdvertisement(otherLine, false);
|
|
269
|
-
* // {
|
|
270
|
-
* // oid: 'def456789012345678901234567890abcdef12',
|
|
271
|
-
* // name: 'refs/heads/feature'
|
|
272
|
-
* // }
|
|
273
|
-
* ```
|
|
274
|
-
*/
|
|
275
|
-
export function parseRefAdvertisement(line, isFirst) {
|
|
276
|
-
// Remove trailing newline if present
|
|
277
|
-
const cleanLine = line.replace(/\n$/, '');
|
|
278
|
-
let oid;
|
|
279
|
-
let name;
|
|
280
|
-
let capabilities;
|
|
281
|
-
if (isFirst) {
|
|
282
|
-
// First line has capabilities after NUL byte
|
|
283
|
-
const nulIndex = cleanLine.indexOf('\0');
|
|
284
|
-
if (nulIndex === -1) {
|
|
285
|
-
throw new Error('First ref advertisement line must contain NUL byte');
|
|
286
|
-
}
|
|
287
|
-
const refPart = cleanLine.slice(0, nulIndex);
|
|
288
|
-
const spaceIndex = refPart.indexOf(' ');
|
|
289
|
-
if (spaceIndex === -1) {
|
|
290
|
-
throw new Error('Invalid ref advertisement format: missing space between OID and refname');
|
|
291
|
-
}
|
|
292
|
-
oid = refPart.slice(0, spaceIndex);
|
|
293
|
-
name = refPart.slice(spaceIndex + 1);
|
|
294
|
-
// Validate OID length (should be 40 hex chars for SHA-1)
|
|
295
|
-
if (oid.length !== 40) {
|
|
296
|
-
throw new Error('Invalid OID length in ref advertisement');
|
|
297
|
-
}
|
|
298
|
-
capabilities = parseCapabilityString(cleanLine);
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
// Subsequent lines: just "<oid> <refname>"
|
|
302
|
-
const spaceIndex = cleanLine.indexOf(' ');
|
|
303
|
-
if (spaceIndex === -1) {
|
|
304
|
-
throw new Error('Invalid ref advertisement format: missing space between OID and refname');
|
|
305
|
-
}
|
|
306
|
-
oid = cleanLine.slice(0, spaceIndex);
|
|
307
|
-
name = cleanLine.slice(spaceIndex + 1);
|
|
308
|
-
// Validate OID length
|
|
309
|
-
if (oid.length !== 40) {
|
|
310
|
-
throw new Error('Invalid OID length in ref advertisement');
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return {
|
|
314
|
-
oid,
|
|
315
|
-
name,
|
|
316
|
-
capabilities,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Parse protocol v2 capability advertisement.
|
|
321
|
-
*
|
|
322
|
-
* @description
|
|
323
|
-
* Parses the server's capability advertisement in protocol v2 format.
|
|
324
|
-
* Protocol v2 uses a line-by-line format starting with "version 2",
|
|
325
|
-
* followed by capability lines. Commands and capabilities are distinguished
|
|
326
|
-
* by whether they have values.
|
|
327
|
-
*
|
|
328
|
-
* Response format:
|
|
329
|
-
* ```
|
|
330
|
-
* version 2
|
|
331
|
-
* agent=git/2.30.0
|
|
332
|
-
* ls-refs
|
|
333
|
-
* fetch=shallow filter
|
|
334
|
-
* server-option
|
|
335
|
-
* object-format=sha1
|
|
336
|
-
* ```
|
|
337
|
-
*
|
|
338
|
-
* @param lines - Array of pkt-line data (without length prefixes)
|
|
339
|
-
* @returns Parsed server capabilities
|
|
340
|
-
*
|
|
341
|
-
* @throws {Error} If lines is empty or doesn't start with "version 2"
|
|
342
|
-
*
|
|
343
|
-
* @example
|
|
344
|
-
* ```typescript
|
|
345
|
-
* const lines = [
|
|
346
|
-
* 'version 2',
|
|
347
|
-
* 'agent=git/2.40.0',
|
|
348
|
-
* 'ls-refs',
|
|
349
|
-
* 'fetch=shallow filter',
|
|
350
|
-
* 'server-option',
|
|
351
|
-
* 'object-format=sha1'
|
|
352
|
-
* ];
|
|
353
|
-
*
|
|
354
|
-
* const serverCaps = parseServerCapabilitiesV2(lines);
|
|
355
|
-
* // {
|
|
356
|
-
* // version: 2,
|
|
357
|
-
* // commands: ['ls-refs', 'fetch', 'server-option'],
|
|
358
|
-
* // agent: 'git/2.40.0',
|
|
359
|
-
* // objectFormat: 'sha1',
|
|
360
|
-
* // capabilities: Map { 'ls-refs' => undefined, 'fetch' => 'shallow filter', ... }
|
|
361
|
-
* // }
|
|
362
|
-
*
|
|
363
|
-
* if (serverCaps.commands.includes('fetch')) {
|
|
364
|
-
* console.log('Server supports fetch with:', serverCaps.capabilities.get('fetch'));
|
|
365
|
-
* }
|
|
366
|
-
* ```
|
|
367
|
-
*/
|
|
368
|
-
export function parseServerCapabilitiesV2(lines) {
|
|
369
|
-
if (lines.length === 0 || lines[0] !== 'version 2') {
|
|
370
|
-
throw new Error('Invalid protocol v2 response: must start with "version 2"');
|
|
371
|
-
}
|
|
372
|
-
const commands = [];
|
|
373
|
-
const capabilities = new Map();
|
|
374
|
-
let agent;
|
|
375
|
-
let objectFormat;
|
|
376
|
-
// Process each line after "version 2"
|
|
377
|
-
for (let i = 1; i < lines.length; i++) {
|
|
378
|
-
const line = lines[i];
|
|
379
|
-
const eqIndex = line.indexOf('=');
|
|
380
|
-
if (eqIndex === -1) {
|
|
381
|
-
// This is a command without a value
|
|
382
|
-
commands.push(line);
|
|
383
|
-
capabilities.set(line, undefined);
|
|
384
|
-
}
|
|
385
|
-
else {
|
|
386
|
-
const name = line.slice(0, eqIndex);
|
|
387
|
-
const value = line.slice(eqIndex + 1);
|
|
388
|
-
if (name === 'agent') {
|
|
389
|
-
agent = value;
|
|
390
|
-
}
|
|
391
|
-
else if (name === 'object-format') {
|
|
392
|
-
objectFormat = value;
|
|
393
|
-
}
|
|
394
|
-
else if (name === 'fetch' || name === 'ls-refs' || name === 'server-option') {
|
|
395
|
-
// Commands with sub-capabilities
|
|
396
|
-
commands.push(name);
|
|
397
|
-
capabilities.set(name, value);
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
capabilities.set(name, value);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
return {
|
|
405
|
-
version: 2,
|
|
406
|
-
commands,
|
|
407
|
-
agent,
|
|
408
|
-
objectFormat,
|
|
409
|
-
capabilities,
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
// ============================================================================
|
|
413
|
-
// Building Functions
|
|
414
|
-
// ============================================================================
|
|
415
|
-
/**
|
|
416
|
-
* Build a capability string for want/have request (protocol v1).
|
|
417
|
-
*
|
|
418
|
-
* @description
|
|
419
|
-
* Constructs a space-separated capability string from an array of
|
|
420
|
-
* capability entries. Capabilities with values are formatted as
|
|
421
|
-
* `name=value`, while those without are just the name.
|
|
422
|
-
*
|
|
423
|
-
* @param capabilities - Capabilities to include
|
|
424
|
-
* @returns Space-separated capability string
|
|
425
|
-
*
|
|
426
|
-
* @example
|
|
427
|
-
* ```typescript
|
|
428
|
-
* const caps: CapabilityEntry[] = [
|
|
429
|
-
* { name: 'multi_ack_detailed' },
|
|
430
|
-
* { name: 'side-band-64k' },
|
|
431
|
-
* { name: 'agent', value: 'gitdo/1.0' }
|
|
432
|
-
* ];
|
|
433
|
-
*
|
|
434
|
-
* const str = buildCapabilityString(caps);
|
|
435
|
-
* // 'multi_ack_detailed side-band-64k agent=gitdo/1.0'
|
|
436
|
-
* ```
|
|
437
|
-
*/
|
|
438
|
-
export function buildCapabilityString(capabilities) {
|
|
439
|
-
return capabilities
|
|
440
|
-
.map((cap) => {
|
|
441
|
-
if (cap.value !== undefined) {
|
|
442
|
-
return `${cap.name}=${cap.value}`;
|
|
443
|
-
}
|
|
444
|
-
return cap.name;
|
|
445
|
-
})
|
|
446
|
-
.join(' ');
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Build a want line with capabilities (first want only).
|
|
450
|
-
*
|
|
451
|
-
* @description
|
|
452
|
-
* Constructs a want line for a fetch request. The first want line
|
|
453
|
-
* includes capabilities, while subsequent want lines contain only
|
|
454
|
-
* the object ID.
|
|
455
|
-
*
|
|
456
|
-
* Format: `want <oid> <capabilities>\n` (first line)
|
|
457
|
-
* Format: `want <oid>\n` (subsequent lines)
|
|
458
|
-
*
|
|
459
|
-
* @param oid - The object ID to want (40-character SHA-1 hex string)
|
|
460
|
-
* @param capabilities - Capabilities to include (optional, first want only)
|
|
461
|
-
* @returns Formatted want line with trailing newline
|
|
462
|
-
*
|
|
463
|
-
* @example
|
|
464
|
-
* ```typescript
|
|
465
|
-
* // First want line with capabilities
|
|
466
|
-
* const firstWant = buildWantLine(
|
|
467
|
-
* 'abc123def456789012345678901234567890abcd',
|
|
468
|
-
* [{ name: 'multi_ack' }, { name: 'agent', value: 'gitdo/1.0' }]
|
|
469
|
-
* );
|
|
470
|
-
* // 'want abc123def456789012345678901234567890abcd multi_ack agent=gitdo/1.0\n'
|
|
471
|
-
*
|
|
472
|
-
* // Subsequent want line (no capabilities)
|
|
473
|
-
* const nextWant = buildWantLine('def456789012345678901234567890abcdef12');
|
|
474
|
-
* // 'want def456789012345678901234567890abcdef12\n'
|
|
475
|
-
* ```
|
|
476
|
-
*/
|
|
477
|
-
export function buildWantLine(oid, capabilities) {
|
|
478
|
-
if (capabilities && capabilities.length > 0) {
|
|
479
|
-
const capString = buildCapabilityString(capabilities);
|
|
480
|
-
return `want ${oid} ${capString}\n`;
|
|
481
|
-
}
|
|
482
|
-
return `want ${oid}\n`;
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Build a have line for negotiation.
|
|
486
|
-
*
|
|
487
|
-
* @description
|
|
488
|
-
* Constructs a have line used during fetch negotiation. Have lines
|
|
489
|
-
* inform the server what objects the client already has, allowing
|
|
490
|
-
* the server to determine the minimal set of objects to send.
|
|
491
|
-
*
|
|
492
|
-
* Format: `have <oid>\n`
|
|
493
|
-
*
|
|
494
|
-
* @param oid - The object ID we have (40-character SHA-1 hex string)
|
|
495
|
-
* @returns Formatted have line with trailing newline
|
|
496
|
-
*
|
|
497
|
-
* @example
|
|
498
|
-
* ```typescript
|
|
499
|
-
* const haveLine = buildHaveLine('abc123def456789012345678901234567890abcd');
|
|
500
|
-
* // 'have abc123def456789012345678901234567890abcd\n'
|
|
501
|
-
*
|
|
502
|
-
* // OID is normalized to lowercase
|
|
503
|
-
* const normalized = buildHaveLine('ABC123DEF456789012345678901234567890ABCD');
|
|
504
|
-
* // 'have abc123def456789012345678901234567890abcd\n'
|
|
505
|
-
* ```
|
|
506
|
-
*/
|
|
507
|
-
export function buildHaveLine(oid) {
|
|
508
|
-
return `have ${oid.toLowerCase()}\n`;
|
|
509
|
-
}
|
|
510
|
-
/**
|
|
511
|
-
* Build a complete want/have request.
|
|
512
|
-
*
|
|
513
|
-
* @description
|
|
514
|
-
* Constructs all want lines for a fetch request. The first want line
|
|
515
|
-
* includes the client's capabilities, while subsequent want lines
|
|
516
|
-
* contain only the object IDs.
|
|
517
|
-
*
|
|
518
|
-
* @param request - The want request containing object IDs and capabilities
|
|
519
|
-
* @returns Array of formatted want lines (ready for pkt-line encoding)
|
|
520
|
-
*
|
|
521
|
-
* @example
|
|
522
|
-
* ```typescript
|
|
523
|
-
* const request: WantRequest = {
|
|
524
|
-
* wants: [
|
|
525
|
-
* 'abc123def456789012345678901234567890abcd',
|
|
526
|
-
* 'def456789012345678901234567890abcdef12',
|
|
527
|
-
* '123456789012345678901234567890abcdef00'
|
|
528
|
-
* ],
|
|
529
|
-
* capabilities: [
|
|
530
|
-
* { name: 'multi_ack_detailed' },
|
|
531
|
-
* { name: 'side-band-64k' }
|
|
532
|
-
* ]
|
|
533
|
-
* };
|
|
534
|
-
*
|
|
535
|
-
* const lines = buildFetchRequest(request);
|
|
536
|
-
* // [
|
|
537
|
-
* // 'want abc123... multi_ack_detailed side-band-64k\n',
|
|
538
|
-
* // 'want def456...\n',
|
|
539
|
-
* // 'want 123456...\n'
|
|
540
|
-
* // ]
|
|
541
|
-
* ```
|
|
542
|
-
*/
|
|
543
|
-
export function buildFetchRequest(request) {
|
|
544
|
-
const lines = [];
|
|
545
|
-
for (let i = 0; i < request.wants.length; i++) {
|
|
546
|
-
const oid = request.wants[i];
|
|
547
|
-
if (i === 0) {
|
|
548
|
-
// First want line includes capabilities
|
|
549
|
-
lines.push(buildWantLine(oid, request.capabilities));
|
|
550
|
-
}
|
|
551
|
-
else {
|
|
552
|
-
// Subsequent want lines don't include capabilities
|
|
553
|
-
lines.push(buildWantLine(oid));
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return lines;
|
|
557
|
-
}
|
|
558
|
-
/**
|
|
559
|
-
* Build protocol v2 command request.
|
|
560
|
-
*
|
|
561
|
-
* @description
|
|
562
|
-
* Constructs a protocol v2 command request. Protocol v2 uses a structured
|
|
563
|
-
* format with command specification, capabilities, and optional arguments.
|
|
564
|
-
*
|
|
565
|
-
* Request format:
|
|
566
|
-
* ```
|
|
567
|
-
* command=<cmd>
|
|
568
|
-
* capability1
|
|
569
|
-
* capability2=value
|
|
570
|
-
* 0001 (delimiter - added by caller)
|
|
571
|
-
* <command-specific args>
|
|
572
|
-
* 0000 (flush - added by caller)
|
|
573
|
-
* ```
|
|
574
|
-
*
|
|
575
|
-
* @param command - The v2 command (e.g., 'fetch', 'ls-refs')
|
|
576
|
-
* @param capabilities - Client capabilities to advertise
|
|
577
|
-
* @param args - Command-specific arguments (optional)
|
|
578
|
-
* @returns Array of lines (ready for pkt-line encoding)
|
|
579
|
-
*
|
|
580
|
-
* @example
|
|
581
|
-
* ```typescript
|
|
582
|
-
* // ls-refs request
|
|
583
|
-
* const lsRefsLines = buildV2CommandRequest(
|
|
584
|
-
* 'ls-refs',
|
|
585
|
-
* [{ name: 'agent', value: 'gitdo/1.0' }],
|
|
586
|
-
* ['peel', 'symrefs', 'ref-prefix refs/heads/']
|
|
587
|
-
* );
|
|
588
|
-
* // [
|
|
589
|
-
* // 'command=ls-refs',
|
|
590
|
-
* // 'agent=gitdo/1.0',
|
|
591
|
-
* // 'peel',
|
|
592
|
-
* // 'symrefs',
|
|
593
|
-
* // 'ref-prefix refs/heads/'
|
|
594
|
-
* // ]
|
|
595
|
-
*
|
|
596
|
-
* // fetch request
|
|
597
|
-
* const fetchLines = buildV2CommandRequest(
|
|
598
|
-
* 'fetch',
|
|
599
|
-
* [{ name: 'agent', value: 'gitdo/1.0' }, { name: 'thin-pack' }],
|
|
600
|
-
* ['want abc123...', 'have def456...', 'done']
|
|
601
|
-
* );
|
|
602
|
-
* ```
|
|
603
|
-
*/
|
|
604
|
-
export function buildV2CommandRequest(command, capabilities, args) {
|
|
605
|
-
const lines = [];
|
|
606
|
-
// Add command line
|
|
607
|
-
lines.push(`command=${command}`);
|
|
608
|
-
// Add capabilities
|
|
609
|
-
for (const cap of capabilities) {
|
|
610
|
-
if (cap.value !== undefined) {
|
|
611
|
-
lines.push(`${cap.name}=${cap.value}`);
|
|
612
|
-
}
|
|
613
|
-
else {
|
|
614
|
-
lines.push(cap.name);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
// Add arguments if present
|
|
618
|
-
if (args && args.length > 0) {
|
|
619
|
-
for (const arg of args) {
|
|
620
|
-
lines.push(arg);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return lines;
|
|
624
|
-
}
|
|
625
|
-
// ============================================================================
|
|
626
|
-
// Negotiation Functions
|
|
627
|
-
// ============================================================================
|
|
628
|
-
/**
|
|
629
|
-
* Negotiate protocol version with server.
|
|
630
|
-
*
|
|
631
|
-
* @description
|
|
632
|
-
* Determines the protocol version to use based on the server's advertisement
|
|
633
|
-
* and the client's preference. The negotiated version is the highest version
|
|
634
|
-
* supported by both parties.
|
|
635
|
-
*
|
|
636
|
-
* @param serverAdvertisement - First line from server's response
|
|
637
|
-
* @param preferredVersion - Client's preferred protocol version (default: 2)
|
|
638
|
-
* @returns Negotiation result with agreed version
|
|
639
|
-
*
|
|
640
|
-
* @example
|
|
641
|
-
* ```typescript
|
|
642
|
-
* // Server supports v2, client prefers v2
|
|
643
|
-
* const v2Result = negotiateVersion('version 2', 2);
|
|
644
|
-
* // { version: 2, serverSupportsV2: true, commonCapabilities: [] }
|
|
645
|
-
*
|
|
646
|
-
* // Server is v1 only, client prefers v2
|
|
647
|
-
* const v1Result = negotiateVersion('abc123... refs/heads/main\0multi_ack', 2);
|
|
648
|
-
* // { version: 1, serverSupportsV2: false, commonCapabilities: [] }
|
|
649
|
-
*
|
|
650
|
-
* // Client explicitly wants v1
|
|
651
|
-
* const explicitV1 = negotiateVersion('version 2', 1);
|
|
652
|
-
* // { version: 1, serverSupportsV2: true, commonCapabilities: [] }
|
|
653
|
-
* ```
|
|
654
|
-
*/
|
|
655
|
-
export function negotiateVersion(serverAdvertisement, preferredVersion = 2) {
|
|
656
|
-
const serverSupportsV2 = serverAdvertisement.startsWith('version 2');
|
|
657
|
-
let version;
|
|
658
|
-
if (serverSupportsV2 && preferredVersion === 2) {
|
|
659
|
-
version = 2;
|
|
660
|
-
}
|
|
661
|
-
else {
|
|
662
|
-
version = 1;
|
|
663
|
-
}
|
|
664
|
-
return {
|
|
665
|
-
version,
|
|
666
|
-
serverSupportsV2,
|
|
667
|
-
commonCapabilities: [],
|
|
668
|
-
};
|
|
669
|
-
}
|
|
670
|
-
/**
|
|
671
|
-
* Find common capabilities between client and server.
|
|
672
|
-
*
|
|
673
|
-
* @description
|
|
674
|
-
* Determines which capabilities are supported by both the client and server.
|
|
675
|
-
* This is used to select the optimal set of capabilities for the session.
|
|
676
|
-
*
|
|
677
|
-
* @param clientCaps - Client's supported capabilities
|
|
678
|
-
* @param serverCaps - Server's advertised capabilities
|
|
679
|
-
* @returns Array of capability names supported by both parties
|
|
680
|
-
*
|
|
681
|
-
* @example
|
|
682
|
-
* ```typescript
|
|
683
|
-
* const clientCaps: CapabilityEntry[] = [
|
|
684
|
-
* { name: 'multi_ack_detailed' },
|
|
685
|
-
* { name: 'side-band-64k' },
|
|
686
|
-
* { name: 'thin-pack' },
|
|
687
|
-
* { name: 'ofs-delta' }
|
|
688
|
-
* ];
|
|
689
|
-
*
|
|
690
|
-
* const serverCaps: CapabilitySet = {
|
|
691
|
-
* version: 1,
|
|
692
|
-
* capabilities: new Map([
|
|
693
|
-
* ['multi_ack', undefined],
|
|
694
|
-
* ['multi_ack_detailed', undefined],
|
|
695
|
-
* ['side-band-64k', undefined],
|
|
696
|
-
* ['shallow', undefined]
|
|
697
|
-
* ])
|
|
698
|
-
* };
|
|
699
|
-
*
|
|
700
|
-
* const common = findCommonCapabilities(clientCaps, serverCaps);
|
|
701
|
-
* // ['multi_ack_detailed', 'side-band-64k']
|
|
702
|
-
* ```
|
|
703
|
-
*/
|
|
704
|
-
export function findCommonCapabilities(clientCaps, serverCaps) {
|
|
705
|
-
const common = [];
|
|
706
|
-
for (const clientCap of clientCaps) {
|
|
707
|
-
if (serverCaps.capabilities.has(clientCap.name)) {
|
|
708
|
-
common.push(clientCap.name);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
return common;
|
|
712
|
-
}
|
|
713
|
-
/**
|
|
714
|
-
* Check if a specific capability is supported.
|
|
715
|
-
*
|
|
716
|
-
* @description
|
|
717
|
-
* Checks whether a capability is present in the capability set.
|
|
718
|
-
* This is a convenience wrapper around Map.has().
|
|
719
|
-
*
|
|
720
|
-
* @param capSet - The capability set to check
|
|
721
|
-
* @param name - The capability name to look for
|
|
722
|
-
* @returns True if the capability is present
|
|
723
|
-
*
|
|
724
|
-
* @example
|
|
725
|
-
* ```typescript
|
|
726
|
-
* const caps: CapabilitySet = {
|
|
727
|
-
* version: 1,
|
|
728
|
-
* capabilities: new Map([
|
|
729
|
-
* ['multi_ack', undefined],
|
|
730
|
-
* ['side-band-64k', undefined],
|
|
731
|
-
* ['agent', 'git/2.30.0']
|
|
732
|
-
* ])
|
|
733
|
-
* };
|
|
734
|
-
*
|
|
735
|
-
* hasCapability(caps, 'multi_ack'); // true
|
|
736
|
-
* hasCapability(caps, 'side-band-64k'); // true
|
|
737
|
-
* hasCapability(caps, 'thin-pack'); // false
|
|
738
|
-
* ```
|
|
739
|
-
*/
|
|
740
|
-
export function hasCapability(capSet, name) {
|
|
741
|
-
return capSet.capabilities.has(name);
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Get the value of a capability (if it has one).
|
|
745
|
-
*
|
|
746
|
-
* @description
|
|
747
|
-
* Retrieves the value associated with a capability. Returns undefined
|
|
748
|
-
* if the capability is not present or has no value.
|
|
749
|
-
*
|
|
750
|
-
* @param capSet - The capability set to query
|
|
751
|
-
* @param name - The capability name
|
|
752
|
-
* @returns The capability value, or undefined if not present/no value
|
|
753
|
-
*
|
|
754
|
-
* @example
|
|
755
|
-
* ```typescript
|
|
756
|
-
* const caps: CapabilitySet = {
|
|
757
|
-
* version: 1,
|
|
758
|
-
* capabilities: new Map([
|
|
759
|
-
* ['multi_ack', undefined],
|
|
760
|
-
* ['agent', 'git/2.30.0'],
|
|
761
|
-
* ['symref', 'HEAD:refs/heads/main']
|
|
762
|
-
* ])
|
|
763
|
-
* };
|
|
764
|
-
*
|
|
765
|
-
* getCapabilityValue(caps, 'agent'); // 'git/2.30.0'
|
|
766
|
-
* getCapabilityValue(caps, 'symref'); // 'HEAD:refs/heads/main'
|
|
767
|
-
* getCapabilityValue(caps, 'multi_ack'); // undefined (present but no value)
|
|
768
|
-
* getCapabilityValue(caps, 'thin-pack'); // undefined (not present)
|
|
769
|
-
* ```
|
|
770
|
-
*/
|
|
771
|
-
export function getCapabilityValue(capSet, name) {
|
|
772
|
-
return capSet.capabilities.get(name);
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Create a capability set from entries.
|
|
776
|
-
*
|
|
777
|
-
* @description
|
|
778
|
-
* Constructs a CapabilitySet from an array of capability entries.
|
|
779
|
-
* This is useful for creating capability sets programmatically.
|
|
780
|
-
*
|
|
781
|
-
* @param version - Protocol version (1 or 2)
|
|
782
|
-
* @param entries - Array of capability entries
|
|
783
|
-
* @returns A new CapabilitySet
|
|
784
|
-
*
|
|
785
|
-
* @example
|
|
786
|
-
* ```typescript
|
|
787
|
-
* const entries: CapabilityEntry[] = [
|
|
788
|
-
* { name: 'multi_ack_detailed' },
|
|
789
|
-
* { name: 'side-band-64k' },
|
|
790
|
-
* { name: 'agent', value: 'gitdo/1.0' }
|
|
791
|
-
* ];
|
|
792
|
-
*
|
|
793
|
-
* const capSet = createCapabilitySet(1, entries);
|
|
794
|
-
* // {
|
|
795
|
-
* // version: 1,
|
|
796
|
-
* // capabilities: Map {
|
|
797
|
-
* // 'multi_ack_detailed' => undefined,
|
|
798
|
-
* // 'side-band-64k' => undefined,
|
|
799
|
-
* // 'agent' => 'gitdo/1.0'
|
|
800
|
-
* // }
|
|
801
|
-
* // }
|
|
802
|
-
* ```
|
|
803
|
-
*/
|
|
804
|
-
export function createCapabilitySet(version, entries) {
|
|
805
|
-
const capabilities = new Map();
|
|
806
|
-
for (const entry of entries) {
|
|
807
|
-
capabilities.set(entry.name, entry.value);
|
|
808
|
-
}
|
|
809
|
-
return {
|
|
810
|
-
version,
|
|
811
|
-
capabilities,
|
|
812
|
-
};
|
|
813
|
-
}
|
|
814
|
-
/**
|
|
815
|
-
* Select optimal capabilities for a fetch operation.
|
|
816
|
-
*
|
|
817
|
-
* @description
|
|
818
|
-
* Filters client-preferred capabilities to only those supported by the server.
|
|
819
|
-
* The client's values are preserved (not the server's), maintaining client
|
|
820
|
-
* identification and preferences.
|
|
821
|
-
*
|
|
822
|
-
* @param serverCaps - Server's advertised capabilities
|
|
823
|
-
* @param clientPrefs - Client's preferred capabilities (in priority order)
|
|
824
|
-
* @returns Array of capabilities to use (subset of client preferences)
|
|
825
|
-
*
|
|
826
|
-
* @example
|
|
827
|
-
* ```typescript
|
|
828
|
-
* const serverCaps: CapabilitySet = {
|
|
829
|
-
* version: 1,
|
|
830
|
-
* capabilities: new Map([
|
|
831
|
-
* ['multi_ack', undefined],
|
|
832
|
-
* ['side-band-64k', undefined],
|
|
833
|
-
* ['thin-pack', undefined]
|
|
834
|
-
* ])
|
|
835
|
-
* };
|
|
836
|
-
*
|
|
837
|
-
* const clientPrefs: CapabilityEntry[] = [
|
|
838
|
-
* { name: 'multi_ack_detailed' }, // Not supported by server
|
|
839
|
-
* { name: 'multi_ack' }, // Supported
|
|
840
|
-
* { name: 'side-band-64k' }, // Supported
|
|
841
|
-
* { name: 'ofs-delta' }, // Not supported
|
|
842
|
-
* { name: 'agent', value: 'gitdo/1.0' } // Not in server caps
|
|
843
|
-
* ];
|
|
844
|
-
*
|
|
845
|
-
* const selected = selectFetchCapabilities(serverCaps, clientPrefs);
|
|
846
|
-
* // [
|
|
847
|
-
* // { name: 'multi_ack' },
|
|
848
|
-
* // { name: 'side-band-64k' }
|
|
849
|
-
* // ]
|
|
850
|
-
* ```
|
|
851
|
-
*/
|
|
852
|
-
export function selectFetchCapabilities(serverCaps, clientPrefs) {
|
|
853
|
-
const selected = [];
|
|
854
|
-
for (const pref of clientPrefs) {
|
|
855
|
-
if (serverCaps.capabilities.has(pref.name)) {
|
|
856
|
-
// Use the client's value, not the server's
|
|
857
|
-
selected.push(pref);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
return selected;
|
|
861
|
-
}
|
|
862
|
-
// ============================================================================
|
|
863
|
-
// Validation Functions
|
|
864
|
-
// ============================================================================
|
|
865
|
-
/**
|
|
866
|
-
* Validate that a capability name is well-formed.
|
|
867
|
-
*
|
|
868
|
-
* @description
|
|
869
|
-
* Checks that a capability name follows the Git protocol requirements.
|
|
870
|
-
* Capability names must be non-empty and cannot contain spaces, NUL bytes,
|
|
871
|
-
* or newline characters.
|
|
872
|
-
*
|
|
873
|
-
* @param name - The capability name to validate
|
|
874
|
-
* @returns True if the name is valid
|
|
875
|
-
*
|
|
876
|
-
* @example
|
|
877
|
-
* ```typescript
|
|
878
|
-
* isValidCapabilityName('multi_ack'); // true
|
|
879
|
-
* isValidCapabilityName('side-band-64k'); // true
|
|
880
|
-
* isValidCapabilityName('agent'); // true
|
|
881
|
-
* isValidCapabilityName(''); // false (empty)
|
|
882
|
-
* isValidCapabilityName('multi ack'); // false (contains space)
|
|
883
|
-
* isValidCapabilityName('cap\0name'); // false (contains NUL)
|
|
884
|
-
* isValidCapabilityName('cap\nname'); // false (contains newline)
|
|
885
|
-
* ```
|
|
886
|
-
*/
|
|
887
|
-
export function isValidCapabilityName(name) {
|
|
888
|
-
if (name === '') {
|
|
889
|
-
return false;
|
|
890
|
-
}
|
|
891
|
-
// Check for invalid characters (spaces, NUL, newlines)
|
|
892
|
-
if (/[\s\0\n]/.test(name)) {
|
|
893
|
-
return false;
|
|
894
|
-
}
|
|
895
|
-
return true;
|
|
896
|
-
}
|
|
897
|
-
/**
|
|
898
|
-
* Validate that required capabilities are present.
|
|
899
|
-
*
|
|
900
|
-
* @description
|
|
901
|
-
* Checks a capability set for the presence of all required capabilities.
|
|
902
|
-
* Returns an array of missing capability names. An empty array indicates
|
|
903
|
-
* all requirements are satisfied.
|
|
904
|
-
*
|
|
905
|
-
* @param capSet - The capability set to validate
|
|
906
|
-
* @param required - Array of required capability names
|
|
907
|
-
* @returns Array of missing capability names (empty if all present)
|
|
908
|
-
*
|
|
909
|
-
* @example
|
|
910
|
-
* ```typescript
|
|
911
|
-
* const caps: CapabilitySet = {
|
|
912
|
-
* version: 1,
|
|
913
|
-
* capabilities: new Map([
|
|
914
|
-
* ['multi_ack', undefined],
|
|
915
|
-
* ['side-band-64k', undefined]
|
|
916
|
-
* ])
|
|
917
|
-
* };
|
|
918
|
-
*
|
|
919
|
-
* // All present
|
|
920
|
-
* const missing1 = validateRequiredCapabilities(caps, ['multi_ack']);
|
|
921
|
-
* // []
|
|
922
|
-
*
|
|
923
|
-
* // Some missing
|
|
924
|
-
* const missing2 = validateRequiredCapabilities(caps, ['multi_ack', 'thin-pack', 'ofs-delta']);
|
|
925
|
-
* // ['thin-pack', 'ofs-delta']
|
|
926
|
-
*
|
|
927
|
-
* if (missing2.length > 0) {
|
|
928
|
-
* throw new Error(`Server missing capabilities: ${missing2.join(', ')}`);
|
|
929
|
-
* }
|
|
930
|
-
* ```
|
|
931
|
-
*/
|
|
932
|
-
export function validateRequiredCapabilities(capSet, required) {
|
|
933
|
-
const missing = [];
|
|
934
|
-
for (const reqCap of required) {
|
|
935
|
-
if (!capSet.capabilities.has(reqCap)) {
|
|
936
|
-
missing.push(reqCap);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
return missing;
|
|
940
|
-
}
|
|
941
|
-
//# sourceMappingURL=capabilities.js.map
|