@soulcraft/brainy 4.11.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,431 @@
1
+ /**
2
+ * CommitObject: Snapshot metadata for COW (Copy-on-Write)
3
+ *
4
+ * Similar to Git commits, represents a point-in-time snapshot of a Brainy instance.
5
+ * Commits reference tree objects and parent commits, forming a directed acyclic graph (DAG).
6
+ *
7
+ * Structure:
8
+ * - tree: hash of root tree object (contains all data)
9
+ * - parent: hash of parent commit (null for initial commit)
10
+ * - message: human-readable commit message
11
+ * - author: who/what created this commit
12
+ * - timestamp: when this commit was created
13
+ * - metadata: additional commit metadata (tags, etc.)
14
+ *
15
+ * @module storage/cow/CommitObject
16
+ */
17
+ import { BlobStorage } from './BlobStorage.js';
18
+ /**
19
+ * CommitBuilder: Fluent API for building commit objects
20
+ *
21
+ * Example:
22
+ * ```typescript
23
+ * const commit = await CommitBuilder.create(blobStorage)
24
+ * .tree(treeHash)
25
+ * .parent(parentHash)
26
+ * .message('Add user entities')
27
+ * .author('system')
28
+ * .tag('v1.0.0')
29
+ * .build()
30
+ * ```
31
+ */
32
+ export class CommitBuilder {
33
+ constructor(blobStorage) {
34
+ this._message = 'Auto-commit';
35
+ this._author = 'system';
36
+ this._timestamp = Date.now();
37
+ this._metadata = {};
38
+ this.blobStorage = blobStorage;
39
+ }
40
+ static create(blobStorage) {
41
+ return new CommitBuilder(blobStorage);
42
+ }
43
+ /**
44
+ * Set tree hash
45
+ */
46
+ tree(hash) {
47
+ this._tree = hash;
48
+ return this;
49
+ }
50
+ /**
51
+ * Set parent commit hash (null for initial commit)
52
+ */
53
+ parent(hash) {
54
+ this._parent = hash;
55
+ return this;
56
+ }
57
+ /**
58
+ * Set commit message
59
+ */
60
+ message(message) {
61
+ this._message = message;
62
+ return this;
63
+ }
64
+ /**
65
+ * Set commit author
66
+ */
67
+ author(author) {
68
+ this._author = author;
69
+ return this;
70
+ }
71
+ /**
72
+ * Set commit timestamp (defaults to now)
73
+ */
74
+ timestamp(timestamp) {
75
+ this._timestamp = typeof timestamp === 'number' ? timestamp : timestamp.getTime();
76
+ return this;
77
+ }
78
+ /**
79
+ * Add tag to commit
80
+ */
81
+ tag(tag) {
82
+ if (!this._metadata.tags) {
83
+ this._metadata.tags = [];
84
+ }
85
+ this._metadata.tags.push(tag);
86
+ return this;
87
+ }
88
+ /**
89
+ * Set branch name
90
+ */
91
+ branch(branch) {
92
+ this._metadata.branch = branch;
93
+ return this;
94
+ }
95
+ /**
96
+ * Set operation type
97
+ */
98
+ operation(operation) {
99
+ this._metadata.operation = operation;
100
+ return this;
101
+ }
102
+ /**
103
+ * Set entity count
104
+ */
105
+ entityCount(count) {
106
+ this._metadata.entityCount = count;
107
+ return this;
108
+ }
109
+ /**
110
+ * Set relationship count
111
+ */
112
+ relationshipCount(count) {
113
+ this._metadata.relationshipCount = count;
114
+ return this;
115
+ }
116
+ /**
117
+ * Set custom metadata
118
+ */
119
+ meta(key, value) {
120
+ this._metadata[key] = value;
121
+ return this;
122
+ }
123
+ /**
124
+ * Build and persist the commit object
125
+ *
126
+ * @returns Commit hash
127
+ */
128
+ async build() {
129
+ if (!this._tree) {
130
+ throw new Error('CommitBuilder: tree hash is required');
131
+ }
132
+ const commit = {
133
+ tree: this._tree,
134
+ parent: this._parent ?? null,
135
+ message: this._message,
136
+ author: this._author,
137
+ timestamp: this._timestamp,
138
+ metadata: Object.keys(this._metadata).length > 0 ? this._metadata : undefined
139
+ };
140
+ return CommitObject.write(this.blobStorage, commit);
141
+ }
142
+ }
143
+ /**
144
+ * CommitObject: Represents a point-in-time snapshot in COW storage
145
+ */
146
+ export class CommitObject {
147
+ /**
148
+ * Serialize commit object to Buffer
149
+ *
150
+ * Format: JSON (simple, debuggable)
151
+ * Future: Could use protobuf for efficiency
152
+ *
153
+ * @param commit - Commit object
154
+ * @returns Serialized commit
155
+ */
156
+ static serialize(commit) {
157
+ return Buffer.from(JSON.stringify(commit, null, 0)); // Compact JSON
158
+ }
159
+ /**
160
+ * Deserialize commit object from Buffer
161
+ *
162
+ * @param data - Serialized commit
163
+ * @returns Commit object
164
+ */
165
+ static deserialize(data) {
166
+ const commit = JSON.parse(data.toString());
167
+ // Validate structure
168
+ if (typeof commit.tree !== 'string' || commit.tree.length !== 64) {
169
+ throw new Error('Invalid commit object: tree must be 64-char SHA-256');
170
+ }
171
+ if (commit.parent !== null && (typeof commit.parent !== 'string' || commit.parent.length !== 64)) {
172
+ throw new Error('Invalid commit object: parent must be 64-char SHA-256 or null');
173
+ }
174
+ if (typeof commit.message !== 'string') {
175
+ throw new Error('Invalid commit object: message must be string');
176
+ }
177
+ if (typeof commit.author !== 'string') {
178
+ throw new Error('Invalid commit object: author must be string');
179
+ }
180
+ if (typeof commit.timestamp !== 'number') {
181
+ throw new Error('Invalid commit object: timestamp must be number');
182
+ }
183
+ if (commit.metadata !== undefined && typeof commit.metadata !== 'object') {
184
+ throw new Error('Invalid commit object: metadata must be object');
185
+ }
186
+ return commit;
187
+ }
188
+ /**
189
+ * Compute hash of commit object
190
+ *
191
+ * @param commit - Commit object
192
+ * @returns SHA-256 hash
193
+ */
194
+ static hash(commit) {
195
+ const data = CommitObject.serialize(commit);
196
+ return BlobStorage.hash(data);
197
+ }
198
+ /**
199
+ * Write commit object to blob storage
200
+ *
201
+ * @param blobStorage - Blob storage instance
202
+ * @param commit - Commit object
203
+ * @returns Commit hash
204
+ */
205
+ static async write(blobStorage, commit) {
206
+ const data = CommitObject.serialize(commit);
207
+ return blobStorage.write(data, { type: 'commit', compression: 'auto' });
208
+ }
209
+ /**
210
+ * Read commit object from blob storage
211
+ *
212
+ * @param blobStorage - Blob storage instance
213
+ * @param hash - Commit hash
214
+ * @returns Commit object
215
+ */
216
+ static async read(blobStorage, hash) {
217
+ const data = await blobStorage.read(hash);
218
+ return CommitObject.deserialize(data);
219
+ }
220
+ /**
221
+ * Check if commit is initial commit (has no parent)
222
+ *
223
+ * @param commit - Commit object
224
+ * @returns True if initial commit
225
+ */
226
+ static isInitial(commit) {
227
+ return commit.parent === null;
228
+ }
229
+ /**
230
+ * Check if commit is merge commit (has multiple parents)
231
+ * (Future enhancement: support merge commits with multiple parents)
232
+ *
233
+ * @param commit - Commit object
234
+ * @returns True if merge commit
235
+ */
236
+ static isMerge(commit) {
237
+ // For now, we only support single-parent commits
238
+ // Future: extend to support multiple parents
239
+ return commit.metadata?.merge !== undefined;
240
+ }
241
+ /**
242
+ * Check if commit has a specific tag
243
+ *
244
+ * @param commit - Commit object
245
+ * @param tag - Tag to check
246
+ * @returns True if commit has tag
247
+ */
248
+ static hasTag(commit, tag) {
249
+ return commit.metadata?.tags?.includes(tag) ?? false;
250
+ }
251
+ /**
252
+ * Get all tags from commit
253
+ *
254
+ * @param commit - Commit object
255
+ * @returns Array of tags
256
+ */
257
+ static getTags(commit) {
258
+ return commit.metadata?.tags ?? [];
259
+ }
260
+ /**
261
+ * Walk commit history (traverse DAG)
262
+ *
263
+ * Yields commits in reverse chronological order (newest first)
264
+ *
265
+ * @param blobStorage - Blob storage instance
266
+ * @param startHash - Starting commit hash
267
+ * @param options - Walk options
268
+ */
269
+ static async *walk(blobStorage, startHash, options) {
270
+ let currentHash = startHash;
271
+ let depth = 0;
272
+ while (currentHash) {
273
+ // Check max depth
274
+ if (options?.maxDepth && depth >= options.maxDepth) {
275
+ break;
276
+ }
277
+ // Read commit
278
+ const commit = await CommitObject.read(blobStorage, currentHash);
279
+ // Check filter
280
+ if (options?.filter && !options.filter(commit)) {
281
+ currentHash = commit.parent;
282
+ depth++;
283
+ continue;
284
+ }
285
+ // Yield commit
286
+ yield commit;
287
+ // Check stop conditions
288
+ if (options?.until && commit.timestamp < options.until) {
289
+ break;
290
+ }
291
+ if (options?.stopAt && currentHash === options.stopAt) {
292
+ break;
293
+ }
294
+ // Move to parent
295
+ currentHash = commit.parent;
296
+ depth++;
297
+ }
298
+ }
299
+ /**
300
+ * Find commit at or before a specific timestamp
301
+ *
302
+ * @param blobStorage - Blob storage instance
303
+ * @param startHash - Starting commit hash (e.g., 'main' ref)
304
+ * @param timestamp - Target timestamp
305
+ * @returns Commit at or before timestamp, or null if not found
306
+ */
307
+ static async findAtTime(blobStorage, startHash, timestamp) {
308
+ for await (const commit of CommitObject.walk(blobStorage, startHash)) {
309
+ if (commit.timestamp <= timestamp) {
310
+ return commit;
311
+ }
312
+ }
313
+ return null;
314
+ }
315
+ /**
316
+ * Get commit history as array (newest first)
317
+ *
318
+ * @param blobStorage - Blob storage instance
319
+ * @param startHash - Starting commit hash
320
+ * @param options - Walk options
321
+ * @returns Array of commits
322
+ */
323
+ static async getHistory(blobStorage, startHash, options) {
324
+ const commits = [];
325
+ for await (const commit of CommitObject.walk(blobStorage, startHash, options)) {
326
+ commits.push(commit);
327
+ }
328
+ return commits;
329
+ }
330
+ /**
331
+ * Find common ancestor of two commits (merge base)
332
+ *
333
+ * Useful for diff/merge operations
334
+ *
335
+ * @param blobStorage - Blob storage instance
336
+ * @param hash1 - First commit hash
337
+ * @param hash2 - Second commit hash
338
+ * @returns Common ancestor commit, or null if not found
339
+ */
340
+ static async findCommonAncestor(blobStorage, hash1, hash2) {
341
+ // Build set of all ancestors of commit1
342
+ const ancestors1 = new Set();
343
+ for await (const commit of CommitObject.walk(blobStorage, hash1)) {
344
+ ancestors1.add(CommitObject.hash(commit));
345
+ }
346
+ // Walk commit2 history, find first commit in ancestors1
347
+ for await (const commit of CommitObject.walk(blobStorage, hash2)) {
348
+ const commitHash = CommitObject.hash(commit);
349
+ if (ancestors1.has(commitHash)) {
350
+ return commit;
351
+ }
352
+ }
353
+ return null;
354
+ }
355
+ /**
356
+ * Count commits between two commits
357
+ *
358
+ * @param blobStorage - Blob storage instance
359
+ * @param fromHash - Starting commit hash
360
+ * @param toHash - Ending commit hash
361
+ * @returns Number of commits between (inclusive)
362
+ */
363
+ static async countBetween(blobStorage, fromHash, toHash) {
364
+ let count = 0;
365
+ for await (const commit of CommitObject.walk(blobStorage, fromHash, {
366
+ stopAt: toHash
367
+ })) {
368
+ count++;
369
+ }
370
+ return count;
371
+ }
372
+ /**
373
+ * Get commits in time range
374
+ *
375
+ * @param blobStorage - Blob storage instance
376
+ * @param startHash - Starting commit hash
377
+ * @param startTime - Start of time range
378
+ * @param endTime - End of time range
379
+ * @returns Array of commits in range
380
+ */
381
+ static async getInTimeRange(blobStorage, startHash, startTime, endTime) {
382
+ const commits = [];
383
+ for await (const commit of CommitObject.walk(blobStorage, startHash, {
384
+ until: startTime
385
+ })) {
386
+ if (commit.timestamp >= startTime && commit.timestamp <= endTime) {
387
+ commits.push(commit);
388
+ }
389
+ }
390
+ return commits;
391
+ }
392
+ /**
393
+ * Get commits by author
394
+ *
395
+ * @param blobStorage - Blob storage instance
396
+ * @param startHash - Starting commit hash
397
+ * @param author - Author name
398
+ * @param options - Walk options
399
+ * @returns Array of commits by author
400
+ */
401
+ static async getByAuthor(blobStorage, startHash, author, options) {
402
+ const commits = [];
403
+ for await (const commit of CommitObject.walk(blobStorage, startHash, {
404
+ ...options,
405
+ filter: c => c.author === author
406
+ })) {
407
+ commits.push(commit);
408
+ }
409
+ return commits;
410
+ }
411
+ /**
412
+ * Get commits by operation type
413
+ *
414
+ * @param blobStorage - Blob storage instance
415
+ * @param startHash - Starting commit hash
416
+ * @param operation - Operation type (e.g., 'add', 'update', 'delete')
417
+ * @param options - Walk options
418
+ * @returns Array of commits by operation
419
+ */
420
+ static async getByOperation(blobStorage, startHash, operation, options) {
421
+ const commits = [];
422
+ for await (const commit of CommitObject.walk(blobStorage, startHash, {
423
+ ...options,
424
+ filter: c => c.metadata?.operation === operation
425
+ })) {
426
+ commits.push(commit);
427
+ }
428
+ return commits;
429
+ }
430
+ }
431
+ //# sourceMappingURL=CommitObject.js.map
@@ -0,0 +1,213 @@
1
+ /**
2
+ * RefManager: Branch and reference management for COW (Copy-on-Write)
3
+ *
4
+ * Similar to Git refs, manages symbolic names (branches, tags) that point to commits.
5
+ *
6
+ * Structure:
7
+ * - refs/heads/main → commit hash (main branch)
8
+ * - refs/heads/experiment → commit hash (experiment branch)
9
+ * - refs/tags/v1.0.0 → commit hash (version tag)
10
+ * - HEAD → ref name (current branch)
11
+ *
12
+ * Features:
13
+ * - Branch management (create, delete, list)
14
+ * - Tag management
15
+ * - HEAD pointer (current branch)
16
+ * - Fast-forward and force updates
17
+ * - Atomic operations
18
+ *
19
+ * @module storage/cow/RefManager
20
+ */
21
+ import type { COWStorageAdapter } from './BlobStorage.js';
22
+ /**
23
+ * Reference type
24
+ */
25
+ export type RefType = 'branch' | 'tag' | 'remote';
26
+ /**
27
+ * Reference object
28
+ */
29
+ export interface Ref {
30
+ name: string;
31
+ commitHash: string;
32
+ type: RefType;
33
+ createdAt: number;
34
+ updatedAt: number;
35
+ metadata?: {
36
+ description?: string;
37
+ author?: string;
38
+ [key: string]: any;
39
+ };
40
+ }
41
+ /**
42
+ * Ref update options
43
+ */
44
+ export interface RefUpdateOptions {
45
+ force?: boolean;
46
+ createOnly?: boolean;
47
+ updateOnly?: boolean;
48
+ expectedOldValue?: string;
49
+ }
50
+ /**
51
+ * RefManager: Manages branches, tags, and HEAD pointer
52
+ *
53
+ * Pure implementation for v5.0.0 - no backward compatibility
54
+ */
55
+ export declare class RefManager {
56
+ private adapter;
57
+ private cache;
58
+ private cacheValid;
59
+ constructor(adapter: COWStorageAdapter);
60
+ /**
61
+ * Get reference by name
62
+ *
63
+ * @param name - Reference name (e.g., 'main', 'refs/heads/main')
64
+ * @returns Reference object or undefined
65
+ */
66
+ getRef(name: string): Promise<Ref | undefined>;
67
+ /**
68
+ * Set reference to point to commit
69
+ *
70
+ * @param name - Reference name
71
+ * @param commitHash - Commit hash to point to
72
+ * @param options - Update options
73
+ */
74
+ setRef(name: string, commitHash: string, options?: RefUpdateOptions): Promise<void>;
75
+ /**
76
+ * Delete reference
77
+ *
78
+ * @param name - Reference name
79
+ */
80
+ deleteRef(name: string): Promise<void>;
81
+ /**
82
+ * List all references
83
+ *
84
+ * @param type - Filter by type (optional)
85
+ * @returns Array of references
86
+ */
87
+ listRefs(type?: RefType): Promise<Ref[]>;
88
+ /**
89
+ * Copy reference (create branch from existing ref)
90
+ *
91
+ * @param sourceName - Source reference name
92
+ * @param targetName - Target reference name
93
+ * @param options - Update options
94
+ */
95
+ copyRef(sourceName: string, targetName: string, options?: RefUpdateOptions): Promise<void>;
96
+ /**
97
+ * Get current HEAD (current branch)
98
+ *
99
+ * @returns HEAD reference or undefined
100
+ */
101
+ getHead(): Promise<Ref | undefined>;
102
+ /**
103
+ * Set HEAD to point to branch
104
+ *
105
+ * @param branchName - Branch name (e.g., 'main', 'refs/heads/experiment')
106
+ */
107
+ setHead(branchName: string): Promise<void>;
108
+ /**
109
+ * Get current commit hash (resolves HEAD)
110
+ *
111
+ * @returns Current commit hash or undefined
112
+ */
113
+ getCurrentCommit(): Promise<string | undefined>;
114
+ /**
115
+ * Create branch
116
+ *
117
+ * @param name - Branch name (e.g., 'experiment')
118
+ * @param commitHash - Commit hash to point to
119
+ * @param options - Create options
120
+ */
121
+ createBranch(name: string, commitHash: string, options?: {
122
+ description?: string;
123
+ author?: string;
124
+ }): Promise<void>;
125
+ /**
126
+ * Delete branch
127
+ *
128
+ * @param name - Branch name
129
+ */
130
+ deleteBranch(name: string): Promise<void>;
131
+ /**
132
+ * List all branches
133
+ *
134
+ * @returns Array of branch references
135
+ */
136
+ listBranches(): Promise<Ref[]>;
137
+ /**
138
+ * Create tag
139
+ *
140
+ * @param name - Tag name (e.g., 'v1.0.0')
141
+ * @param commitHash - Commit hash to point to
142
+ * @param options - Create options
143
+ */
144
+ createTag(name: string, commitHash: string, options?: {
145
+ description?: string;
146
+ author?: string;
147
+ }): Promise<void>;
148
+ /**
149
+ * Delete tag
150
+ *
151
+ * @param name - Tag name
152
+ */
153
+ deleteTag(name: string): Promise<void>;
154
+ /**
155
+ * List all tags
156
+ *
157
+ * @returns Array of tag references
158
+ */
159
+ listTags(): Promise<Ref[]>;
160
+ /**
161
+ * Check if reference exists
162
+ *
163
+ * @param name - Reference name
164
+ * @returns True if reference exists
165
+ */
166
+ hasRef(name: string): Promise<boolean>;
167
+ /**
168
+ * Update reference to new commit (with validation)
169
+ *
170
+ * @param name - Reference name
171
+ * @param newCommitHash - New commit hash
172
+ * @param oldCommitHash - Expected old commit hash (for safety)
173
+ */
174
+ updateRef(name: string, newCommitHash: string, oldCommitHash?: string): Promise<void>;
175
+ /**
176
+ * Get commit hash for reference
177
+ *
178
+ * @param name - Reference name
179
+ * @returns Commit hash or undefined
180
+ */
181
+ resolveRef(name: string): Promise<string | undefined>;
182
+ /**
183
+ * Find references pointing to commit
184
+ *
185
+ * @param commitHash - Commit hash
186
+ * @returns Array of references pointing to this commit
187
+ */
188
+ findRefsPointingTo(commitHash: string): Promise<Ref[]>;
189
+ /**
190
+ * Clear cache (useful for testing)
191
+ */
192
+ clearCache(): void;
193
+ /**
194
+ * Normalize reference name to full format
195
+ *
196
+ * Examples:
197
+ * - 'main' → 'refs/heads/main'
198
+ * - 'v1.0.0' (with type='tag') → 'refs/tags/v1.0.0'
199
+ * - 'refs/heads/experiment' → 'refs/heads/experiment'
200
+ *
201
+ * @param name - Reference name
202
+ * @param type - Reference type hint
203
+ * @returns Full reference name
204
+ */
205
+ private normalizeRefName;
206
+ /**
207
+ * Get reference type from full name
208
+ *
209
+ * @param fullName - Full reference name
210
+ * @returns Reference type
211
+ */
212
+ private getRefType;
213
+ }