@diffohq/diffo 0.9.0 → 0.10.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/dist/cli.mjs +163 -39
- package/dist/client/assets/{abnfDiagram-O67JEVCF-Dh4is2Nj.js → abnfDiagram-O67JEVCF-DBlSJW53.js} +1 -1
- package/dist/client/assets/{arc-CMN0Gb1g.js → arc-BZZ_ET2N.js} +1 -1
- package/dist/client/assets/architecture-7GRP2DOG-Cds-sOVr.js +1 -0
- package/dist/client/assets/{architectureDiagram-NJMV4G6O-DxVGDSE1.js → architectureDiagram-NJMV4G6O-CmS7e2K4.js} +1 -1
- package/dist/client/assets/{blockDiagram-BEXU5L5S-T1F9hSwZ.js → blockDiagram-BEXU5L5S-Kj8H2ODF.js} +1 -1
- package/dist/client/assets/{c4Diagram-YGBWAQC7-DC6zwYuX.js → c4Diagram-YGBWAQC7-CixbFJxb.js} +1 -1
- package/dist/client/assets/channel-BI4dhOPo.js +1 -0
- package/dist/client/assets/{chunk-3FUC2YCW-D0PAoCRl.js → chunk-3FUC2YCW-CRdo0Gw5.js} +1 -1
- package/dist/client/assets/{chunk-5DYCD2WN-NqHzOtt0.js → chunk-5DYCD2WN-Bw9n6qPh.js} +1 -1
- package/dist/client/assets/{chunk-742MDFTN-B9Y5HHB7.js → chunk-742MDFTN-BsvOSyDz.js} +1 -1
- package/dist/client/assets/{chunk-7INBJB4K-CBOWlj91.js → chunk-7INBJB4K-ie7EoCoW.js} +1 -1
- package/dist/client/assets/{chunk-7M6MHVWA-D2dZ_rQI.js → chunk-7M6MHVWA-DJlbYDLH.js} +1 -1
- package/dist/client/assets/{chunk-7PRAP22T-7dilu8x-.js → chunk-7PRAP22T-Hs5y39V8.js} +1 -1
- package/dist/client/assets/{chunk-GTNCS2PH-at0ENjhB.js → chunk-GTNCS2PH-DNq6Ojc7.js} +1 -1
- package/dist/client/assets/{chunk-GWA4HPMP-C06n_37U.js → chunk-GWA4HPMP-vgn0kxbp.js} +1 -1
- package/dist/client/assets/{chunk-MBY4JIJT-DuowBunG.js → chunk-MBY4JIJT-C7fMuqSl.js} +1 -1
- package/dist/client/assets/{chunk-NETBCI7D-ClNA7QFH.js → chunk-NETBCI7D-ZJtjvGwt.js} +1 -1
- package/dist/client/assets/{chunk-O7XYJQB3-BjcjInBL.js → chunk-O7XYJQB3-DESjnZZT.js} +1 -1
- package/dist/client/assets/{chunk-UA2S7LBM-B-PAn1hd.js → chunk-UA2S7LBM-Cmf087s8.js} +1 -1
- package/dist/client/assets/{chunk-WEXAMYUT-Br84uK4B.js → chunk-WEXAMYUT-U1TW5ZAF.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-BbBZykgX.js → chunk-XXDRQBXY-D1B1IYm1.js} +1 -1
- package/dist/client/assets/{chunk-Z7XXMR3K-CQ-grwfe.js → chunk-Z7XXMR3K-D3BUH0qv.js} +1 -1
- package/dist/client/assets/{chunk-ZIGJFQKS-CLwfE-hs.js → chunk-ZIGJFQKS-Bx6yZhwg.js} +1 -1
- package/dist/client/assets/{classDiagram-v2-NBCMYWYE-DD-bcOHO.js → classDiagram-v2-NBCMYWYE-sjiBGJdD.js} +1 -1
- package/dist/client/assets/{core-BADyutHL.js → core-6IJUkucC.js} +1 -1
- package/dist/client/assets/{cose-bilkent-JH36ORCC-fuMg3J_l.js → cose-bilkent-JH36ORCC-xaMCCs4V.js} +1 -1
- package/dist/client/assets/{cynefin-OW5HDTMX-DwFXJ_x8.js → cynefin-OW5HDTMX-C5k9L5NF.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-VND7K2PF-cGL_k8gj.js → cynefinDiagram-VND7K2PF-BqiILl4G.js} +1 -1
- package/dist/client/assets/{dagre-6A5THRUB-BetgCBn9.js → dagre-6A5THRUB-BtgX5KvU.js} +1 -1
- package/dist/client/assets/{diagram-22UHCM2B-C7qT2Ix2.js → diagram-22UHCM2B-BcDIKY8C.js} +1 -1
- package/dist/client/assets/{diagram-3UASUU5V-DGUO9DWV.js → diagram-3UASUU5V-C-DrBJ6K.js} +1 -1
- package/dist/client/assets/{diagram-ATOU4E4O-f520gjx5.js → diagram-ATOU4E4O-B0W6FX6y.js} +1 -1
- package/dist/client/assets/{diagram-CDSNMT55-Dhrm4GzB.js → diagram-CDSNMT55-D1O9NYQ3.js} +1 -1
- package/dist/client/assets/{diagram-MLGK6HIB-Di0aRiuU.js → diagram-MLGK6HIB-WC8izmk2.js} +1 -1
- package/dist/client/assets/{diagram-MPIPVDR6-BHhVZUxI.js → diagram-MPIPVDR6-D5ko9V-k.js} +1 -1
- package/dist/client/assets/{dist-Cezvykkn.js → dist-BBmACywa.js} +1 -1
- package/dist/client/assets/{dist-CbdYocyN.js → dist-Bss8uvIz.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-ZINNZB2B-CQA0eib_.js → ebnfDiagram-ZINNZB2B-BFIYDkrv.js} +1 -1
- package/dist/client/assets/{elk-276RUBZZ-CZKKVSu0.js → elk-276RUBZZ-WsSc0CyB.js} +1 -1
- package/dist/client/assets/{engine-oniguruma-B68c_YAs.js → engine-oniguruma-DmL6leO7.js} +1 -1
- package/dist/client/assets/{erDiagram-OPXOYQCR-DcNnfq_S.js → erDiagram-OPXOYQCR-CjaKeGKh.js} +1 -1
- package/dist/client/assets/eventmodeling-NTZA5JFV-DE5ql6lo.js +1 -0
- package/dist/client/assets/flowDiagram-KWPJA3E3-CObKVtKJ.js +1 -0
- package/dist/client/assets/{ganttDiagram-FUAMR5RP-Dteh33ci.js → ganttDiagram-FUAMR5RP-22OFMoat.js} +1 -1
- package/dist/client/assets/{gitGraph-4MIJSDKK-DMTE1jTV.js → gitGraph-4MIJSDKK-BP0idayz.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-X574FWY7-EV_0IPok.js → gitGraphDiagram-X574FWY7-Cx5rPzjc.js} +1 -1
- package/dist/client/assets/{index-B__poZ2b.js → index-DXMhk4ok.js} +11 -11
- package/dist/client/assets/{info-A6RAGUB7-WkHzxEDS.js → info-A6RAGUB7-BvZZu8IY.js} +1 -1
- package/dist/client/assets/{infoDiagram-VRGFBTTK-BcH5U5G2.js → infoDiagram-VRGFBTTK-Bbk1aZco.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-OU5B5YK6-Z8vZnj1l.js → ishikawaDiagram-OU5B5YK6-DbyWrUBY.js} +1 -1
- package/dist/client/assets/{journeyDiagram-ZHPQQLJL-Cn-m-UYg.js → journeyDiagram-ZHPQQLJL-Ka7zlagO.js} +1 -1
- package/dist/client/assets/{kanban-definition-PNTS6WVX-DLOrUOML.js → kanban-definition-PNTS6WVX-Q_NAEdRt.js} +1 -1
- package/dist/client/assets/{line-DhKwT3E3.js → line-SDAA9fEr.js} +1 -1
- package/dist/client/assets/{linear-DvwuMCFu.js → linear-BPf2OGUv.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-CwcOI9nG.js → mermaid-parser.core-BjLzFSoy.js} +3 -3
- package/dist/client/assets/{mermaid.core-BabKb2iQ.js → mermaid.core-vvUsQbpX.js} +4 -4
- package/dist/client/assets/{mindmap-definition-NLK3R4M7-D7wpNtz6.js → mindmap-definition-NLK3R4M7-DuVzu_4M.js} +1 -1
- package/dist/client/assets/{packet-AYTQ26CC-BJablqte.js → packet-AYTQ26CC-A66za-wc.js} +1 -1
- package/dist/client/assets/{pegDiagram-GJSIUBJH-EAlXAkUz.js → pegDiagram-GJSIUBJH-ClH92MDY.js} +1 -1
- package/dist/client/assets/{pie-WAS4IAKB-BAAbtIiO.js → pie-WAS4IAKB-sJ2H2jF4.js} +1 -1
- package/dist/client/assets/{pieDiagram-5QR66LMP-b1pYP4ES.js → pieDiagram-5QR66LMP-BmlcB4fD.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-O4NWA36T-B_LJNNdE.js → quadrantDiagram-O4NWA36T-BUdNHhtc.js} +1 -1
- package/dist/client/assets/{radar-RG4KPBEZ-BKldNRRC.js → radar-RG4KPBEZ-B3MRkz7l.js} +1 -1
- package/dist/client/assets/{railroad-74A4TZTK-LvULpHpQ.js → railroad-74A4TZTK-CPQddZw9.js} +1 -1
- package/dist/client/assets/railroad-abnf-HS5TGJTU-CijG3JBn.js +1 -0
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-CNgXgJgc.js +1 -0
- package/dist/client/assets/railroad-peg-WCYAUIDC-BkwGmMtj.js +1 -0
- package/dist/client/assets/{railroadDiagram-XR7U4H2S-kp6LeaeM.js → railroadDiagram-XR7U4H2S-B2wicmNk.js} +1 -1
- package/dist/client/assets/{requirementDiagram-PLB6GJNP-Br-OgP6b.js → requirementDiagram-PLB6GJNP-4fGmNry2.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-IPEJSGJF-DSIbAxcK.js → sankeyDiagram-IPEJSGJF-ByuhL9nO.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-PO4LG4MO-uVv8u8VI.js → sequenceDiagram-PO4LG4MO-CHeFXNrS.js} +1 -1
- package/dist/client/assets/{src-Bmfs-TFn.js → src-DuEx6MBc.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-GCMORJYK-Crql1z4d.js → stateDiagram-v2-GCMORJYK-Dsfly8rU.js} +1 -1
- package/dist/client/assets/{swimlanes-2SLR337P-CnpU7AM-.js → swimlanes-2SLR337P-DDb1xXAV.js} +1 -1
- package/dist/client/assets/swimlanesDiagram-TC7HE7FX-DXpYpXP-.js +8 -0
- package/dist/client/assets/{timeline-definition-EJHVYXUP-CqzGi-Sd.js → timeline-definition-EJHVYXUP-iC4lxMiK.js} +1 -1
- package/dist/client/assets/{treeView-Q6P3EWNA-N_EzEUvg.js → treeView-Q6P3EWNA-zXF3RWVa.js} +1 -1
- package/dist/client/assets/{treemap-WGGIJYW6-BYeVgP-v.js → treemap-WGGIJYW6-BvA719M0.js} +1 -1
- package/dist/client/assets/{usecaseDiagram-POWQR4AR-Dyeq2Zcy.js → usecaseDiagram-POWQR4AR-CXzh5cdW.js} +1 -1
- package/dist/client/assets/{vennDiagram-UO4OBE2U-CuCjJaOo.js → vennDiagram-UO4OBE2U-BuMQeNKp.js} +1 -1
- package/dist/client/assets/{wardley-WFR3VGLG-AV0-dO_Z.js → wardley-WFR3VGLG-m1zZI6-6.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-VNRHLVJA-f0dVm2LA.js → wardleyDiagram-VNRHLVJA-DiB2QtGU.js} +1 -1
- package/dist/client/assets/{xychartDiagram-PMCCYNJV-BNjSW7P1.js → xychartDiagram-PMCCYNJV-CKRPoaV8.js} +1 -1
- package/dist/client/index.html +1 -1
- package/package.json +8 -8
- package/plugin.json +1 -1
- package/dist/client/assets/architecture-7GRP2DOG-DaIFNiR2.js +0 -1
- package/dist/client/assets/channel-Ci5ZF5xD.js +0 -1
- package/dist/client/assets/eventmodeling-NTZA5JFV-C61pKQjq.js +0 -1
- package/dist/client/assets/flowDiagram-KWPJA3E3-BolWtw3C.js +0 -1
- package/dist/client/assets/railroad-abnf-HS5TGJTU-CORGw4Le.js +0 -1
- package/dist/client/assets/railroad-ebnf-LZEXJU2U-BXCX4hkl.js +0 -1
- package/dist/client/assets/railroad-peg-WCYAUIDC-C6BQUpjn.js +0 -1
- package/dist/client/assets/swimlanesDiagram-TC7HE7FX-Crca2YKi.js +0 -8
package/dist/cli.mjs
CHANGED
|
@@ -1959,7 +1959,6 @@ query($owner: String!, $name: String!, $number: Int!) {
|
|
|
1959
1959
|
messageHeadline
|
|
1960
1960
|
committedDate
|
|
1961
1961
|
author { name user { login avatarUrl } }
|
|
1962
|
-
statusCheckRollup { state }
|
|
1963
1962
|
}
|
|
1964
1963
|
}
|
|
1965
1964
|
}
|
|
@@ -1974,6 +1973,24 @@ query($owner: String!, $name: String!, $number: Int!) {
|
|
|
1974
1973
|
}
|
|
1975
1974
|
}
|
|
1976
1975
|
}`;
|
|
1976
|
+
/**
|
|
1977
|
+
* CI status, asked for on its own. Reading it needs the checks and statuses
|
|
1978
|
+
* scopes, which a fine-grained or SAML-limited token may lack while reading
|
|
1979
|
+
* everything else about the pull request; GraphQL answers that with a
|
|
1980
|
+
* FORBIDDEN error on this one field, and `gh` turns any error into a failed
|
|
1981
|
+
* call. Kept out of the PR query so a token that cannot see CI still opens
|
|
1982
|
+
* the review.
|
|
1983
|
+
*/
|
|
1984
|
+
const CHECKS_QUERY = `
|
|
1985
|
+
query($owner: String!, $name: String!, $number: Int!) {
|
|
1986
|
+
repository(owner: $owner, name: $name) {
|
|
1987
|
+
pullRequest(number: $number) {
|
|
1988
|
+
commits(last: 1) {
|
|
1989
|
+
nodes { commit { statusCheckRollup { state } } }
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
}`;
|
|
1977
1994
|
/** The pages after the first of each connection the PR query opens. */
|
|
1978
1995
|
const REVIEWS_QUERY = `
|
|
1979
1996
|
query($owner: String!, $name: String!, $number: Int!, $cursor: String) {
|
|
@@ -2220,18 +2237,32 @@ var GhClient = class {
|
|
|
2220
2237
|
reviewNodes
|
|
2221
2238
|
};
|
|
2222
2239
|
}
|
|
2240
|
+
/** CI on the head commit, best effort: a token without the checks scope
|
|
2241
|
+
* gets `unknown` and the review opens anyway. Never throws. */
|
|
2242
|
+
async checks(ref) {
|
|
2243
|
+
try {
|
|
2244
|
+
const rollup = ((await this.graphql(ref.host, CHECKS_QUERY, {
|
|
2245
|
+
owner: ref.owner,
|
|
2246
|
+
name: ref.repo,
|
|
2247
|
+
number: ref.number
|
|
2248
|
+
})).repository?.pullRequest?.commits.nodes.at(-1)?.commit)?.statusCheckRollup;
|
|
2249
|
+
return checkState(rollup?.state);
|
|
2250
|
+
} catch {
|
|
2251
|
+
return "unknown";
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2223
2254
|
async getPr(ref) {
|
|
2224
|
-
const { viewer, pr, reviewNodes } = await this.head(ref);
|
|
2225
|
-
return prInfo(ref, viewer, pr, reviewNodes);
|
|
2255
|
+
const [{ viewer, pr, reviewNodes }, checks] = await Promise.all([this.head(ref), this.checks(ref)]);
|
|
2256
|
+
return prInfo(ref, viewer, pr, reviewNodes, checks);
|
|
2226
2257
|
}
|
|
2227
2258
|
async listThreads(ref) {
|
|
2228
2259
|
const { pr, reviewNodes } = await this.head(ref);
|
|
2229
2260
|
return this.conversation(ref, pr, reviewNodes);
|
|
2230
2261
|
}
|
|
2231
2262
|
async fetchPr(ref) {
|
|
2232
|
-
const { viewer, pr, reviewNodes } = await this.head(ref);
|
|
2263
|
+
const [{ viewer, pr, reviewNodes }, checks] = await Promise.all([this.head(ref), this.checks(ref)]);
|
|
2233
2264
|
return {
|
|
2234
|
-
pr: prInfo(ref, viewer, pr, reviewNodes),
|
|
2265
|
+
pr: prInfo(ref, viewer, pr, reviewNodes, checks),
|
|
2235
2266
|
threads: await this.conversation(ref, pr, reviewNodes)
|
|
2236
2267
|
};
|
|
2237
2268
|
}
|
|
@@ -2345,9 +2376,8 @@ var GhClient = class {
|
|
|
2345
2376
|
}
|
|
2346
2377
|
};
|
|
2347
2378
|
/** The PR query's answer as a PrInfo, with every review page in hand. */
|
|
2348
|
-
function prInfo(ref, viewer, pr, reviewNodes) {
|
|
2349
|
-
const
|
|
2350
|
-
const commits = commitNodes.map((n) => {
|
|
2379
|
+
function prInfo(ref, viewer, pr, reviewNodes, checks) {
|
|
2380
|
+
const commits = (pr.commits.nodes ?? []).map((n) => {
|
|
2351
2381
|
const c = n.commit;
|
|
2352
2382
|
const author = c.author;
|
|
2353
2383
|
return {
|
|
@@ -2357,7 +2387,6 @@ function prInfo(ref, viewer, pr, reviewNodes) {
|
|
|
2357
2387
|
at: String(c.committedDate ?? "")
|
|
2358
2388
|
};
|
|
2359
2389
|
});
|
|
2360
|
-
const rollup = (commitNodes.at(-1)?.commit)?.statusCheckRollup;
|
|
2361
2390
|
const reviews = reviewNodes.map((r) => ({
|
|
2362
2391
|
id: String(r.id),
|
|
2363
2392
|
author: user(r.author),
|
|
@@ -2390,7 +2419,7 @@ function prInfo(ref, viewer, pr, reviewNodes) {
|
|
|
2390
2419
|
sha: String(pr.headRefOid)
|
|
2391
2420
|
},
|
|
2392
2421
|
commits,
|
|
2393
|
-
checks: { state:
|
|
2422
|
+
checks: { state: checks },
|
|
2394
2423
|
reviews: reviews.filter((r) => r.state !== "PENDING"),
|
|
2395
2424
|
approvals: [...latest.values()].filter((s) => s === "APPROVED").length,
|
|
2396
2425
|
changesRequested: [...latest.values()].filter((s) => s === "CHANGES_REQUESTED").length,
|
|
@@ -2608,6 +2637,25 @@ function branchExists(root, branch) {
|
|
|
2608
2637
|
return err.status !== 1;
|
|
2609
2638
|
}
|
|
2610
2639
|
}
|
|
2640
|
+
/** {@link branchExists}, off the event loop, for the daemon's background sweep
|
|
2641
|
+
* over every repo it has ever reviewed. Same lean: true unless git said no. */
|
|
2642
|
+
async function branchExistsAsync(root, branch) {
|
|
2643
|
+
try {
|
|
2644
|
+
await execFileAsync("git", [
|
|
2645
|
+
"show-ref",
|
|
2646
|
+
"--verify",
|
|
2647
|
+
"--quiet",
|
|
2648
|
+
`refs/heads/${branch}`
|
|
2649
|
+
], {
|
|
2650
|
+
cwd: root,
|
|
2651
|
+
encoding: "utf-8",
|
|
2652
|
+
maxBuffer: MAX_BUFFER
|
|
2653
|
+
});
|
|
2654
|
+
return true;
|
|
2655
|
+
} catch (err) {
|
|
2656
|
+
return err.code !== 1;
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2611
2659
|
/**
|
|
2612
2660
|
* The name of the linked worktree we're in, or null in the main one. A linked
|
|
2613
2661
|
* worktree's git dir is `<main>/.git/worktrees/<name>`, while the main worktree's
|
|
@@ -3242,7 +3290,17 @@ const SCHEMA_VERSION = 3;
|
|
|
3242
3290
|
const REVIEWS_RESHAPED_AT = 2;
|
|
3243
3291
|
var DiffoDb = class {
|
|
3244
3292
|
db;
|
|
3293
|
+
closed = false;
|
|
3245
3294
|
path;
|
|
3295
|
+
/**
|
|
3296
|
+
* Opening is cheap on purpose: the schema, and one SQL statement for the TTL.
|
|
3297
|
+
* Nothing here touches the filesystem beyond the database file, and nothing
|
|
3298
|
+
* asks git. It used to: every open checked every review's branch with a
|
|
3299
|
+
* `git show-ref`, across every repo this machine ever reviewed — 73 git
|
|
3300
|
+
* processes, 1.5s, on a database a few weeks old, and an open constructs
|
|
3301
|
+
* this five times. The checks live in {@link pruneRepo} (one repo, before its
|
|
3302
|
+
* review loads) and {@link maintain} (everything, in the daemon's background).
|
|
3303
|
+
*/
|
|
3246
3304
|
constructor(path = defaultDbPath()) {
|
|
3247
3305
|
this.path = path;
|
|
3248
3306
|
mkdirSync(dirname(path), {
|
|
@@ -3254,7 +3312,7 @@ var DiffoDb = class {
|
|
|
3254
3312
|
chmodSync(path, 384);
|
|
3255
3313
|
this.db.exec("PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 3000; PRAGMA journal_size_limit = 4194304;");
|
|
3256
3314
|
this.ensureSchema();
|
|
3257
|
-
this.
|
|
3315
|
+
this.pruneExpiredReviews();
|
|
3258
3316
|
}
|
|
3259
3317
|
/**
|
|
3260
3318
|
* Creates what is missing and retires what is unknown. Runs at open, and
|
|
@@ -3323,38 +3381,69 @@ var DiffoDb = class {
|
|
|
3323
3381
|
}
|
|
3324
3382
|
}
|
|
3325
3383
|
/**
|
|
3326
|
-
*
|
|
3327
|
-
*
|
|
3328
|
-
*
|
|
3329
|
-
|
|
3384
|
+
* Reviews untouched for `REVIEW_TTL_DAYS` go on every open: one statement,
|
|
3385
|
+
* no git, and the backstop for everything the two targeted sweeps below leave
|
|
3386
|
+
* for later.
|
|
3387
|
+
*/
|
|
3388
|
+
pruneExpiredReviews() {
|
|
3389
|
+
const cutoff = (/* @__PURE__ */ new Date(Date.now() - 5184e6)).toISOString();
|
|
3390
|
+
const stale = this.db.prepare("DELETE FROM reviews WHERE updated_at < ?").run(cutoff);
|
|
3391
|
+
if (Number(stale.changes) > 0) this.checkpoint();
|
|
3392
|
+
}
|
|
3393
|
+
/**
|
|
3394
|
+
* Retire this one repo's reviews for branches it no longer has. The server
|
|
3395
|
+
* runs it before loading the review it is about to serve, so a branch
|
|
3396
|
+
* deleted since the last server start is forgotten before a namesake could
|
|
3397
|
+
* inherit its threads. Only this repo's branches are asked about, so the
|
|
3398
|
+
* cost is a handful of `git show-ref`s, once per server start, never per CLI
|
|
3399
|
+
* call. Branch existence is asked of git rather than assumed — when git can't
|
|
3400
|
+
* answer the row is kept, because guessing here deletes threads.
|
|
3401
|
+
*/
|
|
3402
|
+
pruneRepo(repoPath) {
|
|
3403
|
+
const rows = this.db.prepare("SELECT DISTINCT branch FROM reviews WHERE repo_path = ?").all(repoPath);
|
|
3404
|
+
let dropped = 0;
|
|
3405
|
+
for (const { branch } of rows) {
|
|
3406
|
+
if (branch === "" || branchExists(repoPath, branch)) continue;
|
|
3407
|
+
dropped += this.dropReviews(repoPath, branch);
|
|
3408
|
+
}
|
|
3409
|
+
return dropped;
|
|
3410
|
+
}
|
|
3411
|
+
/**
|
|
3412
|
+
* The full sweep, for a long-lived process with time on its hands: rows about
|
|
3413
|
+
* repos that are gone, and reviews for branches that are. The daemon runs it
|
|
3414
|
+
* once, a few seconds after it is serving; `diffo clean` runs it on demand.
|
|
3415
|
+
* Git is asked asynchronously and one repo at a time, so a sweep over every
|
|
3416
|
+
* repo this machine ever reviewed never blocks a request. A `close()` midway
|
|
3417
|
+
* ends it quietly between steps.
|
|
3418
|
+
*
|
|
3419
|
+
* Rows about a worktree that is gone can never be reached again — the path
|
|
3420
|
+
* *is* the key. An unmounted volume looks the same as a deleted worktree, so
|
|
3421
|
+
* its rows go too; the `servers` row needs its process dead as well, or a live
|
|
3422
|
+
* server's claim could be dropped over a moment of unreachability.
|
|
3330
3423
|
*/
|
|
3331
|
-
|
|
3332
|
-
|
|
3424
|
+
async maintain() {
|
|
3425
|
+
const report = { reviews: 0 };
|
|
3426
|
+
if (this.closed) return report;
|
|
3427
|
+
for (const { repo_path } of this.distinctRepoPaths("reviews")) if (!existsSync(repo_path)) report.reviews += this.dropReviews(repo_path);
|
|
3333
3428
|
for (const { repo_path } of this.distinctRepoPaths("repo_ports")) if (!existsSync(repo_path)) this.db.prepare("DELETE FROM repo_ports WHERE repo_path = ?").run(repo_path);
|
|
3334
3429
|
const worktrees = this.db.prepare("SELECT worktree_path, repo_path FROM worktrees").all();
|
|
3335
3430
|
for (const { worktree_path, repo_path } of worktrees) if (!existsSync(repo_path)) this.db.prepare("DELETE FROM worktrees WHERE worktree_path = ?").run(worktree_path);
|
|
3336
3431
|
const servers = this.db.prepare("SELECT repo_path, pid FROM servers").all();
|
|
3337
3432
|
for (const { repo_path, pid } of servers) if (!existsSync(repo_path) && !pidAlive$1(pid)) this.db.prepare("DELETE FROM servers WHERE repo_path = ?").run(repo_path);
|
|
3338
|
-
this.pruneStaleReviews();
|
|
3339
|
-
}
|
|
3340
|
-
/**
|
|
3341
|
-
* Reviews the reviewer can never return to: a branch that no longer exists, or
|
|
3342
|
-
* one untouched for `REVIEW_TTL_DAYS`. Branch existence is asked of git rather
|
|
3343
|
-
* than assumed — if git can't answer the row is kept, because guessing here
|
|
3344
|
-
* deletes threads.
|
|
3345
|
-
*/
|
|
3346
|
-
pruneStaleReviews() {
|
|
3347
|
-
const cutoff = (/* @__PURE__ */ new Date(Date.now() - 5184e6)).toISOString();
|
|
3348
|
-
const stale = this.db.prepare("DELETE FROM reviews WHERE updated_at < ?").run(cutoff);
|
|
3349
|
-
if (Number(stale.changes) > 0) {
|
|
3350
|
-
this.checkpoint();
|
|
3351
|
-
return;
|
|
3352
|
-
}
|
|
3353
3433
|
const rows = this.db.prepare("SELECT DISTINCT repo_path, branch FROM reviews").all();
|
|
3354
3434
|
for (const { repo_path, branch } of rows) {
|
|
3355
|
-
if (branch === ""
|
|
3356
|
-
|
|
3435
|
+
if (branch === "") continue;
|
|
3436
|
+
const exists = await branchExistsAsync(repo_path, branch);
|
|
3437
|
+
if (this.closed) return report;
|
|
3438
|
+
if (!exists) report.reviews += this.dropReviews(repo_path, branch);
|
|
3357
3439
|
}
|
|
3440
|
+
if (report.reviews > 0) this.checkpoint();
|
|
3441
|
+
return report;
|
|
3442
|
+
}
|
|
3443
|
+
/** Every review of a repo, or only one branch's. Returns the rows dropped. */
|
|
3444
|
+
dropReviews(repoPath, branch) {
|
|
3445
|
+
const result = branch === void 0 ? this.db.prepare("DELETE FROM reviews WHERE repo_path = ?").run(repoPath) : this.db.prepare("DELETE FROM reviews WHERE repo_path = ? AND branch = ?").run(repoPath, branch);
|
|
3446
|
+
return Number(result.changes);
|
|
3358
3447
|
}
|
|
3359
3448
|
/** Fold the write-ahead log back into the database file and truncate it. WAL
|
|
3360
3449
|
* only shrinks at a checkpoint, which a long-lived server never reaches. */
|
|
@@ -3452,6 +3541,7 @@ var DiffoDb = class {
|
|
|
3452
3541
|
else this.db.prepare("DELETE FROM servers WHERE repo_path = ? AND port = ?").run(repoPath, port);
|
|
3453
3542
|
}
|
|
3454
3543
|
close() {
|
|
3544
|
+
this.closed = true;
|
|
3455
3545
|
this.db.close();
|
|
3456
3546
|
}
|
|
3457
3547
|
};
|
|
@@ -3517,6 +3607,9 @@ var DeliveryQueue = class DeliveryQueue {
|
|
|
3517
3607
|
stalled = false;
|
|
3518
3608
|
stallTimer = null;
|
|
3519
3609
|
betweenPolls = false;
|
|
3610
|
+
/** Why the agent is parked between polls: a reply just landed, or its poll
|
|
3611
|
+
* ended on its own and the re-poll is expected. */
|
|
3612
|
+
parked = "replied";
|
|
3520
3613
|
graceTimer = null;
|
|
3521
3614
|
lastDetach = null;
|
|
3522
3615
|
since = Date.now();
|
|
@@ -3664,7 +3757,7 @@ var DeliveryQueue = class DeliveryQueue {
|
|
|
3664
3757
|
if (this.awaitingReply) return this.stalled ? "stalled" : "delivered";
|
|
3665
3758
|
if (this.outlining() !== null) return "delivered";
|
|
3666
3759
|
if (this.waiter) return "polling";
|
|
3667
|
-
if (this.betweenPolls) return
|
|
3760
|
+
if (this.betweenPolls) return this.parked;
|
|
3668
3761
|
return this.lastDetach ?? "no-agent";
|
|
3669
3762
|
}
|
|
3670
3763
|
hasListener() {
|
|
@@ -3717,9 +3810,10 @@ var DeliveryQueue = class DeliveryQueue {
|
|
|
3717
3810
|
if (this.stallTimer !== null) clearTimeout(this.stallTimer);
|
|
3718
3811
|
this.stallTimer = null;
|
|
3719
3812
|
}
|
|
3720
|
-
armGrace() {
|
|
3813
|
+
armGrace(why = "replied") {
|
|
3721
3814
|
this.clearGrace();
|
|
3722
3815
|
this.betweenPolls = true;
|
|
3816
|
+
this.parked = why;
|
|
3723
3817
|
const drop = () => {
|
|
3724
3818
|
this.graceTimer = null;
|
|
3725
3819
|
if (!this.betweenPolls) return;
|
|
@@ -3754,6 +3848,24 @@ var DeliveryQueue = class DeliveryQueue {
|
|
|
3754
3848
|
this.graceTimer = null;
|
|
3755
3849
|
this.betweenPolls = false;
|
|
3756
3850
|
}
|
|
3851
|
+
/**
|
|
3852
|
+
* A poll ended without `diffo end`: the poll window closed, or the process
|
|
3853
|
+
* died under its harness. Neither means the agent left — the CLI tells the
|
|
3854
|
+
* session to re-run the poll, and it usually does within moments — so the
|
|
3855
|
+
* reviewer keeps an agent instead of being asked to invite one again. The
|
|
3856
|
+
* agent parks in the same grace a reply uses: a named session is watched
|
|
3857
|
+
* until its process is gone (or the cap), an unnamed one gets the fixed
|
|
3858
|
+
* window. Only a session that is already dead reads as gone at once. When
|
|
3859
|
+
* the grace runs out without a re-poll, the reason it fell to waiting is
|
|
3860
|
+
* still the dead poll.
|
|
3861
|
+
*/
|
|
3862
|
+
pollDropped() {
|
|
3863
|
+
this.lastDetach = "disconnected";
|
|
3864
|
+
const pid = this.owner;
|
|
3865
|
+
const isAlive = this.liveness.isAlive ?? pidAlive;
|
|
3866
|
+
if (!(pid !== null && !isAlive(pid)) && !this.awaitingReply && this.outlining() === null) this.armGrace("repolling");
|
|
3867
|
+
this.notify();
|
|
3868
|
+
}
|
|
3757
3869
|
enqueueThreads(threadIds) {
|
|
3758
3870
|
const bucket = this.bucket();
|
|
3759
3871
|
for (const id of threadIds) bucket.threads.add(id);
|
|
@@ -3841,8 +3953,7 @@ var DeliveryQueue = class DeliveryQueue {
|
|
|
3841
3953
|
onAbort?.(() => {
|
|
3842
3954
|
if (this.waiter === waiter) {
|
|
3843
3955
|
this.releaseWaiter(null);
|
|
3844
|
-
this.
|
|
3845
|
-
this.notify();
|
|
3956
|
+
this.pollDropped();
|
|
3846
3957
|
}
|
|
3847
3958
|
});
|
|
3848
3959
|
});
|
|
@@ -6489,6 +6600,9 @@ const POLL_HEARTBEAT_MS = 15e3;
|
|
|
6489
6600
|
* aborts, and a listening poll blocks the daemon's idle reap.
|
|
6490
6601
|
*/
|
|
6491
6602
|
const POLL_MAX_MS = 18e5;
|
|
6603
|
+
/** How long after listening the daemon starts the machine-wide database sweep:
|
|
6604
|
+
* past the first page load, which is what the sweep must never delay. */
|
|
6605
|
+
const MAINTENANCE_DELAY_MS = 5e3;
|
|
6492
6606
|
function createApp(ctx, store, review, queue) {
|
|
6493
6607
|
const app = new Hono();
|
|
6494
6608
|
if (review && queue) queue.onBatchClosed((closed) => {
|
|
@@ -7271,11 +7385,13 @@ function startServer(options) {
|
|
|
7271
7385
|
throw new RepoAlreadyServedError(holder);
|
|
7272
7386
|
}
|
|
7273
7387
|
db.setPreferredPort(resolve(options.root), options.port);
|
|
7388
|
+
const log = (message) => console.log(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false })}] ${message}`);
|
|
7389
|
+
const retired = db.pruneRepo(resolve(options.root));
|
|
7390
|
+
if (retired > 0) log(`retired ${retired} ${retired === 1 ? "review" : "reviews"} for branches this repo no longer has`);
|
|
7274
7391
|
const review = new ReviewStore(options.root, db, options.spec);
|
|
7275
7392
|
const queue = new DeliveryQueue();
|
|
7276
7393
|
queue.rescope(store.get().repo.branch);
|
|
7277
7394
|
rehydrateQueue(review, queue);
|
|
7278
|
-
const log = (message) => console.log(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false })}] ${message}`);
|
|
7279
7395
|
const telemetry = options.telemetry ?? new Telemetry({
|
|
7280
7396
|
store: {
|
|
7281
7397
|
get: (key) => db.getUiSetting(key),
|
|
@@ -7358,6 +7474,12 @@ function startServer(options) {
|
|
|
7358
7474
|
port: options.port,
|
|
7359
7475
|
hostname: "127.0.0.1"
|
|
7360
7476
|
});
|
|
7477
|
+
const maintenance = setTimeout(() => {
|
|
7478
|
+
db.maintain().then((report) => {
|
|
7479
|
+
if (report.reviews > 0) log(`retired ${report.reviews} ${report.reviews === 1 ? "review" : "reviews"} whose repo or branch is gone`);
|
|
7480
|
+
}).catch((err) => log(`database sweep failed: ${err.message}`));
|
|
7481
|
+
}, MAINTENANCE_DELAY_MS);
|
|
7482
|
+
maintenance.unref();
|
|
7361
7483
|
const deregister = () => {
|
|
7362
7484
|
try {
|
|
7363
7485
|
db.removeServer(review.repoPath, options.port);
|
|
@@ -7381,6 +7503,7 @@ function startServer(options) {
|
|
|
7381
7503
|
queue,
|
|
7382
7504
|
telemetry,
|
|
7383
7505
|
stopWatching: async () => {
|
|
7506
|
+
clearTimeout(maintenance);
|
|
7384
7507
|
idle?.stop();
|
|
7385
7508
|
puller?.stop();
|
|
7386
7509
|
unsubscribeReconcile();
|
|
@@ -7986,6 +8109,7 @@ if (command.kind === "setup") {
|
|
|
7986
8109
|
if (command.kind === "clean") {
|
|
7987
8110
|
const db = new DiffoDb();
|
|
7988
8111
|
try {
|
|
8112
|
+
await db.maintain();
|
|
7989
8113
|
const entries = sweepWorktrees(db, void 0, {
|
|
7990
8114
|
force: command.force,
|
|
7991
8115
|
all: command.all
|
package/dist/client/assets/{abnfDiagram-O67JEVCF-Dh4is2Nj.js → abnfDiagram-O67JEVCF-DBlSJW53.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e,t}from"./mermaid-parser.core-
|
|
1
|
+
import{m as e,t}from"./mermaid-parser.core-BjLzFSoy.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-DuEx6MBc.js";import"./chunk-O7XYJQB3-DESjnZZT.js";import{n as i,r as a,t as o}from"./chunk-GTNCS2PH-DNq6Ojc7.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";import"./mermaid.core-vvUsQbpX.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-
|
|
1
|
+
import{n as e,t}from"./path-fybaL0A-.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-BBmACywa.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i){if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-FOHPRMQF-DzxwRta7.js";import{M as e}from"./mermaid-parser.core-BjLzFSoy.js";export{e as createArchitectureServices};
|