@osdlc/typed-gh 0.9.4 → 0.9.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @osdlc/typed-gh
2
2
 
3
+ ## 0.9.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 3d6d087: Document that a `Repository`'s `owner` and `name`, and a `PrSearchHit`'s `repository`, come back in GitHub's casing, not as the caller wrote them. The `Repository` and `PrSearchHit` type docs, the `repoList` and `repoView` doc comments and the README say so.
8
+ - f013f38: The default runners now find `git` and `gh` on absolute `PATH` entries only, and spawn the absolute path found. Before, a bare `git` or `gh` was spawned by name with the child's `cwd` set to the checkout, so a file of that name in the checkout could run in place of the real binary: on Windows, where a bare name is searched in `cwd` first, and on every platform through a relative or empty `PATH` entry. A binary that is not found on an absolute entry fails as a missing binary (`not-installed`). typed-gh's `defaultRunnerFactory` does the same lookup. An injected runner is unchanged.
9
+ - Updated dependencies [f013f38]
10
+ - Updated dependencies [f013f38]
11
+ - @osdlc/command-seam@0.12.1
12
+ - @osdlc/typed-git@0.8.2
13
+
14
+ ## 0.9.5
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies [9616994]
19
+ - Updated dependencies [d243a32]
20
+ - Updated dependencies [d243a32]
21
+ - Updated dependencies [9616994]
22
+ - @osdlc/command-seam@0.12.0
23
+ - @osdlc/typed-git@0.8.1
24
+
3
25
  ## 0.9.4
4
26
 
5
27
  ### Patch Changes
package/README.md CHANGED
@@ -384,7 +384,8 @@ The awaitable form of `Gh`, with the same methods, for code that runs many calls
384
384
  not block the event loop. It has the same three call styles, as Promises. The plain method rejects
385
385
  with the classified failure. `try` resolves to a `Result<T, GhFailure>`. `soft` resolves `null` for
386
386
  a failed query. `raw` and `rawChecked` run any `gh` call. Both clients use the same verb table, so
387
- they cannot drift apart. It takes an `AsyncCommandRunner` and defaults to `spawnRunnerAsync("gh")`.
387
+ they cannot drift apart. It takes an `AsyncCommandRunner` and defaults to
388
+ `spawnRunnerAsync("gh", { absolutePathOnly: true })`.
388
389
 
389
390
  ```ts
390
391
  import { AsyncGh } from '@osdlc/typed-gh'
@@ -450,9 +451,14 @@ const hits: ListResult<PrSearchHit> = gh.searchPrs('TICKET-123', { owner: 'acme'
450
451
  ```
451
452
 
452
453
  A `Repository` carries `nameWithOwner`, `owner`, `name`, `description`, `defaultBranch`,
453
- `visibility`, `isArchived` and `url`. `gh search prs` has its own field set. A `PrSearchHit` names
454
- its `repository` but has no head or base branch, so it is not a `PullRequest`. Its `state` is
455
- upper-cased to match. The query takes GitHub's search syntax and may start with `-`.
454
+ `visibility`, `isArchived` and `url`. `owner` and `name` are cut from `nameWithOwner`, so they
455
+ come back in GitHub's casing, not as you wrote them: `repoList('ACME')` gives `owner: 'acme'` when
456
+ the account is `acme`, and `repoView` works the same way. To build paths that match your own
457
+ config, use your own value.
458
+
459
+ `gh search prs` has its own field set. A `PrSearchHit` names its `repository`, also in GitHub's
460
+ casing, but has no head or base branch, so it is not a `PullRequest`. Its `state` is upper-cased to
461
+ match. The query takes GitHub's search syntax and may start with `-`.
456
462
 
457
463
  ## Passing your own runner
458
464
 
@@ -469,7 +475,9 @@ new Gh('/repo', { runner: empty }).prList() // { rows: [], truncated: false }
469
475
  This package re-exports `CommandFailed` and the runner and result types (`CommandRunner`,
470
476
  `AsyncCommandRunner`, `CommandResult`, `CommandRunOptions`), since they appear in its signatures.
471
477
  Import the runners themselves, such as `spawnRunner` and `spawnRunnerAsync`, from
472
- `@osdlc/command-seam`.
478
+ `@osdlc/command-seam`. The default runners find `gh` on absolute `PATH` entries only, so a file
479
+ named `gh` in the working directory cannot run in its place. Pass `{ absolutePathOnly: true }` when
480
+ you build a real runner yourself.
473
481
 
474
482
  ## API
475
483
 
package/dist/async.d.ts CHANGED
@@ -106,9 +106,15 @@ export declare class AsyncGh {
106
106
  issueList(opts?: IssueListOptions & AsyncCallOptions): Promise<ListResult<IssueRow>>;
107
107
  /** One issue with its labels, or null when there is no such issue. */
108
108
  issueView(number: number, opts?: AsyncCallOptions): Promise<IssueRow | null>;
109
- /** An owner's repositories. Rejects when gh fails. See `Gh.repoList`. */
109
+ /**
110
+ * An owner's repositories. Rejects when gh fails. Each row's `owner` is in
111
+ * GitHub's casing, not as passed. See `Gh.repoList`.
112
+ */
110
113
  repoList(owner: string, opts?: v.RepoListOptions & AsyncCallOptions): Promise<ListResult<Repository>>;
111
- /** One repository, or null when there is no such repository. See `Gh.repoView`. */
114
+ /**
115
+ * One repository, or null when there is no such repository. `owner` and
116
+ * `name` are in GitHub's casing, not as passed. See `Gh.repoView`.
117
+ */
112
118
  repoView(repo?: string | undefined, opts?: {
113
119
  fields?: readonly string[] | undefined;
114
120
  } & AsyncCallOptions): Promise<Repository | null>;
package/dist/async.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * two cannot differ on argv or parsing. Use it where the sync client would
11
11
  * block the event loop, such as many per-PR queries at once.
12
12
  */
13
- import { resolveRunnerAsync, } from '@osdlc/command-seam';
13
+ import { spawnRunnerAsync } from '@osdlc/command-seam';
14
14
  import { runCheckedGhAsync, softAsync, triedAsync } from "./doors.js";
15
15
  import { runVerbAsync } from "./run.js";
16
16
  import { ownerRepoSlug, parseOwnerRepo, parsePrRef } from "./repo.js";
@@ -35,7 +35,7 @@ export class AsyncGh {
35
35
  this.repo = opts.repo;
36
36
  this.hostname = opts.hostname;
37
37
  this.target = { repo: opts.repo, hostname: opts.hostname };
38
- this.runner = resolveRunnerAsync('gh', opts.runner);
38
+ this.runner = opts.runner ?? spawnRunnerAsync('gh', { absolutePathOnly: true });
39
39
  }
40
40
  /** The same client rooted at a different directory (same runner, repo and host). */
41
41
  at(cwd) {
@@ -154,11 +154,17 @@ export class AsyncGh {
154
154
  issueView(number, opts = {}) {
155
155
  return this.exec(v.issueView(number, this.target), opts);
156
156
  }
157
- /** An owner's repositories. Rejects when gh fails. See `Gh.repoList`. */
157
+ /**
158
+ * An owner's repositories. Rejects when gh fails. Each row's `owner` is in
159
+ * GitHub's casing, not as passed. See `Gh.repoList`.
160
+ */
158
161
  repoList(owner, opts = {}) {
159
162
  return this.read(v.repoList(owner, opts, this.target), opts);
160
163
  }
161
- /** One repository, or null when there is no such repository. See `Gh.repoView`. */
164
+ /**
165
+ * One repository, or null when there is no such repository. `owner` and
166
+ * `name` are in GitHub's casing, not as passed. See `Gh.repoView`.
167
+ */
162
168
  repoView(repo, opts = {}) {
163
169
  return this.exec(v.repoView(repo, opts.fields ?? v.REPO_FIELDS, this.target), opts);
164
170
  }
package/dist/sync.d.ts CHANGED
@@ -22,11 +22,15 @@ import type { ApiOptions, ApiResponse, AuthStatus, GhTarget, IssueListOptions, L
22
22
  import type { IssueRow, Label, OwnerRepo, PrSearchHit, PullRequest, Repository } from './types.ts';
23
23
  /**
24
24
  * Makes a runner for a binary. A CLI passes one factory to every place that
25
- * runs a command. Real code uses `spawnRunner`. Tests pass a factory that
26
- * records calls, so the CLI runs offline.
25
+ * runs a command. Real code uses {@link defaultRunnerFactory}. Tests pass a
26
+ * factory that records calls, so the CLI runs offline.
27
27
  */
28
28
  export type RunnerFactory = (binary: string) => CommandRunner;
29
- /** The default factory: real runners that use `spawnSync`. */
29
+ /**
30
+ * The default factory: real runners that use `spawnSync`. A bare name is
31
+ * found on absolute `PATH` entries only, so a file of that name in the
32
+ * working directory cannot run in its place.
33
+ */
30
34
  export declare const defaultRunnerFactory: RunnerFactory;
31
35
  /** What a client is built with: the runner, and the repository and host it talks to. */
32
36
  export interface GhOptions extends GhTarget {
@@ -178,11 +182,18 @@ export declare class Gh {
178
182
  issueView(number: number, opts?: {
179
183
  timeoutMs?: number | undefined;
180
184
  }): IssueRow | null;
181
- /** An owner's repositories (up to `opts.limit`, default 100). Throws when gh fails. */
185
+ /**
186
+ * An owner's repositories (up to `opts.limit`, default 100). Throws when gh
187
+ * fails. Each row's `owner` is in GitHub's casing, not as passed.
188
+ */
182
189
  repoList(owner: string, opts?: v.RepoListOptions & {
183
190
  timeoutMs?: number | undefined;
184
191
  }): ListResult<Repository>;
185
- /** One repository: `repo`, else the client's repo, else the clone at `cwd`. Null when there is no such repository. */
192
+ /**
193
+ * One repository: `repo`, else the client's repo, else the clone at `cwd`.
194
+ * Null when there is no such repository. `owner` and `name` are in
195
+ * GitHub's casing, not as passed.
196
+ */
186
197
  repoView(repo?: string | undefined, opts?: {
187
198
  fields?: readonly string[] | undefined;
188
199
  timeoutMs?: number | undefined;
package/dist/sync.js CHANGED
@@ -15,13 +15,17 @@
15
15
  * the failure as a `Result<T, GhFailure>`. `gh.soft.*` returns `null` for it.
16
16
  * `raw` and `rawChecked` run any `gh` call.
17
17
  */
18
- import { resolveRunner, spawnRunner, } from '@osdlc/command-seam';
18
+ import { spawnRunner } from '@osdlc/command-seam';
19
19
  import { runCheckedGh, softSync, triedSync } from "./doors.js";
20
20
  import { runVerb } from "./run.js";
21
21
  import * as v from "./verbs.js";
22
22
  import { ownerRepoSlug, parseOwnerRepo, parsePrRef } from "./repo.js";
23
- /** The default factory: real runners that use `spawnSync`. */
24
- export const defaultRunnerFactory = spawnRunner;
23
+ /**
24
+ * The default factory: real runners that use `spawnSync`. A bare name is
25
+ * found on absolute `PATH` entries only, so a file of that name in the
26
+ * working directory cannot run in its place.
27
+ */
28
+ export const defaultRunnerFactory = (binary) => spawnRunner(binary, { absolutePathOnly: true });
25
29
  /** How long to wait between merge-state retries, in milliseconds. */
26
30
  const MERGE_STATE_RETRY_DELAY_MS = 1_500;
27
31
  /**
@@ -52,7 +56,7 @@ export class Gh {
52
56
  this.repo = opts.repo;
53
57
  this.hostname = opts.hostname;
54
58
  this.target = { repo: opts.repo, hostname: opts.hostname };
55
- this.runner = resolveRunner('gh', opts.runner);
59
+ this.runner = opts.runner ?? defaultRunnerFactory('gh');
56
60
  }
57
61
  /** The same client rooted at a different directory (same runner, repo and host). */
58
62
  at(cwd) {
@@ -210,11 +214,18 @@ export class Gh {
210
214
  issueView(number, opts = {}) {
211
215
  return this.exec(v.issueView(number, this.target), opts.timeoutMs);
212
216
  }
213
- /** An owner's repositories (up to `opts.limit`, default 100). Throws when gh fails. */
217
+ /**
218
+ * An owner's repositories (up to `opts.limit`, default 100). Throws when gh
219
+ * fails. Each row's `owner` is in GitHub's casing, not as passed.
220
+ */
214
221
  repoList(owner, opts = {}) {
215
222
  return this.read(v.repoList(owner, opts, this.target), opts.timeoutMs);
216
223
  }
217
- /** One repository: `repo`, else the client's repo, else the clone at `cwd`. Null when there is no such repository. */
224
+ /**
225
+ * One repository: `repo`, else the client's repo, else the clone at `cwd`.
226
+ * Null when there is no such repository. `owner` and `name` are in
227
+ * GitHub's casing, not as passed.
228
+ */
218
229
  repoView(repo, opts = {}) {
219
230
  return this.exec(v.repoView(repo, opts.fields ?? v.REPO_FIELDS, this.target), opts.timeoutMs);
220
231
  }
package/dist/types.d.ts CHANGED
@@ -156,9 +156,14 @@ export type RepositoryVisibility = 'PUBLIC' | 'PRIVATE' | 'INTERNAL' | (string &
156
156
  * without one: a row without it is dropped.
157
157
  */
158
158
  export interface Repository {
159
- /** `owner/name`. */
159
+ /** `owner/name`, in GitHub's casing. */
160
160
  nameWithOwner: string;
161
+ /**
162
+ * Cut from `nameWithOwner`, so in GitHub's casing, not as the caller wrote
163
+ * it: `repoList('ACME')` gives `owner: 'acme'` when the account is `acme`.
164
+ */
161
165
  owner: string;
166
+ /** Cut from `nameWithOwner`, so in GitHub's casing. */
162
167
  name: string;
163
168
  description: string;
164
169
  /** The default branch. Null when not asked for, or when the repository is empty. */
@@ -178,6 +183,7 @@ export interface PrSearchHit {
178
183
  state: PullRequestState;
179
184
  url: string;
180
185
  isDraft: boolean;
186
+ /** Cut from the hit's `nameWithOwner`, so in GitHub's casing. */
181
187
  repository: OwnerRepo | null;
182
188
  author: Actor | null;
183
189
  labels: Label[];
package/dist/verbs.d.ts CHANGED
@@ -284,12 +284,15 @@ export interface RepoListOptions {
284
284
  }
285
285
  /**
286
286
  * `gh repo list <owner> --json …`: an owner's repositories. The host comes
287
- * from the target (`GH_HOST`). The target's repo is not used.
287
+ * from the target (`GH_HOST`). The target's repo is not used. Each row's
288
+ * `owner` is in GitHub's casing, not as passed. To build paths that match
289
+ * your config, use your own value.
288
290
  */
289
291
  export declare function repoList(owner: string, opts?: RepoListOptions, target?: GhTarget): GhVerb<ListResult<Repository> | null>;
290
292
  /**
291
293
  * `gh repo view [<[host/]owner/name>] --json …`: one repository. That is `repo`,
292
- * else the target's repo, else the clone at the client's cwd.
294
+ * else the target's repo, else the clone at the client's cwd. The result's
295
+ * `owner` and `name` are in GitHub's casing, not as passed.
293
296
  */
294
297
  export declare function repoView(repo?: string | undefined, fields?: readonly string[], target?: GhTarget): GhVerb<Repository | null>;
295
298
  /** What to narrow a pull request search to. Every filter is optional. */
package/dist/verbs.js CHANGED
@@ -657,7 +657,9 @@ function rowsOrNull(r, parse) {
657
657
  }
658
658
  /**
659
659
  * `gh repo list <owner> --json …`: an owner's repositories. The host comes
660
- * from the target (`GH_HOST`). The target's repo is not used.
660
+ * from the target (`GH_HOST`). The target's repo is not used. Each row's
661
+ * `owner` is in GitHub's casing, not as passed. To build paths that match
662
+ * your config, use your own value.
661
663
  */
662
664
  export function repoList(owner, opts = {}, target) {
663
665
  notAFlag('repoList owner', owner);
@@ -682,7 +684,8 @@ export function repoList(owner, opts = {}, target) {
682
684
  }
683
685
  /**
684
686
  * `gh repo view [<[host/]owner/name>] --json …`: one repository. That is `repo`,
685
- * else the target's repo, else the clone at the client's cwd.
687
+ * else the target's repo, else the clone at the client's cwd. The result's
688
+ * `owner` and `name` are in GitHub's casing, not as passed.
686
689
  */
687
690
  export function repoView(repo, fields = REPO_FIELDS, target) {
688
691
  checkTarget(target);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osdlc/typed-gh",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "A typed GitHub client for TypeScript over the gh CLI. A total PullRequest/Review/CheckRun model, pure argv builders and parsers, and a tri-modal client — queries return parsed values, mutations throw, raw() is the escape hatch. Uses gh's own credentials; reads no token.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "author": "sksizer",
@@ -22,9 +22,9 @@
22
22
  "LICENSE-APACHE"
23
23
  ],
24
24
  "dependencies": {
25
- "@osdlc/command-seam": "^0.11.0",
26
- "@osdlc/typed-git": "^0.8.0",
27
- "@osdlc/result": "^0.1.0"
25
+ "@osdlc/command-seam": "^0.12.1",
26
+ "@osdlc/result": "^0.1.0",
27
+ "@osdlc/typed-git": "^0.8.2"
28
28
  },
29
29
  "engines": {
30
30
  "node": "^22.22.2 || ^24.15.0 || >=26.0.0"