gitx.do 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +156 -0
  3. package/dist/durable-object/object-store.d.ts +113 -0
  4. package/dist/durable-object/object-store.d.ts.map +1 -0
  5. package/dist/durable-object/object-store.js +387 -0
  6. package/dist/durable-object/object-store.js.map +1 -0
  7. package/dist/durable-object/schema.d.ts +17 -0
  8. package/dist/durable-object/schema.d.ts.map +1 -0
  9. package/dist/durable-object/schema.js +43 -0
  10. package/dist/durable-object/schema.js.map +1 -0
  11. package/dist/durable-object/wal.d.ts +111 -0
  12. package/dist/durable-object/wal.d.ts.map +1 -0
  13. package/dist/durable-object/wal.js +200 -0
  14. package/dist/durable-object/wal.js.map +1 -0
  15. package/dist/index.d.ts +24 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +101 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/mcp/adapter.d.ts +231 -0
  20. package/dist/mcp/adapter.d.ts.map +1 -0
  21. package/dist/mcp/adapter.js +502 -0
  22. package/dist/mcp/adapter.js.map +1 -0
  23. package/dist/mcp/sandbox.d.ts +261 -0
  24. package/dist/mcp/sandbox.d.ts.map +1 -0
  25. package/dist/mcp/sandbox.js +983 -0
  26. package/dist/mcp/sandbox.js.map +1 -0
  27. package/dist/mcp/sdk-adapter.d.ts +413 -0
  28. package/dist/mcp/sdk-adapter.d.ts.map +1 -0
  29. package/dist/mcp/sdk-adapter.js +672 -0
  30. package/dist/mcp/sdk-adapter.js.map +1 -0
  31. package/dist/mcp/tools.d.ts +133 -0
  32. package/dist/mcp/tools.d.ts.map +1 -0
  33. package/dist/mcp/tools.js +1604 -0
  34. package/dist/mcp/tools.js.map +1 -0
  35. package/dist/ops/blame.d.ts +148 -0
  36. package/dist/ops/blame.d.ts.map +1 -0
  37. package/dist/ops/blame.js +754 -0
  38. package/dist/ops/blame.js.map +1 -0
  39. package/dist/ops/branch.d.ts +215 -0
  40. package/dist/ops/branch.d.ts.map +1 -0
  41. package/dist/ops/branch.js +608 -0
  42. package/dist/ops/branch.js.map +1 -0
  43. package/dist/ops/commit-traversal.d.ts +209 -0
  44. package/dist/ops/commit-traversal.d.ts.map +1 -0
  45. package/dist/ops/commit-traversal.js +755 -0
  46. package/dist/ops/commit-traversal.js.map +1 -0
  47. package/dist/ops/commit.d.ts +221 -0
  48. package/dist/ops/commit.d.ts.map +1 -0
  49. package/dist/ops/commit.js +606 -0
  50. package/dist/ops/commit.js.map +1 -0
  51. package/dist/ops/merge-base.d.ts +223 -0
  52. package/dist/ops/merge-base.d.ts.map +1 -0
  53. package/dist/ops/merge-base.js +581 -0
  54. package/dist/ops/merge-base.js.map +1 -0
  55. package/dist/ops/merge.d.ts +385 -0
  56. package/dist/ops/merge.d.ts.map +1 -0
  57. package/dist/ops/merge.js +1203 -0
  58. package/dist/ops/merge.js.map +1 -0
  59. package/dist/ops/tag.d.ts +182 -0
  60. package/dist/ops/tag.d.ts.map +1 -0
  61. package/dist/ops/tag.js +608 -0
  62. package/dist/ops/tag.js.map +1 -0
  63. package/dist/ops/tree-builder.d.ts +82 -0
  64. package/dist/ops/tree-builder.d.ts.map +1 -0
  65. package/dist/ops/tree-builder.js +246 -0
  66. package/dist/ops/tree-builder.js.map +1 -0
  67. package/dist/ops/tree-diff.d.ts +243 -0
  68. package/dist/ops/tree-diff.d.ts.map +1 -0
  69. package/dist/ops/tree-diff.js +657 -0
  70. package/dist/ops/tree-diff.js.map +1 -0
  71. package/dist/pack/delta.d.ts +68 -0
  72. package/dist/pack/delta.d.ts.map +1 -0
  73. package/dist/pack/delta.js +343 -0
  74. package/dist/pack/delta.js.map +1 -0
  75. package/dist/pack/format.d.ts +84 -0
  76. package/dist/pack/format.d.ts.map +1 -0
  77. package/dist/pack/format.js +261 -0
  78. package/dist/pack/format.js.map +1 -0
  79. package/dist/pack/full-generation.d.ts +327 -0
  80. package/dist/pack/full-generation.d.ts.map +1 -0
  81. package/dist/pack/full-generation.js +1159 -0
  82. package/dist/pack/full-generation.js.map +1 -0
  83. package/dist/pack/generation.d.ts +118 -0
  84. package/dist/pack/generation.d.ts.map +1 -0
  85. package/dist/pack/generation.js +459 -0
  86. package/dist/pack/generation.js.map +1 -0
  87. package/dist/pack/index.d.ts +181 -0
  88. package/dist/pack/index.d.ts.map +1 -0
  89. package/dist/pack/index.js +552 -0
  90. package/dist/pack/index.js.map +1 -0
  91. package/dist/refs/branch.d.ts +224 -0
  92. package/dist/refs/branch.d.ts.map +1 -0
  93. package/dist/refs/branch.js +170 -0
  94. package/dist/refs/branch.js.map +1 -0
  95. package/dist/refs/storage.d.ts +208 -0
  96. package/dist/refs/storage.d.ts.map +1 -0
  97. package/dist/refs/storage.js +421 -0
  98. package/dist/refs/storage.js.map +1 -0
  99. package/dist/refs/tag.d.ts +230 -0
  100. package/dist/refs/tag.d.ts.map +1 -0
  101. package/dist/refs/tag.js +188 -0
  102. package/dist/refs/tag.js.map +1 -0
  103. package/dist/storage/lru-cache.d.ts +188 -0
  104. package/dist/storage/lru-cache.d.ts.map +1 -0
  105. package/dist/storage/lru-cache.js +410 -0
  106. package/dist/storage/lru-cache.js.map +1 -0
  107. package/dist/storage/object-index.d.ts +140 -0
  108. package/dist/storage/object-index.d.ts.map +1 -0
  109. package/dist/storage/object-index.js +166 -0
  110. package/dist/storage/object-index.js.map +1 -0
  111. package/dist/storage/r2-pack.d.ts +394 -0
  112. package/dist/storage/r2-pack.d.ts.map +1 -0
  113. package/dist/storage/r2-pack.js +1062 -0
  114. package/dist/storage/r2-pack.js.map +1 -0
  115. package/dist/tiered/cdc-pipeline.d.ts +316 -0
  116. package/dist/tiered/cdc-pipeline.d.ts.map +1 -0
  117. package/dist/tiered/cdc-pipeline.js +771 -0
  118. package/dist/tiered/cdc-pipeline.js.map +1 -0
  119. package/dist/tiered/migration.d.ts +242 -0
  120. package/dist/tiered/migration.d.ts.map +1 -0
  121. package/dist/tiered/migration.js +592 -0
  122. package/dist/tiered/migration.js.map +1 -0
  123. package/dist/tiered/parquet-writer.d.ts +248 -0
  124. package/dist/tiered/parquet-writer.d.ts.map +1 -0
  125. package/dist/tiered/parquet-writer.js +555 -0
  126. package/dist/tiered/parquet-writer.js.map +1 -0
  127. package/dist/tiered/read-path.d.ts +141 -0
  128. package/dist/tiered/read-path.d.ts.map +1 -0
  129. package/dist/tiered/read-path.js +204 -0
  130. package/dist/tiered/read-path.js.map +1 -0
  131. package/dist/types/objects.d.ts +53 -0
  132. package/dist/types/objects.d.ts.map +1 -0
  133. package/dist/types/objects.js +291 -0
  134. package/dist/types/objects.js.map +1 -0
  135. package/dist/types/storage.d.ts +117 -0
  136. package/dist/types/storage.d.ts.map +1 -0
  137. package/dist/types/storage.js +8 -0
  138. package/dist/types/storage.js.map +1 -0
  139. package/dist/utils/hash.d.ts +31 -0
  140. package/dist/utils/hash.d.ts.map +1 -0
  141. package/dist/utils/hash.js +60 -0
  142. package/dist/utils/hash.js.map +1 -0
  143. package/dist/utils/sha1.d.ts +26 -0
  144. package/dist/utils/sha1.d.ts.map +1 -0
  145. package/dist/utils/sha1.js +127 -0
  146. package/dist/utils/sha1.js.map +1 -0
  147. package/dist/wire/capabilities.d.ts +236 -0
  148. package/dist/wire/capabilities.d.ts.map +1 -0
  149. package/dist/wire/capabilities.js +437 -0
  150. package/dist/wire/capabilities.js.map +1 -0
  151. package/dist/wire/pkt-line.d.ts +67 -0
  152. package/dist/wire/pkt-line.d.ts.map +1 -0
  153. package/dist/wire/pkt-line.js +145 -0
  154. package/dist/wire/pkt-line.js.map +1 -0
  155. package/dist/wire/receive-pack.d.ts +302 -0
  156. package/dist/wire/receive-pack.d.ts.map +1 -0
  157. package/dist/wire/receive-pack.js +885 -0
  158. package/dist/wire/receive-pack.js.map +1 -0
  159. package/dist/wire/smart-http.d.ts +321 -0
  160. package/dist/wire/smart-http.d.ts.map +1 -0
  161. package/dist/wire/smart-http.js +654 -0
  162. package/dist/wire/smart-http.js.map +1 -0
  163. package/dist/wire/upload-pack.d.ts +333 -0
  164. package/dist/wire/upload-pack.d.ts.map +1 -0
  165. package/dist/wire/upload-pack.js +850 -0
  166. package/dist/wire/upload-pack.js.map +1 -0
  167. package/package.json +61 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,156 @@
1
+ # gitx.do
2
+
3
+ Git on Cloudflare Durable Objects - A complete Git reimplementation for the edge.
4
+
5
+ ## Features
6
+
7
+ - **Pack Files** - Full Git packfile v2/v3 support with delta compression
8
+ - **Wire Protocol** - Smart HTTP protocol implementation for fetch/push operations
9
+ - **MCP Tools** - Model Context Protocol integration for AI-assisted git operations
10
+ - **Tiered Storage** - Hot/warm/cold storage tiers with automatic promotion
11
+ - Hot: Durable Object SQLite (low latency)
12
+ - Warm: R2 object storage (packed objects)
13
+ - Cold: Analytics/Parquet (cold storage)
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install gitx.do
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Pack Index Operations
24
+
25
+ ```typescript
26
+ import { parsePackIndex, createPackIndex, lookupObject } from 'gitx.do/pack'
27
+
28
+ // Parse an existing pack index
29
+ const index = parsePackIndex(indexData)
30
+ console.log(`Pack contains ${index.objectCount} objects`)
31
+
32
+ // Look up an object by SHA
33
+ const entry = lookupObject(index, 'abc123...')
34
+ if (entry) {
35
+ console.log(`Object at offset ${entry.offset}`)
36
+ }
37
+
38
+ // Create a new pack index
39
+ const newIndex = createPackIndex({ packData })
40
+ ```
41
+
42
+ ### Smart HTTP Protocol
43
+
44
+ ```typescript
45
+ import { handleInfoRefs, handleUploadPack, handleReceivePack } from 'gitx.do/wire/smart-http'
46
+
47
+ // Handle ref discovery
48
+ const response = await handleInfoRefs(request, repository, {
49
+ sideBand64k: true,
50
+ ofsDelta: true
51
+ })
52
+
53
+ // Handle fetch
54
+ const packResponse = await handleUploadPack(request, repository)
55
+
56
+ // Handle push
57
+ const pushResponse = await handleReceivePack(request, repository)
58
+ ```
59
+
60
+ ### MCP Tools
61
+
62
+ ```typescript
63
+ import { gitTools, invokeTool, registerTool } from 'gitx.do/mcp/tools'
64
+
65
+ // List available git tools
66
+ console.log(gitTools.map(t => t.name))
67
+ // ['git_status', 'git_log', 'git_diff', 'git_commit', ...]
68
+
69
+ // Invoke a tool
70
+ const result = await invokeTool('git_status', { path: '/repo', short: true })
71
+
72
+ // Register a custom tool
73
+ registerTool({
74
+ name: 'my_tool',
75
+ description: 'Custom git operation',
76
+ inputSchema: { type: 'object', properties: {} },
77
+ handler: async (params) => ({ content: [{ type: 'text', text: 'Done' }] })
78
+ })
79
+ ```
80
+
81
+ ### Tiered Storage
82
+
83
+ ```typescript
84
+ import { TieredReader } from 'gitx.do/tiered/read-path'
85
+
86
+ const reader = new TieredReader(hotBackend, warmBackend, coldBackend, {
87
+ hot: { enabled: true, maxSize: 1024 * 1024 },
88
+ warm: { enabled: true },
89
+ cold: { enabled: true },
90
+ promotionPolicy: 'aggressive'
91
+ })
92
+
93
+ // Read with automatic tier fallback and promotion
94
+ const result = await reader.read(sha)
95
+ console.log(`Found in ${result.tier} tier, latency: ${result.latencyMs}ms`)
96
+ ```
97
+
98
+ ### R2 Pack Storage
99
+
100
+ ```typescript
101
+ import { R2PackStorage } from 'gitx.do/storage/r2-pack'
102
+
103
+ const storage = new R2PackStorage({ bucket, prefix: 'repos/my-repo/' })
104
+
105
+ // Upload a packfile
106
+ const result = await storage.uploadPackfile(packData, indexData)
107
+
108
+ // Download with verification
109
+ const pack = await storage.downloadPackfile(packId, { verify: true })
110
+
111
+ // Use multi-pack index for cross-pack lookups
112
+ await storage.rebuildMultiPackIndex()
113
+ const midx = await storage.getMultiPackIndex()
114
+ ```
115
+
116
+ ## API Overview
117
+
118
+ ### Pack Module (`gitx.do/pack`)
119
+
120
+ - `parsePackIndex(data)` - Parse a pack index file
121
+ - `createPackIndex(options)` - Create a new pack index
122
+ - `lookupObject(index, sha)` - Find object by SHA in index
123
+ - `verifyPackIndex(data)` - Verify index integrity
124
+ - `calculateCRC32(data)` - Compute CRC32 checksum
125
+
126
+ ### Wire Protocol (`gitx.do/wire`)
127
+
128
+ - `handleInfoRefs()` - Ref discovery endpoint
129
+ - `handleUploadPack()` - Fetch data transfer
130
+ - `handleReceivePack()` - Push data transfer
131
+ - `formatRefAdvertisement()` - Format ref list
132
+ - `parseCapabilities()` - Parse protocol capabilities
133
+
134
+ ### MCP Tools (`gitx.do/mcp`)
135
+
136
+ - `gitTools` - Array of available git tool definitions
137
+ - `invokeTool(name, params)` - Execute a tool by name
138
+ - `registerTool(tool)` - Add a custom tool
139
+ - `validateToolInput(tool, params)` - Validate parameters
140
+
141
+ ### Tiered Storage (`gitx.do/tiered`)
142
+
143
+ - `TieredReader` - Multi-tier read path with promotion
144
+ - `StoredObject` - Object representation
145
+ - `TieredStorageConfig` - Configuration options
146
+
147
+ ### R2 Storage (`gitx.do/storage`)
148
+
149
+ - `R2PackStorage` - Packfile management for R2
150
+ - `uploadPackfile()` - Store pack and index
151
+ - `downloadPackfile()` - Retrieve with optional verification
152
+ - `createMultiPackIndex()` - Build cross-pack index
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,113 @@
1
+ /**
2
+ * ObjectStore - Git object storage implementation
3
+ *
4
+ * Handles CRUD operations for git objects (blob, tree, commit, tag)
5
+ * with SHA-1 hash computation and proper git object format.
6
+ */
7
+ import { DurableObjectStorage } from './schema';
8
+ import { ObjectType, BlobObject, TreeObject, CommitObject, TagObject, TreeEntry, Author } from '../types/objects';
9
+ /**
10
+ * Stored object record in SQLite
11
+ */
12
+ export interface StoredObject {
13
+ sha: string;
14
+ type: ObjectType;
15
+ size: number;
16
+ data: Uint8Array;
17
+ createdAt: number;
18
+ }
19
+ /**
20
+ * ObjectStore class for managing git objects in SQLite storage
21
+ */
22
+ export declare class ObjectStore {
23
+ private storage;
24
+ constructor(storage: DurableObjectStorage);
25
+ /**
26
+ * Store a raw object and return its SHA
27
+ */
28
+ putObject(type: ObjectType, data: Uint8Array): Promise<string>;
29
+ /**
30
+ * Store a tree object with entries
31
+ */
32
+ putTreeObject(entries: TreeEntry[]): Promise<string>;
33
+ /**
34
+ * Store a commit object
35
+ */
36
+ putCommitObject(commit: {
37
+ tree: string;
38
+ parents: string[];
39
+ author: Author;
40
+ committer: Author;
41
+ message: string;
42
+ }): Promise<string>;
43
+ /**
44
+ * Store a tag object
45
+ */
46
+ putTagObject(tag: {
47
+ object: string;
48
+ objectType: ObjectType;
49
+ tagger: Author;
50
+ message: string;
51
+ name: string;
52
+ }): Promise<string>;
53
+ /**
54
+ * Retrieve an object by SHA
55
+ */
56
+ getObject(sha: string): Promise<StoredObject | null>;
57
+ /**
58
+ * Delete an object by SHA
59
+ */
60
+ deleteObject(sha: string): Promise<boolean>;
61
+ /**
62
+ * Check if an object exists
63
+ */
64
+ hasObject(sha: string): Promise<boolean>;
65
+ /**
66
+ * Verify an object's integrity by recomputing its hash
67
+ */
68
+ verifyObject(sha: string): Promise<boolean>;
69
+ /**
70
+ * Get object type by SHA
71
+ */
72
+ getObjectType(sha: string): Promise<ObjectType | null>;
73
+ /**
74
+ * Get object size by SHA
75
+ */
76
+ getObjectSize(sha: string): Promise<number | null>;
77
+ /**
78
+ * Store multiple objects in a batch
79
+ */
80
+ putObjects(objects: {
81
+ type: ObjectType;
82
+ data: Uint8Array;
83
+ }[]): Promise<string[]>;
84
+ /**
85
+ * Retrieve multiple objects by SHA
86
+ */
87
+ getObjects(shas: string[]): Promise<(StoredObject | null)[]>;
88
+ /**
89
+ * Get a blob object with parsed content
90
+ */
91
+ getBlobObject(sha: string): Promise<BlobObject | null>;
92
+ /**
93
+ * Get a tree object with parsed entries
94
+ */
95
+ getTreeObject(sha: string): Promise<TreeObject | null>;
96
+ /**
97
+ * Get a commit object with parsed fields
98
+ */
99
+ getCommitObject(sha: string): Promise<CommitObject | null>;
100
+ /**
101
+ * Get a tag object with parsed fields
102
+ */
103
+ getTagObject(sha: string): Promise<TagObject | null>;
104
+ /**
105
+ * Get raw serialized object with git header
106
+ */
107
+ getRawObject(sha: string): Promise<Uint8Array | null>;
108
+ /**
109
+ * Log operation to WAL
110
+ */
111
+ private logToWAL;
112
+ }
113
+ //# sourceMappingURL=object-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-store.d.ts","sourceRoot":"","sources":["../../src/durable-object/object-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,387 @@
1
+ /**
2
+ * ObjectStore - Git object storage implementation
3
+ *
4
+ * Handles CRUD operations for git objects (blob, tree, commit, tag)
5
+ * with SHA-1 hash computation and proper git object format.
6
+ */
7
+ import { hashObject } from '../utils/hash';
8
+ const encoder = new TextEncoder();
9
+ const decoder = new TextDecoder();
10
+ /**
11
+ * ObjectStore class for managing git objects in SQLite storage
12
+ */
13
+ export class ObjectStore {
14
+ storage;
15
+ constructor(storage) {
16
+ this.storage = storage;
17
+ }
18
+ /**
19
+ * Store a raw object and return its SHA
20
+ */
21
+ async putObject(type, data) {
22
+ // Compute SHA-1 hash using git object format: "type size\0content"
23
+ const sha = await hashObject(type, data);
24
+ // Log to WAL first
25
+ await this.logToWAL('PUT', sha, type, data);
26
+ // Store the object
27
+ this.storage.sql.exec('INSERT OR REPLACE INTO objects (sha, type, size, data, created_at) VALUES (?, ?, ?, ?, ?)', sha, type, data.length, data, Date.now());
28
+ // Update object index
29
+ this.storage.sql.exec('INSERT OR REPLACE INTO object_index (sha, tier, location, size, type) VALUES (?, ?, ?, ?, ?)', sha, 'hot', 'local', data.length, type);
30
+ return sha;
31
+ }
32
+ /**
33
+ * Store a tree object with entries
34
+ */
35
+ async putTreeObject(entries) {
36
+ // Sort entries by name (directories get trailing / for sorting)
37
+ const sortedEntries = [...entries].sort((a, b) => {
38
+ const aName = a.mode === '040000' ? a.name + '/' : a.name;
39
+ const bName = b.mode === '040000' ? b.name + '/' : b.name;
40
+ return aName.localeCompare(bName);
41
+ });
42
+ // Build tree content (without header)
43
+ const entryParts = [];
44
+ for (const entry of sortedEntries) {
45
+ const modeName = encoder.encode(`${entry.mode} ${entry.name}\0`);
46
+ const sha20 = hexToBytes(entry.sha);
47
+ const entryData = new Uint8Array(modeName.length + 20);
48
+ entryData.set(modeName);
49
+ entryData.set(sha20, modeName.length);
50
+ entryParts.push(entryData);
51
+ }
52
+ // Combine all entry parts
53
+ const contentLength = entryParts.reduce((sum, part) => sum + part.length, 0);
54
+ const content = new Uint8Array(contentLength);
55
+ let offset = 0;
56
+ for (const part of entryParts) {
57
+ content.set(part, offset);
58
+ offset += part.length;
59
+ }
60
+ return this.putObject('tree', content);
61
+ }
62
+ /**
63
+ * Store a commit object
64
+ */
65
+ async putCommitObject(commit) {
66
+ // Build commit content (without header)
67
+ const lines = [];
68
+ lines.push(`tree ${commit.tree}`);
69
+ for (const parent of commit.parents) {
70
+ lines.push(`parent ${parent}`);
71
+ }
72
+ lines.push(`author ${commit.author.name} <${commit.author.email}> ${commit.author.timestamp} ${commit.author.timezone}`);
73
+ lines.push(`committer ${commit.committer.name} <${commit.committer.email}> ${commit.committer.timestamp} ${commit.committer.timezone}`);
74
+ lines.push('');
75
+ lines.push(commit.message);
76
+ const content = encoder.encode(lines.join('\n'));
77
+ return this.putObject('commit', content);
78
+ }
79
+ /**
80
+ * Store a tag object
81
+ */
82
+ async putTagObject(tag) {
83
+ // Build tag content (without header)
84
+ const lines = [];
85
+ lines.push(`object ${tag.object}`);
86
+ lines.push(`type ${tag.objectType}`);
87
+ lines.push(`tag ${tag.name}`);
88
+ lines.push(`tagger ${tag.tagger.name} <${tag.tagger.email}> ${tag.tagger.timestamp} ${tag.tagger.timezone}`);
89
+ lines.push('');
90
+ lines.push(tag.message);
91
+ const content = encoder.encode(lines.join('\n'));
92
+ return this.putObject('tag', content);
93
+ }
94
+ /**
95
+ * Retrieve an object by SHA
96
+ */
97
+ async getObject(sha) {
98
+ if (!sha || sha.length < 4) {
99
+ return null;
100
+ }
101
+ const result = this.storage.sql.exec('SELECT sha, type, size, data, created_at as createdAt FROM objects WHERE sha = ?', sha);
102
+ const rows = result.toArray();
103
+ if (rows.length === 0) {
104
+ return null;
105
+ }
106
+ return rows[0];
107
+ }
108
+ /**
109
+ * Delete an object by SHA
110
+ */
111
+ async deleteObject(sha) {
112
+ // Check if object exists first
113
+ const exists = await this.hasObject(sha);
114
+ if (!exists) {
115
+ return false;
116
+ }
117
+ // Log to WAL
118
+ await this.logToWAL('DELETE', sha, 'blob', new Uint8Array(0));
119
+ // Delete from objects table
120
+ this.storage.sql.exec('DELETE FROM objects WHERE sha = ?', sha);
121
+ // Delete from object index
122
+ this.storage.sql.exec('DELETE FROM object_index WHERE sha = ?', sha);
123
+ return true;
124
+ }
125
+ /**
126
+ * Check if an object exists
127
+ */
128
+ async hasObject(sha) {
129
+ if (!sha || sha.length < 4) {
130
+ return false;
131
+ }
132
+ // Use getObject and check for null - this works better with the mock
133
+ const obj = await this.getObject(sha);
134
+ return obj !== null;
135
+ }
136
+ /**
137
+ * Verify an object's integrity by recomputing its hash
138
+ */
139
+ async verifyObject(sha) {
140
+ const obj = await this.getObject(sha);
141
+ if (!obj) {
142
+ return false;
143
+ }
144
+ const computedSha = await hashObject(obj.type, obj.data);
145
+ return computedSha === sha;
146
+ }
147
+ /**
148
+ * Get object type by SHA
149
+ */
150
+ async getObjectType(sha) {
151
+ const obj = await this.getObject(sha);
152
+ return obj?.type ?? null;
153
+ }
154
+ /**
155
+ * Get object size by SHA
156
+ */
157
+ async getObjectSize(sha) {
158
+ const obj = await this.getObject(sha);
159
+ return obj?.size ?? null;
160
+ }
161
+ /**
162
+ * Store multiple objects in a batch
163
+ */
164
+ async putObjects(objects) {
165
+ const shas = [];
166
+ for (const obj of objects) {
167
+ const sha = await this.putObject(obj.type, obj.data);
168
+ shas.push(sha);
169
+ }
170
+ return shas;
171
+ }
172
+ /**
173
+ * Retrieve multiple objects by SHA
174
+ */
175
+ async getObjects(shas) {
176
+ const results = [];
177
+ for (const sha of shas) {
178
+ const obj = await this.getObject(sha);
179
+ results.push(obj);
180
+ }
181
+ return results;
182
+ }
183
+ /**
184
+ * Get a blob object with parsed content
185
+ */
186
+ async getBlobObject(sha) {
187
+ const obj = await this.getObject(sha);
188
+ if (!obj || obj.type !== 'blob') {
189
+ return null;
190
+ }
191
+ return {
192
+ type: 'blob',
193
+ data: obj.data
194
+ };
195
+ }
196
+ /**
197
+ * Get a tree object with parsed entries
198
+ */
199
+ async getTreeObject(sha) {
200
+ const obj = await this.getObject(sha);
201
+ if (!obj || obj.type !== 'tree') {
202
+ return null;
203
+ }
204
+ // Parse tree entries from raw data
205
+ const entries = [];
206
+ let offset = 0;
207
+ const data = obj.data;
208
+ while (offset < data.length) {
209
+ // Find the null byte after mode+name
210
+ let nullIndex = offset;
211
+ while (nullIndex < data.length && data[nullIndex] !== 0) {
212
+ nullIndex++;
213
+ }
214
+ const modeNameStr = decoder.decode(data.slice(offset, nullIndex));
215
+ const spaceIndex = modeNameStr.indexOf(' ');
216
+ const mode = modeNameStr.slice(0, spaceIndex);
217
+ const name = modeNameStr.slice(spaceIndex + 1);
218
+ // Read 20-byte SHA
219
+ const sha20 = data.slice(nullIndex + 1, nullIndex + 21);
220
+ const entrySha = bytesToHex(sha20);
221
+ entries.push({ mode, name, sha: entrySha });
222
+ offset = nullIndex + 21;
223
+ }
224
+ return {
225
+ type: 'tree',
226
+ data: obj.data,
227
+ entries
228
+ };
229
+ }
230
+ /**
231
+ * Get a commit object with parsed fields
232
+ */
233
+ async getCommitObject(sha) {
234
+ const obj = await this.getObject(sha);
235
+ if (!obj || obj.type !== 'commit') {
236
+ return null;
237
+ }
238
+ const content = decoder.decode(obj.data);
239
+ const lines = content.split('\n');
240
+ let tree = '';
241
+ const parents = [];
242
+ let author = null;
243
+ let committer = null;
244
+ let messageStartIndex = 0;
245
+ for (let i = 0; i < lines.length; i++) {
246
+ const line = lines[i];
247
+ if (line === '') {
248
+ messageStartIndex = i + 1;
249
+ break;
250
+ }
251
+ if (line.startsWith('tree ')) {
252
+ tree = line.slice(5);
253
+ }
254
+ else if (line.startsWith('parent ')) {
255
+ parents.push(line.slice(7));
256
+ }
257
+ else if (line.startsWith('author ')) {
258
+ author = parseAuthorLine(line);
259
+ }
260
+ else if (line.startsWith('committer ')) {
261
+ committer = parseAuthorLine(line);
262
+ }
263
+ }
264
+ if (!author || !committer) {
265
+ return null;
266
+ }
267
+ const message = lines.slice(messageStartIndex).join('\n');
268
+ return {
269
+ type: 'commit',
270
+ data: obj.data,
271
+ tree,
272
+ parents,
273
+ author,
274
+ committer,
275
+ message
276
+ };
277
+ }
278
+ /**
279
+ * Get a tag object with parsed fields
280
+ */
281
+ async getTagObject(sha) {
282
+ const obj = await this.getObject(sha);
283
+ if (!obj || obj.type !== 'tag') {
284
+ return null;
285
+ }
286
+ const content = decoder.decode(obj.data);
287
+ const lines = content.split('\n');
288
+ let object = '';
289
+ let objectType = 'commit';
290
+ let name = '';
291
+ let tagger = null;
292
+ let messageStartIndex = 0;
293
+ for (let i = 0; i < lines.length; i++) {
294
+ const line = lines[i];
295
+ if (line === '') {
296
+ messageStartIndex = i + 1;
297
+ break;
298
+ }
299
+ if (line.startsWith('object ')) {
300
+ object = line.slice(7);
301
+ }
302
+ else if (line.startsWith('type ')) {
303
+ objectType = line.slice(5);
304
+ }
305
+ else if (line.startsWith('tag ')) {
306
+ name = line.slice(4);
307
+ }
308
+ else if (line.startsWith('tagger ')) {
309
+ tagger = parseAuthorLine(line);
310
+ }
311
+ }
312
+ if (!tagger) {
313
+ return null;
314
+ }
315
+ const message = lines.slice(messageStartIndex).join('\n');
316
+ return {
317
+ type: 'tag',
318
+ data: obj.data,
319
+ object,
320
+ objectType,
321
+ name,
322
+ tagger,
323
+ message
324
+ };
325
+ }
326
+ /**
327
+ * Get raw serialized object with git header
328
+ */
329
+ async getRawObject(sha) {
330
+ const obj = await this.getObject(sha);
331
+ if (!obj) {
332
+ return null;
333
+ }
334
+ // Build git object format: "type size\0content"
335
+ const header = encoder.encode(`${obj.type} ${obj.data.length}\0`);
336
+ const result = new Uint8Array(header.length + obj.data.length);
337
+ result.set(header);
338
+ result.set(obj.data, header.length);
339
+ return result;
340
+ }
341
+ /**
342
+ * Log operation to WAL
343
+ */
344
+ async logToWAL(operation, sha, type, _data) {
345
+ // Create payload with operation details
346
+ const payload = encoder.encode(JSON.stringify({
347
+ sha,
348
+ type,
349
+ timestamp: Date.now()
350
+ }));
351
+ this.storage.sql.exec('INSERT INTO wal (operation, payload, created_at, flushed) VALUES (?, ?, ?, 0)', operation, payload, Date.now());
352
+ }
353
+ }
354
+ /**
355
+ * Helper: Convert hex string to bytes
356
+ */
357
+ function hexToBytes(hex) {
358
+ const bytes = new Uint8Array(hex.length / 2);
359
+ for (let i = 0; i < hex.length; i += 2) {
360
+ bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16);
361
+ }
362
+ return bytes;
363
+ }
364
+ /**
365
+ * Helper: Convert bytes to hex string
366
+ */
367
+ function bytesToHex(bytes) {
368
+ return Array.from(bytes)
369
+ .map(b => b.toString(16).padStart(2, '0'))
370
+ .join('');
371
+ }
372
+ /**
373
+ * Helper: Parse author/committer/tagger line
374
+ */
375
+ function parseAuthorLine(line) {
376
+ const match = line.match(/^(?:author|committer|tagger) (.+) <(.+)> (\d+) ([+-]\d{4})$/);
377
+ if (!match) {
378
+ throw new Error(`Invalid author line: ${line}`);
379
+ }
380
+ return {
381
+ name: match[1],
382
+ email: match[2],
383
+ timestamp: parseInt(match[3], 10),
384
+ timezone: match[4]
385
+ };
386
+ }
387
+ //# sourceMappingURL=object-store.js.map