@graphrefly/stack 0.1.1 → 0.1.2
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 +24 -11
- package/dist/assets/contracts/repository/v1/review-bundle.schema.json +49 -0
- package/dist/assets/contracts/repository/v1/review-decision-request.schema.json +15 -0
- package/dist/assets/contracts/repository/v1/review-decision.schema.json +52 -0
- package/dist/assets/contracts/repository/v1/review.schema.json +2 -1
- package/dist/grfs.js +320 -26
- package/dist/review/assets/{abnfDiagram-VRR7QNED-5JVeQ-ZW.js → abnfDiagram-VRR7QNED-DxGYeLns.js} +1 -1
- package/dist/review/assets/{architectureDiagram-ZJ3FMSHR-BnEvDO7X.js → architectureDiagram-ZJ3FMSHR-CoaG1PSB.js} +1 -1
- package/dist/review/assets/{chunk-EX3LRPZG-BrpCa9zr.js → chunk-EX3LRPZG-C1ZhSVKp.js} +1 -1
- package/dist/review/assets/{chunk-MOJQB5TN-BshP08Ez.js → chunk-MOJQB5TN-Ct40HB9X.js} +1 -1
- package/dist/review/assets/{chunk-PUDLZKDR-HkwEqIVB.js → chunk-PUDLZKDR-C2Is490W.js} +1 -1
- package/dist/review/assets/{chunk-V7JOEXUC-CswDacm3.js → chunk-V7JOEXUC-Z3n_IPe-.js} +1 -1
- package/dist/review/assets/{classDiagram-OUVF2IWQ-U8e_ZvC4.js → classDiagram-OUVF2IWQ-BVlewphI.js} +1 -1
- package/dist/review/assets/{classDiagram-v2-EOCWNBFH-U8e_ZvC4.js → classDiagram-v2-EOCWNBFH-BVlewphI.js} +1 -1
- package/dist/review/assets/{cynefinDiagram-TSTJHNR4-CfYu2mB5.js → cynefinDiagram-TSTJHNR4-Cjm1OFIk.js} +1 -1
- package/dist/review/assets/{dagre-VKFMJZFB-BvyihZfA.js → dagre-VKFMJZFB-EvQo1qj2.js} +1 -1
- package/dist/review/assets/{diagram-G47NLZAW-qFNMv5An.js → diagram-G47NLZAW-k0H6Hccx.js} +1 -1
- package/dist/review/assets/{diagram-NH7WQ7WH-CsvacND9.js → diagram-NH7WQ7WH-VUneNb0M.js} +1 -1
- package/dist/review/assets/{diagram-OA4YK3LP-qFtWcyNi.js → diagram-OA4YK3LP-tUGLM6tT.js} +1 -1
- package/dist/review/assets/{diagram-WEI45ONY-C5nYGL8t.js → diagram-WEI45ONY-aN8naqL-.js} +1 -1
- package/dist/review/assets/{ebnfDiagram-CCIWWBDH-D_0nhpry.js → ebnfDiagram-CCIWWBDH-PjAeMUc1.js} +1 -1
- package/dist/review/assets/{erDiagram-Q63AITRT-CIS7hpdK.js → erDiagram-Q63AITRT-CkxN3vs2.js} +1 -1
- package/dist/review/assets/{flowDiagram-23GEKE2U-Df5sjnlg.js → flowDiagram-23GEKE2U-ChS1-2Qo.js} +1 -1
- package/dist/review/assets/index-CUaeHlgA.js +64 -0
- package/dist/review/assets/index-Cfj-JRmr.css +1 -0
- package/dist/review/assets/{infoDiagram-FWYZ7A6U-CT7TZgi6.js → infoDiagram-FWYZ7A6U-CcLr4Fgp.js} +1 -1
- package/dist/review/assets/{ishikawaDiagram-FXEZZL3T-BUpotH7r.js → ishikawaDiagram-FXEZZL3T-p7DbRTIs.js} +1 -1
- package/dist/review/assets/{kanban-definition-HUTT4EX6-e-emfNuh.js → kanban-definition-HUTT4EX6-BgXdqTkK.js} +1 -1
- package/dist/review/assets/{mindmap-definition-LN4V7U3C-ClSEPrvR.js → mindmap-definition-LN4V7U3C-zdfirl2I.js} +1 -1
- package/dist/review/assets/{pegDiagram-2B236MQR-Ck_v89jl.js → pegDiagram-2B236MQR-ScPkpaWR.js} +1 -1
- package/dist/review/assets/{pieDiagram-ENE6RG2P-C63dxyZj.js → pieDiagram-ENE6RG2P-BAQHg9fC.js} +1 -1
- package/dist/review/assets/{railroadDiagram-RFXS5EU6-DYI7CPMt.js → railroadDiagram-RFXS5EU6-DV8AQwDM.js} +1 -1
- package/dist/review/assets/{requirementDiagram-TGXJPOKE-BMW46SXM.js → requirementDiagram-TGXJPOKE-CnPqfxpt.js} +1 -1
- package/dist/review/assets/{sequenceDiagram-DBY2YBRQ-DVtyVuxr.js → sequenceDiagram-DBY2YBRQ-Bl7q6mbL.js} +1 -1
- package/dist/review/assets/{stateDiagram-2N3HPSRC-BM1s-zUo.js → stateDiagram-2N3HPSRC-iIZcI3Ny.js} +1 -1
- package/dist/review/assets/{stateDiagram-v2-6OUMAXLB-BEfolAkc.js → stateDiagram-v2-6OUMAXLB-Ct4036z_.js} +1 -1
- package/dist/review/assets/{swimlanes-5IMT3BWC-Bnu9mzxz.js → swimlanes-5IMT3BWC-DQR9TtlF.js} +1 -1
- package/dist/review/assets/{swimlanesDiagram-G3AALYLV-DlNNBK53.js → swimlanesDiagram-G3AALYLV-LS62ia1i.js} +1 -1
- package/dist/review/assets/{timeline-definition-FHXFAJF6-CwlO71Lp.js → timeline-definition-FHXFAJF6-Dm738Pbf.js} +1 -1
- package/dist/review/assets/{vennDiagram-L72KCM5P-DZxbcc_O.js → vennDiagram-L72KCM5P-DBxm9_YT.js} +1 -1
- package/dist/review/assets/{wardleyDiagram-EHGQE667-DGQto3hn.js → wardleyDiagram-EHGQE667-CKwVekAc.js} +1 -1
- package/dist/review/assets/{xychartDiagram-FW5EYKEG-BfD1lSXX.js → xychartDiagram-FW5EYKEG-TVh8_sN-.js} +1 -1
- package/dist/review/index.html +2 -2
- package/package.json +1 -1
- package/dist/review/assets/index-Bt3i8n39.css +0 -1
- package/dist/review/assets/index-CqjR4YYp.js +0 -64
package/dist/grfs.js
CHANGED
|
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
|
|
|
2980
2980
|
const schOrFunc = root.refs[ref];
|
|
2981
2981
|
if (schOrFunc)
|
|
2982
2982
|
return schOrFunc;
|
|
2983
|
-
let _sch =
|
|
2983
|
+
let _sch = resolve12.call(this, root, ref);
|
|
2984
2984
|
if (_sch === void 0) {
|
|
2985
2985
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
2986
2986
|
const { schemaId } = this.opts;
|
|
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
|
|
|
3007
3007
|
function sameSchemaEnv(s1, s2) {
|
|
3008
3008
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3009
3009
|
}
|
|
3010
|
-
function
|
|
3010
|
+
function resolve12(root, ref) {
|
|
3011
3011
|
let sch;
|
|
3012
3012
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3013
3013
|
ref = sch;
|
|
@@ -3638,7 +3638,7 @@ var require_fast_uri = __commonJS({
|
|
|
3638
3638
|
}
|
|
3639
3639
|
return uri;
|
|
3640
3640
|
}
|
|
3641
|
-
function
|
|
3641
|
+
function resolve12(baseURI, relativeURI, options) {
|
|
3642
3642
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3643
3643
|
const resolved = resolveComponent(parse2(baseURI, schemelessOptions), parse2(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3644
3644
|
schemelessOptions.skipEscape = true;
|
|
@@ -3896,7 +3896,7 @@ var require_fast_uri = __commonJS({
|
|
|
3896
3896
|
var fastUri = {
|
|
3897
3897
|
SCHEMES,
|
|
3898
3898
|
normalize,
|
|
3899
|
-
resolve:
|
|
3899
|
+
resolve: resolve12,
|
|
3900
3900
|
resolveComponent,
|
|
3901
3901
|
equal,
|
|
3902
3902
|
serialize: serialize2,
|
|
@@ -8564,8 +8564,8 @@ var require_semver2 = __commonJS({
|
|
|
8564
8564
|
|
|
8565
8565
|
// packages/cli/dist/cli.js
|
|
8566
8566
|
import { realpathSync } from "node:fs";
|
|
8567
|
-
import { readFile as
|
|
8568
|
-
import { resolve as
|
|
8567
|
+
import { readFile as readFile8 } from "node:fs/promises";
|
|
8568
|
+
import { resolve as resolve11 } from "node:path";
|
|
8569
8569
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
8570
8570
|
|
|
8571
8571
|
// packages/contracts/dist/index.js
|
|
@@ -8627,6 +8627,8 @@ function sha256Jcs(value) {
|
|
|
8627
8627
|
// packages/contracts/dist/repository-review.js
|
|
8628
8628
|
var REPOSITORY_CONFIG_SCHEMA = "graphrefly.stack.repository.v1";
|
|
8629
8629
|
var REPOSITORY_REVIEW_SCHEMA = "graphrefly.stack.review.v1";
|
|
8630
|
+
var REPOSITORY_REVIEW_DECISION_SCHEMA = "graphrefly.stack.repository-review-decision.v1";
|
|
8631
|
+
var REPOSITORY_REVIEW_BUNDLE_SCHEMA = "graphrefly.stack.repository-review-bundle.v1";
|
|
8630
8632
|
var SUPPORTED_GRAPHREFLY_RANGE = ">=0.3.0 <0.4.0";
|
|
8631
8633
|
|
|
8632
8634
|
// packages/contracts/dist/index.js
|
|
@@ -10572,6 +10574,27 @@ async function validateReview(review) {
|
|
|
10572
10574
|
throw new RepositoryReviewError("REVIEW_SCHEMA_INVALID", `Repository review failed validation: ${JSON.stringify(validate.errors)}`);
|
|
10573
10575
|
}
|
|
10574
10576
|
}
|
|
10577
|
+
function reviewHeadLabel(repository, requested, headOid) {
|
|
10578
|
+
try {
|
|
10579
|
+
const symbolic = gitText(repository, ["rev-parse", "--symbolic-full-name", requested]);
|
|
10580
|
+
if (symbolic.startsWith("refs/heads/"))
|
|
10581
|
+
return symbolic.slice("refs/heads/".length);
|
|
10582
|
+
} catch {
|
|
10583
|
+
}
|
|
10584
|
+
try {
|
|
10585
|
+
const branch = gitText(repository, [
|
|
10586
|
+
"for-each-ref",
|
|
10587
|
+
"--format=%(refname:short)",
|
|
10588
|
+
"--points-at",
|
|
10589
|
+
headOid,
|
|
10590
|
+
"refs/heads"
|
|
10591
|
+
]).split("\n").find(Boolean);
|
|
10592
|
+
if (branch !== void 0)
|
|
10593
|
+
return branch;
|
|
10594
|
+
} catch {
|
|
10595
|
+
}
|
|
10596
|
+
return headOid.slice(0, 12);
|
|
10597
|
+
}
|
|
10575
10598
|
async function createRepositoryReview(options) {
|
|
10576
10599
|
let repository;
|
|
10577
10600
|
try {
|
|
@@ -10656,6 +10679,7 @@ async function createRepositoryReview(options) {
|
|
|
10656
10679
|
source: "generic-repository",
|
|
10657
10680
|
repository: {
|
|
10658
10681
|
label: basename2(repository),
|
|
10682
|
+
headLabel: reviewHeadLabel(repository, options.head, headOid),
|
|
10659
10683
|
graphreflyVersion: runtime.version,
|
|
10660
10684
|
entrypoint: config.blueprint.entrypoint,
|
|
10661
10685
|
baseOid,
|
|
@@ -10673,7 +10697,172 @@ async function createRepositoryReview(options) {
|
|
|
10673
10697
|
import { createReadStream } from "node:fs";
|
|
10674
10698
|
import { stat as stat4 } from "node:fs/promises";
|
|
10675
10699
|
import { createServer } from "node:http";
|
|
10676
|
-
import { extname, resolve as
|
|
10700
|
+
import { extname, resolve as resolve10, sep as sep6 } from "node:path";
|
|
10701
|
+
|
|
10702
|
+
// packages/cli/dist/repository-review-state.js
|
|
10703
|
+
import { randomUUID } from "node:crypto";
|
|
10704
|
+
import { lstat, mkdir as mkdir6, readdir, readFile as readFile7, realpath as realpath4, rename, writeFile as writeFile6 } from "node:fs/promises";
|
|
10705
|
+
import { resolve as resolve9, sep as sep5 } from "node:path";
|
|
10706
|
+
var schemaPaths = [
|
|
10707
|
+
"contracts/repository/v1/repository-config.schema.json",
|
|
10708
|
+
"contracts/repository/v1/review.schema.json",
|
|
10709
|
+
"contracts/repository/v1/review-decision-request.schema.json",
|
|
10710
|
+
"contracts/repository/v1/review-decision.schema.json",
|
|
10711
|
+
"contracts/repository/v1/review-bundle.schema.json"
|
|
10712
|
+
];
|
|
10713
|
+
var RepositoryReviewStateError = class extends Error {
|
|
10714
|
+
code;
|
|
10715
|
+
constructor(code, message) {
|
|
10716
|
+
super(message);
|
|
10717
|
+
this.code = code;
|
|
10718
|
+
this.name = "RepositoryReviewStateError";
|
|
10719
|
+
}
|
|
10720
|
+
};
|
|
10721
|
+
async function validators() {
|
|
10722
|
+
const schemas2 = await Promise.all(schemaPaths.map(async (path) => JSON.parse(await readFile7(runtimeAssetPath(path), "utf8"))));
|
|
10723
|
+
const ajv = createStrictAjv();
|
|
10724
|
+
for (const schema of schemas2)
|
|
10725
|
+
ajv.addSchema(schema);
|
|
10726
|
+
return {
|
|
10727
|
+
request: ajv.getSchema("urn:graphrefly-stack:schema:repository-review-decision-request:v1"),
|
|
10728
|
+
decision: ajv.getSchema("urn:graphrefly-stack:schema:repository-review-decision:v1"),
|
|
10729
|
+
bundle: ajv.getSchema("urn:graphrefly-stack:schema:repository-review-bundle:v1")
|
|
10730
|
+
};
|
|
10731
|
+
}
|
|
10732
|
+
async function safeDirectory(path, parent) {
|
|
10733
|
+
try {
|
|
10734
|
+
const current = await lstat(path);
|
|
10735
|
+
if (!current.isDirectory() || current.isSymbolicLink()) {
|
|
10736
|
+
throw new RepositoryReviewStateError("REVIEW_STATE_PATH_UNSAFE", "Local review state path must be a real directory");
|
|
10737
|
+
}
|
|
10738
|
+
} catch (error) {
|
|
10739
|
+
if (error instanceof RepositoryReviewStateError)
|
|
10740
|
+
throw error;
|
|
10741
|
+
if (error.code !== "ENOENT")
|
|
10742
|
+
throw error;
|
|
10743
|
+
await mkdir6(path, { recursive: false, mode: 448 });
|
|
10744
|
+
}
|
|
10745
|
+
const canonical = await realpath4(path);
|
|
10746
|
+
if (!canonical.startsWith(`${parent}${sep5}`)) {
|
|
10747
|
+
throw new RepositoryReviewStateError("REVIEW_STATE_PATH_UNSAFE", "Local review state escaped the Git common directory");
|
|
10748
|
+
}
|
|
10749
|
+
return canonical;
|
|
10750
|
+
}
|
|
10751
|
+
async function repositoryReviewStateRoot(repository) {
|
|
10752
|
+
const commonDirectory = await realpath4(gitText(repository, ["rev-parse", "--path-format=absolute", "--git-common-dir"]));
|
|
10753
|
+
return safeDirectory(resolve9(commonDirectory, "grfs"), commonDirectory);
|
|
10754
|
+
}
|
|
10755
|
+
async function decisionsDirectory(repository) {
|
|
10756
|
+
const root = await repositoryReviewStateRoot(repository);
|
|
10757
|
+
return safeDirectory(resolve9(root, "reviews"), root);
|
|
10758
|
+
}
|
|
10759
|
+
function blueprintHashFor(review, commitOid) {
|
|
10760
|
+
const commit = review.commits.find((candidate) => candidate.oid === commitOid);
|
|
10761
|
+
const hash2 = commit?.blueprint.hash;
|
|
10762
|
+
if (typeof hash2 !== "object" || hash2 === null)
|
|
10763
|
+
return void 0;
|
|
10764
|
+
return typeof hash2.value === "string" ? hash2.value : void 0;
|
|
10765
|
+
}
|
|
10766
|
+
function matchesCurrentReview(review, record) {
|
|
10767
|
+
const commit = review.commits.find((candidate) => candidate.oid === record.target.commitOid);
|
|
10768
|
+
return commit !== void 0 && record.target.baseOid === review.repository.baseOid && record.target.headOid === review.repository.headOid && record.target.parentOid === commit.parentOid && record.target.blueprintHash === blueprintHashFor(review, commit.oid);
|
|
10769
|
+
}
|
|
10770
|
+
async function readRepositoryReviewDecisions(repository, review) {
|
|
10771
|
+
const directory = await decisionsDirectory(repository);
|
|
10772
|
+
const { decision: validate } = await validators();
|
|
10773
|
+
if (validate === void 0)
|
|
10774
|
+
throw new Error("Repository review decision validator unavailable");
|
|
10775
|
+
const entries = (await readdir(directory, { withFileTypes: true })).filter((entry2) => entry2.isFile() && entry2.name.endsWith(".json")).map((entry2) => entry2.name).sort();
|
|
10776
|
+
const records = [];
|
|
10777
|
+
for (const entry2 of entries) {
|
|
10778
|
+
let value;
|
|
10779
|
+
try {
|
|
10780
|
+
value = JSON.parse(await readFile7(resolve9(directory, entry2), "utf8"));
|
|
10781
|
+
} catch {
|
|
10782
|
+
throw new RepositoryReviewStateError("REVIEW_STATE_INVALID", `Local review record is not valid JSON: ${entry2}`);
|
|
10783
|
+
}
|
|
10784
|
+
if (!validate(value)) {
|
|
10785
|
+
throw new RepositoryReviewStateError("REVIEW_STATE_INVALID", `Local review record failed validation: ${entry2}`);
|
|
10786
|
+
}
|
|
10787
|
+
const record = value;
|
|
10788
|
+
if (matchesCurrentReview(review, record))
|
|
10789
|
+
records.push(record);
|
|
10790
|
+
}
|
|
10791
|
+
return records.sort((left, right) => left.recordedAt.localeCompare(right.recordedAt));
|
|
10792
|
+
}
|
|
10793
|
+
async function writeRepositoryReviewDecision(repository, review, input) {
|
|
10794
|
+
const { request: validateRequest, decision: validateDecision } = await validators();
|
|
10795
|
+
if (validateRequest === void 0 || validateDecision === void 0) {
|
|
10796
|
+
throw new Error("Repository review decision validators unavailable");
|
|
10797
|
+
}
|
|
10798
|
+
if (!validateRequest(input)) {
|
|
10799
|
+
throw new RepositoryReviewStateError("REVIEW_DECISION_INVALID", `Review decision request failed validation: ${JSON.stringify(validateRequest.errors)}`);
|
|
10800
|
+
}
|
|
10801
|
+
const request = input;
|
|
10802
|
+
const reviewerLabel = request.reviewerLabel.trim();
|
|
10803
|
+
const summary = request.summary.trim();
|
|
10804
|
+
if (reviewerLabel.length === 0) {
|
|
10805
|
+
throw new RepositoryReviewStateError("REVIEW_DECISION_INVALID", "Reviewer name cannot be blank");
|
|
10806
|
+
}
|
|
10807
|
+
const commit = review.commits.find((candidate) => candidate.oid === request.commitOid);
|
|
10808
|
+
const blueprintHash = blueprintHashFor(review, request.commitOid);
|
|
10809
|
+
if (commit === void 0 || blueprintHash === void 0) {
|
|
10810
|
+
throw new RepositoryReviewStateError("REVIEW_TARGET_STALE", "The selected commit is not part of the current review range");
|
|
10811
|
+
}
|
|
10812
|
+
const record = {
|
|
10813
|
+
schema: REPOSITORY_REVIEW_DECISION_SCHEMA,
|
|
10814
|
+
id: randomUUID(),
|
|
10815
|
+
target: {
|
|
10816
|
+
baseOid: review.repository.baseOid,
|
|
10817
|
+
headOid: review.repository.headOid,
|
|
10818
|
+
parentOid: commit.parentOid,
|
|
10819
|
+
commitOid: commit.oid,
|
|
10820
|
+
blueprintHash
|
|
10821
|
+
},
|
|
10822
|
+
decision: request.decision,
|
|
10823
|
+
reviewerLabel,
|
|
10824
|
+
summary,
|
|
10825
|
+
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10826
|
+
identityVerified: false
|
|
10827
|
+
};
|
|
10828
|
+
if (!validateDecision(record)) {
|
|
10829
|
+
throw new Error(`Generated review decision failed validation: ${JSON.stringify(validateDecision.errors)}`);
|
|
10830
|
+
}
|
|
10831
|
+
const directory = await decisionsDirectory(repository);
|
|
10832
|
+
const destination = resolve9(directory, `${record.id}.json`);
|
|
10833
|
+
const temporary = resolve9(directory, `.${record.id}.tmp`);
|
|
10834
|
+
await writeFile6(temporary, `${JSON.stringify(record, null, 2)}
|
|
10835
|
+
`, {
|
|
10836
|
+
encoding: "utf8",
|
|
10837
|
+
flag: "wx",
|
|
10838
|
+
mode: 384
|
|
10839
|
+
});
|
|
10840
|
+
await rename(temporary, destination);
|
|
10841
|
+
return record;
|
|
10842
|
+
}
|
|
10843
|
+
async function createRepositoryReviewBundle(repository, review) {
|
|
10844
|
+
const records = await readRepositoryReviewDecisions(repository, review);
|
|
10845
|
+
const bundle = {
|
|
10846
|
+
schema: REPOSITORY_REVIEW_BUNDLE_SCHEMA,
|
|
10847
|
+
repository: {
|
|
10848
|
+
label: review.repository.label,
|
|
10849
|
+
baseOid: review.repository.baseOid,
|
|
10850
|
+
headOid: review.repository.headOid
|
|
10851
|
+
},
|
|
10852
|
+
artifacts: records.map((record) => ({
|
|
10853
|
+
path: `reviews/${record.id}.json`,
|
|
10854
|
+
hash: { algorithm: "sha256", value: sha256Jcs(record) },
|
|
10855
|
+
record
|
|
10856
|
+
}))
|
|
10857
|
+
};
|
|
10858
|
+
const { bundle: validate } = await validators();
|
|
10859
|
+
if (validate === void 0 || !validate(bundle)) {
|
|
10860
|
+
throw new Error(`Generated review bundle failed validation: ${JSON.stringify(validate?.errors)}`);
|
|
10861
|
+
}
|
|
10862
|
+
return bundle;
|
|
10863
|
+
}
|
|
10864
|
+
|
|
10865
|
+
// packages/cli/dist/review-server.js
|
|
10677
10866
|
var defaultReviewDist = runtimeReviewDist();
|
|
10678
10867
|
var contentTypes = {
|
|
10679
10868
|
".css": "text/css; charset=utf-8",
|
|
@@ -10682,6 +10871,32 @@ var contentTypes = {
|
|
|
10682
10871
|
".json": "application/json; charset=utf-8",
|
|
10683
10872
|
".svg": "image/svg+xml"
|
|
10684
10873
|
};
|
|
10874
|
+
async function readJsonBody(request) {
|
|
10875
|
+
const maximumBytes = 32 * 1024;
|
|
10876
|
+
const declaredLength = Number(request.headers["content-length"] ?? "0");
|
|
10877
|
+
if (Number.isFinite(declaredLength) && declaredLength > maximumBytes) {
|
|
10878
|
+
throw new RepositoryReviewStateError("REVIEW_DECISION_TOO_LARGE", "Review decision request exceeds 32 KiB");
|
|
10879
|
+
}
|
|
10880
|
+
const chunks = [];
|
|
10881
|
+
let size = 0;
|
|
10882
|
+
for await (const chunk of request) {
|
|
10883
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
10884
|
+
size += bytes.length;
|
|
10885
|
+
if (size > maximumBytes) {
|
|
10886
|
+
throw new RepositoryReviewStateError("REVIEW_DECISION_TOO_LARGE", "Review decision request exceeds 32 KiB");
|
|
10887
|
+
}
|
|
10888
|
+
chunks.push(bytes);
|
|
10889
|
+
}
|
|
10890
|
+
try {
|
|
10891
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
10892
|
+
} catch {
|
|
10893
|
+
throw new RepositoryReviewStateError("REVIEW_DECISION_INVALID", "Review decision request is not valid JSON");
|
|
10894
|
+
}
|
|
10895
|
+
}
|
|
10896
|
+
function sendJson(response, status, value) {
|
|
10897
|
+
response.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
10898
|
+
response.writeHead(status).end(JSON.stringify(value));
|
|
10899
|
+
}
|
|
10685
10900
|
function safeAssetPath(distDir, requestUrl) {
|
|
10686
10901
|
let pathname;
|
|
10687
10902
|
try {
|
|
@@ -10690,23 +10905,58 @@ function safeAssetPath(distDir, requestUrl) {
|
|
|
10690
10905
|
return null;
|
|
10691
10906
|
}
|
|
10692
10907
|
const relative2 = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
|
|
10693
|
-
const candidate =
|
|
10694
|
-
return candidate === distDir || candidate.startsWith(`${distDir}${
|
|
10908
|
+
const candidate = resolve10(distDir, relative2);
|
|
10909
|
+
return candidate === distDir || candidate.startsWith(`${distDir}${sep6}`) ? candidate : null;
|
|
10695
10910
|
}
|
|
10696
10911
|
async function startReviewServer(options = {}) {
|
|
10697
10912
|
const host = options.host ?? "127.0.0.1";
|
|
10698
10913
|
const port = options.port ?? 4173;
|
|
10699
|
-
const distDir =
|
|
10700
|
-
const evidenceBundlePath = options.evidenceBundlePath === void 0 ? void 0 :
|
|
10914
|
+
const distDir = resolve10(options.distDir ?? defaultReviewDist);
|
|
10915
|
+
const evidenceBundlePath = options.evidenceBundlePath === void 0 ? void 0 : resolve10(options.evidenceBundlePath);
|
|
10701
10916
|
const server = createServer(async (request, response) => {
|
|
10702
10917
|
response.setHeader("Cache-Control", "no-store");
|
|
10703
10918
|
response.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'");
|
|
10704
10919
|
response.setHeader("X-Content-Type-Options", "nosniff");
|
|
10920
|
+
const pathname = new URL(request.url ?? "/", "http://localhost").pathname;
|
|
10921
|
+
if (pathname === "/api/review-decisions" && request.method === "POST") {
|
|
10922
|
+
const state = options.repositoryReviewState;
|
|
10923
|
+
if (state === void 0) {
|
|
10924
|
+
response.writeHead(404).end("Local review state unavailable");
|
|
10925
|
+
return;
|
|
10926
|
+
}
|
|
10927
|
+
const expectedOrigin = request.headers.host === void 0 ? void 0 : `http://${request.headers.host}`;
|
|
10928
|
+
if (request.headers.origin !== expectedOrigin || request.headers["x-graphrefly-review"] !== "1") {
|
|
10929
|
+
response.writeHead(403).end("Same-origin review action required");
|
|
10930
|
+
return;
|
|
10931
|
+
}
|
|
10932
|
+
if (!request.headers["content-type"]?.startsWith("application/json")) {
|
|
10933
|
+
response.writeHead(415).end("application/json required");
|
|
10934
|
+
return;
|
|
10935
|
+
}
|
|
10936
|
+
try {
|
|
10937
|
+
const input = await readJsonBody(request);
|
|
10938
|
+
const record = await writeRepositoryReviewDecision(state.repository, state.review, input);
|
|
10939
|
+
sendJson(response, 201, record);
|
|
10940
|
+
} catch (error) {
|
|
10941
|
+
if (error instanceof RepositoryReviewStateError) {
|
|
10942
|
+
sendJson(response, error.code === "REVIEW_TARGET_STALE" ? 409 : 400, {
|
|
10943
|
+
code: error.code,
|
|
10944
|
+
message: error.message
|
|
10945
|
+
});
|
|
10946
|
+
} else {
|
|
10947
|
+
sendJson(response, 500, {
|
|
10948
|
+
code: "REVIEW_STATE_FAILED",
|
|
10949
|
+
message: "Local review state could not be updated"
|
|
10950
|
+
});
|
|
10951
|
+
}
|
|
10952
|
+
}
|
|
10953
|
+
return;
|
|
10954
|
+
}
|
|
10705
10955
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
10706
10956
|
response.writeHead(405).end("Method not allowed");
|
|
10707
10957
|
return;
|
|
10708
10958
|
}
|
|
10709
|
-
if (
|
|
10959
|
+
if (pathname === "/api/review-data") {
|
|
10710
10960
|
if (options.reviewData === void 0) {
|
|
10711
10961
|
response.writeHead(404).end("Review data unavailable");
|
|
10712
10962
|
return;
|
|
@@ -10719,7 +10969,47 @@ async function startReviewServer(options = {}) {
|
|
|
10719
10969
|
response.end(JSON.stringify(options.reviewData));
|
|
10720
10970
|
return;
|
|
10721
10971
|
}
|
|
10722
|
-
if (
|
|
10972
|
+
if (pathname === "/api/review-decisions") {
|
|
10973
|
+
const state = options.repositoryReviewState;
|
|
10974
|
+
if (state === void 0) {
|
|
10975
|
+
response.writeHead(404).end("Local review state unavailable");
|
|
10976
|
+
return;
|
|
10977
|
+
}
|
|
10978
|
+
try {
|
|
10979
|
+
const records = await readRepositoryReviewDecisions(state.repository, state.review);
|
|
10980
|
+
response.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
10981
|
+
response.writeHead(200);
|
|
10982
|
+
if (request.method === "HEAD")
|
|
10983
|
+
response.end();
|
|
10984
|
+
else
|
|
10985
|
+
response.end(JSON.stringify(records));
|
|
10986
|
+
} catch {
|
|
10987
|
+
response.writeHead(500).end("Local review state unavailable");
|
|
10988
|
+
}
|
|
10989
|
+
return;
|
|
10990
|
+
}
|
|
10991
|
+
if (pathname === "/api/review-decisions/export") {
|
|
10992
|
+
const state = options.repositoryReviewState;
|
|
10993
|
+
if (state === void 0) {
|
|
10994
|
+
response.writeHead(404).end("Portable review bundle unavailable");
|
|
10995
|
+
return;
|
|
10996
|
+
}
|
|
10997
|
+
try {
|
|
10998
|
+
const bundle = await createRepositoryReviewBundle(state.repository, state.review);
|
|
10999
|
+
response.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
11000
|
+
response.setHeader("Content-Disposition", 'attachment; filename="graphrefly-stack-reviews.json"');
|
|
11001
|
+
response.writeHead(200);
|
|
11002
|
+
if (request.method === "HEAD")
|
|
11003
|
+
response.end();
|
|
11004
|
+
else
|
|
11005
|
+
response.end(`${JSON.stringify(bundle, null, 2)}
|
|
11006
|
+
`);
|
|
11007
|
+
} catch {
|
|
11008
|
+
response.writeHead(500).end("Portable review bundle unavailable");
|
|
11009
|
+
}
|
|
11010
|
+
return;
|
|
11011
|
+
}
|
|
11012
|
+
if (pathname === "/api/evidence-bundle") {
|
|
10723
11013
|
if (evidenceBundlePath === void 0) {
|
|
10724
11014
|
response.writeHead(404).end("Portable evidence bundle unavailable");
|
|
10725
11015
|
return;
|
|
@@ -10783,11 +11073,11 @@ Usage:
|
|
|
10783
11073
|
grfs review [--bundle <path>] [--fixture <runtime-suite.json>] [--host 127.0.0.1] [--port 4173]
|
|
10784
11074
|
grfs export [--fixture <runtime-suite.json>] [--plan-run <path>] [--replan-run <path>] [--output <path>] --json
|
|
10785
11075
|
`;
|
|
10786
|
-
var defaultFixtureOutput =
|
|
10787
|
-
var defaultRuntimeSuite =
|
|
11076
|
+
var defaultFixtureOutput = resolve11(".private/fixtures/refresh-token-rotation-v1");
|
|
11077
|
+
var defaultRuntimeSuite = resolve11(defaultFixtureOutput, ".graphrefly-stack/runtime-suite.json");
|
|
10788
11078
|
function isWithin(root, candidate) {
|
|
10789
|
-
const normalizedRoot =
|
|
10790
|
-
const normalizedCandidate =
|
|
11079
|
+
const normalizedRoot = resolve11(root);
|
|
11080
|
+
const normalizedCandidate = resolve11(candidate);
|
|
10791
11081
|
return normalizedCandidate === normalizedRoot || normalizedCandidate.startsWith(`${normalizedRoot}/`);
|
|
10792
11082
|
}
|
|
10793
11083
|
function commandFrom(argv) {
|
|
@@ -10836,12 +11126,12 @@ function caseFrom(runtime, caseId) {
|
|
|
10836
11126
|
return runtime.cases.find((fixtureCase) => fixtureCase.caseId === caseId);
|
|
10837
11127
|
}
|
|
10838
11128
|
async function readJson2(path) {
|
|
10839
|
-
return JSON.parse(await
|
|
11129
|
+
return JSON.parse(await readFile8(path, "utf8"));
|
|
10840
11130
|
}
|
|
10841
11131
|
async function readLiveRun(path, kind) {
|
|
10842
11132
|
if (path === void 0)
|
|
10843
11133
|
return void 0;
|
|
10844
|
-
const value = await readJson2(
|
|
11134
|
+
const value = await readJson2(resolve11(path));
|
|
10845
11135
|
if (value.schema !== "urn:graphrefly-stack:live-run:v1" || value.kind !== kind || value.mode !== "live" || typeof value.provenance !== "object" || value.provenance === null) {
|
|
10846
11136
|
throw new Error(`Invalid ${kind} live-run artifact`);
|
|
10847
11137
|
}
|
|
@@ -10955,14 +11245,14 @@ ${help}`);
|
|
|
10955
11245
|
}
|
|
10956
11246
|
}
|
|
10957
11247
|
if (command === "fixture-create") {
|
|
10958
|
-
const output =
|
|
10959
|
-
if (!isWithin(
|
|
11248
|
+
const output = resolve11(readOption(argv, "--output") ?? defaultFixtureOutput);
|
|
11249
|
+
if (!isWithin(resolve11(".private"), output)) {
|
|
10960
11250
|
return failure(command, json, "OUTPUT_OUTSIDE_PRIVATE_ROOT", output, "replay");
|
|
10961
11251
|
}
|
|
10962
11252
|
const runtime = await createFlagshipFixture(output, argv.includes("--force"));
|
|
10963
11253
|
success(command, "replay", {
|
|
10964
11254
|
repository: runtime.repository,
|
|
10965
|
-
runtimeSuite:
|
|
11255
|
+
runtimeSuite: resolve11(output, ".graphrefly-stack/runtime-suite.json"),
|
|
10966
11256
|
refs: runtime.refs
|
|
10967
11257
|
}, json);
|
|
10968
11258
|
return 0;
|
|
@@ -11013,8 +11303,8 @@ ${help}`);
|
|
|
11013
11303
|
}
|
|
11014
11304
|
if (command === "export") {
|
|
11015
11305
|
const runtime = await readRuntimeSuite(readOption(argv, "--fixture") ?? defaultRuntimeSuite);
|
|
11016
|
-
const requestedOutput =
|
|
11017
|
-
if (!isWithin(
|
|
11306
|
+
const requestedOutput = resolve11(readOption(argv, "--output") ?? resolve11(".private/exports/refresh-token-rotation-v1"));
|
|
11307
|
+
if (!isWithin(resolve11(".private"), requestedOutput) && !isWithin(resolve11("evidence/runs"), requestedOutput)) {
|
|
11018
11308
|
return failure(command, json, "OUTPUT_OUTSIDE_EVIDENCE_ROOT", requestedOutput);
|
|
11019
11309
|
}
|
|
11020
11310
|
const liveRuns = {
|
|
@@ -11036,6 +11326,7 @@ ${help}`);
|
|
|
11036
11326
|
}
|
|
11037
11327
|
let reviewData;
|
|
11038
11328
|
let evidenceBundlePath;
|
|
11329
|
+
let repositoryReviewState;
|
|
11039
11330
|
const repository = readOption(argv, "--repo");
|
|
11040
11331
|
const base = readOption(argv, "--base");
|
|
11041
11332
|
const head = readOption(argv, "--head");
|
|
@@ -11049,7 +11340,9 @@ ${help}`);
|
|
|
11049
11340
|
return failure(command, json, "REVIEW_SOURCE_CONFLICT", "Generic repository review cannot be mixed with fixture or bundle input");
|
|
11050
11341
|
}
|
|
11051
11342
|
try {
|
|
11052
|
-
|
|
11343
|
+
const repositoryReview = await createRepositoryReview({ repository, base, head });
|
|
11344
|
+
reviewData = repositoryReview;
|
|
11345
|
+
repositoryReviewState = { repository: resolve11(repository), review: repositoryReview };
|
|
11053
11346
|
} catch (error) {
|
|
11054
11347
|
if (error instanceof RepositoryReviewError) {
|
|
11055
11348
|
return failure(command, json, error.code, error.message);
|
|
@@ -11100,7 +11393,8 @@ ${help}`);
|
|
|
11100
11393
|
host,
|
|
11101
11394
|
port,
|
|
11102
11395
|
reviewData,
|
|
11103
|
-
evidenceBundlePath
|
|
11396
|
+
evidenceBundlePath,
|
|
11397
|
+
repositoryReviewState
|
|
11104
11398
|
});
|
|
11105
11399
|
process.stderr.write(`GraphReFly Stack review shell (${CORE_ARCHITECTURE.version}) listening at ${running.url}
|
|
11106
11400
|
`);
|
package/dist/review/assets/{abnfDiagram-VRR7QNED-5JVeQ-ZW.js → abnfDiagram-VRR7QNED-DxGYeLns.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{m as e}from"./src-BMa7vLb8.js";import{n as t}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./chunk-WYO6CB5R-C36byBU-.js";import"./index-
|
|
1
|
+
import{m as e}from"./src-BMa7vLb8.js";import{n as t}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import"./chunk-WYO6CB5R-C36byBU-.js";import"./index-CUaeHlgA.js";import{m as n,t as r}from"./mermaid-parser.core-BJLV4lM2.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-Ct40HB9X.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=n().RailroadAbnf.parser.LangiumParser,l=t(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=t(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=t(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=t(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=t(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=t(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=t(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:t(t=>{o.clear(),e.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(t);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new r(n);let i=n.value;e.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),e.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
|