@optique/git 0.9.0-dev.264 → 0.9.0-dev.268
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/dist/index.cjs +137 -62
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +137 -62
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -26,23 +26,50 @@ const __optique_core_nonempty = __toESM(require("@optique/core/nonempty"));
|
|
|
26
26
|
const isomorphic_git = __toESM(require("isomorphic-git"));
|
|
27
27
|
const node_fs_promises = __toESM(require("node:fs/promises"));
|
|
28
28
|
const node_process = __toESM(require("node:process"));
|
|
29
|
+
const __logtape_logtape = __toESM(require("@logtape/logtape"));
|
|
29
30
|
|
|
30
31
|
//#region src/index.ts
|
|
32
|
+
const logger = (0, __logtape_logtape.getLogger)(["optique", "git"]);
|
|
33
|
+
/**
|
|
34
|
+
* Read-only filesystem interface passed to isomorphic-git.
|
|
35
|
+
*
|
|
36
|
+
* This package only performs read operations (validation and listing).
|
|
37
|
+
* Write methods are implemented as stubs that throw errors if called,
|
|
38
|
+
* enforcing the read-only contract and preventing accidental writes.
|
|
39
|
+
*/
|
|
31
40
|
const gitFs = {
|
|
32
41
|
readFile: node_fs_promises.readFile,
|
|
33
|
-
writeFile:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
writeFile: () => {
|
|
43
|
+
throw new Error("gitFs is read-only: writeFile is disabled.");
|
|
44
|
+
},
|
|
45
|
+
mkdir: () => {
|
|
46
|
+
throw new Error("gitFs is read-only: mkdir is disabled.");
|
|
47
|
+
},
|
|
48
|
+
rmdir: () => {
|
|
49
|
+
throw new Error("gitFs is read-only: rmdir is disabled.");
|
|
50
|
+
},
|
|
51
|
+
unlink: () => {
|
|
52
|
+
throw new Error("gitFs is read-only: unlink is disabled.");
|
|
53
|
+
},
|
|
37
54
|
readdir: node_fs_promises.readdir,
|
|
38
55
|
readlink: node_fs_promises.readlink,
|
|
39
|
-
symlink:
|
|
56
|
+
symlink: () => {
|
|
57
|
+
throw new Error("gitFs is read-only: symlink is disabled.");
|
|
58
|
+
},
|
|
40
59
|
stat: node_fs_promises.stat,
|
|
41
60
|
lstat: node_fs_promises.lstat
|
|
42
61
|
};
|
|
43
62
|
const METAVAR_BRANCH = "BRANCH";
|
|
44
63
|
const METAVAR_TAG = "TAG";
|
|
45
64
|
const METAVAR_REMOTE = "REMOTE";
|
|
65
|
+
/**
|
|
66
|
+
* Resolves the repository directory from the provided option or process.cwd().
|
|
67
|
+
*
|
|
68
|
+
* Note: This function does not validate that the directory exists or is
|
|
69
|
+
* accessible. Directory validation is deferred to the Git operations
|
|
70
|
+
* themselves, which will produce appropriate error messages if the directory
|
|
71
|
+
* is invalid or not a Git repository.
|
|
72
|
+
*/
|
|
46
73
|
function getRepoDir(dirOption) {
|
|
47
74
|
if (dirOption != null) return dirOption;
|
|
48
75
|
if (typeof node_process.default !== "undefined" && typeof node_process.default.cwd === "function") return node_process.default.cwd();
|
|
@@ -56,6 +83,8 @@ function listFailureMessage(error, dir, errors, fallback) {
|
|
|
56
83
|
if (hasErrorCode(error, "NotAGitRepositoryError") || hasErrorCode(error, "NotFoundError")) return __optique_core_message.message`${(0, __optique_core_message.value)(dir)} is not a git repository.`;
|
|
57
84
|
return fallback;
|
|
58
85
|
}
|
|
86
|
+
/** Default depth for commit suggestions. */
|
|
87
|
+
const DEFAULT_SUGGESTION_DEPTH = 15;
|
|
59
88
|
function createAsyncValueParser(options, metavar, parseFn, suggestFn) {
|
|
60
89
|
(0, __optique_core_nonempty.ensureNonEmptyString)(metavar);
|
|
61
90
|
return {
|
|
@@ -70,7 +99,10 @@ function createAsyncValueParser(options, metavar, parseFn, suggestFn) {
|
|
|
70
99
|
},
|
|
71
100
|
async *suggest(prefix) {
|
|
72
101
|
const dir = getRepoDir(options?.dir);
|
|
73
|
-
if (suggestFn)
|
|
102
|
+
if (suggestFn) {
|
|
103
|
+
const depth = options?.suggestionDepth ?? DEFAULT_SUGGESTION_DEPTH;
|
|
104
|
+
yield* suggestFn(dir, prefix, depth);
|
|
105
|
+
}
|
|
74
106
|
}
|
|
75
107
|
};
|
|
76
108
|
}
|
|
@@ -119,7 +151,7 @@ function gitBranch(options) {
|
|
|
119
151
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
120
152
|
};
|
|
121
153
|
}
|
|
122
|
-
}, async function* suggestBranch(dir, prefix) {
|
|
154
|
+
}, async function* suggestBranch(dir, prefix, _depth) {
|
|
123
155
|
try {
|
|
124
156
|
const branches = await isomorphic_git.listBranches({
|
|
125
157
|
fs: gitFs,
|
|
@@ -129,7 +161,13 @@ function gitBranch(options) {
|
|
|
129
161
|
kind: "literal",
|
|
130
162
|
text: branch
|
|
131
163
|
};
|
|
132
|
-
} catch {
|
|
164
|
+
} catch (error) {
|
|
165
|
+
logger.debug("Failed to list branches for suggestions.", {
|
|
166
|
+
dir,
|
|
167
|
+
prefix,
|
|
168
|
+
error
|
|
169
|
+
});
|
|
170
|
+
}
|
|
133
171
|
});
|
|
134
172
|
}
|
|
135
173
|
/**
|
|
@@ -179,7 +217,7 @@ function gitRemoteBranch(remote, options) {
|
|
|
179
217
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
180
218
|
};
|
|
181
219
|
}
|
|
182
|
-
}, async function* suggestRemoteBranch(dir, prefix) {
|
|
220
|
+
}, async function* suggestRemoteBranch(dir, prefix, _depth) {
|
|
183
221
|
try {
|
|
184
222
|
const branches = await isomorphic_git.listBranches({
|
|
185
223
|
fs: gitFs,
|
|
@@ -190,7 +228,14 @@ function gitRemoteBranch(remote, options) {
|
|
|
190
228
|
kind: "literal",
|
|
191
229
|
text: branch
|
|
192
230
|
};
|
|
193
|
-
} catch {
|
|
231
|
+
} catch (error) {
|
|
232
|
+
logger.debug("Failed to list remote branches for suggestions.", {
|
|
233
|
+
dir,
|
|
234
|
+
remote,
|
|
235
|
+
prefix,
|
|
236
|
+
error
|
|
237
|
+
});
|
|
238
|
+
}
|
|
194
239
|
});
|
|
195
240
|
}
|
|
196
241
|
/**
|
|
@@ -227,7 +272,7 @@ function gitTag(options) {
|
|
|
227
272
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
228
273
|
};
|
|
229
274
|
}
|
|
230
|
-
}, async function* suggestTag(dir, prefix) {
|
|
275
|
+
}, async function* suggestTag(dir, prefix, _depth) {
|
|
231
276
|
try {
|
|
232
277
|
const tags = await isomorphic_git.listTags({
|
|
233
278
|
fs: gitFs,
|
|
@@ -237,7 +282,13 @@ function gitTag(options) {
|
|
|
237
282
|
kind: "literal",
|
|
238
283
|
text: tag
|
|
239
284
|
};
|
|
240
|
-
} catch {
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger.debug("Failed to list tags for suggestions.", {
|
|
287
|
+
dir,
|
|
288
|
+
prefix,
|
|
289
|
+
error
|
|
290
|
+
});
|
|
291
|
+
}
|
|
241
292
|
});
|
|
242
293
|
}
|
|
243
294
|
/**
|
|
@@ -275,7 +326,7 @@ function gitRemote(options) {
|
|
|
275
326
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
276
327
|
};
|
|
277
328
|
}
|
|
278
|
-
}, async function* suggestRemote(dir, prefix) {
|
|
329
|
+
}, async function* suggestRemote(dir, prefix, _depth) {
|
|
279
330
|
try {
|
|
280
331
|
const remotes = await isomorphic_git.listRemotes({
|
|
281
332
|
fs: gitFs,
|
|
@@ -285,7 +336,13 @@ function gitRemote(options) {
|
|
|
285
336
|
kind: "literal",
|
|
286
337
|
text: r.remote
|
|
287
338
|
};
|
|
288
|
-
} catch {
|
|
339
|
+
} catch (error) {
|
|
340
|
+
logger.debug("Failed to list remotes for suggestions.", {
|
|
341
|
+
dir,
|
|
342
|
+
prefix,
|
|
343
|
+
error
|
|
344
|
+
});
|
|
345
|
+
}
|
|
289
346
|
});
|
|
290
347
|
}
|
|
291
348
|
/**
|
|
@@ -301,26 +358,14 @@ function gitRemote(options) {
|
|
|
301
358
|
function gitCommit(options) {
|
|
302
359
|
const metavar = options?.metavar ?? "COMMIT";
|
|
303
360
|
return createAsyncValueParser(options, metavar, async (dir, input, errors) => {
|
|
304
|
-
|
|
305
|
-
(0, __optique_core_nonempty.ensureNonEmptyString)(input);
|
|
306
|
-
} catch {
|
|
307
|
-
if (errors?.invalidFormat) return {
|
|
308
|
-
success: false,
|
|
309
|
-
error: errors.invalidFormat(input)
|
|
310
|
-
};
|
|
311
|
-
return {
|
|
312
|
-
success: false,
|
|
313
|
-
error: __optique_core_message.message`Invalid commit SHA: ${(0, __optique_core_message.value)(input)}`
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
if (input.length < 4 || input.length > 40) {
|
|
361
|
+
if (!/^[0-9a-f]{4,40}$/i.test(input)) {
|
|
317
362
|
if (errors?.invalidFormat) return {
|
|
318
363
|
success: false,
|
|
319
364
|
error: errors.invalidFormat(input)
|
|
320
365
|
};
|
|
321
366
|
return {
|
|
322
367
|
success: false,
|
|
323
|
-
error: __optique_core_message.message`
|
|
368
|
+
error: __optique_core_message.message`Invalid commit SHA: ${(0, __optique_core_message.value)(input)}. Provide an abbreviated (4+) or full (40) hexadecimal commit SHA.`
|
|
324
369
|
};
|
|
325
370
|
}
|
|
326
371
|
try {
|
|
@@ -343,12 +388,12 @@ function gitCommit(options) {
|
|
|
343
388
|
error: __optique_core_message.message`Commit ${(0, __optique_core_message.value)(input)} does not exist. Provide a valid commit SHA.`
|
|
344
389
|
};
|
|
345
390
|
}
|
|
346
|
-
}, async function* suggestCommit(dir, prefix) {
|
|
391
|
+
}, async function* suggestCommit(dir, prefix, depth) {
|
|
347
392
|
try {
|
|
348
393
|
const commits = await isomorphic_git.log({
|
|
349
394
|
fs: gitFs,
|
|
350
395
|
dir,
|
|
351
|
-
depth
|
|
396
|
+
depth
|
|
352
397
|
});
|
|
353
398
|
for (const commit of commits) if (commit.oid.startsWith(prefix)) {
|
|
354
399
|
const shortOid = commit.oid.slice(0, 7);
|
|
@@ -359,7 +404,13 @@ function gitCommit(options) {
|
|
|
359
404
|
description: __optique_core_message.message`${firstLine}`
|
|
360
405
|
};
|
|
361
406
|
}
|
|
362
|
-
} catch {
|
|
407
|
+
} catch (error) {
|
|
408
|
+
logger.debug("Failed to list commits for suggestions.", {
|
|
409
|
+
dir,
|
|
410
|
+
prefix,
|
|
411
|
+
error
|
|
412
|
+
});
|
|
413
|
+
}
|
|
363
414
|
});
|
|
364
415
|
}
|
|
365
416
|
/**
|
|
@@ -375,39 +426,39 @@ function gitCommit(options) {
|
|
|
375
426
|
function gitRef(options) {
|
|
376
427
|
const metavar = options?.metavar ?? "REF";
|
|
377
428
|
return createAsyncValueParser(options, metavar, async (dir, input, errors) => {
|
|
429
|
+
let resolved;
|
|
378
430
|
try {
|
|
379
|
-
|
|
431
|
+
resolved = await isomorphic_git.resolveRef({
|
|
380
432
|
fs: gitFs,
|
|
381
433
|
dir,
|
|
382
434
|
ref: input
|
|
383
435
|
});
|
|
436
|
+
} catch {}
|
|
437
|
+
if (resolved) return {
|
|
438
|
+
success: true,
|
|
439
|
+
value: resolved
|
|
440
|
+
};
|
|
441
|
+
try {
|
|
442
|
+
const oid = await isomorphic_git.expandOid({
|
|
443
|
+
fs: gitFs,
|
|
444
|
+
dir,
|
|
445
|
+
oid: input
|
|
446
|
+
});
|
|
384
447
|
return {
|
|
385
448
|
success: true,
|
|
386
|
-
value:
|
|
449
|
+
value: oid
|
|
387
450
|
};
|
|
388
451
|
} catch {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
value: oid
|
|
398
|
-
};
|
|
399
|
-
} catch {
|
|
400
|
-
if (errors?.notFound) return {
|
|
401
|
-
success: false,
|
|
402
|
-
error: errors.notFound(input)
|
|
403
|
-
};
|
|
404
|
-
return {
|
|
405
|
-
success: false,
|
|
406
|
-
error: __optique_core_message.message`Reference ${(0, __optique_core_message.value)(input)} does not exist. Provide a valid branch, tag, or commit SHA.`
|
|
407
|
-
};
|
|
408
|
-
}
|
|
452
|
+
if (errors?.notFound) return {
|
|
453
|
+
success: false,
|
|
454
|
+
error: errors.notFound(input)
|
|
455
|
+
};
|
|
456
|
+
return {
|
|
457
|
+
success: false,
|
|
458
|
+
error: __optique_core_message.message`Reference ${(0, __optique_core_message.value)(input)} does not exist. Provide a valid branch, tag, or commit SHA.`
|
|
459
|
+
};
|
|
409
460
|
}
|
|
410
|
-
}, async function* suggestRef(dir, prefix) {
|
|
461
|
+
}, async function* suggestRef(dir, prefix, depth) {
|
|
411
462
|
try {
|
|
412
463
|
const [branches, tags, commits] = await Promise.all([
|
|
413
464
|
isomorphic_git.listBranches({
|
|
@@ -421,7 +472,7 @@ function gitRef(options) {
|
|
|
421
472
|
isomorphic_git.log({
|
|
422
473
|
fs: gitFs,
|
|
423
474
|
dir,
|
|
424
|
-
depth
|
|
475
|
+
depth
|
|
425
476
|
})
|
|
426
477
|
]);
|
|
427
478
|
for (const branch of branches) if (branch.startsWith(prefix)) yield {
|
|
@@ -441,7 +492,13 @@ function gitRef(options) {
|
|
|
441
492
|
description: __optique_core_message.message`${firstLine}`
|
|
442
493
|
};
|
|
443
494
|
}
|
|
444
|
-
} catch {
|
|
495
|
+
} catch (error) {
|
|
496
|
+
logger.debug("Failed to list refs for suggestions.", {
|
|
497
|
+
dir,
|
|
498
|
+
prefix,
|
|
499
|
+
error
|
|
500
|
+
});
|
|
501
|
+
}
|
|
445
502
|
});
|
|
446
503
|
}
|
|
447
504
|
/**
|
|
@@ -453,12 +510,30 @@ function gitRef(options) {
|
|
|
453
510
|
*/
|
|
454
511
|
function createGitParsers(options) {
|
|
455
512
|
return {
|
|
456
|
-
branch: (branchOptions) => gitBranch(
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
513
|
+
branch: (branchOptions) => gitBranch({
|
|
514
|
+
...options,
|
|
515
|
+
...branchOptions
|
|
516
|
+
}),
|
|
517
|
+
remoteBranch: (remote, branchOptions) => gitRemoteBranch(remote, {
|
|
518
|
+
...options,
|
|
519
|
+
...branchOptions
|
|
520
|
+
}),
|
|
521
|
+
tag: (tagOptions) => gitTag({
|
|
522
|
+
...options,
|
|
523
|
+
...tagOptions
|
|
524
|
+
}),
|
|
525
|
+
remote: (remoteOptions) => gitRemote({
|
|
526
|
+
...options,
|
|
527
|
+
...remoteOptions
|
|
528
|
+
}),
|
|
529
|
+
commit: (commitOptions) => gitCommit({
|
|
530
|
+
...options,
|
|
531
|
+
...commitOptions
|
|
532
|
+
}),
|
|
533
|
+
ref: (refOptions) => gitRef({
|
|
534
|
+
...options,
|
|
535
|
+
...refOptions
|
|
536
|
+
})
|
|
462
537
|
};
|
|
463
538
|
}
|
|
464
539
|
|
package/dist/index.d.cts
CHANGED
|
@@ -27,6 +27,14 @@ interface GitParserOptions {
|
|
|
27
27
|
* @since 0.9.0
|
|
28
28
|
*/
|
|
29
29
|
errors?: GitParserErrors;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum number of recent commits to include in shell completion suggestions.
|
|
32
|
+
* Only applies to `gitCommit()` and `gitRef()` parsers.
|
|
33
|
+
* Defaults to 15.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.9.0
|
|
36
|
+
*/
|
|
37
|
+
suggestionDepth?: number;
|
|
30
38
|
}
|
|
31
39
|
/**
|
|
32
40
|
* Custom error messages for git value parsers.
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,14 @@ interface GitParserOptions {
|
|
|
27
27
|
* @since 0.9.0
|
|
28
28
|
*/
|
|
29
29
|
errors?: GitParserErrors;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum number of recent commits to include in shell completion suggestions.
|
|
32
|
+
* Only applies to `gitCommit()` and `gitRef()` parsers.
|
|
33
|
+
* Defaults to 15.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.9.0
|
|
36
|
+
*/
|
|
37
|
+
suggestionDepth?: number;
|
|
30
38
|
}
|
|
31
39
|
/**
|
|
32
40
|
* Custom error messages for git value parsers.
|
package/dist/index.js
CHANGED
|
@@ -4,23 +4,50 @@ import * as git from "isomorphic-git";
|
|
|
4
4
|
import { expandOid, listBranches, listRemotes, listTags, readObject, resolveRef } from "isomorphic-git";
|
|
5
5
|
import * as fs from "node:fs/promises";
|
|
6
6
|
import process from "node:process";
|
|
7
|
+
import { getLogger } from "@logtape/logtape";
|
|
7
8
|
|
|
8
9
|
//#region src/index.ts
|
|
10
|
+
const logger = getLogger(["optique", "git"]);
|
|
11
|
+
/**
|
|
12
|
+
* Read-only filesystem interface passed to isomorphic-git.
|
|
13
|
+
*
|
|
14
|
+
* This package only performs read operations (validation and listing).
|
|
15
|
+
* Write methods are implemented as stubs that throw errors if called,
|
|
16
|
+
* enforcing the read-only contract and preventing accidental writes.
|
|
17
|
+
*/
|
|
9
18
|
const gitFs = {
|
|
10
19
|
readFile: fs.readFile,
|
|
11
|
-
writeFile:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
writeFile: () => {
|
|
21
|
+
throw new Error("gitFs is read-only: writeFile is disabled.");
|
|
22
|
+
},
|
|
23
|
+
mkdir: () => {
|
|
24
|
+
throw new Error("gitFs is read-only: mkdir is disabled.");
|
|
25
|
+
},
|
|
26
|
+
rmdir: () => {
|
|
27
|
+
throw new Error("gitFs is read-only: rmdir is disabled.");
|
|
28
|
+
},
|
|
29
|
+
unlink: () => {
|
|
30
|
+
throw new Error("gitFs is read-only: unlink is disabled.");
|
|
31
|
+
},
|
|
15
32
|
readdir: fs.readdir,
|
|
16
33
|
readlink: fs.readlink,
|
|
17
|
-
symlink:
|
|
34
|
+
symlink: () => {
|
|
35
|
+
throw new Error("gitFs is read-only: symlink is disabled.");
|
|
36
|
+
},
|
|
18
37
|
stat: fs.stat,
|
|
19
38
|
lstat: fs.lstat
|
|
20
39
|
};
|
|
21
40
|
const METAVAR_BRANCH = "BRANCH";
|
|
22
41
|
const METAVAR_TAG = "TAG";
|
|
23
42
|
const METAVAR_REMOTE = "REMOTE";
|
|
43
|
+
/**
|
|
44
|
+
* Resolves the repository directory from the provided option or process.cwd().
|
|
45
|
+
*
|
|
46
|
+
* Note: This function does not validate that the directory exists or is
|
|
47
|
+
* accessible. Directory validation is deferred to the Git operations
|
|
48
|
+
* themselves, which will produce appropriate error messages if the directory
|
|
49
|
+
* is invalid or not a Git repository.
|
|
50
|
+
*/
|
|
24
51
|
function getRepoDir(dirOption) {
|
|
25
52
|
if (dirOption != null) return dirOption;
|
|
26
53
|
if (typeof process !== "undefined" && typeof process.cwd === "function") return process.cwd();
|
|
@@ -34,6 +61,8 @@ function listFailureMessage(error, dir, errors, fallback) {
|
|
|
34
61
|
if (hasErrorCode(error, "NotAGitRepositoryError") || hasErrorCode(error, "NotFoundError")) return message`${value(dir)} is not a git repository.`;
|
|
35
62
|
return fallback;
|
|
36
63
|
}
|
|
64
|
+
/** Default depth for commit suggestions. */
|
|
65
|
+
const DEFAULT_SUGGESTION_DEPTH = 15;
|
|
37
66
|
function createAsyncValueParser(options, metavar, parseFn, suggestFn) {
|
|
38
67
|
ensureNonEmptyString(metavar);
|
|
39
68
|
return {
|
|
@@ -48,7 +77,10 @@ function createAsyncValueParser(options, metavar, parseFn, suggestFn) {
|
|
|
48
77
|
},
|
|
49
78
|
async *suggest(prefix) {
|
|
50
79
|
const dir = getRepoDir(options?.dir);
|
|
51
|
-
if (suggestFn)
|
|
80
|
+
if (suggestFn) {
|
|
81
|
+
const depth = options?.suggestionDepth ?? DEFAULT_SUGGESTION_DEPTH;
|
|
82
|
+
yield* suggestFn(dir, prefix, depth);
|
|
83
|
+
}
|
|
52
84
|
}
|
|
53
85
|
};
|
|
54
86
|
}
|
|
@@ -97,7 +129,7 @@ function gitBranch(options) {
|
|
|
97
129
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
98
130
|
};
|
|
99
131
|
}
|
|
100
|
-
}, async function* suggestBranch(dir, prefix) {
|
|
132
|
+
}, async function* suggestBranch(dir, prefix, _depth) {
|
|
101
133
|
try {
|
|
102
134
|
const branches = await git.listBranches({
|
|
103
135
|
fs: gitFs,
|
|
@@ -107,7 +139,13 @@ function gitBranch(options) {
|
|
|
107
139
|
kind: "literal",
|
|
108
140
|
text: branch
|
|
109
141
|
};
|
|
110
|
-
} catch {
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger.debug("Failed to list branches for suggestions.", {
|
|
144
|
+
dir,
|
|
145
|
+
prefix,
|
|
146
|
+
error
|
|
147
|
+
});
|
|
148
|
+
}
|
|
111
149
|
});
|
|
112
150
|
}
|
|
113
151
|
/**
|
|
@@ -157,7 +195,7 @@ function gitRemoteBranch(remote, options) {
|
|
|
157
195
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
158
196
|
};
|
|
159
197
|
}
|
|
160
|
-
}, async function* suggestRemoteBranch(dir, prefix) {
|
|
198
|
+
}, async function* suggestRemoteBranch(dir, prefix, _depth) {
|
|
161
199
|
try {
|
|
162
200
|
const branches = await git.listBranches({
|
|
163
201
|
fs: gitFs,
|
|
@@ -168,7 +206,14 @@ function gitRemoteBranch(remote, options) {
|
|
|
168
206
|
kind: "literal",
|
|
169
207
|
text: branch
|
|
170
208
|
};
|
|
171
|
-
} catch {
|
|
209
|
+
} catch (error) {
|
|
210
|
+
logger.debug("Failed to list remote branches for suggestions.", {
|
|
211
|
+
dir,
|
|
212
|
+
remote,
|
|
213
|
+
prefix,
|
|
214
|
+
error
|
|
215
|
+
});
|
|
216
|
+
}
|
|
172
217
|
});
|
|
173
218
|
}
|
|
174
219
|
/**
|
|
@@ -205,7 +250,7 @@ function gitTag(options) {
|
|
|
205
250
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
206
251
|
};
|
|
207
252
|
}
|
|
208
|
-
}, async function* suggestTag(dir, prefix) {
|
|
253
|
+
}, async function* suggestTag(dir, prefix, _depth) {
|
|
209
254
|
try {
|
|
210
255
|
const tags = await git.listTags({
|
|
211
256
|
fs: gitFs,
|
|
@@ -215,7 +260,13 @@ function gitTag(options) {
|
|
|
215
260
|
kind: "literal",
|
|
216
261
|
text: tag
|
|
217
262
|
};
|
|
218
|
-
} catch {
|
|
263
|
+
} catch (error) {
|
|
264
|
+
logger.debug("Failed to list tags for suggestions.", {
|
|
265
|
+
dir,
|
|
266
|
+
prefix,
|
|
267
|
+
error
|
|
268
|
+
});
|
|
269
|
+
}
|
|
219
270
|
});
|
|
220
271
|
}
|
|
221
272
|
/**
|
|
@@ -253,7 +304,7 @@ function gitRemote(options) {
|
|
|
253
304
|
error: listFailureMessage(error, dir, errors, fallback)
|
|
254
305
|
};
|
|
255
306
|
}
|
|
256
|
-
}, async function* suggestRemote(dir, prefix) {
|
|
307
|
+
}, async function* suggestRemote(dir, prefix, _depth) {
|
|
257
308
|
try {
|
|
258
309
|
const remotes = await git.listRemotes({
|
|
259
310
|
fs: gitFs,
|
|
@@ -263,7 +314,13 @@ function gitRemote(options) {
|
|
|
263
314
|
kind: "literal",
|
|
264
315
|
text: r.remote
|
|
265
316
|
};
|
|
266
|
-
} catch {
|
|
317
|
+
} catch (error) {
|
|
318
|
+
logger.debug("Failed to list remotes for suggestions.", {
|
|
319
|
+
dir,
|
|
320
|
+
prefix,
|
|
321
|
+
error
|
|
322
|
+
});
|
|
323
|
+
}
|
|
267
324
|
});
|
|
268
325
|
}
|
|
269
326
|
/**
|
|
@@ -279,26 +336,14 @@ function gitRemote(options) {
|
|
|
279
336
|
function gitCommit(options) {
|
|
280
337
|
const metavar = options?.metavar ?? "COMMIT";
|
|
281
338
|
return createAsyncValueParser(options, metavar, async (dir, input, errors) => {
|
|
282
|
-
|
|
283
|
-
ensureNonEmptyString(input);
|
|
284
|
-
} catch {
|
|
285
|
-
if (errors?.invalidFormat) return {
|
|
286
|
-
success: false,
|
|
287
|
-
error: errors.invalidFormat(input)
|
|
288
|
-
};
|
|
289
|
-
return {
|
|
290
|
-
success: false,
|
|
291
|
-
error: message`Invalid commit SHA: ${value(input)}`
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
if (input.length < 4 || input.length > 40) {
|
|
339
|
+
if (!/^[0-9a-f]{4,40}$/i.test(input)) {
|
|
295
340
|
if (errors?.invalidFormat) return {
|
|
296
341
|
success: false,
|
|
297
342
|
error: errors.invalidFormat(input)
|
|
298
343
|
};
|
|
299
344
|
return {
|
|
300
345
|
success: false,
|
|
301
|
-
error: message`
|
|
346
|
+
error: message`Invalid commit SHA: ${value(input)}. Provide an abbreviated (4+) or full (40) hexadecimal commit SHA.`
|
|
302
347
|
};
|
|
303
348
|
}
|
|
304
349
|
try {
|
|
@@ -321,12 +366,12 @@ function gitCommit(options) {
|
|
|
321
366
|
error: message`Commit ${value(input)} does not exist. Provide a valid commit SHA.`
|
|
322
367
|
};
|
|
323
368
|
}
|
|
324
|
-
}, async function* suggestCommit(dir, prefix) {
|
|
369
|
+
}, async function* suggestCommit(dir, prefix, depth) {
|
|
325
370
|
try {
|
|
326
371
|
const commits = await git.log({
|
|
327
372
|
fs: gitFs,
|
|
328
373
|
dir,
|
|
329
|
-
depth
|
|
374
|
+
depth
|
|
330
375
|
});
|
|
331
376
|
for (const commit of commits) if (commit.oid.startsWith(prefix)) {
|
|
332
377
|
const shortOid = commit.oid.slice(0, 7);
|
|
@@ -337,7 +382,13 @@ function gitCommit(options) {
|
|
|
337
382
|
description: message`${firstLine}`
|
|
338
383
|
};
|
|
339
384
|
}
|
|
340
|
-
} catch {
|
|
385
|
+
} catch (error) {
|
|
386
|
+
logger.debug("Failed to list commits for suggestions.", {
|
|
387
|
+
dir,
|
|
388
|
+
prefix,
|
|
389
|
+
error
|
|
390
|
+
});
|
|
391
|
+
}
|
|
341
392
|
});
|
|
342
393
|
}
|
|
343
394
|
/**
|
|
@@ -353,39 +404,39 @@ function gitCommit(options) {
|
|
|
353
404
|
function gitRef(options) {
|
|
354
405
|
const metavar = options?.metavar ?? "REF";
|
|
355
406
|
return createAsyncValueParser(options, metavar, async (dir, input, errors) => {
|
|
407
|
+
let resolved;
|
|
356
408
|
try {
|
|
357
|
-
|
|
409
|
+
resolved = await git.resolveRef({
|
|
358
410
|
fs: gitFs,
|
|
359
411
|
dir,
|
|
360
412
|
ref: input
|
|
361
413
|
});
|
|
414
|
+
} catch {}
|
|
415
|
+
if (resolved) return {
|
|
416
|
+
success: true,
|
|
417
|
+
value: resolved
|
|
418
|
+
};
|
|
419
|
+
try {
|
|
420
|
+
const oid = await git.expandOid({
|
|
421
|
+
fs: gitFs,
|
|
422
|
+
dir,
|
|
423
|
+
oid: input
|
|
424
|
+
});
|
|
362
425
|
return {
|
|
363
426
|
success: true,
|
|
364
|
-
value:
|
|
427
|
+
value: oid
|
|
365
428
|
};
|
|
366
429
|
} catch {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
value: oid
|
|
376
|
-
};
|
|
377
|
-
} catch {
|
|
378
|
-
if (errors?.notFound) return {
|
|
379
|
-
success: false,
|
|
380
|
-
error: errors.notFound(input)
|
|
381
|
-
};
|
|
382
|
-
return {
|
|
383
|
-
success: false,
|
|
384
|
-
error: message`Reference ${value(input)} does not exist. Provide a valid branch, tag, or commit SHA.`
|
|
385
|
-
};
|
|
386
|
-
}
|
|
430
|
+
if (errors?.notFound) return {
|
|
431
|
+
success: false,
|
|
432
|
+
error: errors.notFound(input)
|
|
433
|
+
};
|
|
434
|
+
return {
|
|
435
|
+
success: false,
|
|
436
|
+
error: message`Reference ${value(input)} does not exist. Provide a valid branch, tag, or commit SHA.`
|
|
437
|
+
};
|
|
387
438
|
}
|
|
388
|
-
}, async function* suggestRef(dir, prefix) {
|
|
439
|
+
}, async function* suggestRef(dir, prefix, depth) {
|
|
389
440
|
try {
|
|
390
441
|
const [branches, tags, commits] = await Promise.all([
|
|
391
442
|
git.listBranches({
|
|
@@ -399,7 +450,7 @@ function gitRef(options) {
|
|
|
399
450
|
git.log({
|
|
400
451
|
fs: gitFs,
|
|
401
452
|
dir,
|
|
402
|
-
depth
|
|
453
|
+
depth
|
|
403
454
|
})
|
|
404
455
|
]);
|
|
405
456
|
for (const branch of branches) if (branch.startsWith(prefix)) yield {
|
|
@@ -419,7 +470,13 @@ function gitRef(options) {
|
|
|
419
470
|
description: message`${firstLine}`
|
|
420
471
|
};
|
|
421
472
|
}
|
|
422
|
-
} catch {
|
|
473
|
+
} catch (error) {
|
|
474
|
+
logger.debug("Failed to list refs for suggestions.", {
|
|
475
|
+
dir,
|
|
476
|
+
prefix,
|
|
477
|
+
error
|
|
478
|
+
});
|
|
479
|
+
}
|
|
423
480
|
});
|
|
424
481
|
}
|
|
425
482
|
/**
|
|
@@ -431,12 +488,30 @@ function gitRef(options) {
|
|
|
431
488
|
*/
|
|
432
489
|
function createGitParsers(options) {
|
|
433
490
|
return {
|
|
434
|
-
branch: (branchOptions) => gitBranch(
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
491
|
+
branch: (branchOptions) => gitBranch({
|
|
492
|
+
...options,
|
|
493
|
+
...branchOptions
|
|
494
|
+
}),
|
|
495
|
+
remoteBranch: (remote, branchOptions) => gitRemoteBranch(remote, {
|
|
496
|
+
...options,
|
|
497
|
+
...branchOptions
|
|
498
|
+
}),
|
|
499
|
+
tag: (tagOptions) => gitTag({
|
|
500
|
+
...options,
|
|
501
|
+
...tagOptions
|
|
502
|
+
}),
|
|
503
|
+
remote: (remoteOptions) => gitRemote({
|
|
504
|
+
...options,
|
|
505
|
+
...remoteOptions
|
|
506
|
+
}),
|
|
507
|
+
commit: (commitOptions) => gitCommit({
|
|
508
|
+
...options,
|
|
509
|
+
...commitOptions
|
|
510
|
+
}),
|
|
511
|
+
ref: (refOptions) => gitRef({
|
|
512
|
+
...options,
|
|
513
|
+
...refOptions
|
|
514
|
+
})
|
|
440
515
|
};
|
|
441
516
|
}
|
|
442
517
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@optique/git",
|
|
3
|
-
"version": "0.9.0-dev.
|
|
3
|
+
"version": "0.9.0-dev.268+5fccc2e2",
|
|
4
4
|
"description": "Git value parsers for Optique",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"CLI",
|
|
@@ -57,8 +57,9 @@
|
|
|
57
57
|
},
|
|
58
58
|
"sideEffects": false,
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"
|
|
61
|
-
"@optique/core": ""
|
|
60
|
+
"@logtape/logtape": "^1.2.2",
|
|
61
|
+
"@optique/core": "",
|
|
62
|
+
"isomorphic-git": "^1.36.1"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
65
|
"@types/node": "^20.19.9",
|