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
package/dist/types/objects.js
DELETED
|
@@ -1,837 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Git Object Types and Serialization
|
|
3
|
-
*
|
|
4
|
-
* This module defines the core Git object types (blob, tree, commit, tag) and provides
|
|
5
|
-
* functions for serializing and deserializing these objects in the Git format.
|
|
6
|
-
*
|
|
7
|
-
* Git uses a content-addressable storage model where each object is identified by
|
|
8
|
-
* its SHA-1 hash. The format for each object type is:
|
|
9
|
-
* - Header: "{type} {size}\0"
|
|
10
|
-
* - Content: type-specific binary data
|
|
11
|
-
*
|
|
12
|
-
* @module types/objects
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```typescript
|
|
16
|
-
* import { serializeBlob, parseBlob, isBlob } from './types/objects'
|
|
17
|
-
*
|
|
18
|
-
* // Create and serialize a blob
|
|
19
|
-
* const content = new TextEncoder().encode('Hello, World!')
|
|
20
|
-
* const serialized = serializeBlob(content)
|
|
21
|
-
*
|
|
22
|
-
* // Parse it back
|
|
23
|
-
* const blob = parseBlob(serialized)
|
|
24
|
-
* console.log(blob.type) // 'blob'
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
// ============================================================================
|
|
28
|
-
// Validation Helpers
|
|
29
|
-
// ============================================================================
|
|
30
|
-
/**
|
|
31
|
-
* Valid SHA-1 hash pattern (40 lowercase hexadecimal characters).
|
|
32
|
-
*
|
|
33
|
-
* @description
|
|
34
|
-
* Regular expression for validating SHA-1 hashes used in Git.
|
|
35
|
-
* Matches exactly 40 lowercase hexadecimal characters.
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* if (SHA_PATTERN.test(input)) {
|
|
40
|
-
* // Valid SHA-1 format
|
|
41
|
-
* }
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
export const SHA_PATTERN = /^[0-9a-f]{40}$/;
|
|
45
|
-
/**
|
|
46
|
-
* Valid file modes in Git.
|
|
47
|
-
*
|
|
48
|
-
* @description
|
|
49
|
-
* The set of valid mode strings for tree entries:
|
|
50
|
-
* - '100644': Regular file (non-executable)
|
|
51
|
-
* - '100755': Executable file
|
|
52
|
-
* - '040000': Directory (tree)
|
|
53
|
-
* - '120000': Symbolic link
|
|
54
|
-
* - '160000': Git submodule (gitlink)
|
|
55
|
-
*/
|
|
56
|
-
export const VALID_MODES = new Set(['100644', '100755', '040000', '120000', '160000']);
|
|
57
|
-
/**
|
|
58
|
-
* Validate a SHA-1 hash string.
|
|
59
|
-
*
|
|
60
|
-
* @description
|
|
61
|
-
* Checks if a string is a valid Git SHA-1 hash (40 lowercase hex characters).
|
|
62
|
-
* Use this to validate user input or data from external sources.
|
|
63
|
-
*
|
|
64
|
-
* @param sha - The string to validate
|
|
65
|
-
* @returns True if the string is a valid SHA-1 hash
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```typescript
|
|
69
|
-
* if (isValidSha('abc123')) {
|
|
70
|
-
* console.log('Invalid SHA') // Too short
|
|
71
|
-
* }
|
|
72
|
-
*
|
|
73
|
-
* if (isValidSha('da39a3ee5e6b4b0d3255bfef95601890afd80709')) {
|
|
74
|
-
* console.log('Valid SHA')
|
|
75
|
-
* }
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
export function isValidSha(sha) {
|
|
79
|
-
return typeof sha === 'string' && SHA_PATTERN.test(sha);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Validate a Git object type string.
|
|
83
|
-
*
|
|
84
|
-
* @description
|
|
85
|
-
* Checks if a string is one of the four valid Git object types.
|
|
86
|
-
*
|
|
87
|
-
* @param type - The string to validate
|
|
88
|
-
* @returns True if the string is a valid object type
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* if (isValidObjectType(input)) {
|
|
93
|
-
* // input is 'blob' | 'tree' | 'commit' | 'tag'
|
|
94
|
-
* }
|
|
95
|
-
* ```
|
|
96
|
-
*/
|
|
97
|
-
export function isValidObjectType(type) {
|
|
98
|
-
return type === 'blob' || type === 'tree' || type === 'commit' || type === 'tag';
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Validate a tree entry mode string.
|
|
102
|
-
*
|
|
103
|
-
* @description
|
|
104
|
-
* Checks if a string is a valid Git tree entry mode.
|
|
105
|
-
*
|
|
106
|
-
* @param mode - The mode string to validate
|
|
107
|
-
* @returns True if the mode is valid
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```typescript
|
|
111
|
-
* if (isValidMode('100644')) {
|
|
112
|
-
* console.log('Valid regular file mode')
|
|
113
|
-
* }
|
|
114
|
-
* ```
|
|
115
|
-
*/
|
|
116
|
-
export function isValidMode(mode) {
|
|
117
|
-
return VALID_MODES.has(mode);
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Validate a tree entry object.
|
|
121
|
-
*
|
|
122
|
-
* @description
|
|
123
|
-
* Validates all fields of a tree entry including mode, name, and SHA.
|
|
124
|
-
* Returns an object with validity status and optional error message.
|
|
125
|
-
*
|
|
126
|
-
* @param entry - The tree entry to validate
|
|
127
|
-
* @returns Validation result with isValid boolean and optional error message
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```typescript
|
|
131
|
-
* const result = validateTreeEntry({ mode: '100644', name: 'file.txt', sha: 'abc...' })
|
|
132
|
-
* if (!result.isValid) {
|
|
133
|
-
* console.error(result.error)
|
|
134
|
-
* }
|
|
135
|
-
* ```
|
|
136
|
-
*/
|
|
137
|
-
export function validateTreeEntry(entry) {
|
|
138
|
-
if (!isValidMode(entry.mode)) {
|
|
139
|
-
return { isValid: false, error: `Invalid mode: ${entry.mode}. Valid modes: ${Array.from(VALID_MODES).join(', ')}` };
|
|
140
|
-
}
|
|
141
|
-
if (!entry.name || typeof entry.name !== 'string') {
|
|
142
|
-
return { isValid: false, error: 'Entry name is required and must be a string' };
|
|
143
|
-
}
|
|
144
|
-
if (entry.name.includes('/') || entry.name.includes('\0')) {
|
|
145
|
-
return { isValid: false, error: 'Entry name cannot contain "/" or null characters' };
|
|
146
|
-
}
|
|
147
|
-
if (!isValidSha(entry.sha)) {
|
|
148
|
-
return { isValid: false, error: `Invalid SHA: ${entry.sha}. Must be 40 lowercase hex characters` };
|
|
149
|
-
}
|
|
150
|
-
return { isValid: true };
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Validate an author object.
|
|
154
|
-
*
|
|
155
|
-
* @description
|
|
156
|
-
* Validates all fields of an Author object including name, email,
|
|
157
|
-
* timestamp, and timezone format.
|
|
158
|
-
*
|
|
159
|
-
* @param author - The author object to validate
|
|
160
|
-
* @returns Validation result with isValid boolean and optional error message
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* ```typescript
|
|
164
|
-
* const result = validateAuthor({
|
|
165
|
-
* name: 'Alice',
|
|
166
|
-
* email: 'alice@example.com',
|
|
167
|
-
* timestamp: 1704067200,
|
|
168
|
-
* timezone: '+0000'
|
|
169
|
-
* })
|
|
170
|
-
* if (!result.isValid) {
|
|
171
|
-
* console.error(result.error)
|
|
172
|
-
* }
|
|
173
|
-
* ```
|
|
174
|
-
*/
|
|
175
|
-
export function validateAuthor(author) {
|
|
176
|
-
if (!author.name || typeof author.name !== 'string') {
|
|
177
|
-
return { isValid: false, error: 'Author name is required and must be a string' };
|
|
178
|
-
}
|
|
179
|
-
if (!author.email || typeof author.email !== 'string') {
|
|
180
|
-
return { isValid: false, error: 'Author email is required and must be a string' };
|
|
181
|
-
}
|
|
182
|
-
if (typeof author.timestamp !== 'number' || !Number.isInteger(author.timestamp) || author.timestamp < 0) {
|
|
183
|
-
return { isValid: false, error: 'Timestamp must be a non-negative integer (Unix seconds)' };
|
|
184
|
-
}
|
|
185
|
-
if (!/^[+-]\d{4}$/.test(author.timezone)) {
|
|
186
|
-
return { isValid: false, error: `Invalid timezone format: ${author.timezone}. Expected +/-HHMM (e.g., +0530, -0800)` };
|
|
187
|
-
}
|
|
188
|
-
return { isValid: true };
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Validate a commit object (excluding type and data fields).
|
|
192
|
-
*
|
|
193
|
-
* @description
|
|
194
|
-
* Validates the structure and content of commit fields.
|
|
195
|
-
* Checks tree SHA, parent SHAs, author, committer, and message.
|
|
196
|
-
*
|
|
197
|
-
* @param commit - The commit data to validate
|
|
198
|
-
* @returns Validation result with isValid boolean and optional error message
|
|
199
|
-
*
|
|
200
|
-
* @example
|
|
201
|
-
* ```typescript
|
|
202
|
-
* const result = validateCommit({
|
|
203
|
-
* tree: 'abc123...',
|
|
204
|
-
* parents: ['parent1...'],
|
|
205
|
-
* author: { ... },
|
|
206
|
-
* committer: { ... },
|
|
207
|
-
* message: 'Initial commit'
|
|
208
|
-
* })
|
|
209
|
-
* ```
|
|
210
|
-
*/
|
|
211
|
-
export function validateCommit(commit) {
|
|
212
|
-
if (!isValidSha(commit.tree)) {
|
|
213
|
-
return { isValid: false, error: `Invalid tree SHA: ${commit.tree}` };
|
|
214
|
-
}
|
|
215
|
-
for (let i = 0; i < commit.parents.length; i++) {
|
|
216
|
-
if (!isValidSha(commit.parents[i])) {
|
|
217
|
-
return { isValid: false, error: `Invalid parent SHA at index ${i}: ${commit.parents[i]}` };
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
const authorResult = validateAuthor(commit.author);
|
|
221
|
-
if (!authorResult.isValid) {
|
|
222
|
-
return { isValid: false, error: `Invalid author: ${authorResult.error}` };
|
|
223
|
-
}
|
|
224
|
-
const committerResult = validateAuthor(commit.committer);
|
|
225
|
-
if (!committerResult.isValid) {
|
|
226
|
-
return { isValid: false, error: `Invalid committer: ${committerResult.error}` };
|
|
227
|
-
}
|
|
228
|
-
if (typeof commit.message !== 'string') {
|
|
229
|
-
return { isValid: false, error: 'Commit message must be a string' };
|
|
230
|
-
}
|
|
231
|
-
return { isValid: true };
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Validate a tag object (excluding type and data fields).
|
|
235
|
-
*
|
|
236
|
-
* @description
|
|
237
|
-
* Validates the structure and content of tag fields.
|
|
238
|
-
* Checks object SHA, object type, name, tagger, and message.
|
|
239
|
-
*
|
|
240
|
-
* @param tag - The tag data to validate
|
|
241
|
-
* @returns Validation result with isValid boolean and optional error message
|
|
242
|
-
*
|
|
243
|
-
* @example
|
|
244
|
-
* ```typescript
|
|
245
|
-
* const result = validateTag({
|
|
246
|
-
* object: 'commitsha...',
|
|
247
|
-
* objectType: 'commit',
|
|
248
|
-
* name: 'v1.0.0',
|
|
249
|
-
* tagger: { ... },
|
|
250
|
-
* message: 'Release v1.0.0'
|
|
251
|
-
* })
|
|
252
|
-
* ```
|
|
253
|
-
*/
|
|
254
|
-
export function validateTag(tag) {
|
|
255
|
-
if (!isValidSha(tag.object)) {
|
|
256
|
-
return { isValid: false, error: `Invalid object SHA: ${tag.object}` };
|
|
257
|
-
}
|
|
258
|
-
if (!isValidObjectType(tag.objectType)) {
|
|
259
|
-
return { isValid: false, error: `Invalid object type: ${tag.objectType}` };
|
|
260
|
-
}
|
|
261
|
-
if (!tag.name || typeof tag.name !== 'string') {
|
|
262
|
-
return { isValid: false, error: 'Tag name is required and must be a string' };
|
|
263
|
-
}
|
|
264
|
-
if (tag.tagger) {
|
|
265
|
-
const taggerResult = validateAuthor(tag.tagger);
|
|
266
|
-
if (!taggerResult.isValid) {
|
|
267
|
-
return { isValid: false, error: `Invalid tagger: ${taggerResult.error}` };
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
if (typeof tag.message !== 'string') {
|
|
271
|
-
return { isValid: false, error: 'Tag message must be a string' };
|
|
272
|
-
}
|
|
273
|
-
return { isValid: true };
|
|
274
|
-
}
|
|
275
|
-
// ============================================================================
|
|
276
|
-
// Type Guards
|
|
277
|
-
// ============================================================================
|
|
278
|
-
/**
|
|
279
|
-
* Type guard to check if a GitObject is a BlobObject.
|
|
280
|
-
*
|
|
281
|
-
* @description
|
|
282
|
-
* Narrows the type of a GitObject to BlobObject based on the type field.
|
|
283
|
-
*
|
|
284
|
-
* @param obj - The Git object to check
|
|
285
|
-
* @returns True if the object is a blob, false otherwise
|
|
286
|
-
*
|
|
287
|
-
* @example
|
|
288
|
-
* ```typescript
|
|
289
|
-
* const obj: GitObject = getObject(sha)
|
|
290
|
-
* if (isBlob(obj)) {
|
|
291
|
-
* // obj is now typed as BlobObject
|
|
292
|
-
* const content = new TextDecoder().decode(obj.data)
|
|
293
|
-
* }
|
|
294
|
-
* ```
|
|
295
|
-
*/
|
|
296
|
-
export function isBlob(obj) {
|
|
297
|
-
return obj.type === 'blob';
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* Type guard to check if a GitObject is a TreeObject.
|
|
301
|
-
*
|
|
302
|
-
* @description
|
|
303
|
-
* Narrows the type of a GitObject to TreeObject based on the type field.
|
|
304
|
-
*
|
|
305
|
-
* @param obj - The Git object to check
|
|
306
|
-
* @returns True if the object is a tree, false otherwise
|
|
307
|
-
*
|
|
308
|
-
* @example
|
|
309
|
-
* ```typescript
|
|
310
|
-
* const obj: GitObject = getObject(sha)
|
|
311
|
-
* if (isTree(obj)) {
|
|
312
|
-
* // obj is now typed as TreeObject
|
|
313
|
-
* for (const entry of obj.entries) {
|
|
314
|
-
* console.log(entry.name, entry.mode)
|
|
315
|
-
* }
|
|
316
|
-
* }
|
|
317
|
-
* ```
|
|
318
|
-
*/
|
|
319
|
-
export function isTree(obj) {
|
|
320
|
-
return obj.type === 'tree';
|
|
321
|
-
}
|
|
322
|
-
/**
|
|
323
|
-
* Type guard to check if a GitObject is a CommitObject.
|
|
324
|
-
*
|
|
325
|
-
* @description
|
|
326
|
-
* Narrows the type of a GitObject to CommitObject based on the type field.
|
|
327
|
-
*
|
|
328
|
-
* @param obj - The Git object to check
|
|
329
|
-
* @returns True if the object is a commit, false otherwise
|
|
330
|
-
*
|
|
331
|
-
* @example
|
|
332
|
-
* ```typescript
|
|
333
|
-
* const obj: GitObject = getObject(sha)
|
|
334
|
-
* if (isCommit(obj)) {
|
|
335
|
-
* // obj is now typed as CommitObject
|
|
336
|
-
* console.log(obj.message, obj.author.name)
|
|
337
|
-
* }
|
|
338
|
-
* ```
|
|
339
|
-
*/
|
|
340
|
-
export function isCommit(obj) {
|
|
341
|
-
return obj.type === 'commit';
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* Type guard to check if a GitObject is a TagObject.
|
|
345
|
-
*
|
|
346
|
-
* @description
|
|
347
|
-
* Narrows the type of a GitObject to TagObject based on the type field.
|
|
348
|
-
*
|
|
349
|
-
* @param obj - The Git object to check
|
|
350
|
-
* @returns True if the object is a tag, false otherwise
|
|
351
|
-
*
|
|
352
|
-
* @example
|
|
353
|
-
* ```typescript
|
|
354
|
-
* const obj: GitObject = getObject(sha)
|
|
355
|
-
* if (isTag(obj)) {
|
|
356
|
-
* // obj is now typed as TagObject
|
|
357
|
-
* console.log(obj.name, obj.message)
|
|
358
|
-
* }
|
|
359
|
-
* ```
|
|
360
|
-
*/
|
|
361
|
-
export function isTag(obj) {
|
|
362
|
-
return obj.type === 'tag';
|
|
363
|
-
}
|
|
364
|
-
// ============================================================================
|
|
365
|
-
// Helper Functions (internal)
|
|
366
|
-
// ============================================================================
|
|
367
|
-
const encoder = new TextEncoder();
|
|
368
|
-
const decoder = new TextDecoder();
|
|
369
|
-
/**
|
|
370
|
-
* Convert a hexadecimal string to a Uint8Array.
|
|
371
|
-
*
|
|
372
|
-
* @param hex - Hexadecimal string (must have even length)
|
|
373
|
-
* @returns Binary representation as Uint8Array
|
|
374
|
-
* @internal
|
|
375
|
-
*/
|
|
376
|
-
function hexToBytes(hex) {
|
|
377
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
378
|
-
for (let i = 0; i < hex.length; i += 2) {
|
|
379
|
-
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
|
|
380
|
-
}
|
|
381
|
-
return bytes;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Convert a Uint8Array to a lowercase hexadecimal string.
|
|
385
|
-
*
|
|
386
|
-
* @param bytes - Binary data to convert
|
|
387
|
-
* @returns Lowercase hexadecimal string
|
|
388
|
-
* @internal
|
|
389
|
-
*/
|
|
390
|
-
function bytesToHex(bytes) {
|
|
391
|
-
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Format an Author object as a Git author/committer/tagger line.
|
|
395
|
-
*
|
|
396
|
-
* @param prefix - Line prefix ('author', 'committer', or 'tagger')
|
|
397
|
-
* @param author - Author information
|
|
398
|
-
* @returns Formatted line string
|
|
399
|
-
* @internal
|
|
400
|
-
*/
|
|
401
|
-
function formatAuthor(prefix, author) {
|
|
402
|
-
return `${prefix} ${author.name} <${author.email}> ${author.timestamp} ${author.timezone}`;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Parse a Git author/committer/tagger line into an Author object.
|
|
406
|
-
*
|
|
407
|
-
* @param line - The full line including prefix
|
|
408
|
-
* @returns Parsed Author object
|
|
409
|
-
* @throws Error if the line format is invalid
|
|
410
|
-
* @internal
|
|
411
|
-
*/
|
|
412
|
-
function parseAuthorLine(line) {
|
|
413
|
-
// Format: "author Name <email> timestamp timezone"
|
|
414
|
-
// or "committer Name <email> timestamp timezone"
|
|
415
|
-
// or "tagger Name <email> timestamp timezone"
|
|
416
|
-
const match = line.match(/^(?:author|committer|tagger) (.+) <(.+)> (\d+) ([+-]\d{4})$/);
|
|
417
|
-
if (!match) {
|
|
418
|
-
throw new Error(`Invalid author line: ${line}`);
|
|
419
|
-
}
|
|
420
|
-
return {
|
|
421
|
-
name: match[1],
|
|
422
|
-
email: match[2],
|
|
423
|
-
timestamp: parseInt(match[3], 10),
|
|
424
|
-
timezone: match[4]
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
// ============================================================================
|
|
428
|
-
// Serialization Functions
|
|
429
|
-
// ============================================================================
|
|
430
|
-
/**
|
|
431
|
-
* Serialize raw blob data into Git blob object format.
|
|
432
|
-
*
|
|
433
|
-
* @description
|
|
434
|
-
* Creates a complete Git blob object with header: "blob {size}\0{content}"
|
|
435
|
-
* This format is used for hashing and storage.
|
|
436
|
-
*
|
|
437
|
-
* @param data - Raw file content as binary data
|
|
438
|
-
* @returns Complete blob object with Git header
|
|
439
|
-
*
|
|
440
|
-
* @example
|
|
441
|
-
* ```typescript
|
|
442
|
-
* const content = new TextEncoder().encode('Hello, World!')
|
|
443
|
-
* const blob = serializeBlob(content)
|
|
444
|
-
* // blob contains: "blob 13\0Hello, World!"
|
|
445
|
-
*
|
|
446
|
-
* // Hash it to get the SHA
|
|
447
|
-
* const sha = await sha1(blob)
|
|
448
|
-
* ```
|
|
449
|
-
*/
|
|
450
|
-
export function serializeBlob(data) {
|
|
451
|
-
// Git format: "blob <size>\0<content>"
|
|
452
|
-
const header = encoder.encode(`blob ${data.length}\0`);
|
|
453
|
-
const result = new Uint8Array(header.length + data.length);
|
|
454
|
-
result.set(header);
|
|
455
|
-
result.set(data, header.length);
|
|
456
|
-
return result;
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Serialize tree entries into Git tree object format.
|
|
460
|
-
*
|
|
461
|
-
* @description
|
|
462
|
-
* Creates a complete Git tree object with header and sorted entries.
|
|
463
|
-
* Each entry format: "{mode} {name}\0{20-byte-sha}"
|
|
464
|
-
* Entries are sorted by name with directories treated as having trailing slashes.
|
|
465
|
-
*
|
|
466
|
-
* @param entries - Array of tree entries to serialize
|
|
467
|
-
* @returns Complete tree object with Git header
|
|
468
|
-
*
|
|
469
|
-
* @example
|
|
470
|
-
* ```typescript
|
|
471
|
-
* const entries: TreeEntry[] = [
|
|
472
|
-
* { mode: '100644', name: 'file.txt', sha: 'abc...' },
|
|
473
|
-
* { mode: '040000', name: 'src', sha: 'def...' }
|
|
474
|
-
* ]
|
|
475
|
-
* const tree = serializeTree(entries)
|
|
476
|
-
* const sha = await sha1(tree)
|
|
477
|
-
* ```
|
|
478
|
-
*/
|
|
479
|
-
export function serializeTree(entries) {
|
|
480
|
-
// Git format: "tree <size>\0<entries>"
|
|
481
|
-
// Each entry: "<mode> <name>\0<20-byte-sha>"
|
|
482
|
-
// Sort entries by name (Git sorts directories as if they have trailing /)
|
|
483
|
-
const sortedEntries = [...entries].sort((a, b) => {
|
|
484
|
-
const aName = a.mode === '040000' ? a.name + '/' : a.name;
|
|
485
|
-
const bName = b.mode === '040000' ? b.name + '/' : b.name;
|
|
486
|
-
return aName.localeCompare(bName);
|
|
487
|
-
});
|
|
488
|
-
// Build entry content
|
|
489
|
-
const entryParts = [];
|
|
490
|
-
for (const entry of sortedEntries) {
|
|
491
|
-
const modeName = encoder.encode(`${entry.mode} ${entry.name}\0`);
|
|
492
|
-
const sha20 = hexToBytes(entry.sha);
|
|
493
|
-
const entryData = new Uint8Array(modeName.length + 20);
|
|
494
|
-
entryData.set(modeName);
|
|
495
|
-
entryData.set(sha20, modeName.length);
|
|
496
|
-
entryParts.push(entryData);
|
|
497
|
-
}
|
|
498
|
-
// Calculate total content length
|
|
499
|
-
const contentLength = entryParts.reduce((sum, part) => sum + part.length, 0);
|
|
500
|
-
const content = new Uint8Array(contentLength);
|
|
501
|
-
let offset = 0;
|
|
502
|
-
for (const part of entryParts) {
|
|
503
|
-
content.set(part, offset);
|
|
504
|
-
offset += part.length;
|
|
505
|
-
}
|
|
506
|
-
// Add header
|
|
507
|
-
const header = encoder.encode(`tree ${contentLength}\0`);
|
|
508
|
-
const result = new Uint8Array(header.length + contentLength);
|
|
509
|
-
result.set(header);
|
|
510
|
-
result.set(content, header.length);
|
|
511
|
-
return result;
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Serialize commit data into Git commit object format.
|
|
515
|
-
*
|
|
516
|
-
* @description
|
|
517
|
-
* Creates a complete Git commit object with header and formatted content.
|
|
518
|
-
* The content includes tree SHA, parent SHAs, author, committer, and message.
|
|
519
|
-
*
|
|
520
|
-
* @param commit - Commit data (without 'type' and 'data' fields)
|
|
521
|
-
* @returns Complete commit object with Git header
|
|
522
|
-
*
|
|
523
|
-
* @example
|
|
524
|
-
* ```typescript
|
|
525
|
-
* const commit = serializeCommit({
|
|
526
|
-
* tree: 'abc123...',
|
|
527
|
-
* parents: ['parent1...'],
|
|
528
|
-
* author: { name: 'Alice', email: 'alice@example.com', timestamp: 1704067200, timezone: '+0000' },
|
|
529
|
-
* committer: { name: 'Alice', email: 'alice@example.com', timestamp: 1704067200, timezone: '+0000' },
|
|
530
|
-
* message: 'Initial commit'
|
|
531
|
-
* })
|
|
532
|
-
* const sha = await sha1(commit)
|
|
533
|
-
* ```
|
|
534
|
-
*/
|
|
535
|
-
export function serializeCommit(commit) {
|
|
536
|
-
// Git format: "commit <size>\0<content>"
|
|
537
|
-
// Content:
|
|
538
|
-
// tree <sha>\n
|
|
539
|
-
// parent <sha>\n (for each parent)
|
|
540
|
-
// author <name> <email> <timestamp> <timezone>\n
|
|
541
|
-
// committer <name> <email> <timestamp> <timezone>\n
|
|
542
|
-
// \n
|
|
543
|
-
// <message>
|
|
544
|
-
const lines = [];
|
|
545
|
-
lines.push(`tree ${commit.tree}`);
|
|
546
|
-
for (const parent of commit.parents) {
|
|
547
|
-
lines.push(`parent ${parent}`);
|
|
548
|
-
}
|
|
549
|
-
lines.push(formatAuthor('author', commit.author));
|
|
550
|
-
lines.push(formatAuthor('committer', commit.committer));
|
|
551
|
-
lines.push('');
|
|
552
|
-
lines.push(commit.message);
|
|
553
|
-
const content = lines.join('\n');
|
|
554
|
-
const header = `commit ${encoder.encode(content).length}\0`;
|
|
555
|
-
return encoder.encode(header + content);
|
|
556
|
-
}
|
|
557
|
-
/**
|
|
558
|
-
* Serialize tag data into Git tag object format.
|
|
559
|
-
*
|
|
560
|
-
* @description
|
|
561
|
-
* Creates a complete Git tag object with header and formatted content.
|
|
562
|
-
* The content includes object SHA, object type, tag name, tagger (optional), and message.
|
|
563
|
-
*
|
|
564
|
-
* @param tag - Tag data (without 'type' and 'data' fields)
|
|
565
|
-
* @returns Complete tag object with Git header
|
|
566
|
-
*
|
|
567
|
-
* @example
|
|
568
|
-
* ```typescript
|
|
569
|
-
* const tag = serializeTag({
|
|
570
|
-
* object: 'commitsha...',
|
|
571
|
-
* objectType: 'commit',
|
|
572
|
-
* name: 'v1.0.0',
|
|
573
|
-
* tagger: { name: 'Bob', email: 'bob@example.com', timestamp: 1704067200, timezone: '+0000' },
|
|
574
|
-
* message: 'Release v1.0.0'
|
|
575
|
-
* })
|
|
576
|
-
* const sha = await sha1(tag)
|
|
577
|
-
* ```
|
|
578
|
-
*/
|
|
579
|
-
export function serializeTag(tag) {
|
|
580
|
-
// Git format: "tag <size>\0<content>"
|
|
581
|
-
// Content:
|
|
582
|
-
// object <sha>\n
|
|
583
|
-
// type <objecttype>\n
|
|
584
|
-
// tag <name>\n
|
|
585
|
-
// tagger <name> <email> <timestamp> <timezone>\n
|
|
586
|
-
// \n
|
|
587
|
-
// <message>
|
|
588
|
-
const lines = [];
|
|
589
|
-
lines.push(`object ${tag.object}`);
|
|
590
|
-
lines.push(`type ${tag.objectType}`);
|
|
591
|
-
lines.push(`tag ${tag.name}`);
|
|
592
|
-
if (tag.tagger) {
|
|
593
|
-
lines.push(formatAuthor('tagger', tag.tagger));
|
|
594
|
-
}
|
|
595
|
-
lines.push('');
|
|
596
|
-
lines.push(tag.message);
|
|
597
|
-
const content = lines.join('\n');
|
|
598
|
-
const header = `tag ${encoder.encode(content).length}\0`;
|
|
599
|
-
return encoder.encode(header + content);
|
|
600
|
-
}
|
|
601
|
-
// ============================================================================
|
|
602
|
-
// Deserialization (Parsing) Functions
|
|
603
|
-
// ============================================================================
|
|
604
|
-
/**
|
|
605
|
-
* Parse a Git blob object from its serialized format.
|
|
606
|
-
*
|
|
607
|
-
* @description
|
|
608
|
-
* Parses a complete Git blob object (with header) back into a BlobObject.
|
|
609
|
-
* Validates the header format and extracts the content.
|
|
610
|
-
*
|
|
611
|
-
* @param data - Complete blob object data including Git header
|
|
612
|
-
* @returns Parsed BlobObject
|
|
613
|
-
* @throws Error if the data is not a valid blob object (missing null byte or invalid header)
|
|
614
|
-
*
|
|
615
|
-
* @example
|
|
616
|
-
* ```typescript
|
|
617
|
-
* const rawBlob = await storage.getObject(sha)
|
|
618
|
-
* const blob = parseBlob(rawBlob)
|
|
619
|
-
* const content = new TextDecoder().decode(blob.data)
|
|
620
|
-
* ```
|
|
621
|
-
*/
|
|
622
|
-
export function parseBlob(data) {
|
|
623
|
-
// Git format: "blob <size>\0<content>"
|
|
624
|
-
// Find the null byte to separate header from content
|
|
625
|
-
const nullIndex = data.indexOf(0);
|
|
626
|
-
if (nullIndex === -1) {
|
|
627
|
-
throw new Error('Invalid blob: no null byte found');
|
|
628
|
-
}
|
|
629
|
-
const header = decoder.decode(data.slice(0, nullIndex));
|
|
630
|
-
const match = header.match(/^blob (\d+)$/);
|
|
631
|
-
if (!match) {
|
|
632
|
-
throw new Error(`Invalid blob header: ${header}`);
|
|
633
|
-
}
|
|
634
|
-
const content = data.slice(nullIndex + 1);
|
|
635
|
-
return {
|
|
636
|
-
type: 'blob',
|
|
637
|
-
data: content
|
|
638
|
-
};
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Parse a Git tree object from its serialized format.
|
|
642
|
-
*
|
|
643
|
-
* @description
|
|
644
|
-
* Parses a complete Git tree object (with header) back into a TreeObject.
|
|
645
|
-
* Extracts all tree entries with their modes, names, and SHA references.
|
|
646
|
-
*
|
|
647
|
-
* @param data - Complete tree object data including Git header
|
|
648
|
-
* @returns Parsed TreeObject with entries array
|
|
649
|
-
* @throws Error if the data is not a valid tree object (missing null byte or invalid header)
|
|
650
|
-
*
|
|
651
|
-
* @example
|
|
652
|
-
* ```typescript
|
|
653
|
-
* const rawTree = await storage.getObject(sha)
|
|
654
|
-
* const tree = parseTree(rawTree)
|
|
655
|
-
* for (const entry of tree.entries) {
|
|
656
|
-
* console.log(`${entry.mode} ${entry.name} ${entry.sha}`)
|
|
657
|
-
* }
|
|
658
|
-
* ```
|
|
659
|
-
*/
|
|
660
|
-
export function parseTree(data) {
|
|
661
|
-
// Git format: "tree <size>\0<entries>"
|
|
662
|
-
// Each entry: "<mode> <name>\0<20-byte-sha>"
|
|
663
|
-
const nullIndex = data.indexOf(0);
|
|
664
|
-
if (nullIndex === -1) {
|
|
665
|
-
throw new Error('Invalid tree: no null byte found');
|
|
666
|
-
}
|
|
667
|
-
const header = decoder.decode(data.slice(0, nullIndex));
|
|
668
|
-
const match = header.match(/^tree (\d+)$/);
|
|
669
|
-
if (!match) {
|
|
670
|
-
throw new Error(`Invalid tree header: ${header}`);
|
|
671
|
-
}
|
|
672
|
-
const entries = [];
|
|
673
|
-
let offset = nullIndex + 1;
|
|
674
|
-
while (offset < data.length) {
|
|
675
|
-
// Find the null byte after mode+name
|
|
676
|
-
let entryNullIndex = offset;
|
|
677
|
-
while (entryNullIndex < data.length && data[entryNullIndex] !== 0) {
|
|
678
|
-
entryNullIndex++;
|
|
679
|
-
}
|
|
680
|
-
const modeNameStr = decoder.decode(data.slice(offset, entryNullIndex));
|
|
681
|
-
const spaceIndex = modeNameStr.indexOf(' ');
|
|
682
|
-
const mode = modeNameStr.slice(0, spaceIndex);
|
|
683
|
-
const name = modeNameStr.slice(spaceIndex + 1);
|
|
684
|
-
// Read 20-byte SHA
|
|
685
|
-
const sha20 = data.slice(entryNullIndex + 1, entryNullIndex + 21);
|
|
686
|
-
const sha = bytesToHex(sha20);
|
|
687
|
-
entries.push({ mode, name, sha });
|
|
688
|
-
offset = entryNullIndex + 21;
|
|
689
|
-
}
|
|
690
|
-
return {
|
|
691
|
-
type: 'tree',
|
|
692
|
-
data: data.slice(nullIndex + 1),
|
|
693
|
-
entries
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
/**
|
|
697
|
-
* Parse a Git commit object from its serialized format.
|
|
698
|
-
*
|
|
699
|
-
* @description
|
|
700
|
-
* Parses a complete Git commit object (with header) back into a CommitObject.
|
|
701
|
-
* Extracts tree SHA, parent SHAs, author, committer, and message.
|
|
702
|
-
*
|
|
703
|
-
* @param data - Complete commit object data including Git header
|
|
704
|
-
* @returns Parsed CommitObject
|
|
705
|
-
* @throws Error if the data is not a valid commit object (missing null byte, invalid header, or missing author/committer)
|
|
706
|
-
*
|
|
707
|
-
* @example
|
|
708
|
-
* ```typescript
|
|
709
|
-
* const rawCommit = await storage.getObject(sha)
|
|
710
|
-
* const commit = parseCommit(rawCommit)
|
|
711
|
-
* console.log(`Author: ${commit.author.name}`)
|
|
712
|
-
* console.log(`Message: ${commit.message}`)
|
|
713
|
-
* console.log(`Parents: ${commit.parents.length}`)
|
|
714
|
-
* ```
|
|
715
|
-
*/
|
|
716
|
-
export function parseCommit(data) {
|
|
717
|
-
// Git format: "commit <size>\0<content>"
|
|
718
|
-
const nullIndex = data.indexOf(0);
|
|
719
|
-
if (nullIndex === -1) {
|
|
720
|
-
throw new Error('Invalid commit: no null byte found');
|
|
721
|
-
}
|
|
722
|
-
const header = decoder.decode(data.slice(0, nullIndex));
|
|
723
|
-
const match = header.match(/^commit (\d+)$/);
|
|
724
|
-
if (!match) {
|
|
725
|
-
throw new Error(`Invalid commit header: ${header}`);
|
|
726
|
-
}
|
|
727
|
-
const content = decoder.decode(data.slice(nullIndex + 1));
|
|
728
|
-
const lines = content.split('\n');
|
|
729
|
-
let tree = '';
|
|
730
|
-
const parents = [];
|
|
731
|
-
let author = null;
|
|
732
|
-
let committer = null;
|
|
733
|
-
let messageStartIndex = 0;
|
|
734
|
-
for (let i = 0; i < lines.length; i++) {
|
|
735
|
-
const line = lines[i];
|
|
736
|
-
if (line === '') {
|
|
737
|
-
// Empty line separates headers from message
|
|
738
|
-
messageStartIndex = i + 1;
|
|
739
|
-
break;
|
|
740
|
-
}
|
|
741
|
-
if (line.startsWith('tree ')) {
|
|
742
|
-
tree = line.slice(5);
|
|
743
|
-
}
|
|
744
|
-
else if (line.startsWith('parent ')) {
|
|
745
|
-
parents.push(line.slice(7));
|
|
746
|
-
}
|
|
747
|
-
else if (line.startsWith('author ')) {
|
|
748
|
-
author = parseAuthorLine(line);
|
|
749
|
-
}
|
|
750
|
-
else if (line.startsWith('committer ')) {
|
|
751
|
-
committer = parseAuthorLine(line);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
if (!author || !committer) {
|
|
755
|
-
throw new Error('Invalid commit: missing author or committer');
|
|
756
|
-
}
|
|
757
|
-
const message = lines.slice(messageStartIndex).join('\n');
|
|
758
|
-
return {
|
|
759
|
-
type: 'commit',
|
|
760
|
-
data: data.slice(nullIndex + 1),
|
|
761
|
-
tree,
|
|
762
|
-
parents,
|
|
763
|
-
author,
|
|
764
|
-
committer,
|
|
765
|
-
message
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* Parse a Git tag object from its serialized format.
|
|
770
|
-
*
|
|
771
|
-
* @description
|
|
772
|
-
* Parses a complete Git tag object (with header) back into a TagObject.
|
|
773
|
-
* Extracts object SHA, object type, tag name, tagger, and message.
|
|
774
|
-
*
|
|
775
|
-
* @param data - Complete tag object data including Git header
|
|
776
|
-
* @returns Parsed TagObject
|
|
777
|
-
* @throws Error if the data is not a valid tag object (missing null byte, invalid header, or missing tagger)
|
|
778
|
-
*
|
|
779
|
-
* @example
|
|
780
|
-
* ```typescript
|
|
781
|
-
* const rawTag = await storage.getObject(sha)
|
|
782
|
-
* const tag = parseTag(rawTag)
|
|
783
|
-
* console.log(`Tag: ${tag.name}`)
|
|
784
|
-
* console.log(`Points to: ${tag.object} (${tag.objectType})`)
|
|
785
|
-
* console.log(`Message: ${tag.message}`)
|
|
786
|
-
* ```
|
|
787
|
-
*/
|
|
788
|
-
export function parseTag(data) {
|
|
789
|
-
// Git format: "tag <size>\0<content>"
|
|
790
|
-
const nullIndex = data.indexOf(0);
|
|
791
|
-
if (nullIndex === -1) {
|
|
792
|
-
throw new Error('Invalid tag: no null byte found');
|
|
793
|
-
}
|
|
794
|
-
const header = decoder.decode(data.slice(0, nullIndex));
|
|
795
|
-
const match = header.match(/^tag (\d+)$/);
|
|
796
|
-
if (!match) {
|
|
797
|
-
throw new Error(`Invalid tag header: ${header}`);
|
|
798
|
-
}
|
|
799
|
-
const content = decoder.decode(data.slice(nullIndex + 1));
|
|
800
|
-
const lines = content.split('\n');
|
|
801
|
-
let object = '';
|
|
802
|
-
let objectType = 'commit';
|
|
803
|
-
let name = '';
|
|
804
|
-
let tagger = null;
|
|
805
|
-
let messageStartIndex = 0;
|
|
806
|
-
for (let i = 0; i < lines.length; i++) {
|
|
807
|
-
const line = lines[i];
|
|
808
|
-
if (line === '') {
|
|
809
|
-
// Empty line separates headers from message
|
|
810
|
-
messageStartIndex = i + 1;
|
|
811
|
-
break;
|
|
812
|
-
}
|
|
813
|
-
if (line.startsWith('object ')) {
|
|
814
|
-
object = line.slice(7);
|
|
815
|
-
}
|
|
816
|
-
else if (line.startsWith('type ')) {
|
|
817
|
-
objectType = line.slice(5);
|
|
818
|
-
}
|
|
819
|
-
else if (line.startsWith('tag ')) {
|
|
820
|
-
name = line.slice(4);
|
|
821
|
-
}
|
|
822
|
-
else if (line.startsWith('tagger ')) {
|
|
823
|
-
tagger = parseAuthorLine(line);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
const message = lines.slice(messageStartIndex).join('\n');
|
|
827
|
-
return {
|
|
828
|
-
type: 'tag',
|
|
829
|
-
data: data.slice(nullIndex + 1),
|
|
830
|
-
object,
|
|
831
|
-
objectType,
|
|
832
|
-
name,
|
|
833
|
-
tagger: tagger ?? undefined,
|
|
834
|
-
message
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
//# sourceMappingURL=objects.js.map
|