@shivaedev/pr-watch 0.0.0 → 0.0.1

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +21 -0
  3. package/README.md +72 -1
  4. package/dist/GitHub.d.ts +42 -0
  5. package/dist/GitHub.d.ts.map +1 -0
  6. package/dist/GitHub.js +97 -0
  7. package/dist/GitHub.js.map +1 -0
  8. package/dist/GitHubHttp.d.ts +12 -0
  9. package/dist/GitHubHttp.d.ts.map +1 -0
  10. package/dist/GitHubHttp.js +51 -0
  11. package/dist/GitHubHttp.js.map +1 -0
  12. package/dist/StateFile.d.ts +43 -0
  13. package/dist/StateFile.d.ts.map +1 -0
  14. package/dist/StateFile.js +33 -0
  15. package/dist/StateFile.js.map +1 -0
  16. package/dist/Watcher.d.ts +61 -0
  17. package/dist/Watcher.d.ts.map +1 -0
  18. package/dist/Watcher.js +82 -0
  19. package/dist/Watcher.js.map +1 -0
  20. package/dist/arguments.d.ts +21 -0
  21. package/dist/arguments.d.ts.map +1 -0
  22. package/dist/arguments.js +84 -0
  23. package/dist/arguments.js.map +1 -0
  24. package/dist/cli.d.ts +3 -0
  25. package/dist/cli.d.ts.map +1 -0
  26. package/dist/cli.js +34 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/confirmQueue.d.ts +28 -0
  29. package/dist/confirmQueue.d.ts.map +1 -0
  30. package/dist/confirmQueue.js +39 -0
  31. package/dist/confirmQueue.js.map +1 -0
  32. package/dist/events.d.ts +3 -0
  33. package/dist/events.d.ts.map +1 -0
  34. package/dist/events.js +32 -0
  35. package/dist/events.js.map +1 -0
  36. package/dist/httpResponse.d.ts +15 -0
  37. package/dist/httpResponse.d.ts.map +1 -0
  38. package/dist/httpResponse.js +31 -0
  39. package/dist/httpResponse.js.map +1 -0
  40. package/dist/model.d.ts +46 -0
  41. package/dist/model.d.ts.map +1 -0
  42. package/dist/model.js +23 -0
  43. package/dist/model.js.map +1 -0
  44. package/dist/program.d.ts +4 -0
  45. package/dist/program.d.ts.map +1 -0
  46. package/dist/program.js +30 -0
  47. package/dist/program.js.map +1 -0
  48. package/dist/quota.d.ts +10 -0
  49. package/dist/quota.d.ts.map +1 -0
  50. package/dist/quota.js +45 -0
  51. package/dist/quota.js.map +1 -0
  52. package/docs/north-star.md +7 -0
  53. package/docs/roadmap.md +18 -0
  54. package/package.json +70 -4
  55. package/src/GitHub.ts +102 -0
  56. package/src/GitHubHttp.ts +55 -0
  57. package/src/StateFile.ts +38 -0
  58. package/src/Watcher.ts +92 -0
  59. package/src/arguments.ts +83 -0
  60. package/src/cli.ts +48 -0
  61. package/src/confirmQueue.ts +52 -0
  62. package/src/events.ts +33 -0
  63. package/src/httpResponse.ts +37 -0
  64. package/src/model.ts +32 -0
  65. package/src/program.ts +34 -0
  66. package/src/quota.ts +52 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1 - 2026-10-12
4
+
5
+ ### Added
6
+
7
+ - Emit `queued` when a PR enters the merge queue; report `auto-merge-off` only outside the queue, including a confirmed drop with auto-merge still off.
8
+ - Report already-stuck auto-merges on the first observation and stop polling merged or closed PRs while retaining their saved snapshots.
9
+ - Confirm missing queue refs against the PR’s GraphQL queue entry and auto-merge state before reporting a drop.
10
+ - Use Effect `FileSystem` and `HttpClient` for watcher IO; service layers require these native services, and the CLI supplies them with exact `@effect/platform-node` and `@effect/platform-node-shared` peers.
11
+ - Add `pr-watch` for many PRs or every open PR in a repository, with ETag REST polling, quota pacing and merge queue ref observations.
12
+ - Emit JSON lines for merged, closed, conflict, dequeued, auto-merge-off, checks-failed and stuck transitions.
13
+ - Persist snapshots for clean restart comparison; transient reversed transitions are not recovered and stdout is at-least-once across a crash.
14
+
15
+ ### Fixed
16
+
17
+ - Fleet discovery keeps open PRs tracked across failed initial observations and clean restarts, so their later merge is reported once even if the first complete snapshot arrives after merging.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ShivaeDev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,74 @@
1
1
  # @shivaedev/pr-watch
2
2
 
3
- This version only reserves the name. Releases are published from https://github.com/ShivaeDev/platform.
3
+ One process watches pull request transitions across a repository. It gives an orchestrator event lines for merges, conflicts, queue drops and stuck auto-merges, with a saved snapshot that survives a restart.
4
+
5
+ ## Keep the whole repository in view
6
+
7
+ Polling each PR with a separate process repeats setup and burns quota. `pr-watch` shares queue discovery, uses REST ETags and discovers new PRs in fleet mode.
8
+
9
+ ```sh
10
+ pr-watch --repo acme/project --fleet
11
+ ```
12
+
13
+ ```json
14
+ {"branch":"feature","event":"merged","number":42,"title":"Ship the feature"}
15
+ ```
16
+
17
+ Every event occupies one JSON line with its PR number, head branch and title, suitable for the Monitor tool.
18
+
19
+ ## Run the watcher
20
+
21
+ Each successful observation compares a PR with its saved snapshot. The first observation establishes a baseline. If the PR already has auto-merge enabled with conflicts or failing checks, it immediately emits `stuck` and the corresponding `conflict` or `checks-failed` events. Failed requests, invalid responses and pending mergeability retain the previous snapshot. Checks and queue refs are polled even when the PR detail response is unchanged.
22
+
23
+ Install with Node 24 or later and the repository's Effect peer version:
24
+
25
+ ```sh
26
+ npm install @shivaedev/pr-watch effect@4.0.0-rc.112 @effect/platform-node@4.0.0-rc.112 @effect/platform-node-shared@4.0.0-rc.112
27
+ export GH_TOKEN=YOUR_READ_ONLY_TOKEN
28
+ pr-watch --repo acme/project 42 57 --state ./watch-state.json
29
+ pr-watch --repo acme/project --fleet --interval 60 --state ./fleet-state.json
30
+ pr-watch --repo acme/project 42 --once
31
+ ```
32
+
33
+ `GH_TOKEN` takes precedence over `GITHUB_TOKEN`. The token needs read access to pull requests, repository refs, check runs and commit statuses. Queue-drop confirmation also uses authenticated GraphQL access to the PR’s merge queue entry and auto-merge request. Without a token, only public repositories are accessible and GitHub's authenticated conditional-request quota exemption does not apply.
34
+
35
+ Choose explicit PR numbers or `--fleet`. Fleet mode polls all open PRs and discovers PRs opened after startup. Discovery saves open PR numbers before requesting queue refs and full snapshots, so a failed initial observation does not lose the PR. PRs already recorded in the state file stay tracked when they disappear from the open list, so their merge or closure can be observed. After that terminal observation, they stay in the state file but leave the polled set, including after a restart. Keep one watcher per state file. The default state path is `.pr-watch/OWNER-REPO.json`; a file from another repository or an invalid file causes startup to fail instead of resetting history.
36
+
37
+ `--interval SECONDS` sets the minimum interval (default 60, minimum 1). `--once` performs one observation round and exits; a failed round exits unsuccessfully. `--api-url URL` sets the REST base for GitHub Enterprise or a synthetic server, allowing HTTPS or loopback HTTP. The GraphQL endpoint is the same base with `/graphql` appended, or `/api/graphql` for an Enterprise `/api/v3` REST base. `--help` lists the commands. Diagnostics go to stderr. SIGINT and SIGTERM interrupt the watcher.
38
+
39
+ ### Events
40
+
41
+ The watcher emits these transitions, once per successful comparison:
42
+
43
+ | Event | Transition |
44
+ | --- | --- |
45
+ | `merged` | The PR becomes merged. |
46
+ | `closed` | An open PR closes without merging. |
47
+ | `conflict` | An open PR's mergeable state becomes `dirty`. |
48
+ | `queued` | An open PR enters the merge queue. |
49
+ | `dequeued` | A previously queued open PR has no queue ref, and its own queue entry and auto-merge request are both absent. |
50
+ | `auto-merge-off` | An open PR's auto-merge is disabled outside the queue after being enabled or queued. |
51
+ | `checks-failed` | An open PR changes from no observed failing checks to failing checks. |
52
+ | `stuck` | An open PR enters auto-merge enabled with conflicts or failing checks. |
53
+
54
+ Failure evidence includes completed check runs with `failure`, `timed_out`, `cancelled`, `action_required`, `startup_failure` or `stale`, and combined commit statuses with `failure` or `error`. Pending, successful, neutral and skipped check runs do not count as failures. Checks use the current head SHA and include all response pages. Queue discovery uses the REST matching refs endpoint, recognizing `gh-readonly-queue/.../pr-N-...` names; it does not discover queue membership from auto-merge settings. Entering the queue emits `queued` even when GitHub clears the auto-merge request. A queued PR never emits `auto-merge-off`. When a previously queued open PR loses its refs, a GraphQL request confirms the PR’s own queue entry and auto-merge request. Either one still present retains its previous queue membership during a rebuild. A confirmed drop with auto-merge still off emits both `dequeued` and `auto-merge-off`. A failed, incomplete or errored confirmation retains the last successful snapshot.
55
+
56
+ ### Quota cost
57
+
58
+ At a 60-second interval, for N open PRs, one page of checks per PR and one page of queue refs, the watcher sends up to `60 × (3N + 1)` REST requests per hour. Fleet mode adds up to `60 × max(1, ceil(open PRs / 100))` discovery requests. Each additional check or queue page adds 60 requests per hour. Each suspected queue drop adds one GraphQL request for that PR in that round. Merged and closed PRs make no further requests after their terminal observation. With no active PRs, queue discovery also stops; fleet discovery continues.
59
+
60
+ These are request counts, not necessarily charged quota. With authentication, a successful ETag conditional request returning 304 costs zero primary rate-limit quota. Every REST endpoint sends `If-None-Match` after receiving an ETag and reuses its successful cached body on 304. Initial requests, changed responses and endpoints without ETags consume quota normally. Caches are in memory, so a restart makes fresh requests. Secondary limits still apply.
61
+
62
+ The minimum interval doubles below 50% remaining quota, increases fivefold below 20% and tenfold below 10%. At low quota, the delay also spreads the observed round's request count over the remaining reset window. HTTP 403 and 429 trigger a cooldown of at least the supplied `Retry-After` (60 seconds when absent); exhausted primary quota waits until reset. No further request starts during a known cooldown.
63
+
64
+ ### Restart and delivery limits
65
+
66
+ The state file records discovered open PR numbers and the last successfully compared snapshot per PR. Snapshot replacement is atomic and follows output. A clean restart compares each persisted open snapshot with the next successful observation, emits the defined events for those differences and does not repeat previously saved events. A discovered PR whose first full snapshot is merged or closed emits that terminal event, including after a failed initial observation or a restart. Differences that do not match an event, such as a title edit, still update the saved snapshot.
67
+
68
+ Transitions that reverse between observations, including while the watcher is down, are not recovered. For example, leaving and rejoining the merge queue before the next observation produces no `dequeued` event. Stdout delivery is at-least-once across a crash: a crash after output but before the state replacement can repeat a line. There is no event history or acknowledged output protocol.
69
+
70
+ The exported `StateFile.layer` requires Effect `FileSystem`, and `GitHubHttp.layer` requires Effect `HttpClient`. The CLI supplies `NodeFileSystem.layer` and a `FetchHttpClient` layer that refuses redirects. HTTP and file failures remain typed `WatchError` values.
71
+
72
+ ### Evidence
73
+
74
+ Synthetic HTTP tests cover conditional 304 responses, all eight events, already-stuck PRs at startup, terminal pruning across restarts, queue entry with a cleared auto-merge request, confirmed queue drops and rebuilds, manual auto-merge disablement, native IO injection and typed failures, checks independent of PR ETags, fleet discovery and pagination, quota slowdown, failed requests, and clean restart comparison. A CLI subprocess test verifies that pending mergeability diagnostics stay on stderr while stdout contains JSON event lines. The packed consumer runs the installed executable against a synthetic server. These tests establish the REST and GraphQL handling and installed package behavior; they do not establish live GitHub or Monitor adoption.
@@ -0,0 +1,42 @@
1
+ import { Effect } from "effect";
2
+ import { type Options, WatchError } from "./model.ts";
3
+ export declare const GitHub: import("effect/Context").Service<"@shivaedev/pr-watch/GitHub", Readonly<{
4
+ readonly confirmQueue: (options: Options, snapshot: {
5
+ readonly autoMerge: boolean;
6
+ readonly branch: string;
7
+ readonly dirty: boolean;
8
+ readonly failed: boolean;
9
+ readonly merged: boolean;
10
+ readonly number: number;
11
+ readonly queued: boolean;
12
+ readonly state: "closed" | "open";
13
+ readonly title: string;
14
+ }) => Effect.Effect<{
15
+ branch: string;
16
+ dirty: boolean;
17
+ failed: boolean;
18
+ merged: boolean;
19
+ number: number;
20
+ state: "closed" | "open";
21
+ title: string;
22
+ autoMerge: boolean;
23
+ queued: boolean;
24
+ }, WatchError, never>;
25
+ readonly delay: (interval: number) => Effect.Effect<number, never, never>;
26
+ readonly discover: (options: Options) => Effect.Effect<number[], WatchError, never>;
27
+ readonly queue: (options: Options) => Effect.Effect<Set<number>, WatchError, never>;
28
+ readonly snapshot: (options: Options, number: number, queued: ReadonlySet<number>) => Effect.Effect<{
29
+ autoMerge: boolean;
30
+ branch: string;
31
+ dirty: boolean;
32
+ failed: boolean;
33
+ merged: boolean;
34
+ number: number;
35
+ queued: boolean;
36
+ state: "closed" | "open";
37
+ title: string;
38
+ }, WatchError, never>;
39
+ }>> & {
40
+ readonly layer: import("effect/Layer").Layer<"@shivaedev/pr-watch/GitHub", never, "@shivaedev/pr-watch/GitHubHttp">;
41
+ };
42
+ //# sourceMappingURL=GitHub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitHub.d.ts","sourceRoot":"","sources":["../src/GitHub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAIxC,OAAO,EAAE,KAAK,OAAO,EAAiB,UAAU,EAAE,MAAM,YAAY,CAAC;AAkBrE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+EjB,CAAC"}
package/dist/GitHub.js ADDED
@@ -0,0 +1,97 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { defineService } from "@shivaedev/effect-service/define-service.ts";
3
+ import { confirmQueue } from "./confirmQueue.js";
4
+ import { GitHubHttp } from "./GitHubHttp.js";
5
+ import { WatchError } from "./model.js";
6
+ const Pull = Schema.Struct({
7
+ autoMerge: Schema.NullOr(Schema.Struct({ mergeMethod: Schema.String }).pipe(Schema.encodeKeys({ mergeMethod: "merge_method" }))),
8
+ head: Schema.Struct({ ref: Schema.String, sha: Schema.String }),
9
+ mergeable: Schema.NullOr(Schema.Boolean),
10
+ mergeableState: Schema.String,
11
+ merged: Schema.Boolean,
12
+ number: Schema.Int,
13
+ state: Schema.Literals(["open", "closed"]),
14
+ title: Schema.String,
15
+ }).pipe(Schema.encodeKeys({ autoMerge: "auto_merge", mergeableState: "mergeable_state" }));
16
+ const Ref = Schema.Struct({ ref: Schema.String });
17
+ const ListedPull = Schema.Struct({ number: Schema.Int });
18
+ const Check = Schema.Struct({ conclusion: Schema.NullOr(Schema.String), status: Schema.String });
19
+ const Checks = Schema.Struct({ checkRuns: Schema.Array(Check) }).pipe(Schema.encodeKeys({ checkRuns: "check_runs" }));
20
+ const Status = Schema.Struct({ state: Schema.String });
21
+ const failures = new Set(["failure", "timed_out", "cancelled", "action_required", "startup_failure", "stale"]);
22
+ export const GitHub = defineService({
23
+ id: "@shivaedev/pr-watch/GitHub",
24
+ initialize: GitHubHttp,
25
+ methods: (http) => {
26
+ function decode(schema, body) {
27
+ return Schema.decodeUnknownEffect(schema)(body).pipe(Effect.mapError(() => new WatchError({ message: "Invalid GitHub response" })));
28
+ }
29
+ function pages(options, path) {
30
+ return Effect.gen(function* () {
31
+ const bodies = [];
32
+ const visited = new Set();
33
+ let url = `${options.apiUrl}/repos/${options.repository}${path}`;
34
+ while (url !== undefined) {
35
+ if (visited.has(url)) {
36
+ return yield* Effect.fail(new WatchError({ message: "Repeated pagination URL" }));
37
+ }
38
+ visited.add(url);
39
+ const response = yield* http.get(url, options.token);
40
+ bodies.push(response.body);
41
+ url = response.next;
42
+ }
43
+ return bodies;
44
+ });
45
+ }
46
+ return {
47
+ confirmQueue: (options, snapshot) => confirmQueue(http, options, snapshot),
48
+ delay: http.delay,
49
+ discover: (options) => Effect.gen(function* () {
50
+ const bodies = yield* pages(options, "/pulls?state=open&per_page=100");
51
+ const lists = yield* Effect.forEach(bodies, (body) => decode(Schema.Array(ListedPull), body));
52
+ return lists.flat().map((pr) => pr.number);
53
+ }),
54
+ queue: (options) => Effect.gen(function* () {
55
+ const bodies = yield* pages(options, "/git/matching-refs/heads/gh-readonly-queue/");
56
+ const lists = yield* Effect.forEach(bodies, (body) => decode(Schema.Array(Ref), body));
57
+ return new Set(lists.flat().flatMap(({ ref }) => {
58
+ const number = ref.match(/\/pr-(?<number>\d+)-/u)?.groups?.number;
59
+ return number === undefined ? [] : [Number(number)];
60
+ }));
61
+ }),
62
+ snapshot: (options, number, queued) => Effect.gen(function* () {
63
+ const response = yield* http.get(`${options.apiUrl}/repos/${options.repository}/pulls/${number}`, options.token);
64
+ const pr = yield* decode(Pull, response.body);
65
+ if (pr.number !== number) {
66
+ return yield* Effect.fail(new WatchError({ message: "GitHub returned the wrong PR" }));
67
+ }
68
+ if (pr.state === "open" && (pr.mergeable === null || pr.mergeableState === "unknown")) {
69
+ return yield* Effect.fail(new WatchError({ message: `Mergeability pending for PR #${number}` }));
70
+ }
71
+ let failed = false;
72
+ if (pr.state === "open") {
73
+ const bodies = yield* pages(options, `/commits/${pr.head.sha}/check-runs?per_page=100&filter=latest`);
74
+ const checks = yield* Effect.forEach(bodies, (body) => decode(Checks, body));
75
+ const statusResponse = yield* http.get(`${options.apiUrl}/repos/${options.repository}/commits/${pr.head.sha}/status`, options.token);
76
+ const status = yield* decode(Status, statusResponse.body);
77
+ failed =
78
+ checks.some((page) => page.checkRuns.some((check) => check.status === "completed" && failures.has(check.conclusion ?? "")))
79
+ || ["failure", "error"].includes(status.state);
80
+ }
81
+ return {
82
+ autoMerge: pr.autoMerge !== null,
83
+ branch: pr.head.ref,
84
+ dirty: pr.mergeableState === "dirty",
85
+ failed,
86
+ merged: pr.merged,
87
+ number,
88
+ queued: queued.has(number),
89
+ state: pr.state,
90
+ title: pr.title,
91
+ };
92
+ }),
93
+ };
94
+ },
95
+ requires: [GitHubHttp],
96
+ });
97
+ //# sourceMappingURL=GitHub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitHub.js","sourceRoot":"","sources":["../src/GitHub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Effect } from "effect";
2
+ import { HttpClient } from "effect/unstable/http";
3
+ import { type CachedResponse } from "./httpResponse.ts";
4
+ import { WatchError } from "./model.ts";
5
+ export declare const GitHubHttp: import("effect/Context").Service<"@shivaedev/pr-watch/GitHubHttp", Readonly<{
6
+ readonly delay: (interval: number) => Effect.Effect<number, never, never>;
7
+ readonly get: (url: string, token: string) => Effect.Effect<CachedResponse, WatchError, never>;
8
+ readonly post: (url: string, token: string, body: unknown) => Effect.Effect<CachedResponse, WatchError, never>;
9
+ }>> & {
10
+ readonly layer: import("effect/Layer").Layer<"@shivaedev/pr-watch/GitHubHttp", never, HttpClient.HttpClient>;
11
+ };
12
+ //# sourceMappingURL=GitHubHttp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitHubHttp.d.ts","sourceRoot":"","sources":["../src/GitHubHttp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,OAAO,EAAE,KAAK,cAAc,EAAiC,MAAM,mBAAmB,CAAC;AACvF,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,eAAO,MAAM,UAAU;;;;;;CAgDrB,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { Clock, Effect } from "effect";
2
+ import { HttpClient } from "effect/unstable/http";
3
+ import { defineService } from "@shivaedev/effect-service/define-service.ts";
4
+ import { decodeResponse, fetchResponse } from "./httpResponse.js";
5
+ import { WatchError } from "./model.js";
6
+ import { observeBudget, pollDelay } from "./quota.js";
7
+ export const GitHubHttp = defineService({
8
+ id: "@shivaedev/pr-watch/GitHubHttp",
9
+ initialize: Effect.gen(function* () {
10
+ return {
11
+ budget: { blockedUntil: 0, limit: 5000, remaining: 5000, reset: 0 },
12
+ cache: new Map(),
13
+ client: yield* HttpClient.HttpClient,
14
+ requests: 0,
15
+ };
16
+ }),
17
+ methods: (state) => {
18
+ function request(url, token, body) {
19
+ return Effect.gen(function* () {
20
+ const now = yield* Clock.currentTimeMillis;
21
+ if (state.budget.blockedUntil > now) {
22
+ return yield* Effect.fail(new WatchError({ message: "GitHub quota cooldown" }));
23
+ }
24
+ const cached = body === undefined ? state.cache.get(url) : undefined;
25
+ state.requests += 1;
26
+ const response = yield* fetchResponse(state.client, url, token, cached, body);
27
+ observeBudget(state.budget, response, yield* Clock.currentTimeMillis);
28
+ if (response.status === 304 && cached !== undefined) {
29
+ return cached;
30
+ }
31
+ const result = yield* decodeResponse(url, response);
32
+ if (body === undefined) {
33
+ state.cache.set(url, result);
34
+ }
35
+ return result;
36
+ }).pipe(Effect.scoped, Effect.timeout("30 seconds"), Effect.mapError((cause) => (cause instanceof WatchError ? cause : new WatchError({ message: cause.message }))));
37
+ }
38
+ return {
39
+ delay: (interval) => Effect.gen(function* () {
40
+ const now = yield* Clock.currentTimeMillis;
41
+ const delay = pollDelay(state.budget, interval, Math.max(1, state.requests), now);
42
+ state.requests = 0;
43
+ return delay;
44
+ }),
45
+ get: (url, token) => request(url, token),
46
+ post: (url, token, body) => request(url, token, body),
47
+ };
48
+ },
49
+ requires: [HttpClient.HttpClient],
50
+ });
51
+ //# sourceMappingURL=GitHubHttp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GitHubHttp.js","sourceRoot":"","sources":["../src/GitHubHttp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { Effect, FileSystem } from "effect";
2
+ import { WatchError } from "./model.ts";
3
+ export declare const StateFile: import("effect/Context").Service<"@shivaedev/pr-watch/StateFile", Readonly<{
4
+ readonly load: (path: string, repository: string) => Effect.Effect<{
5
+ readonly openNumbers?: readonly number[];
6
+ readonly prs: {
7
+ readonly [x: string]: {
8
+ readonly autoMerge: boolean;
9
+ readonly branch: string;
10
+ readonly dirty: boolean;
11
+ readonly failed: boolean;
12
+ readonly merged: boolean;
13
+ readonly number: number;
14
+ readonly queued: boolean;
15
+ readonly state: "closed" | "open";
16
+ readonly title: string;
17
+ };
18
+ };
19
+ readonly repository: string;
20
+ readonly version: 1;
21
+ }, WatchError, never>;
22
+ readonly save: (path: string, state: {
23
+ readonly openNumbers?: readonly number[];
24
+ readonly prs: {
25
+ readonly [x: string]: {
26
+ readonly autoMerge: boolean;
27
+ readonly branch: string;
28
+ readonly dirty: boolean;
29
+ readonly failed: boolean;
30
+ readonly merged: boolean;
31
+ readonly number: number;
32
+ readonly queued: boolean;
33
+ readonly state: "closed" | "open";
34
+ readonly title: string;
35
+ };
36
+ };
37
+ readonly repository: string;
38
+ readonly version: 1;
39
+ }) => Effect.Effect<void, WatchError, never>;
40
+ }>> & {
41
+ readonly layer: import("effect/Layer").Layer<"@shivaedev/pr-watch/StateFile", never, FileSystem.FileSystem>;
42
+ };
43
+ //# sourceMappingURL=StateFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateFile.d.ts","sourceRoot":"","sources":["../src/StateFile.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,UAAU,EAAU,MAAM,QAAQ,CAAC;AAEpD,OAAO,EAAS,UAAU,EAAE,MAAM,YAAY,CAAC;AAK/C,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BpB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { dirname } from "node:path";
2
+ import process from "node:process";
3
+ import { Effect, FileSystem, Schema } from "effect";
4
+ import { defineService } from "@shivaedev/effect-service/define-service.ts";
5
+ import { State, WatchError } from "./model.js";
6
+ function failure(cause) {
7
+ return new WatchError({ message: cause instanceof Error ? cause.message : "State file operation failed" });
8
+ }
9
+ export const StateFile = defineService({
10
+ id: "@shivaedev/pr-watch/StateFile",
11
+ initialize: FileSystem.FileSystem,
12
+ methods: (fs) => ({
13
+ load: (path, repository) => Effect.gen(function* () {
14
+ const text = yield* fs.readFileString(path).pipe(Effect.catchTag("PlatformError", (error) => (error.reason._tag === "NotFound" ? Effect.succeed(undefined) : Effect.fail(error))), Effect.mapError(failure));
15
+ if (text === undefined) {
16
+ return { prs: {}, repository, version: 1 };
17
+ }
18
+ const state = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(State))(text).pipe(Effect.mapError(failure));
19
+ if (state.repository !== repository) {
20
+ return yield* Effect.fail(new WatchError({ message: "State file belongs to a different repository" }));
21
+ }
22
+ return state;
23
+ }),
24
+ save: (path, state) => Effect.gen(function* () {
25
+ yield* fs.makeDirectory(dirname(path), { recursive: true });
26
+ const temporary = `${path}.${process.pid}.tmp`;
27
+ yield* fs.writeFileString(temporary, `${JSON.stringify(state)}\n`, { mode: 0o600 });
28
+ yield* fs.rename(temporary, path);
29
+ }).pipe(Effect.mapError(failure)),
30
+ }),
31
+ requires: [FileSystem.FileSystem],
32
+ });
33
+ //# sourceMappingURL=StateFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateFile.js","sourceRoot":"","sources":["../src/StateFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AAC5E,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE/C,SAAS,OAAO,CAAC,KAAc;IAC9B,OAAO,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,6BAA6B,EAAE,CAAC,CAAC;AAC5G,CAAC;AACD,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC;IACtC,EAAE,EAAE,+BAA+B;IACnC,UAAU,EAAE,UAAU,CAAC,UAAU;IACjC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACjB,IAAI,EAAE,CAAC,IAAY,EAAE,UAAkB,EAAE,EAAE,CAC1C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CAC/C,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAChI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CACxB,CAAC;YACF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,EAAkB,CAAC;YAC5D,CAAC;YACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YACnH,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;gBACrC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,EAAE,OAAO,EAAE,8CAA8C,EAAE,CAAC,CAAC,CAAC;YACxG,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC,CAAC;QACH,IAAI,EAAE,CAAC,IAAY,EAAE,KAAY,EAAE,EAAE,CACpC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,KAAK,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;YAC/C,KAAK,CAAC,CAAC,EAAE,CAAC,eAAe,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACpF,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;KAClC,CAAC;IACF,QAAQ,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;CACjC,CAAC,CAAC"}
@@ -0,0 +1,61 @@
1
+ import { Effect } from "effect";
2
+ import type { Options, WatchError } from "./model.ts";
3
+ export declare const Watcher: import("effect/Context").Service<"@shivaedev/pr-watch/Watcher", Readonly<{
4
+ readonly delay: (interval: number) => Effect.Effect<number, never, never>;
5
+ readonly load: (options: Options) => Effect.Effect<{
6
+ readonly openNumbers?: readonly number[];
7
+ readonly prs: {
8
+ readonly [x: string]: {
9
+ readonly autoMerge: boolean;
10
+ readonly branch: string;
11
+ readonly dirty: boolean;
12
+ readonly failed: boolean;
13
+ readonly merged: boolean;
14
+ readonly number: number;
15
+ readonly queued: boolean;
16
+ readonly state: "closed" | "open";
17
+ readonly title: string;
18
+ };
19
+ };
20
+ readonly repository: string;
21
+ readonly version: 1;
22
+ }, WatchError, never>;
23
+ readonly poll: (options: Options, previous: {
24
+ readonly openNumbers?: readonly number[];
25
+ readonly prs: {
26
+ readonly [x: string]: {
27
+ readonly autoMerge: boolean;
28
+ readonly branch: string;
29
+ readonly dirty: boolean;
30
+ readonly failed: boolean;
31
+ readonly merged: boolean;
32
+ readonly number: number;
33
+ readonly queued: boolean;
34
+ readonly state: "closed" | "open";
35
+ readonly title: string;
36
+ };
37
+ };
38
+ readonly repository: string;
39
+ readonly version: 1;
40
+ }, emit: (line: string) => Effect.Effect<void, WatchError>, diagnostic: (message: string) => Effect.Effect<void, WatchError>) => Effect.Effect<{
41
+ readonly openNumbers?: readonly number[];
42
+ readonly prs: {
43
+ readonly [x: string]: {
44
+ readonly autoMerge: boolean;
45
+ readonly branch: string;
46
+ readonly dirty: boolean;
47
+ readonly failed: boolean;
48
+ readonly merged: boolean;
49
+ readonly number: number;
50
+ readonly queued: boolean;
51
+ readonly state: "closed" | "open";
52
+ readonly title: string;
53
+ };
54
+ };
55
+ readonly repository: string;
56
+ readonly version: 1;
57
+ }, WatchError, never>;
58
+ }>> & {
59
+ readonly layer: import("effect/Layer").Layer<"@shivaedev/pr-watch/Watcher", never, "@shivaedev/pr-watch/GitHub" | "@shivaedev/pr-watch/StateFile">;
60
+ };
61
+ //# sourceMappingURL=Watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Watcher.d.ts","sourceRoot":"","sources":["../src/Watcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,OAAO,KAAK,EAAE,OAAO,EAAmB,UAAU,EAAE,MAAM,YAAY,CAAC;AASvE,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAoDH,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,wBACjC,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;CAyBlE,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { Effect } from "effect";
2
+ import { defineService } from "@shivaedev/effect-service/define-service.ts";
3
+ import { events } from "./events.js";
4
+ import { GitHub } from "./GitHub.js";
5
+ import { StateFile } from "./StateFile.js";
6
+ function activeNumbers(options, state) {
7
+ return [...new Set([...options.numbers, ...(state.openNumbers ?? []), ...Object.keys(state.prs).map(Number)])].filter((number) => {
8
+ const saved = state.prs[String(number)];
9
+ return saved === undefined || (saved.state === "open" && !saved.merged);
10
+ });
11
+ }
12
+ export const Watcher = defineService({
13
+ id: "@shivaedev/pr-watch/Watcher",
14
+ initialize: Effect.gen(function* () {
15
+ return { file: yield* StateFile, github: yield* GitHub };
16
+ }),
17
+ methods: ({ file, github }) => {
18
+ function commit(state, snapshot, options, emit) {
19
+ return Effect.uninterruptible(Effect.gen(function* () {
20
+ const before = state.prs[String(snapshot.number)];
21
+ const baseline = before === undefined && snapshot.state === "closed" && state.openNumbers?.includes(snapshot.number)
22
+ ? { ...snapshot, merged: false, state: "open" }
23
+ : before;
24
+ for (const line of events(baseline, snapshot)) {
25
+ yield* emit(line);
26
+ }
27
+ const next = {
28
+ ...state,
29
+ openNumbers: (state.openNumbers ?? []).filter((number) => number !== snapshot.number),
30
+ prs: { ...state.prs, [String(snapshot.number)]: snapshot },
31
+ };
32
+ yield* file.save(options.stateFile, next);
33
+ return next;
34
+ }));
35
+ }
36
+ function observe(options, number, queue, before) {
37
+ return Effect.gen(function* () {
38
+ const snapshot = yield* github.snapshot(options, number, queue);
39
+ return before?.queued === true && snapshot.state === "open" && !snapshot.queued ? yield* github.confirmQueue(options, snapshot) : snapshot;
40
+ });
41
+ }
42
+ function targets(options, state) {
43
+ return Effect.gen(function* () {
44
+ const discovered = options.fleet ? yield* github.discover(options) : [];
45
+ const openNumbers = [...new Set([...(state.openNumbers ?? []), ...discovered])].filter((number) => state.prs[String(number)] === undefined);
46
+ const next = openNumbers.length === (state.openNumbers?.length ?? 0) ? state : { ...state, openNumbers };
47
+ if (next !== state) {
48
+ yield* Effect.uninterruptible(file.save(options.stateFile, next));
49
+ }
50
+ const numbers = activeNumbers(options, next);
51
+ const queue = numbers.length > 0 ? yield* github.queue(options) : new Set();
52
+ return { numbers, queue, state: next };
53
+ });
54
+ }
55
+ return {
56
+ delay: github.delay,
57
+ load: (options) => file.load(options.stateFile, options.repository),
58
+ poll: (options, previous, emit, diagnostic) => Effect.gen(function* () {
59
+ let state = { ...previous, prs: { ...previous.prs } };
60
+ const targetsForRound = yield* targets(options, state);
61
+ const { numbers, queue } = targetsForRound;
62
+ state = targetsForRound.state;
63
+ let failure;
64
+ for (const number of numbers) {
65
+ const result = yield* Effect.result(observe(options, number, queue, state.prs[String(number)]));
66
+ if (result._tag === "Failure") {
67
+ yield* diagnostic(result.failure.message);
68
+ failure = result.failure;
69
+ continue;
70
+ }
71
+ state = yield* commit(state, result.success, options, emit);
72
+ }
73
+ if (failure !== undefined) {
74
+ return yield* Effect.fail(failure);
75
+ }
76
+ return state;
77
+ }),
78
+ };
79
+ },
80
+ requires: [GitHub, StateFile],
81
+ });
82
+ //# sourceMappingURL=Watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Watcher.js","sourceRoot":"","sources":["../src/Watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { Effect } from "effect";
2
+ import { WatchError } from "./model.ts";
3
+ export declare const help = "pr-watch --repo OWNER/REPO [--fleet | PR_NUMBER ...]\n --state PATH Snapshot file (default .pr-watch/OWNER-REPO.json)\n --interval SECONDS Poll interval (default 60; slows as quota falls)\n --once Poll once and exit\n --api-url URL GitHub REST base URL (default https://api.github.com)\n --help Show this help\nAuthentication: GH_TOKEN or GITHUB_TOKEN";
4
+ export declare function argumentsFor(args: readonly string[], token: string): Effect.Effect<{
5
+ help: true;
6
+ once?: never;
7
+ options?: never;
8
+ } | {
9
+ help: false;
10
+ once: boolean;
11
+ options: {
12
+ apiUrl: string;
13
+ fleet: boolean;
14
+ interval: number;
15
+ numbers: readonly number[];
16
+ repository: string;
17
+ stateFile: string;
18
+ token: string;
19
+ };
20
+ }, WatchError, never>;
21
+ //# sourceMappingURL=arguments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arguments.d.ts","sourceRoot":"","sources":["../src/arguments.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAgB,UAAU,EAAE,MAAM,YAAY,CAAC;AACtD,eAAO,MAAM,IAAI,4YAMwB,CAAC;AAC1C,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;sBA0ClE"}