@gitgov/core 2.1.2 → 2.2.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,472 @@
1
+ import { Octokit } from '@octokit/rest';
2
+ export { Octokit, RestEndpointMethodTypes } from '@octokit/rest';
3
+ import { h as FileLister, i as FileListOptions, j as FileStats, I as IdEncoder, R as RecordStore, a as IGitModule, d as ChangedFile, e as GetCommitHistoryOptions, f as CommitInfo, g as CommitAuthor, E as ExecOptions, c as ExecResult, C as ConfigStore, G as GitGovConfig } from './index-D1RVufxB.js';
4
+
5
+ /**
6
+ * Types for GitHubFileLister module.
7
+ *
8
+ * @module file_lister/github/github_file_lister.types
9
+ */
10
+ /**
11
+ * Configuration options for GitHubFileLister.
12
+ * Auth and API base URL are configured on the Octokit instance, not here.
13
+ */
14
+ type GitHubFileListerOptions = {
15
+ /** GitHub repository owner (user or org) */
16
+ owner: string;
17
+ /** GitHub repository name */
18
+ repo: string;
19
+ /** Git ref to use (branch, tag, or SHA). Default: 'gitgov-state' */
20
+ ref?: string;
21
+ /** Base path within the repo to scope operations. Default: '' (repo root) */
22
+ basePath?: string;
23
+ };
24
+
25
+ /**
26
+ * GitHubFileLister - GitHub REST API implementation of FileLister
27
+ *
28
+ * Provides file listing and reading operations via GitHub's REST API
29
+ * for SaaS environments where direct filesystem access is not available.
30
+ *
31
+ * Uses the Git Trees API for listing (with caching) and the Contents API
32
+ * for reading individual files. Falls back to the Blobs API for files
33
+ * larger than 1MB where the Contents API returns null content.
34
+ *
35
+ * @module file_lister/github/github_file_lister
36
+ */
37
+
38
+ /**
39
+ * GitHubFileLister - GitHub REST API FileLister implementation.
40
+ *
41
+ * Implements the FileLister interface using GitHub's REST API endpoints:
42
+ * - Trees API for listing files (cached)
43
+ * - Contents API for reading, stat, and exists
44
+ * - Blobs API as fallback for large files (>1MB)
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * import { Octokit } from '@octokit/rest';
49
+ * const octokit = new Octokit({ auth: 'ghp_xxx' });
50
+ * const lister = new GitHubFileLister({
51
+ * owner: 'myorg',
52
+ * repo: 'myrepo',
53
+ * ref: 'gitgov-state',
54
+ * basePath: '.gitgov',
55
+ * }, octokit);
56
+ *
57
+ * const files = await lister.list(['**\/*.ts']);
58
+ * const content = await lister.read('config.json');
59
+ * ```
60
+ */
61
+ declare class GitHubFileLister implements FileLister {
62
+ private readonly owner;
63
+ private readonly repo;
64
+ private readonly ref;
65
+ private readonly basePath;
66
+ private readonly octokit;
67
+ /** Cached tree entries from the Trees API */
68
+ private treeCache;
69
+ constructor(options: GitHubFileListerOptions, octokit: Octokit);
70
+ /**
71
+ * [EARS-A1] Lists files matching glob patterns.
72
+ * [EARS-B1] Uses Trees API with recursive=1 and picomatch filter.
73
+ * [EARS-B3] Applies basePath prefix for tree entries, strips from results.
74
+ * [EARS-B6] Caches tree between list() calls.
75
+ */
76
+ list(patterns: string[], options?: FileListOptions): Promise<string[]>;
77
+ /**
78
+ * [EARS-A2] Checks if a file exists via Contents API.
79
+ * [EARS-B4] Returns false for 404 responses.
80
+ */
81
+ exists(filePath: string): Promise<boolean>;
82
+ /**
83
+ * [EARS-A3] Reads file content as string.
84
+ * [EARS-B2] Decodes base64 content from Contents API.
85
+ * [EARS-B7] Falls back to Blobs API for files >1MB (null content).
86
+ */
87
+ read(filePath: string): Promise<string>;
88
+ /**
89
+ * [EARS-A4] Gets file statistics via Contents API.
90
+ * Returns size from API, mtime as 0 (not available via Contents API), isFile as true.
91
+ */
92
+ stat(filePath: string): Promise<FileStats>;
93
+ /**
94
+ * Builds the full file path including basePath prefix.
95
+ */
96
+ private buildFullPath;
97
+ /**
98
+ * [EARS-B6] Fetches and caches the full repository tree.
99
+ * [EARS-C3] Throws READ_ERROR if the tree response is truncated.
100
+ */
101
+ private fetchTree;
102
+ /**
103
+ * [EARS-B7] Reads file content via the Blobs API (fallback for >1MB files).
104
+ */
105
+ private readViaBlobs;
106
+ }
107
+
108
+ /**
109
+ * Options for GitHubRecordStore.
110
+ * Auth and API base URL are configured on the Octokit instance, not here.
111
+ */
112
+ type GitHubRecordStoreOptions = {
113
+ /** GitHub repository owner (user or org) */
114
+ owner: string;
115
+ /** GitHub repository name */
116
+ repo: string;
117
+ /** Branch ref (default: 'gitgov-state') */
118
+ ref?: string;
119
+ /** Base directory path in the repo (e.g., '.gitgov/actors') */
120
+ basePath: string;
121
+ /** File extension for records (default: '.json') */
122
+ extension?: string;
123
+ /** ID encoder for filename-safe IDs (default: undefined = no encoding) */
124
+ idEncoder?: IdEncoder;
125
+ };
126
+ /**
127
+ * Result returned by write operations (put, putMany, delete) on GitHubRecordStore.
128
+ * Contains the commit SHA from the GitHub API response.
129
+ */
130
+ type GitHubWriteResult = {
131
+ /** SHA of the commit created by the write operation */
132
+ commitSha?: string;
133
+ };
134
+ /**
135
+ * Options for write operations on GitHubRecordStore.
136
+ */
137
+ type GitHubWriteOpts = {
138
+ /** Custom commit message (default: auto-generated) */
139
+ commitMessage?: string;
140
+ };
141
+
142
+ /**
143
+ * GitHubRecordStore<V> - GitHub Contents API implementation of RecordStore<V, GitHubWriteResult, GitHubWriteOpts>
144
+ *
145
+ * Persists records as JSON files in a GitHub repository via Octokit.
146
+ * Supports SHA caching to avoid redundant GET calls before PUT/DELETE.
147
+ */
148
+ declare class GitHubRecordStore<V> implements RecordStore<V, GitHubWriteResult, GitHubWriteOpts> {
149
+ private readonly owner;
150
+ private readonly repo;
151
+ private readonly ref;
152
+ private readonly basePath;
153
+ private readonly extension;
154
+ private readonly idEncoder;
155
+ private readonly octokit;
156
+ /** SHA cache keyed by full file path (basePath/encoded + extension) */
157
+ private readonly shaCache;
158
+ /** IGitModule dependency for putMany() atomic commits. Optional — only needed for putMany(). */
159
+ private readonly gitModule;
160
+ constructor(options: GitHubRecordStoreOptions, octokit: Octokit, gitModule?: IGitModule);
161
+ get(id: string): Promise<V | null>;
162
+ put(id: string, value: V, opts?: GitHubWriteOpts): Promise<GitHubWriteResult>;
163
+ /**
164
+ * [EARS-A11, EARS-A12, EARS-B8] Persists multiple records in a single atomic commit.
165
+ * Uses GitHubGitModule staging buffer: add() with contentMap, then commit().
166
+ * Empty entries array returns { commitSha: undefined } without API calls.
167
+ * Requires gitModule dependency — throws if not injected.
168
+ */
169
+ putMany(entries: Array<{
170
+ id: string;
171
+ value: V;
172
+ }>, opts?: GitHubWriteOpts): Promise<GitHubWriteResult>;
173
+ delete(id: string, opts?: GitHubWriteOpts): Promise<GitHubWriteResult>;
174
+ list(): Promise<string[]>;
175
+ exists(id: string): Promise<boolean>;
176
+ private validateId;
177
+ private buildFilePath;
178
+ }
179
+
180
+ /**
181
+ * Types for GitHubGitModule.
182
+ * All EARS prefixes map to github_git_module.md
183
+ */
184
+ /**
185
+ * Configuration for GitHubGitModule.
186
+ * All operations target the specified owner/repo via GitHub REST API.
187
+ * Note: defaultBranch (not ref) because GitModule tracks which branch
188
+ * operations target, switchable via checkoutBranch().
189
+ */
190
+ type GitHubGitModuleOptions = {
191
+ /** GitHub repository owner (user or organization) */
192
+ owner: string;
193
+ /** GitHub repository name */
194
+ repo: string;
195
+ /** Default branch name (default: 'gitgov-state') */
196
+ defaultBranch?: string;
197
+ };
198
+
199
+ /**
200
+ * GitHubGitModule - GitHub REST API implementation of IGitModule
201
+ *
202
+ * Implements IGitModule for SaaS environments where direct filesystem
203
+ * and git CLI are not available. Uses GitHub REST API via Octokit for all operations.
204
+ *
205
+ * Method categories:
206
+ * - Category A (Implement): Real API calls — getFileContent, getCommitHash, etc.
207
+ * - Category B (No-op): Return sensible defaults — push, fetch, stash, etc.
208
+ * - Category C (Not Supported): Throw GitError — rebase, resetHard, etc.
209
+ *
210
+ * All EARS prefixes map to github_git_module.md
211
+ *
212
+ * @module git/github
213
+ */
214
+
215
+ declare class GitHubGitModule implements IGitModule {
216
+ private readonly owner;
217
+ private readonly repo;
218
+ private readonly defaultBranch;
219
+ private readonly octokit;
220
+ /** Staging buffer: path → content (null = delete) */
221
+ private stagingBuffer;
222
+ /** Active ref for operations (can be changed via checkoutBranch) */
223
+ private activeRef;
224
+ constructor(options: GitHubGitModuleOptions, octokit: Octokit);
225
+ /** Category C: Not supported via GitHub API */
226
+ private notSupported;
227
+ /**
228
+ * [EARS-A1] Read file content via Contents API + base64 decode
229
+ * [EARS-A2] Fallback to Blobs API for files >1MB
230
+ */
231
+ getFileContent(commitHash: string, filePath: string): Promise<string>;
232
+ /**
233
+ * [EARS-A3] Get commit SHA from branch via Refs API
234
+ * [EARS-B4] Return SHA directly if already a 40-char hex
235
+ */
236
+ getCommitHash(ref?: string): Promise<string>;
237
+ /**
238
+ * [EARS-A4] List changed files via Compare API
239
+ */
240
+ getChangedFiles(fromCommit: string, toCommit: string, pathFilter: string): Promise<ChangedFile[]>;
241
+ /**
242
+ * [EARS-A5] Get commit history via Commits API
243
+ */
244
+ getCommitHistory(branch: string, options?: GetCommitHistoryOptions): Promise<CommitInfo[]>;
245
+ /**
246
+ * [EARS-B3] Get commit history between two commits via Compare API
247
+ */
248
+ getCommitHistoryRange(fromHash: string, toHash: string, options?: GetCommitHistoryOptions): Promise<CommitInfo[]>;
249
+ /**
250
+ * [EARS-A6] Get commit message via Commits API
251
+ */
252
+ getCommitMessage(commitHash: string): Promise<string>;
253
+ /**
254
+ * [EARS-B1] Check if branch exists via Branches API
255
+ */
256
+ branchExists(branchName: string): Promise<boolean>;
257
+ /**
258
+ * [EARS-B2] List remote branches via Branches API
259
+ * remoteName is ignored — repo itself is the implicit remote
260
+ */
261
+ listRemoteBranches(_remoteName: string): Promise<string[]>;
262
+ /** [EARS-C1] Read file content and store in staging buffer */
263
+ add(filePaths: string[], options?: {
264
+ force?: boolean;
265
+ contentMap?: Record<string, string>;
266
+ }): Promise<void>;
267
+ /** [EARS-C2] Mark files as deleted in staging buffer */
268
+ rm(filePaths: string[]): Promise<void>;
269
+ /** [EARS-C7] Return staged file paths from buffer */
270
+ getStagedFiles(): Promise<string[]>;
271
+ /**
272
+ * [EARS-C6] Create branch via Refs API POST
273
+ */
274
+ createBranch(branchName: string, startPoint?: string): Promise<void>;
275
+ /**
276
+ * Internal commit implementation shared by commit() and commitAllowEmpty().
277
+ *
278
+ * [EARS-C3] 6-step atomic transaction
279
+ * [EARS-C4] Clears staging buffer after successful commit
280
+ * [EARS-C5] Throws if staging buffer is empty (unless allowEmpty)
281
+ */
282
+ private commitInternal;
283
+ /**
284
+ * [EARS-C3] Commit staged changes via 6-step atomic transaction
285
+ * [EARS-C5] Throws if staging buffer is empty
286
+ */
287
+ commit(message: string, author?: CommitAuthor): Promise<string>;
288
+ /** [EARS-D5] exec not supported in API mode */
289
+ exec(_command: string, _args: string[], _options?: ExecOptions): Promise<ExecResult>;
290
+ /** No-op: repos are created via GitHub API, not initialized locally */
291
+ init(): Promise<void>;
292
+ /** [EARS-D1] Return virtual path representing the repo */
293
+ getRepoRoot(): Promise<string>;
294
+ /** [EARS-D1] Return active ref (starts as defaultBranch) */
295
+ getCurrentBranch(): Promise<string>;
296
+ /** No-op: git config doesn't apply to GitHub API */
297
+ setConfig(_key: string, _value: string, _scope?: 'local' | 'global' | 'system'): Promise<void>;
298
+ /** [EARS-D1] Return true if staging buffer has entries */
299
+ hasUncommittedChanges(_pathFilter?: string): Promise<boolean>;
300
+ /** No-op: GitHub API doesn't have rebase-in-progress concept */
301
+ isRebaseInProgress(): Promise<boolean>;
302
+ /** [EARS-D1] GitHub repos always have 'origin' conceptually */
303
+ isRemoteConfigured(_remoteName: string): Promise<boolean>;
304
+ /** No-op: always 'origin' */
305
+ getBranchRemote(_branchName: string): Promise<string | null>;
306
+ /** No-op: GitHub API handles merges atomically */
307
+ getConflictedFiles(): Promise<string[]>;
308
+ /** [EARS-D2] Update activeRef for subsequent operations */
309
+ checkoutBranch(branchName: string): Promise<void>;
310
+ /** No-op: GitHub API doesn't have stash concept */
311
+ stash(_message?: string): Promise<string | null>;
312
+ /** No-op */
313
+ stashPop(): Promise<boolean>;
314
+ /** No-op */
315
+ stashDrop(_stashHash?: string): Promise<void>;
316
+ /** No-op: API always fresh */
317
+ fetch(_remote: string): Promise<void>;
318
+ /** No-op: API mode */
319
+ pull(_remote: string, _branchName: string): Promise<void>;
320
+ /** No-op: API mode */
321
+ pullRebase(_remote: string, _branchName: string): Promise<void>;
322
+ /** [EARS-D4] No-op: commits via API are already remote */
323
+ push(_remote: string, _branchName: string): Promise<void>;
324
+ /** [EARS-D4] No-op: commits via API are already remote */
325
+ pushWithUpstream(_remote: string, _branchName: string): Promise<void>;
326
+ /** No-op: API mode */
327
+ setUpstream(_branchName: string, _remote: string, _remoteBranch: string): Promise<void>;
328
+ /** No-op */
329
+ rebaseAbort(): Promise<void>;
330
+ /** [EARS-D1] Delegates to commitInternal, allowing empty staging buffer */
331
+ commitAllowEmpty(message: string, author?: CommitAuthor): Promise<string>;
332
+ /** [EARS-D3] Not supported via GitHub API */
333
+ rebase(_targetBranch: string): Promise<void>;
334
+ /** [EARS-D3] Not supported via GitHub API */
335
+ rebaseContinue(): Promise<string>;
336
+ /** [EARS-D3] Not supported via GitHub API */
337
+ resetHard(_target: string): Promise<void>;
338
+ /** [EARS-D3] Not supported via GitHub API */
339
+ checkoutOrphanBranch(_branchName: string): Promise<void>;
340
+ /** [EARS-D3] Not supported via GitHub API */
341
+ checkoutFilesFromBranch(_sourceBranch: string, _filePaths: string[]): Promise<void>;
342
+ /** [EARS-D3] Not supported via GitHub API */
343
+ getMergeBase(_branchA: string, _branchB: string): Promise<string>;
344
+ }
345
+
346
+ /**
347
+ * GitHubConfigStore Types
348
+ *
349
+ * Configuration types for the GitHub-backed ConfigStore implementation.
350
+ * Auth (token) and base URL are configured via the Octokit instance.
351
+ */
352
+ /**
353
+ * Options for constructing a GitHubConfigStore instance.
354
+ * Auth and API base URL are configured on the Octokit instance, not here.
355
+ */
356
+ type GitHubConfigStoreOptions = {
357
+ /** GitHub repository owner (user or organization) */
358
+ owner: string;
359
+ /** GitHub repository name */
360
+ repo: string;
361
+ /** Branch to read from / write to (default: 'gitgov-state'). Must be a branch name for saves. */
362
+ ref?: string;
363
+ /** Base path within the repo (default: '.gitgov') */
364
+ basePath?: string;
365
+ };
366
+ /**
367
+ * Result returned by saveConfig on GitHubConfigStore.
368
+ * Contains the commit SHA from the GitHub API response.
369
+ */
370
+ type GitHubSaveResult = {
371
+ /**
372
+ * SHA of the commit created by the save operation.
373
+ * Always present — saveConfig either creates a commit (success) or throws (failure).
374
+ * Contrast with GitHubWriteResult.commitSha which is optional (idempotent delete).
375
+ */
376
+ commitSha: string;
377
+ };
378
+
379
+ /**
380
+ * GitHubConfigStore - GitHub Contents API implementation of ConfigStore
381
+ *
382
+ * Persists config.json to a GitHub repository via Octokit.
383
+ * Used by SaaS/server-side environments where the project lives on GitHub
384
+ * and direct filesystem access is not available.
385
+ *
386
+ * Key behaviors:
387
+ * - loadConfig: GET contents, base64 decode, JSON parse. Fail-safe on 404/invalid JSON.
388
+ * - saveConfig: JSON serialize + base64 encode, PUT contents with optional SHA for updates.
389
+ * - Caches blob SHA from loadConfig for subsequent saveConfig (optimistic concurrency).
390
+ */
391
+
392
+ declare class GitHubConfigStore implements ConfigStore<GitHubSaveResult> {
393
+ private readonly owner;
394
+ private readonly repo;
395
+ private readonly ref;
396
+ private readonly basePath;
397
+ private readonly octokit;
398
+ /** Cached blob SHA from the last loadConfig call, used for PUT updates */
399
+ private cachedSha;
400
+ constructor(options: GitHubConfigStoreOptions, octokit: Octokit);
401
+ /**
402
+ * Load project configuration from GitHub Contents API.
403
+ *
404
+ * [EARS-A1] Returns GitGovConfig when valid JSON is found.
405
+ * [EARS-A2] Returns null on 404 (fail-safe).
406
+ * [EARS-A3] Returns null on invalid JSON (fail-safe).
407
+ * [EARS-B1] Fetches via Contents API with base64 decode.
408
+ * [EARS-B2] Caches SHA from response for subsequent saveConfig.
409
+ */
410
+ loadConfig(): Promise<GitGovConfig | null>;
411
+ /**
412
+ * Save project configuration to GitHub via Contents API PUT.
413
+ *
414
+ * [EARS-A4] Writes config via PUT to Contents API.
415
+ * [EARS-B3] Includes cached SHA for updates (optimistic concurrency).
416
+ * [EARS-B4] Omits SHA for initial creation.
417
+ * [EARS-C1] Throws PERMISSION_DENIED on 401/403.
418
+ * [EARS-C2] Throws CONFLICT on 409.
419
+ * [EARS-C3] Throws SERVER_ERROR on 5xx.
420
+ */
421
+ saveConfig(config: GitGovConfig): Promise<GitHubSaveResult>;
422
+ }
423
+
424
+ /**
425
+ * GitHub API implementations for @gitgov/core/github
426
+ *
427
+ * This module exports all implementations that use GitHub REST API.
428
+ * Suitable for SaaS environments, Forge apps, GitHub Actions,
429
+ * and any context without local filesystem access.
430
+ *
431
+ * Usage:
432
+ * import { GitHubFileLister, GitHubRecordStore, GitHubGitModule, GitHubConfigStore } from '@gitgov/core/github';
433
+ *
434
+ * Each implementation receives an `Octokit` instance for testability and shared auth/base-URL config.
435
+ */
436
+
437
+ /**
438
+ * Error codes for GitHub API errors.
439
+ * Semantic codes that abstract HTTP status codes.
440
+ */
441
+ type GitHubApiErrorCode = 'PERMISSION_DENIED' | 'NOT_FOUND' | 'CONFLICT' | 'SERVER_ERROR' | 'NETWORK_ERROR' | 'INVALID_ID' | 'INVALID_RESPONSE';
442
+ /**
443
+ * Typed error for GitHub API operations.
444
+ * Used by RecordStore, ConfigStore, and other modules that
445
+ * interact with GitHub Contents API directly.
446
+ */
447
+ declare class GitHubApiError extends Error {
448
+ /** Semantic error code */
449
+ readonly code: GitHubApiErrorCode;
450
+ /** HTTP status code (if applicable) */
451
+ readonly statusCode?: number | undefined;
452
+ constructor(message: string,
453
+ /** Semantic error code */
454
+ code: GitHubApiErrorCode,
455
+ /** HTTP status code (if applicable) */
456
+ statusCode?: number | undefined);
457
+ }
458
+ /**
459
+ * Type guard: checks if an error is an Octokit RequestError (duck-typing).
460
+ * Avoids runtime import of ESM-only @octokit/request-error.
461
+ */
462
+ declare function isOctokitRequestError(error: unknown): error is {
463
+ status: number;
464
+ message: string;
465
+ };
466
+ /**
467
+ * Maps Octokit RequestError (and unknown errors) to GitHubApiError.
468
+ * Shared utility used by all GitHub backend modules.
469
+ */
470
+ declare function mapOctokitError(error: unknown, context: string): GitHubApiError;
471
+
472
+ export { GitHubApiError, type GitHubApiErrorCode, GitHubConfigStore, type GitHubConfigStoreOptions, GitHubFileLister, type GitHubFileListerOptions, GitHubGitModule, type GitHubGitModuleOptions, GitHubRecordStore, type GitHubRecordStoreOptions, type GitHubSaveResult, type GitHubWriteOpts, type GitHubWriteResult, isOctokitRequestError, mapOctokitError };