@nanonets/graft 0.16.0 → 0.19.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 +10 -3
- package/dist/app/brain-build-process.d.ts +22 -0
- package/dist/app/brain-build-process.d.ts.map +1 -0
- package/dist/app/brain-build-process.js +129 -0
- package/dist/app/brain-build-process.js.map +1 -0
- package/dist/app/brain-build-worker.d.ts +35 -0
- package/dist/app/brain-build-worker.d.ts.map +1 -0
- package/dist/app/brain-build-worker.js +56 -0
- package/dist/app/brain-build-worker.js.map +1 -0
- package/dist/app/brain-build.d.ts +160 -0
- package/dist/app/brain-build.d.ts.map +1 -0
- package/dist/app/brain-build.js +278 -0
- package/dist/app/brain-build.js.map +1 -0
- package/dist/app/checkout.d.ts +53 -3
- package/dist/app/checkout.d.ts.map +1 -1
- package/dist/app/checkout.js +195 -27
- package/dist/app/checkout.js.map +1 -1
- package/dist/app/events.d.ts +7 -0
- package/dist/app/events.d.ts.map +1 -1
- package/dist/app/events.js +7 -2
- package/dist/app/events.js.map +1 -1
- package/dist/app/history.d.ts +128 -0
- package/dist/app/history.d.ts.map +1 -0
- package/dist/app/history.js +266 -0
- package/dist/app/history.js.map +1 -0
- package/dist/app/identity.d.ts +56 -0
- package/dist/app/identity.d.ts.map +1 -1
- package/dist/app/identity.js +119 -0
- package/dist/app/identity.js.map +1 -1
- package/dist/app/main.js +14 -0
- package/dist/app/main.js.map +1 -1
- package/dist/app/pages.d.ts +17 -0
- package/dist/app/pages.d.ts.map +1 -1
- package/dist/app/pages.js +112 -5
- package/dist/app/pages.js.map +1 -1
- package/dist/app/review-process.d.ts +66 -0
- package/dist/app/review-process.d.ts.map +1 -0
- package/dist/app/review-process.js +199 -0
- package/dist/app/review-process.js.map +1 -0
- package/dist/app/review-worker.d.ts +2 -0
- package/dist/app/review-worker.d.ts.map +1 -0
- package/dist/app/review-worker.js +108 -0
- package/dist/app/review-worker.js.map +1 -0
- package/dist/app/review.d.ts.map +1 -1
- package/dist/app/review.js +4 -1
- package/dist/app/review.js.map +1 -1
- package/dist/app/server.d.ts +31 -2
- package/dist/app/server.d.ts.map +1 -1
- package/dist/app/server.js +130 -3
- package/dist/app/server.js.map +1 -1
- package/dist/app/sources.d.ts +68 -0
- package/dist/app/sources.d.ts.map +1 -0
- package/dist/app/sources.js +342 -0
- package/dist/app/sources.js.map +1 -0
- package/dist/ask/ask.d.ts +6 -0
- package/dist/ask/ask.d.ts.map +1 -1
- package/dist/ask/ask.js +30 -2
- package/dist/ask/ask.js.map +1 -1
- package/dist/brain/attach.d.ts +44 -0
- package/dist/brain/attach.d.ts.map +1 -0
- package/dist/brain/attach.js +63 -0
- package/dist/brain/attach.js.map +1 -0
- package/dist/brain/connect.d.ts +59 -0
- package/dist/brain/connect.d.ts.map +1 -0
- package/dist/brain/connect.js +80 -0
- package/dist/brain/connect.js.map +1 -0
- package/dist/brain/link.d.ts +89 -0
- package/dist/brain/link.d.ts.map +1 -0
- package/dist/brain/link.js +146 -0
- package/dist/brain/link.js.map +1 -0
- package/dist/brain/push.d.ts +72 -0
- package/dist/brain/push.d.ts.map +1 -0
- package/dist/brain/push.js +191 -0
- package/dist/brain/push.js.map +1 -0
- package/dist/brain/signup.d.ts +73 -0
- package/dist/brain/signup.d.ts.map +1 -0
- package/dist/brain/signup.js +186 -0
- package/dist/brain/signup.js.map +1 -0
- package/dist/brain/watch.d.ts +136 -0
- package/dist/brain/watch.d.ts.map +1 -0
- package/dist/brain/watch.js +172 -0
- package/dist/brain/watch.js.map +1 -0
- package/dist/brain/wire.d.ts +38 -0
- package/dist/brain/wire.d.ts.map +1 -0
- package/dist/brain/wire.js +133 -0
- package/dist/brain/wire.js.map +1 -0
- package/dist/claude/format.d.ts.map +1 -1
- package/dist/claude/format.js +10 -3
- package/dist/claude/format.js.map +1 -1
- package/dist/claude/hooks.d.ts.map +1 -1
- package/dist/claude/hooks.js +32 -1
- package/dist/claude/hooks.js.map +1 -1
- package/dist/claude/init.d.ts +5 -0
- package/dist/claude/init.d.ts.map +1 -1
- package/dist/claude/init.js +9 -1
- package/dist/claude/init.js.map +1 -1
- package/dist/claude/session-metrics.d.ts +10 -0
- package/dist/claude/session-metrics.d.ts.map +1 -1
- package/dist/claude/session-metrics.js +25 -0
- package/dist/claude/session-metrics.js.map +1 -1
- package/dist/claude/settings-merge.d.ts +18 -0
- package/dist/claude/settings-merge.d.ts.map +1 -1
- package/dist/claude/settings-merge.js +42 -8
- package/dist/claude/settings-merge.js.map +1 -1
- package/dist/claude/skill-template.d.ts.map +1 -1
- package/dist/claude/skill-template.js +6 -0
- package/dist/claude/skill-template.js.map +1 -1
- package/dist/claude/state.d.ts +14 -0
- package/dist/claude/state.d.ts.map +1 -1
- package/dist/claude/state.js.map +1 -1
- package/dist/claude/tally.d.ts +27 -0
- package/dist/claude/tally.d.ts.map +1 -1
- package/dist/claude/tally.js +74 -1
- package/dist/claude/tally.js.map +1 -1
- package/dist/cli.js +310 -3
- package/dist/cli.js.map +1 -1
- package/dist/context/node-file.d.ts +4 -10
- package/dist/context/node-file.d.ts.map +1 -1
- package/dist/context/node-file.js +6 -2
- package/dist/context/node-file.js.map +1 -1
- package/dist/context/price.d.ts +49 -0
- package/dist/context/price.d.ts.map +1 -0
- package/dist/context/price.js +83 -0
- package/dist/context/price.js.map +1 -0
- package/dist/context/savings.d.ts +13 -3
- package/dist/context/savings.d.ts.map +1 -1
- package/dist/context/savings.js +44 -5
- package/dist/context/savings.js.map +1 -1
- package/dist/hosts/claude-global.d.ts +33 -0
- package/dist/hosts/claude-global.d.ts.map +1 -0
- package/dist/hosts/claude-global.js +118 -0
- package/dist/hosts/claude-global.js.map +1 -0
- package/dist/hosts/plan.d.ts.map +1 -1
- package/dist/hosts/plan.js +5 -1
- package/dist/hosts/plan.js.map +1 -1
- package/dist/hosts/retract.d.ts.map +1 -1
- package/dist/hosts/retract.js +33 -13
- package/dist/hosts/retract.js.map +1 -1
- package/dist/hosts/sections.d.ts +23 -2
- package/dist/hosts/sections.d.ts.map +1 -1
- package/dist/hosts/sections.js +25 -9
- package/dist/hosts/sections.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +33 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +5 -1
- package/dist/mcp/tools.js.map +1 -1
- package/dist/telemetry/contract.d.ts +12 -0
- package/dist/telemetry/contract.d.ts.map +1 -1
- package/dist/telemetry/contract.js +19 -0
- package/dist/telemetry/contract.js.map +1 -1
- package/dist/telemetry/gate.d.ts +14 -0
- package/dist/telemetry/gate.d.ts.map +1 -1
- package/dist/telemetry/gate.js +18 -2
- package/dist/telemetry/gate.js.map +1 -1
- package/dist/upkeep-run.d.ts.map +1 -1
- package/dist/upkeep-run.js +12 -2
- package/dist/upkeep-run.js.map +1 -1
- package/dist/upkeep.d.ts +18 -0
- package/dist/upkeep.d.ts.map +1 -1
- package/dist/upkeep.js +38 -0
- package/dist/upkeep.js.map +1 -1
- package/dist/util/state.d.ts +9 -0
- package/dist/util/state.d.ts.map +1 -1
- package/dist/util/state.js.map +1 -1
- package/package.json +1 -1
package/dist/app/identity.js
CHANGED
|
@@ -38,6 +38,125 @@ export function appJwt(creds, nowMs) {
|
|
|
38
38
|
* Tokens last an hour and a busy repository can fire a dozen webhooks a minute;
|
|
39
39
|
* without the cache every one of them spends a round trip and a signature.
|
|
40
40
|
*/
|
|
41
|
+
/**
|
|
42
|
+
* The installation id for one repository.
|
|
43
|
+
*
|
|
44
|
+
* Every existing caller gets the id from a webhook payload, because every
|
|
45
|
+
* existing caller is reacting to one. A request that names a repository instead
|
|
46
|
+
* has to look it up, and this is the only endpoint that answers it: it is
|
|
47
|
+
* App-JWT authed, so it works before any installation token exists.
|
|
48
|
+
*
|
|
49
|
+
* Returns null when the App is not installed on the repository — which is the
|
|
50
|
+
* expected answer for "the user pasted a repo we cannot see", not an error.
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* Headers for a GitHub REST call, with or without a token.
|
|
54
|
+
*
|
|
55
|
+
* An empty token means anonymous, and anonymous has to mean NO authorization
|
|
56
|
+
* header — `Bearer ` with nothing after it is a 401, not a fallback. That
|
|
57
|
+
* distinction is the whole reason this is a function: a public repository read
|
|
58
|
+
* without an installation goes down exactly this path.
|
|
59
|
+
*/
|
|
60
|
+
export function ghHeaders(token) {
|
|
61
|
+
const headers = {
|
|
62
|
+
accept: "application/vnd.github+json",
|
|
63
|
+
"user-agent": "graft-app",
|
|
64
|
+
};
|
|
65
|
+
if (token)
|
|
66
|
+
headers.authorization = `Bearer ${token}`;
|
|
67
|
+
return headers;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* An installation token for reading PUBLIC repositories the App is not
|
|
71
|
+
* installed on.
|
|
72
|
+
*
|
|
73
|
+
* GitHub answers any public resource to any installation token, at 5,000
|
|
74
|
+
* requests an hour. Anonymous answers the same resources at 60 an hour for the
|
|
75
|
+
* whole box, which one repository's pull-request walk spends in a minute — so
|
|
76
|
+
* without this, the second public repo of the hour reads as "we cannot see it".
|
|
77
|
+
*
|
|
78
|
+
* `owner` is our own account, so this borrows OUR installation's rate limit
|
|
79
|
+
* rather than some customer's. Falls back to the first installation, and to
|
|
80
|
+
* null when the App has none, in which case the caller reads anonymously.
|
|
81
|
+
*/
|
|
82
|
+
export async function publicReadInstallation(creds, owner, fetchImpl, nowMs = Date.now(), api = "https://api.github.com") {
|
|
83
|
+
const res = await fetchImpl(`${api}/app/installations?per_page=100`, {
|
|
84
|
+
headers: {
|
|
85
|
+
authorization: `Bearer ${appJwt(creds, nowMs)}`,
|
|
86
|
+
accept: "application/vnd.github+json",
|
|
87
|
+
"user-agent": "graft-app",
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
if (!res.ok)
|
|
91
|
+
return null;
|
|
92
|
+
const list = JSON.parse(await res.text());
|
|
93
|
+
if (!Array.isArray(list) || list.length === 0)
|
|
94
|
+
return null;
|
|
95
|
+
const wanted = owner.toLowerCase();
|
|
96
|
+
const mine = list.find((i) => (i.account?.login ?? "").toLowerCase() === wanted);
|
|
97
|
+
return (mine ?? list[0]).id ?? null;
|
|
98
|
+
}
|
|
99
|
+
export async function installationFor(creds, owner, repo, fetchImpl, nowMs = Date.now(), api = "https://api.github.com") {
|
|
100
|
+
const res = await fetchImpl(`${api}/repos/${owner}/${repo}/installation`, {
|
|
101
|
+
headers: {
|
|
102
|
+
authorization: `Bearer ${appJwt(creds, nowMs)}`,
|
|
103
|
+
accept: "application/vnd.github+json",
|
|
104
|
+
"user-agent": "graft-app",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
if (res.status === 404)
|
|
108
|
+
return null;
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
const body = await res.text();
|
|
111
|
+
throw new Error(`installation lookup for ${owner}/${repo} failed: ${res.status} ${body.slice(0, 200)}`);
|
|
112
|
+
}
|
|
113
|
+
const parsed = JSON.parse(await res.text());
|
|
114
|
+
return typeof parsed.id === "number" ? parsed.id : null;
|
|
115
|
+
}
|
|
116
|
+
/** Which of the two gaps applies to owner/repo. */
|
|
117
|
+
export async function repoAccessGap(creds, owner, fetchImpl, nowMs = Date.now(), api = "https://api.github.com") {
|
|
118
|
+
const headers = {
|
|
119
|
+
authorization: `Bearer ${appJwt(creds, nowMs)}`,
|
|
120
|
+
accept: "application/vnd.github+json",
|
|
121
|
+
"user-agent": "graft-app",
|
|
122
|
+
};
|
|
123
|
+
// An account is an org or a user and the endpoints differ, so try the org one
|
|
124
|
+
// and fall back rather than making the caller know which it is.
|
|
125
|
+
for (const path of [`/orgs/${owner}/installation`, `/users/${owner}/installation`]) {
|
|
126
|
+
try {
|
|
127
|
+
const res = await fetchImpl(`${api}${path}`, { headers });
|
|
128
|
+
if (!res.ok)
|
|
129
|
+
continue;
|
|
130
|
+
const parsed = JSON.parse(await res.text());
|
|
131
|
+
return {
|
|
132
|
+
reason: "repo_not_selected",
|
|
133
|
+
ownerId: parsed.account?.id ?? null,
|
|
134
|
+
installationId: parsed.id ?? null,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Try the other shape; a network failure here only costs a less specific
|
|
139
|
+
// message, never the whole answer.
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { reason: "not_installed", ownerId: await ownerIdFor(owner, fetchImpl, api), installationId: null };
|
|
143
|
+
}
|
|
144
|
+
/** The account's numeric id, for preselecting it on the install screen. */
|
|
145
|
+
async function ownerIdFor(owner, fetchImpl, api) {
|
|
146
|
+
try {
|
|
147
|
+
// Unauthenticated: a public account's id is public, and this runs on a path
|
|
148
|
+
// where the App has no installation token to use anyway.
|
|
149
|
+
const res = await fetchImpl(`${api}/users/${owner}`, {
|
|
150
|
+
headers: { accept: "application/vnd.github+json", "user-agent": "graft-app" },
|
|
151
|
+
});
|
|
152
|
+
if (!res.ok)
|
|
153
|
+
return null;
|
|
154
|
+
return JSON.parse(await res.text()).id ?? null;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
41
160
|
export class InstallationTokens {
|
|
42
161
|
creds;
|
|
43
162
|
fetchImpl;
|
package/dist/app/identity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/app/identity.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/app/identity.ts"],"names":[],"mappings":"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"}
|
package/dist/app/main.js
CHANGED
|
@@ -37,6 +37,20 @@ const { server, queue } = createApp({
|
|
|
37
37
|
publicUrl: required("GRAFT_PUBLIC_URL"),
|
|
38
38
|
port,
|
|
39
39
|
concurrency: Number(process.env.GRAFT_CONCURRENCY ?? 2),
|
|
40
|
+
// Optional, unlike the four above, and deliberately so: point it at a mounted
|
|
41
|
+
// volume and the pages already linked from pull requests survive a restart;
|
|
42
|
+
// leave it unset and they do not. Neither is worth refusing to boot over — a
|
|
43
|
+
// server that will not start reviews nothing at all, which is strictly worse
|
|
44
|
+
// than one whose old links go stale.
|
|
45
|
+
pageDir: process.env.GRAFT_PAGE_DIR,
|
|
46
|
+
// Optional for the same reason as pageDir: unset simply disables
|
|
47
|
+
// `POST /brain/build`, and a deployment that only reviews pull requests wants
|
|
48
|
+
// exactly that. The route is the one place the app writes to the platform, so
|
|
49
|
+
// it stays off until someone deliberately turns it on.
|
|
50
|
+
brainBuildSecret: process.env.GRAFT_BRAIN_BUILD_SECRET,
|
|
51
|
+
brainBaseUrl: process.env.GRAFT_BRAIN_URL,
|
|
52
|
+
publicToken: process.env.GRAFT_PUBLIC_GITHUB_TOKEN || process.env.GITHUB_TOKEN,
|
|
53
|
+
publicOwner: process.env.GRAFT_PUBLIC_INSTALLATION_OWNER,
|
|
40
54
|
});
|
|
41
55
|
/**
|
|
42
56
|
* Finish what is in flight before dying.
|
package/dist/app/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/app/main.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC9C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IAClC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,EAAE;IACxB,aAAa,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAC/C,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC;IACvC,IAAI;IACJ,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/app/main.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,cAAc,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC9C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3D,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAC9C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IAClC,KAAK,EAAE,QAAQ,CAAC,cAAc,CAAC;IAC/B,UAAU,EAAE,UAAU,EAAE;IACxB,aAAa,EAAE,QAAQ,CAAC,sBAAsB,CAAC;IAC/C,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC;IACvC,IAAI;IACJ,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,CAAC;IACvD,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,6EAA6E;IAC7E,qCAAqC;IACrC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;IACnC,iEAAiE;IACjE,8EAA8E;IAC9E,8EAA8E;IAC9E,uDAAuD;IACvD,gBAAgB,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB;IACtD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;IACzC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;IAC9E,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,+BAA+B;CACzD,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,KAAK,MAAM,MAAM,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,cAAc,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/app/pages.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface PageStoreOptions {
|
|
|
7
7
|
ttlMs?: number;
|
|
8
8
|
maxPages?: number;
|
|
9
9
|
now?: () => number;
|
|
10
|
+
/** Kept across restarts here. Absent: pages live only as long as the process. */
|
|
11
|
+
dir?: string;
|
|
10
12
|
}
|
|
11
13
|
export declare class PageStore {
|
|
12
14
|
private readonly pages;
|
|
@@ -14,6 +16,7 @@ export declare class PageStore {
|
|
|
14
16
|
private readonly ttlMs;
|
|
15
17
|
private readonly maxPages;
|
|
16
18
|
private readonly now;
|
|
19
|
+
private readonly dir;
|
|
17
20
|
constructor(opts: PageStoreOptions);
|
|
18
21
|
/** `owner/repo#number` → a path segment safe to put in a URL. */
|
|
19
22
|
static idFor(owner: string, repo: string, number: number): string;
|
|
@@ -28,6 +31,9 @@ export declare class PageStore {
|
|
|
28
31
|
* Expiry is checked against the STORED time rather than encoded in the token,
|
|
29
32
|
* so a link cannot outlive the data it points at — and re-reviewing a PR
|
|
30
33
|
* refreshes both together.
|
|
34
|
+
*
|
|
35
|
+
* Answered from memory even when there is a directory: `id` comes off a URL,
|
|
36
|
+
* and an id that never reaches the filesystem cannot be walked out of it.
|
|
31
37
|
*/
|
|
32
38
|
get(id: string, token: string | undefined): string | null;
|
|
33
39
|
get size(): number;
|
|
@@ -35,5 +41,16 @@ export declare class PageStore {
|
|
|
35
41
|
private valid;
|
|
36
42
|
/** Oldest out first, plus anything past its TTL. */
|
|
37
43
|
private evict;
|
|
44
|
+
/** Take back what a previous process left on disk. */
|
|
45
|
+
private hydrate;
|
|
46
|
+
/** A stored page, or nothing: truncated, corrupt and gone are one answer here. */
|
|
47
|
+
private read;
|
|
48
|
+
/**
|
|
49
|
+
* Temp file, then rename: a reader only ever sees a whole page, and two reviews
|
|
50
|
+
* of the same pull request racing each other leave one of them intact rather
|
|
51
|
+
* than the two interleaved.
|
|
52
|
+
*/
|
|
53
|
+
private write;
|
|
54
|
+
private remove;
|
|
38
55
|
}
|
|
39
56
|
//# sourceMappingURL=pages.d.ts.map
|
package/dist/app/pages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;gBAE7B,IAAI,EAAE,gBAAgB;IASlC,iEAAiE;IACjE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAIjE,qFAAqF;IACrF,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAS7F;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;IAUzD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,KAAK;IAQb,oDAAoD;IACpD,OAAO,CAAC,KAAK;IAUb,sDAAsD;IACtD,OAAO,CAAC,OAAO;IA+Bf,kFAAkF;IAClF,OAAO,CAAC,IAAI;IAWZ;;;;OAIG;IACH,OAAO,CAAC,KAAK;IAsBb,OAAO,CAAC,MAAM;CASf"}
|
package/dist/app/pages.js
CHANGED
|
@@ -10,23 +10,38 @@
|
|
|
10
10
|
* capability, not a login. Anyone with the link can read that one page until it
|
|
11
11
|
* expires, which is the same property a GitHub artifact link has, and the link
|
|
12
12
|
* only ever appears in a comment on the pull request it describes.
|
|
13
|
+
*
|
|
14
|
+
* Pages are mirrored to `dir`, when one is given, because the token is DERIVED
|
|
15
|
+
* from the id rather than stored: a link keeps passing the signature check for as
|
|
16
|
+
* long as the secret lives, so a process that lost its pages answers 404 to a
|
|
17
|
+
* link that looks perfectly valid. That failure is silent at both ends — the
|
|
18
|
+
* comment still sits on the pull request, the reviewer just sees "not found" —
|
|
19
|
+
* and one restart of a container with nothing mounted stranded 29 of them. Disk
|
|
20
|
+
* is the fix; without a directory this behaves exactly as it did before.
|
|
13
21
|
*/
|
|
14
|
-
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
22
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
23
|
+
import { mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
24
|
+
import { join } from "node:path";
|
|
15
25
|
/** Long enough to review a PR at leisure, short enough that a leaked link dies. */
|
|
16
26
|
const DEFAULT_TTL_MS = 14 * 24 * 60 * 60 * 1000;
|
|
17
27
|
/** A page is ~50 kB; a few hundred of them is nothing, and eviction is by age. */
|
|
18
28
|
const DEFAULT_MAX_PAGES = 500;
|
|
29
|
+
/** One file per page, named by its id. The extension also means "fully written". */
|
|
30
|
+
const SUFFIX = ".json";
|
|
19
31
|
export class PageStore {
|
|
20
32
|
pages = new Map();
|
|
21
33
|
secret;
|
|
22
34
|
ttlMs;
|
|
23
35
|
maxPages;
|
|
24
36
|
now;
|
|
37
|
+
dir;
|
|
25
38
|
constructor(opts) {
|
|
26
39
|
this.secret = opts.secret;
|
|
27
40
|
this.ttlMs = opts.ttlMs ?? DEFAULT_TTL_MS;
|
|
28
41
|
this.maxPages = opts.maxPages ?? DEFAULT_MAX_PAGES;
|
|
29
42
|
this.now = opts.now ?? Date.now;
|
|
43
|
+
this.dir = opts.dir;
|
|
44
|
+
this.hydrate();
|
|
30
45
|
}
|
|
31
46
|
/** `owner/repo#number` → a path segment safe to put in a URL. */
|
|
32
47
|
static idFor(owner, repo, number) {
|
|
@@ -35,7 +50,9 @@ export class PageStore {
|
|
|
35
50
|
/** Store (replacing any previous page for the same PR) and return `id` + `token`. */
|
|
36
51
|
put(owner, repo, number, html) {
|
|
37
52
|
const id = PageStore.idFor(owner, repo, number);
|
|
38
|
-
|
|
53
|
+
const page = { html, storedMs: this.now() };
|
|
54
|
+
this.pages.set(id, page);
|
|
55
|
+
this.write(id, page);
|
|
39
56
|
this.evict();
|
|
40
57
|
return { id, token: this.sign(id) };
|
|
41
58
|
}
|
|
@@ -45,13 +62,16 @@ export class PageStore {
|
|
|
45
62
|
* Expiry is checked against the STORED time rather than encoded in the token,
|
|
46
63
|
* so a link cannot outlive the data it points at — and re-reviewing a PR
|
|
47
64
|
* refreshes both together.
|
|
65
|
+
*
|
|
66
|
+
* Answered from memory even when there is a directory: `id` comes off a URL,
|
|
67
|
+
* and an id that never reaches the filesystem cannot be walked out of it.
|
|
48
68
|
*/
|
|
49
69
|
get(id, token) {
|
|
50
70
|
const page = this.pages.get(id);
|
|
51
71
|
if (!page || !token || !this.valid(id, token))
|
|
52
72
|
return null;
|
|
53
73
|
if (this.now() - page.storedMs > this.ttlMs) {
|
|
54
|
-
this.
|
|
74
|
+
this.remove(id);
|
|
55
75
|
return null;
|
|
56
76
|
}
|
|
57
77
|
return page.html;
|
|
@@ -74,13 +94,100 @@ export class PageStore {
|
|
|
74
94
|
const cutoff = this.now() - this.ttlMs;
|
|
75
95
|
for (const [id, page] of this.pages)
|
|
76
96
|
if (page.storedMs < cutoff)
|
|
77
|
-
this.
|
|
97
|
+
this.remove(id);
|
|
78
98
|
while (this.pages.size > this.maxPages) {
|
|
79
99
|
const oldest = this.pages.keys().next();
|
|
80
100
|
if (oldest.done)
|
|
81
101
|
break;
|
|
82
|
-
this.
|
|
102
|
+
this.remove(oldest.value);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Take back what a previous process left on disk. */
|
|
106
|
+
hydrate() {
|
|
107
|
+
const dir = this.dir;
|
|
108
|
+
if (!dir)
|
|
109
|
+
return;
|
|
110
|
+
let names;
|
|
111
|
+
try {
|
|
112
|
+
mkdirSync(dir, { recursive: true });
|
|
113
|
+
names = readdirSync(dir);
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
// Nothing usable there: carry on in memory, which is exactly what every
|
|
117
|
+
// deployment without a directory does anyway.
|
|
118
|
+
console.warn(`page store: ${dir} is unusable, pages will not survive a restart (${reason(err)})`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const loaded = [];
|
|
122
|
+
for (const name of names) {
|
|
123
|
+
// The suffix also skips the temp files a write leaves behind when it dies
|
|
124
|
+
// partway, so a half-written page is never a candidate to begin with.
|
|
125
|
+
if (!name.endsWith(SUFFIX))
|
|
126
|
+
continue;
|
|
127
|
+
const page = this.read(join(dir, name));
|
|
128
|
+
if (page)
|
|
129
|
+
loaded.push([name.slice(0, -SUFFIX.length), page]);
|
|
130
|
+
}
|
|
131
|
+
// Oldest first, because eviction takes the front of the Map and readdir order
|
|
132
|
+
// is the filesystem's business rather than a record of what happened when.
|
|
133
|
+
loaded.sort((a, b) => a[1].storedMs - b[1].storedMs);
|
|
134
|
+
for (const [id, page] of loaded)
|
|
135
|
+
this.pages.set(id, page);
|
|
136
|
+
// A restart is also when pages that expired while the process was down go.
|
|
137
|
+
this.evict();
|
|
138
|
+
}
|
|
139
|
+
/** A stored page, or nothing: truncated, corrupt and gone are one answer here. */
|
|
140
|
+
read(file) {
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
143
|
+
const { html, storedMs } = (parsed ?? {});
|
|
144
|
+
if (typeof html !== "string" || typeof storedMs !== "number" || !Number.isFinite(storedMs))
|
|
145
|
+
return null;
|
|
146
|
+
return { html, storedMs };
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Temp file, then rename: a reader only ever sees a whole page, and two reviews
|
|
154
|
+
* of the same pull request racing each other leave one of them intact rather
|
|
155
|
+
* than the two interleaved.
|
|
156
|
+
*/
|
|
157
|
+
write(id, page) {
|
|
158
|
+
const dir = this.dir;
|
|
159
|
+
if (!dir)
|
|
160
|
+
return;
|
|
161
|
+
const tmp = join(dir, `${id}.${randomBytes(6).toString("hex")}.tmp`);
|
|
162
|
+
try {
|
|
163
|
+
mkdirSync(dir, { recursive: true });
|
|
164
|
+
writeFileSync(tmp, JSON.stringify(page));
|
|
165
|
+
renameSync(tmp, join(dir, `${id}${SUFFIX}`));
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
// The page is in memory and its link works; it just will not outlive this
|
|
169
|
+
// process. Worth a line in the log and nothing more: throwing here would
|
|
170
|
+
// fail a review that has already done all of its work.
|
|
171
|
+
console.warn(`page store: could not write ${id} (${reason(err)})`);
|
|
172
|
+
try {
|
|
173
|
+
unlinkSync(tmp);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Never created, or already renamed into place.
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
remove(id) {
|
|
181
|
+
this.pages.delete(id);
|
|
182
|
+
if (!this.dir)
|
|
183
|
+
return;
|
|
184
|
+
try {
|
|
185
|
+
unlinkSync(join(this.dir, `${id}${SUFFIX}`));
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Already gone: deleted underneath us, or it never reached the disk.
|
|
83
189
|
}
|
|
84
190
|
}
|
|
85
191
|
}
|
|
192
|
+
const reason = (err) => (err instanceof Error ? err.message : String(err));
|
|
86
193
|
//# sourceMappingURL=pages.js.map
|
package/dist/app/pages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../../src/app/pages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,mFAAmF;AACnF,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAChD,kFAAkF;AAClF,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAC9B,oFAAoF;AACpF,MAAM,MAAM,GAAG,OAAO,CAAC;AAgBvB,MAAM,OAAO,SAAS;IACH,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IACtC,MAAM,CAAS;IACf,KAAK,CAAS;IACd,QAAQ,CAAS;IACjB,GAAG,CAAe;IAClB,GAAG,CAAqB;IAEzC,YAAY,IAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,cAAc,CAAC;QAC1C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,iBAAiB,CAAC;QACnD,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QAChC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,iEAAiE;IACjE,MAAM,CAAC,KAAK,CAAC,KAAa,EAAE,IAAY,EAAE,MAAc;QACtD,OAAO,GAAG,KAAK,KAAK,IAAI,KAAK,MAAM,EAAE,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;IACzE,CAAC;IAED,qFAAqF;IACrF,GAAG,CAAC,KAAa,EAAE,IAAY,EAAE,MAAc,EAAE,IAAY;QAC3D,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,IAAI,GAAe,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACtC,CAAC;IAED;;;;;;;;;OASG;IACH,GAAG,CAAC,EAAU,EAAE,KAAyB;QACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3D,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAEO,IAAI,CAAC,EAAU;QACrB,OAAO,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjF,CAAC;IAEO,KAAK,CAAC,EAAU,EAAE,KAAa;QACrC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,yEAAyE;QACzE,+CAA+C;QAC/C,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,oDAAoD;IAC5C,KAAK;QACX,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACvC,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,IAAI,CAAC,QAAQ,GAAG,MAAM;gBAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM;YACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,sDAAsD;IAC9C,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpC,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,wEAAwE;YACxE,8CAA8C;YAC9C,OAAO,CAAC,IAAI,CAAC,eAAe,GAAG,mDAAmD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClG,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAgC,EAAE,CAAC;QAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,0EAA0E;YAC1E,sEAAsE;YACtE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,SAAS;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;YACxC,IAAI,IAAI;gBAAE,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,8EAA8E;QAC9E,2EAA2E;QAC3E,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACrD,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,MAAM;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1D,2EAA2E;QAC3E,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,kFAAkF;IAC1E,IAAI,CAAC,IAAY;QACvB,IAAI,CAAC;YACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;YAC/D,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAwB,CAAC;YACjE,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAAE,OAAO,IAAI,CAAC;YACxG,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,EAAU,EAAE,IAAgB;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,IAAI,CAAC,GAAG;YAAE,OAAO;QAEjB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACrE,IAAI,CAAC;YACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,0EAA0E;YAC1E,yEAAyE;YACzE,uDAAuD;YACvD,OAAO,CAAC,IAAI,CAAC,+BAA+B,EAAE,KAAK,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACnE,IAAI,CAAC;gBACH,UAAU,CAAC,GAAG,CAAC,CAAC;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,gDAAgD;YAClD,CAAC;QACH,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,EAAU;QACvB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,GAAG;YAAE,OAAO;QACtB,IAAI,CAAC;YACH,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,qEAAqE;QACvE,CAAC;IACH,CAAC;CACF;AAED,MAAM,MAAM,GAAG,CAAC,GAAY,EAAU,EAAE,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type ReviewJob } from "./events.js";
|
|
2
|
+
import type { reviewPullRequest, ReviewResult } from "./review.js";
|
|
3
|
+
/** Parent → child, once, immediately after the fork. */
|
|
4
|
+
export interface StartMessage {
|
|
5
|
+
t: "start";
|
|
6
|
+
job: ReviewJob;
|
|
7
|
+
/** Minted by the parent; the child never sees the App's private key. */
|
|
8
|
+
token: string;
|
|
9
|
+
api?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Parent → child: the URL a `publish` request ended up at, or null. */
|
|
12
|
+
export interface PublishedMessage {
|
|
13
|
+
t: "published";
|
|
14
|
+
seq: number;
|
|
15
|
+
url: string | null;
|
|
16
|
+
}
|
|
17
|
+
export type ToChild = StartMessage | PublishedMessage;
|
|
18
|
+
/** Child → parent: a line for the App's log, so a review is still traceable. */
|
|
19
|
+
export interface LogMessage {
|
|
20
|
+
t: "log";
|
|
21
|
+
msg: string;
|
|
22
|
+
}
|
|
23
|
+
/** Child → parent: store this page and tell me its URL. */
|
|
24
|
+
export interface PublishMessage {
|
|
25
|
+
t: "publish";
|
|
26
|
+
seq: number;
|
|
27
|
+
html: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DoneMessage {
|
|
30
|
+
t: "done";
|
|
31
|
+
result: ReviewResult;
|
|
32
|
+
}
|
|
33
|
+
export interface FailedMessage {
|
|
34
|
+
t: "failed";
|
|
35
|
+
message: string;
|
|
36
|
+
}
|
|
37
|
+
export type FromChild = LogMessage | PublishMessage | DoneMessage | FailedMessage;
|
|
38
|
+
export interface ChildReviewerOptions {
|
|
39
|
+
/** The module the child runs. Only tests pass this. */
|
|
40
|
+
entry?: string;
|
|
41
|
+
timeoutMs?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The module a review runs in.
|
|
45
|
+
*
|
|
46
|
+
* `dist/app/review-worker.js` in an installed or containerised App. Run from a
|
|
47
|
+
* checkout there is no `dist`, so fall back to the TypeScript source next door:
|
|
48
|
+
* `fork` inherits `process.execArgv`, so a parent started through tsx hands the
|
|
49
|
+
* child the same loader and a `.ts` entry runs. Resolved once, at import.
|
|
50
|
+
*
|
|
51
|
+
* Exported so a test can assert the path a deployed App will fork actually exists —
|
|
52
|
+
* getting this wrong is the one failure here that no unit test of the protocol
|
|
53
|
+
* would catch and every review would hit.
|
|
54
|
+
*/
|
|
55
|
+
export declare function reviewWorkerEntry(): string;
|
|
56
|
+
/**
|
|
57
|
+
* A reviewer with `reviewPullRequest`'s signature that runs it in a child process.
|
|
58
|
+
*
|
|
59
|
+
* Same shape on purpose: `server.ts` swaps one for the other in a single line, and
|
|
60
|
+
* every existing test that injects its own reviewer through `AppSeams.review` keeps
|
|
61
|
+
* running in-process, which is what a test wants.
|
|
62
|
+
*/
|
|
63
|
+
export declare function childReviewer(opts?: ChildReviewerOptions): typeof reviewPullRequest;
|
|
64
|
+
/** The App's default reviewer. */
|
|
65
|
+
export declare const reviewInChildProcess: typeof reviewPullRequest;
|
|
66
|
+
//# sourceMappingURL=review-process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-process.d.ts","sourceRoot":"","sources":["../../src/app/review-process.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAc,YAAY,EAAE,MAAM,aAAa,CAAC;AAa/E,wDAAwD;AACxD,MAAM,WAAW,YAAY;IAC3B,CAAC,EAAE,OAAO,CAAC;IACX,GAAG,EAAE,SAAS,CAAC;IACf,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,WAAW,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,MAAM,OAAO,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAEtD,gFAAgF;AAChF,MAAM,WAAW,UAAU;IACzB,CAAC,EAAE,KAAK,CAAC;IACT,GAAG,EAAE,MAAM,CAAC;CACb;AAED,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,SAAS,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,EAAE,QAAQ,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,cAAc,GAAG,WAAW,GAAG,aAAa,CAAC;AAElF,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAK1C;AAsCD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,OAAO,iBAAiB,CAIvF;AAoFD,kCAAkC;AAClC,eAAO,MAAM,oBAAoB,0BAAkB,CAAC"}
|