@effected/github 0.23.1 → 0.25.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.
package/README.md CHANGED
@@ -184,9 +184,13 @@ const program = Effect.gen(function* () {
184
184
  security_and_analysis: { secret_scanning: "enabled" },
185
185
  });
186
186
  });
187
- // AppliedSettings — { rest: [...], graphql: [...] }, in the caller's own key names.
188
- // The two lists agree with the input right up until preparation drops a field
187
+ // AppliedSettings — { rest: [...], graphql: [...], ignored: [...] }, in the caller's own key names.
188
+ // The lists agree with the input right up until preparation drops a field
189
189
  // GitHub would reject, which is what a person reading a dry run is checking for.
190
+ // `ignored` names keys neither API accepts (a typo, say): they are never sent,
191
+ // because the REST PATCH would ignore them and still answer 200. The REST key
192
+ // list is generated from GitHub's OpenAPI description; a key a later
193
+ // api-version removed is ignored only for a client pinned at or past it.
190
194
  ```
191
195
 
192
196
  `updateSettings` takes octokit's own generated params, and those spell an optional field as `has_issues?: boolean` rather than `has_issues?: boolean | undefined` — so under `exactOptionalPropertyTypes` a `Partial<T>` built from your own settings schema does not assign to it at all. `repositoryPatch` is the supported way out, and it exists so the answer is never a cast:
@@ -295,14 +299,14 @@ const TestClient = GitHubClient.layerFixture(fixtures);
295
299
 
296
300
  - `GitHubClient` — the typed transport: `request`, `requestDecoded` (a mandatory-schema escape hatch for routes outside the generated map), `paginate` / `paginateStream`, `graphql`, and `rateLimit` (observation only — nothing here throttles on your behalf).
297
301
  - `Repo` / `RepoRef` — the `{ owner, repo }` coordinate, resolved per call through `R`, with `Repo.provide` for multi-repository programs.
298
- - `GitHubRepository` — the repository's settings as GitHub's own generated type, plus `defaultBranch`, `nodeId`, `ownerType` for gating organization-only fields, and `applySettings` reporting the keys it actually sent.
302
+ - `GitHubRepository` — the repository's settings as GitHub's own generated type, plus `defaultBranch`, `nodeId`, `ownerType` for gating organization-only fields, `applySettings` reporting the keys it actually sent, and `topics` / `setTopics(names)`, where `setTopics` replaces the whole set and returns what GitHub stored (lowercased).
299
303
  - `repositoryPatch` / `RepositoryPatchDraft` — build an `updateSettings` patch from fields that may be `undefined`, under `exactOptionalPropertyTypes`, without a cast.
300
304
  - `GitHubError` / `GitHubGraphQLError` — one error per transport, `kind`-routed with `hasKind` for `Effect.catchIf`; a REST 422's validation entries ride on `validation`, matched with `hasValidationCode`.
301
305
  - `RetryPolicy` — the client's one retry policy: full-jitter backoff, server-advised delays honored up to a ceiling.
302
306
  - `GitHubApp` — App JWT signing, installation token minting/revocation, app and installation identity, and `clientLayer` for an App-authenticated `GitHubClient`.
303
307
  - `GitBranch` / `GitTag` — Git Database API refs, with `upsert` collapsing the create-or-reset dance to one call and `GitTag.latestSemver` picking the newest version-shaped tag in one pass.
304
308
  - `CheckRun` — `withCheckRun` concludes on every exit path; `CheckRunOutput.truncated()` cuts rendered output to GitHub's byte limits.
305
- - `PullRequest` / `PullRequestComment` — upserts for both, `listFiles` answering with the same full `CommitFile` records a commit read returns, `headSha`/`baseSha` on `PullRequestInfo`, plus `CommentMarker` for finding a sticky comment again.
309
+ - `PullRequest` / `PullRequestComment` — upserts for both, `listFiles` answering with the same full `CommitFile` records a commit read returns, `headSha`/`baseSha` on `PullRequestInfo`, plus `CommentMarker` for finding a sticky comment again. `PullRequestInfo.mergeCommitSha` is read from GraphQL `mergeCommit.oid` for merged pull requests only, because the REST object no longer carries it: `get`, `update` and `upsert` always fill it, while `list` and `listAssociatedWithCommit` fill it only with `mergeCommits: true` (one GraphQL call per 100 merged pull requests).
306
310
  - `GitHubIssue` — issues and their comments, including `commentOnce` for a marked comment that is posted exactly once and never edited, and `linkedIssues` for what a pull request closes.
307
311
  - `harvestIssueReferences` / `parseBareLineReference` — a compatibility re-export of the two closing-reference dialects that moved to `@effected/github-references`, with `CLOSING_KEYWORDS` and their result types.
308
312
  - `GitHubRelease` — releases and asset uploads, including the one route (`uploadAsset`, with the endpoint's optional display label) outside GitHub's generated endpoint map.
@@ -1,4 +1,5 @@
1
1
  import { GitHubError } from "./GitHubError.js";
2
+ import { unstubbed } from "./internal/unstubbed.js";
2
3
  import { GitHubClient } from "./GitHubClient.js";
3
4
  import { Repo } from "./Repo.js";
4
5
  import { encryptSecret } from "./internal/crypto.js";
@@ -70,21 +71,19 @@ var RepositorySecret = class RepositorySecret extends Context.Service()("@effect
70
71
  * further down throws at import time with a clean typecheck.
71
72
  */
72
73
  static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
73
- /** An in-memory double; unstubbed members die naming themselves. */
74
+ /** An in-memory double; an unstubbed member dies when run, naming itself. */
74
75
  static makeTest = (overrides = {}) => ({
75
- set: overrides.set ?? (() => unstubbed("set")),
76
- list: overrides.list ?? (() => unstubbed("list")),
77
- delete: overrides.delete ?? (() => unstubbed("delete")),
78
- setForEnvironment: overrides.setForEnvironment ?? (() => unstubbed("setForEnvironment")),
79
- listForEnvironment: overrides.listForEnvironment ?? (() => unstubbed("listForEnvironment")),
80
- deleteForEnvironment: overrides.deleteForEnvironment ?? (() => unstubbed("deleteForEnvironment"))
76
+ set: overrides.set ?? (() => dies("set")),
77
+ list: overrides.list ?? (() => dies("list")),
78
+ delete: overrides.delete ?? (() => dies("delete")),
79
+ setForEnvironment: overrides.setForEnvironment ?? (() => dies("setForEnvironment")),
80
+ listForEnvironment: overrides.listForEnvironment ?? (() => dies("listForEnvironment")),
81
+ deleteForEnvironment: overrides.deleteForEnvironment ?? (() => dies("deleteForEnvironment"))
81
82
  });
82
83
  /** {@link RepositorySecret.makeTest} behind a `Layer`. */
83
84
  static layerTest = (overrides = {}) => Layer.succeed(RepositorySecret, RepositorySecret.makeTest(overrides));
84
85
  };
85
- const unstubbed = (member) => {
86
- throw new Error(`RepositorySecret.makeTest: ${member}() was called but not stubbed — pass an override.`);
87
- };
86
+ const dies = unstubbed("RepositorySecret.makeTest");
88
87
  /**
89
88
  * Seal a value, turning a malformed public key into a typed failure.
90
89
  *
@@ -1,3 +1,4 @@
1
+ import { unstubbed } from "./internal/unstubbed.js";
1
2
  import { GitHubClient } from "./GitHubClient.js";
2
3
  import { Repo } from "./Repo.js";
3
4
  import { Context, Effect, Layer } from "effect";
@@ -56,21 +57,19 @@ var RepositorySecurity = class RepositorySecurity extends Context.Service()("@ef
56
57
  * further down throws at import time with a clean typecheck.
57
58
  */
58
59
  static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
59
- /** An in-memory double; unstubbed members die naming themselves. */
60
+ /** An in-memory double; an unstubbed member dies when run, naming itself. */
60
61
  static makeTest = (overrides = {}) => ({
61
- vulnerabilityAlerts: overrides.vulnerabilityAlerts ?? (() => unstubbed("vulnerabilityAlerts")),
62
- setVulnerabilityAlerts: overrides.setVulnerabilityAlerts ?? (() => unstubbed("setVulnerabilityAlerts")),
63
- automatedSecurityFixes: overrides.automatedSecurityFixes ?? (() => unstubbed("automatedSecurityFixes")),
64
- setAutomatedSecurityFixes: overrides.setAutomatedSecurityFixes ?? (() => unstubbed("setAutomatedSecurityFixes")),
65
- privateVulnerabilityReporting: overrides.privateVulnerabilityReporting ?? (() => unstubbed("privateVulnerabilityReporting")),
66
- setPrivateVulnerabilityReporting: overrides.setPrivateVulnerabilityReporting ?? (() => unstubbed("setPrivateVulnerabilityReporting"))
62
+ vulnerabilityAlerts: overrides.vulnerabilityAlerts ?? (() => dies("vulnerabilityAlerts")),
63
+ setVulnerabilityAlerts: overrides.setVulnerabilityAlerts ?? (() => dies("setVulnerabilityAlerts")),
64
+ automatedSecurityFixes: overrides.automatedSecurityFixes ?? (() => dies("automatedSecurityFixes")),
65
+ setAutomatedSecurityFixes: overrides.setAutomatedSecurityFixes ?? (() => dies("setAutomatedSecurityFixes")),
66
+ privateVulnerabilityReporting: overrides.privateVulnerabilityReporting ?? (() => dies("privateVulnerabilityReporting")),
67
+ setPrivateVulnerabilityReporting: overrides.setPrivateVulnerabilityReporting ?? (() => dies("setPrivateVulnerabilityReporting"))
67
68
  });
68
69
  /** {@link RepositorySecurity.makeTest} behind a `Layer`. */
69
70
  static layerTest = (overrides = {}) => Layer.succeed(RepositorySecurity, RepositorySecurity.makeTest(overrides));
70
71
  };
71
- const unstubbed = (member) => {
72
- throw new Error(`RepositorySecurity.makeTest: ${member}() was called but not stubbed — pass an override.`);
73
- };
72
+ const dies = unstubbed("RepositorySecurity.makeTest");
74
73
  const make = (client) => {
75
74
  return {
76
75
  vulnerabilityAlerts: Effect.fn("RepositorySecurity.vulnerabilityAlerts")(function* () {
@@ -1,3 +1,4 @@
1
+ import { unstubbed } from "./internal/unstubbed.js";
1
2
  import { GitHubClient } from "./GitHubClient.js";
2
3
  import { Repo } from "./Repo.js";
3
4
  import { Context, Effect, Layer } from "effect";
@@ -40,21 +41,19 @@ var RepositoryVariable = class RepositoryVariable extends Context.Service()("@ef
40
41
  * further down throws at import time with a clean typecheck.
41
42
  */
42
43
  static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
43
- /** An in-memory double; unstubbed members die naming themselves. */
44
+ /** An in-memory double; an unstubbed member dies when run, naming itself. */
44
45
  static makeTest = (overrides = {}) => ({
45
- set: overrides.set ?? (() => unstubbed("set")),
46
- list: overrides.list ?? (() => unstubbed("list")),
47
- delete: overrides.delete ?? (() => unstubbed("delete")),
48
- setForEnvironment: overrides.setForEnvironment ?? (() => unstubbed("setForEnvironment")),
49
- listForEnvironment: overrides.listForEnvironment ?? (() => unstubbed("listForEnvironment")),
50
- deleteForEnvironment: overrides.deleteForEnvironment ?? (() => unstubbed("deleteForEnvironment"))
46
+ set: overrides.set ?? (() => dies("set")),
47
+ list: overrides.list ?? (() => dies("list")),
48
+ delete: overrides.delete ?? (() => dies("delete")),
49
+ setForEnvironment: overrides.setForEnvironment ?? (() => dies("setForEnvironment")),
50
+ listForEnvironment: overrides.listForEnvironment ?? (() => dies("listForEnvironment")),
51
+ deleteForEnvironment: overrides.deleteForEnvironment ?? (() => dies("deleteForEnvironment"))
51
52
  });
52
53
  /** {@link RepositoryVariable.makeTest} behind a `Layer`. */
53
54
  static layerTest = (overrides = {}) => Layer.succeed(RepositoryVariable, RepositoryVariable.makeTest(overrides));
54
55
  };
55
- const unstubbed = (member) => {
56
- throw new Error(`RepositoryVariable.makeTest: ${member}() was called but not stubbed — pass an override.`);
57
- };
56
+ const dies = unstubbed("RepositoryVariable.makeTest");
58
57
  const make = (client) => {
59
58
  /**
60
59
  * Does this variable already exist? One by-name read, not a listing.
package/Ruleset.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { GitHubError } from "./GitHubError.js";
2
+ import { unstubbed } from "./internal/unstubbed.js";
2
3
  import { GitHubClient } from "./GitHubClient.js";
3
4
  import { Repo } from "./Repo.js";
4
5
  import { Context, Effect, Layer } from "effect";
@@ -52,20 +53,18 @@ var Ruleset = class Ruleset extends Context.Service()("@effected/github/Ruleset"
52
53
  * further down throws at import time with a clean typecheck.
53
54
  */
54
55
  static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
55
- /** An in-memory double; unstubbed members die naming themselves. */
56
+ /** An in-memory double; an unstubbed member dies when run, naming itself. */
56
57
  static makeTest = (overrides = {}) => ({
57
- upsert: overrides.upsert ?? (() => unstubbed("upsert")),
58
- list: overrides.list ?? (() => unstubbed("list")),
59
- delete: overrides.delete ?? (() => unstubbed("delete")),
60
- teamId: overrides.teamId ?? (() => unstubbed("teamId")),
61
- roleId: overrides.roleId ?? (() => unstubbed("roleId"))
58
+ upsert: overrides.upsert ?? (() => dies("upsert")),
59
+ list: overrides.list ?? (() => dies("list")),
60
+ delete: overrides.delete ?? (() => dies("delete")),
61
+ teamId: overrides.teamId ?? (() => dies("teamId")),
62
+ roleId: overrides.roleId ?? (() => dies("roleId"))
62
63
  });
63
64
  /** {@link Ruleset.makeTest} behind a `Layer`. */
64
65
  static layerTest = (overrides = {}) => Layer.succeed(Ruleset, Ruleset.makeTest(overrides));
65
66
  };
66
- const unstubbed = (member) => {
67
- throw new Error(`Ruleset.makeTest: ${member}() was called but not stubbed — pass an override.`);
68
- };
67
+ const dies = unstubbed("Ruleset.makeTest");
69
68
  /** An inherited ruleset belongs to the organization and is not this repository's to write. */
70
69
  const isOwnedByRepository = (ruleset) => ruleset.source_type !== "Organization";
71
70
  const make = (client) => {
@@ -1,4 +1,5 @@
1
1
  import { GitHubError } from "./GitHubError.js";
2
+ import { unstubbed, unstubbedRead } from "./internal/unstubbed.js";
2
3
  import { GitHubClient } from "./GitHubClient.js";
3
4
  import { Repo } from "./Repo.js";
4
5
  import { numericId } from "./internal/ids.js";
@@ -75,21 +76,20 @@ const DEFAULT_TIMEOUT = Duration.minutes(5);
75
76
  var WorkflowDispatch = class WorkflowDispatch extends Context.Service()("@effected/github/WorkflowDispatch") {
76
77
  /** The live service, built over a `GitHubClient`. */
77
78
  static layer = Layer.effect(this, Effect.map(GitHubClient, (client) => make(client)));
78
- /** An in-memory double; unstubbed members die naming themselves. */
79
+ /** An in-memory double; an unstubbed member dies when run, naming itself. */
79
80
  static makeTest = (overrides = {}) => ({
80
- dispatch: overrides.dispatch ?? (() => unstubbed("dispatch")),
81
- dispatchWithRun: overrides.dispatchWithRun ?? (() => unstubbed("dispatchWithRun")),
82
- runStatus: overrides.runStatus ?? (() => unstubbed("runStatus")),
83
- cancelRun: overrides.cancelRun ?? (() => unstubbed("cancelRun")),
84
- list: overrides.list ?? Effect.sync(() => unstubbed("list")),
85
- dispatchAndWait: overrides.dispatchAndWait ?? (() => unstubbed("dispatchAndWait"))
81
+ dispatch: overrides.dispatch ?? (() => dies("dispatch")),
82
+ dispatchWithRun: overrides.dispatchWithRun ?? (() => dies("dispatchWithRun")),
83
+ runStatus: overrides.runStatus ?? (() => dies("runStatus")),
84
+ cancelRun: overrides.cancelRun ?? (() => dies("cancelRun")),
85
+ list: overrides.list ?? unread("list"),
86
+ dispatchAndWait: overrides.dispatchAndWait ?? (() => dies("dispatchAndWait"))
86
87
  });
87
88
  /** {@link WorkflowDispatch.makeTest} behind a `Layer`. */
88
89
  static layerTest = (overrides = {}) => Layer.succeed(WorkflowDispatch, WorkflowDispatch.makeTest(overrides));
89
90
  };
90
- const unstubbed = (member) => {
91
- throw new Error(`WorkflowDispatch.makeTest: ${member}() was called but not stubbed — pass an override.`);
92
- };
91
+ const dies = unstubbed("WorkflowDispatch.makeTest");
92
+ const unread = unstubbedRead("WorkflowDispatch.makeTest");
93
93
  const statusOf = (raw) => WorkflowRunStatus.make({
94
94
  id: numericId(raw.id),
95
95
  status: raw.status ?? "unknown",
@@ -104,6 +104,8 @@ const DispatchResponse = Schema.Struct({
104
104
  });
105
105
  const decodeDispatchResponse = Schema.decodeUnknownEffect(DispatchResponse);
106
106
  const decodeDispatchedRun = Schema.decodeUnknownEffect(DispatchedRun);
107
+ /** The api-version that reports the run unasked and removed `return_run_details`. */
108
+ const RUN_DETAILS_BY_DEFAULT = "2026-03-10";
107
109
  const make = (client) => {
108
110
  const dispatch = Effect.fn("WorkflowDispatch.dispatch")(function* (workflow, ref, inputs) {
109
111
  const { owner, repo } = yield* Repo;
@@ -135,7 +137,7 @@ const make = (client) => {
135
137
  workflow_id: workflow,
136
138
  ref,
137
139
  ...inputs !== void 0 ? { inputs } : {},
138
- return_run_details: true
140
+ ...client.apiVersion < RUN_DETAILS_BY_DEFAULT ? { return_run_details: true } : {}
139
141
  });
140
142
  if (data === "" || data === null) return Option.none();
141
143
  const run = yield* decodeDispatchResponse(data).pipe(Effect.flatMap((raw) => decodeDispatchedRun({