@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 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: node_fs_promises.writeFile,
34
- mkdir: node_fs_promises.mkdir,
35
- rmdir: node_fs_promises.rmdir,
36
- unlink: node_fs_promises.unlink,
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: node_fs_promises.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) yield* suggestFn(dir, prefix);
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
- try {
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`Commit ${(0, __optique_core_message.value)(input)} must be between 4 and 40 characters.`
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: 15
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
- const resolved = await isomorphic_git.resolveRef({
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: resolved
449
+ value: oid
387
450
  };
388
451
  } catch {
389
- try {
390
- const oid = await isomorphic_git.expandOid({
391
- fs: gitFs,
392
- dir,
393
- oid: input
394
- });
395
- return {
396
- success: true,
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: 10
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(branchOptions ?? options),
457
- remoteBranch: (remote, branchOptions) => gitRemoteBranch(remote, branchOptions ?? options),
458
- tag: (tagOptions) => gitTag(tagOptions ?? options),
459
- remote: (remoteOptions) => gitRemote(remoteOptions ?? options),
460
- commit: (commitOptions) => gitCommit(commitOptions ?? options),
461
- ref: (refOptions) => gitRef(refOptions ?? options)
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: fs.writeFile,
12
- mkdir: fs.mkdir,
13
- rmdir: fs.rmdir,
14
- unlink: fs.unlink,
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: fs.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) yield* suggestFn(dir, prefix);
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
- try {
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`Commit ${value(input)} must be between 4 and 40 characters.`
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: 15
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
- const resolved = await git.resolveRef({
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: resolved
427
+ value: oid
365
428
  };
366
429
  } catch {
367
- try {
368
- const oid = await git.expandOid({
369
- fs: gitFs,
370
- dir,
371
- oid: input
372
- });
373
- return {
374
- success: true,
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: 10
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(branchOptions ?? options),
435
- remoteBranch: (remote, branchOptions) => gitRemoteBranch(remote, branchOptions ?? options),
436
- tag: (tagOptions) => gitTag(tagOptions ?? options),
437
- remote: (remoteOptions) => gitRemote(remoteOptions ?? options),
438
- commit: (commitOptions) => gitCommit(commitOptions ?? options),
439
- ref: (refOptions) => gitRef(refOptions ?? options)
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.264+07364667",
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
- "isomorphic-git": "^1.36.1",
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",