@wairon/cli 5.1.1-dev.69 → 5.1.1-dev.70
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/index.js +243 -20
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +209 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var init_defaults = __esm({
|
|
|
65
65
|
copilot: ".github/prompts",
|
|
66
66
|
codex: ".codex/agents"
|
|
67
67
|
};
|
|
68
|
-
WAIRON_VERSION = "5.1.1-dev.
|
|
68
|
+
WAIRON_VERSION = "5.1.1-dev.70";
|
|
69
69
|
GITHUB_REPO = "SYW-Apps/Waffle-AIron";
|
|
70
70
|
SUPPORTED_ALIASES = ["wai"];
|
|
71
71
|
SCAN_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -14374,6 +14374,135 @@ var init_param_conformance = __esm({
|
|
|
14374
14374
|
}
|
|
14375
14375
|
});
|
|
14376
14376
|
|
|
14377
|
+
// src/core/rules/conformance/route-coverage.ts
|
|
14378
|
+
function liesUnder2(path71, prefix) {
|
|
14379
|
+
return path71 === prefix || path71.startsWith(prefix + "/");
|
|
14380
|
+
}
|
|
14381
|
+
function normalizeSegment(segment) {
|
|
14382
|
+
return /^\{.+\}$/.test(segment) || segment.startsWith(":") ? "*" : segment;
|
|
14383
|
+
}
|
|
14384
|
+
function completed(route, heads) {
|
|
14385
|
+
const variants = route.segments[0] === "*" && heads.length > 0 ? heads.map((head) => [head, ...route.segments.slice(1)]) : [route.segments];
|
|
14386
|
+
return variants.map((segments) => ({
|
|
14387
|
+
verb: route.verb.toUpperCase(),
|
|
14388
|
+
segments: segments.map(normalizeSegment),
|
|
14389
|
+
exactLength: route.exactLength,
|
|
14390
|
+
key: `${route.verb.toUpperCase()} /${segments.join("/")}${route.exactLength ? "" : "/\u2026"}`
|
|
14391
|
+
}));
|
|
14392
|
+
}
|
|
14393
|
+
function answers(route, endpoint) {
|
|
14394
|
+
if (route.verb !== endpoint.verb) return false;
|
|
14395
|
+
if (route.exactLength ? endpoint.segments.length !== route.segments.length : endpoint.segments.length < route.segments.length) return false;
|
|
14396
|
+
return route.segments.every((segment, index) => segment === endpoint.segments[index]);
|
|
14397
|
+
}
|
|
14398
|
+
var routeCoverageRule;
|
|
14399
|
+
var init_route_coverage = __esm({
|
|
14400
|
+
"src/core/rules/conformance/route-coverage.ts"() {
|
|
14401
|
+
"use strict";
|
|
14402
|
+
init_models();
|
|
14403
|
+
routeCoverageRule = {
|
|
14404
|
+
name: "route-coverage",
|
|
14405
|
+
description: "Code-to-contract for the ROUTES: does every route a router actually answers have a contract endpoint, and does every contract endpoint have a route that answers it? A portal's endpoints are what its contract promises; the router is what the code serves; nothing compared the two, so a route with no contract \u2014 including a write \u2014 could run for months without a single rule noticing. The listener's mount names the router function (its `via`), and the routes are read out of that function's guards; the path segment the router never checks is the one the mount's prefix guarantees. One idiom is read \u2014 a method comparison with comparisons on the path's split segments \u2014 and a mounted router that yields no route in it is reported as unread, never passed: a check that cannot see a router must say so rather than stay quiet.",
|
|
14406
|
+
codes: [
|
|
14407
|
+
{
|
|
14408
|
+
code: "UNDECLARED_ROUTE",
|
|
14409
|
+
defaultSeverity: "warning",
|
|
14410
|
+
summary: "A router answers a route that no contract endpoint of the portal it serves declares \u2014 a surface the code serves and the design never promised",
|
|
14411
|
+
// Measured code↔contract drift, route by route, at the mount's router
|
|
14412
|
+
// entry — which is why every one of them hands over `parts`.
|
|
14413
|
+
carryable: true
|
|
14414
|
+
},
|
|
14415
|
+
{
|
|
14416
|
+
code: "UNROUTED_ENDPOINT",
|
|
14417
|
+
defaultSeverity: "warning",
|
|
14418
|
+
summary: "A contract endpoint no route of the portal's router answers \u2014 the contract promises a route that no request can reach",
|
|
14419
|
+
carryable: true
|
|
14420
|
+
},
|
|
14421
|
+
{
|
|
14422
|
+
code: "UNREADABLE_ROUTER",
|
|
14423
|
+
defaultSeverity: "warning",
|
|
14424
|
+
summary: "A mounted portal's router yields no route this analysis can read, so its routes were not checked against the contract at all",
|
|
14425
|
+
carryable: true
|
|
14426
|
+
}
|
|
14427
|
+
],
|
|
14428
|
+
check(ctx) {
|
|
14429
|
+
const code = ctx.codeIndex();
|
|
14430
|
+
const realization = ctx.realizationIndex();
|
|
14431
|
+
for (const listener of ctx.components) {
|
|
14432
|
+
if (listener.componentType !== "Portal") continue;
|
|
14433
|
+
for (const mount of listener.mounts ?? []) {
|
|
14434
|
+
const via = mount.via;
|
|
14435
|
+
if (!via) continue;
|
|
14436
|
+
const portal = ctx.componentMap.get(mount.portal);
|
|
14437
|
+
if (!portal || portal.componentType !== "Portal") continue;
|
|
14438
|
+
const [anchor] = realization.implementationsOf(portal.id).map((impl) => impl.id).sort();
|
|
14439
|
+
if (!anchor) continue;
|
|
14440
|
+
const holders = realization.filesOf(portal.id).map(pathKey).filter((file) => {
|
|
14441
|
+
const facts = code.factsAt(file);
|
|
14442
|
+
return !!facts && facts.status === "analyzed" && facts.analysisGrade === "exact" && !!facts.functionParams && Object.prototype.hasOwnProperty.call(facts.functionParams, via);
|
|
14443
|
+
});
|
|
14444
|
+
if (holders.length === 0) continue;
|
|
14445
|
+
const read = holders.flatMap((file) => {
|
|
14446
|
+
const routes = code.factsAt(file).functionRoutes;
|
|
14447
|
+
return routes && Object.prototype.hasOwnProperty.call(routes, via) ? routes[via] : [];
|
|
14448
|
+
});
|
|
14449
|
+
const draftContext = ctx.isComponentDraft(listener.id) || ctx.isComponentDraft(portal.id);
|
|
14450
|
+
const where = holders.map((file) => `"${file}"`).join(", ");
|
|
14451
|
+
if (read.length === 0) {
|
|
14452
|
+
ctx.addIssue(
|
|
14453
|
+
"warning",
|
|
14454
|
+
"UNREADABLE_ROUTER",
|
|
14455
|
+
`Listener "${listener.id}" mounts portal "${portal.id}" through "${via}" in ${where}, but no branch of "${via}" reads as a route \u2014 so none of its routes were checked against the contract at all. Only one idiom is read: an \`if\` whose conditions, together with those of every enclosing \`if\`, compare \`<request>.method\` with a string and \`parts[i]\` / \`parts.length\` with literals. Silence here would read as a clean router; it is only one this analysis cannot see. Write the router in that idiom, or carry this finding with the reason it cannot be.`,
|
|
14456
|
+
anchor,
|
|
14457
|
+
draftContext,
|
|
14458
|
+
void 0,
|
|
14459
|
+
{ at: via }
|
|
14460
|
+
);
|
|
14461
|
+
continue;
|
|
14462
|
+
}
|
|
14463
|
+
const heads = [...new Set(mount.prefixes.map((prefix) => prefix.split("/").filter(Boolean)[0]).filter((head) => !!head))];
|
|
14464
|
+
const served = read.map((route) => completed(route, heads));
|
|
14465
|
+
const endpoints = ctx.interfaceMethodsOf(portal.id).flatMap((method2) => {
|
|
14466
|
+
const endpoint = method2.endpoint;
|
|
14467
|
+
if (endpoint?.transport !== "HTTP") return [];
|
|
14468
|
+
if (!mount.prefixes.some((prefix) => liesUnder2(endpoint.path, prefix))) return [];
|
|
14469
|
+
return [{
|
|
14470
|
+
verb: endpoint.method.toUpperCase(),
|
|
14471
|
+
segments: endpoint.path.split("/").filter(Boolean).map(normalizeSegment),
|
|
14472
|
+
key: `${endpoint.method.toUpperCase()} ${endpoint.path}`
|
|
14473
|
+
}];
|
|
14474
|
+
});
|
|
14475
|
+
const undeclared = [...new Set(served.filter((variants) => !variants.some((route) => endpoints.some((endpoint) => answers(route, endpoint)))).flatMap((variants) => variants.map((route) => route.key)))].sort();
|
|
14476
|
+
if (undeclared.length > 0) {
|
|
14477
|
+
ctx.addIssue(
|
|
14478
|
+
"warning",
|
|
14479
|
+
"UNDECLARED_ROUTE",
|
|
14480
|
+
`Portal "${portal.id}"'s router "${via}" (${where}), mounted by listener "${listener.id}", answers ${undeclared.length} route(s) no contract endpoint of "${portal.id}" declares \u2014 ${undeclared.map((key) => `"${key}"`).join(", ")}. A surface the code serves and the design never promised is how a write runs with no contract: no brief, no auth review, no rule reading it. Declare each as a contract method with its endpoint, or take the route out of the router.`,
|
|
14481
|
+
anchor,
|
|
14482
|
+
draftContext,
|
|
14483
|
+
void 0,
|
|
14484
|
+
{ at: via, covers: undeclared }
|
|
14485
|
+
);
|
|
14486
|
+
}
|
|
14487
|
+
const unrouted = [...new Set(endpoints.filter((endpoint) => !served.some((variants) => variants.some((route) => answers(route, endpoint)))).map((endpoint) => endpoint.key))].sort();
|
|
14488
|
+
if (unrouted.length > 0) {
|
|
14489
|
+
ctx.addIssue(
|
|
14490
|
+
"warning",
|
|
14491
|
+
"UNROUTED_ENDPOINT",
|
|
14492
|
+
`Portal "${portal.id}" binds ${unrouted.length} HTTP endpoint(s) under listener "${listener.id}"'s mount that no route of its router "${via}" (${where}) answers \u2014 ${unrouted.map((key) => `"${key}"`).join(", ")}. The contract promises a route no request can reach. Route it in the router, or drop the endpoint from the contract.`,
|
|
14493
|
+
anchor,
|
|
14494
|
+
draftContext,
|
|
14495
|
+
void 0,
|
|
14496
|
+
{ at: via, covers: unrouted }
|
|
14497
|
+
);
|
|
14498
|
+
}
|
|
14499
|
+
}
|
|
14500
|
+
}
|
|
14501
|
+
}
|
|
14502
|
+
};
|
|
14503
|
+
}
|
|
14504
|
+
});
|
|
14505
|
+
|
|
14377
14506
|
// src/core/rules/conformance/unclaimed-source.ts
|
|
14378
14507
|
var unclaimedSourceRule;
|
|
14379
14508
|
var init_unclaimed_source = __esm({
|
|
@@ -14695,14 +14824,21 @@ function isPureForwarder(methods) {
|
|
|
14695
14824
|
const narrated = methods.filter((m) => (m.narrative ?? []).length > 0);
|
|
14696
14825
|
return narrated.length > 0 && narrated.every((m) => isForwardingMethod(m.narrative ?? []));
|
|
14697
14826
|
}
|
|
14698
|
-
function
|
|
14827
|
+
function reachesCollaborator(narrative) {
|
|
14828
|
+
return narrative.some((step) => step.type === "call" || step.type === "dispatch" || step.type === "register");
|
|
14829
|
+
}
|
|
14830
|
+
function isRoutingTable(methods) {
|
|
14831
|
+
const reaching = methods.map((m) => m.narrative ?? []).filter(reachesCollaborator);
|
|
14832
|
+
return reaching.length > 0 && reaching.every(isForwardingMethod);
|
|
14833
|
+
}
|
|
14834
|
+
function routingTableComponents(ctx) {
|
|
14699
14835
|
const realized = /* @__PURE__ */ new Map();
|
|
14700
14836
|
for (const impl of ctx.implementations) {
|
|
14701
14837
|
const intf = ctx.interfaceMap.get(impl.contract);
|
|
14702
14838
|
if (!intf) continue;
|
|
14703
14839
|
realized.set(intf.component, [...realized.get(intf.component) ?? [], ...impl.methods]);
|
|
14704
14840
|
}
|
|
14705
|
-
return new Set([...realized].filter(([, methods]) =>
|
|
14841
|
+
return new Set([...realized].filter(([, methods]) => isRoutingTable(methods)).map(([id]) => id));
|
|
14706
14842
|
}
|
|
14707
14843
|
var methodCohesionRule;
|
|
14708
14844
|
var init_method_cohesion = __esm({
|
|
@@ -14767,7 +14903,7 @@ var init_coupling_health = __esm({
|
|
|
14767
14903
|
{ code: "MUTUAL_SUBSYSTEM_DEPENDENCY", defaultSeverity: "warning", summary: "Subsystems depend on each other without a declared trusted link" },
|
|
14768
14904
|
{ code: "INVALID_TRUSTED_LINK", defaultSeverity: "error", summary: "trustedLinks references a non-existent subsystem" },
|
|
14769
14905
|
{ code: "UNUSED_TRUSTED_LINK", defaultSeverity: "warning", summary: "trustedLinks declares a peer no dependency actually reaches" },
|
|
14770
|
-
{ code: "GOD_COMPONENT", defaultSeverity: "warning", summary: "Component with excessive dependency fan-out; not reported on a
|
|
14906
|
+
{ code: "GOD_COMPONENT", defaultSeverity: "warning", summary: "Component with excessive dependency fan-out; not reported on a routing table, which reaches every collaborator it has only through single hand-offs" }
|
|
14771
14907
|
],
|
|
14772
14908
|
check(ctx) {
|
|
14773
14909
|
const subsystemDeps = /* @__PURE__ */ new Map();
|
|
@@ -14839,10 +14975,10 @@ var init_coupling_health = __esm({
|
|
|
14839
14975
|
);
|
|
14840
14976
|
}
|
|
14841
14977
|
}
|
|
14842
|
-
const
|
|
14978
|
+
const routingTables = routingTableComponents(ctx);
|
|
14843
14979
|
for (const comp of ctx.components) {
|
|
14844
14980
|
const threshold = ctx.complexityConfigFor(comp.subsystem)?.maxComponentDependencies ?? DEFAULT_GOD_COMPONENT_THRESHOLD;
|
|
14845
|
-
if (comp.dependsOn.length > threshold && !
|
|
14981
|
+
if (comp.dependsOn.length > threshold && !routingTables.has(comp.id)) {
|
|
14846
14982
|
ctx.addIssue(
|
|
14847
14983
|
"warning",
|
|
14848
14984
|
"GOD_COMPONENT",
|
|
@@ -15475,7 +15611,7 @@ var init_complexity_and_metadata = __esm({
|
|
|
15475
15611
|
{ code: "DESCRIPTION_TOO_SHORT", defaultSeverity: "warning", summary: "Description is shorter than the configured minimum length" },
|
|
15476
15612
|
{ code: "EXCESSIVE_METHODS", defaultSeverity: "warning", summary: "Interface declares more methods than the configured limit" },
|
|
15477
15613
|
{ code: "EXCESSIVE_METHOD_PARAMS", defaultSeverity: "warning", summary: "Interface method declares more parameters than the configured limit" },
|
|
15478
|
-
{ code: "EXCESSIVE_DEPENDENCIES", defaultSeverity: "warning", summary: "Component has more dependencies than the configured limit; not reported on a
|
|
15614
|
+
{ code: "EXCESSIVE_DEPENDENCIES", defaultSeverity: "warning", summary: "Component has more dependencies than the configured limit; not reported on a routing table, which reaches every collaborator it has only through single hand-offs" },
|
|
15479
15615
|
{ code: "EXCESSIVE_SUBSYSTEM_COMPONENTS", defaultSeverity: "warning", summary: "Subsystem has more direct components than the configured limit" }
|
|
15480
15616
|
],
|
|
15481
15617
|
check(ctx) {
|
|
@@ -15495,7 +15631,7 @@ var init_complexity_and_metadata = __esm({
|
|
|
15495
15631
|
);
|
|
15496
15632
|
}
|
|
15497
15633
|
}
|
|
15498
|
-
const
|
|
15634
|
+
const routingTables = routingTableComponents(ctx);
|
|
15499
15635
|
for (const comp of ctx.components) {
|
|
15500
15636
|
const docConfig = ctx.documentationConfigFor(comp.subsystem);
|
|
15501
15637
|
const complexityConfig = ctx.complexityConfigFor(comp.subsystem);
|
|
@@ -15509,7 +15645,7 @@ var init_complexity_and_metadata = __esm({
|
|
|
15509
15645
|
"Component",
|
|
15510
15646
|
isDraft
|
|
15511
15647
|
);
|
|
15512
|
-
if (complexityConfig?.maxComponentDependencies !== void 0 && comp.dependsOn.length > complexityConfig.maxComponentDependencies && !
|
|
15648
|
+
if (complexityConfig?.maxComponentDependencies !== void 0 && comp.dependsOn.length > complexityConfig.maxComponentDependencies && !routingTables.has(comp.id)) {
|
|
15513
15649
|
ctx.addIssue(
|
|
15514
15650
|
"warning",
|
|
15515
15651
|
"EXCESSIVE_DEPENDENCIES",
|
|
@@ -15919,6 +16055,7 @@ var init_repository = __esm({
|
|
|
15919
16055
|
init_type_realization();
|
|
15920
16056
|
init_type_shape();
|
|
15921
16057
|
init_param_conformance();
|
|
16058
|
+
init_route_coverage();
|
|
15922
16059
|
init_unclaimed_source();
|
|
15923
16060
|
init_export_conformance();
|
|
15924
16061
|
init_carried_debt();
|
|
@@ -16098,6 +16235,7 @@ var init_repository = __esm({
|
|
|
16098
16235
|
typeRealizationRule,
|
|
16099
16236
|
typeShapeRule,
|
|
16100
16237
|
paramConformanceRule,
|
|
16238
|
+
routeCoverageRule,
|
|
16101
16239
|
unclaimedSourceRule,
|
|
16102
16240
|
exportConformanceRule,
|
|
16103
16241
|
couplingRule,
|
|
@@ -16267,6 +16405,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
16267
16405
|
const localTypes = /* @__PURE__ */ new Map();
|
|
16268
16406
|
const typeShapes = /* @__PURE__ */ new Map();
|
|
16269
16407
|
const functionParams = /* @__PURE__ */ new Map();
|
|
16408
|
+
const functionRoutes = /* @__PURE__ */ new Map();
|
|
16270
16409
|
const mutableBindings = /* @__PURE__ */ new Set();
|
|
16271
16410
|
const schemaConstants = /* @__PURE__ */ new Map();
|
|
16272
16411
|
const derivedAliases = [];
|
|
@@ -16438,6 +16577,67 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
16438
16577
|
if (fn.body) count(fn.body);
|
|
16439
16578
|
return { score, callees };
|
|
16440
16579
|
};
|
|
16580
|
+
const conjunctsOf = (expression, out = []) => {
|
|
16581
|
+
if (ts.isParenthesizedExpression(expression)) return conjunctsOf(expression.expression, out);
|
|
16582
|
+
if (ts.isBinaryExpression(expression) && expression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
|
|
16583
|
+
conjunctsOf(expression.left, out);
|
|
16584
|
+
conjunctsOf(expression.right, out);
|
|
16585
|
+
return out;
|
|
16586
|
+
}
|
|
16587
|
+
out.push(expression);
|
|
16588
|
+
return out;
|
|
16589
|
+
};
|
|
16590
|
+
const isPartsRef = (expression) => ts.isIdentifier(expression) && expression.text === "parts" || ts.isPropertyAccessExpression(expression) && expression.name.text === "parts";
|
|
16591
|
+
const constraintOf = (conjunct) => {
|
|
16592
|
+
if (!ts.isBinaryExpression(conjunct) || conjunct.operatorToken.kind !== ts.SyntaxKind.EqualsEqualsEqualsToken) {
|
|
16593
|
+
return void 0;
|
|
16594
|
+
}
|
|
16595
|
+
const { left, right } = conjunct;
|
|
16596
|
+
if (ts.isPropertyAccessExpression(left) && left.name.text === "method" && ts.isStringLiteral(right)) {
|
|
16597
|
+
return { verb: right.text };
|
|
16598
|
+
}
|
|
16599
|
+
if (ts.isPropertyAccessExpression(left) && left.name.text === "length" && isPartsRef(left.expression) && ts.isNumericLiteral(right)) {
|
|
16600
|
+
return { length: Number(right.text) };
|
|
16601
|
+
}
|
|
16602
|
+
if (ts.isElementAccessExpression(left) && isPartsRef(left.expression) && ts.isNumericLiteral(left.argumentExpression) && ts.isStringLiteral(right)) {
|
|
16603
|
+
return { index: Number(left.argumentExpression.text), literal: right.text };
|
|
16604
|
+
}
|
|
16605
|
+
return void 0;
|
|
16606
|
+
};
|
|
16607
|
+
const routeOf = (conjuncts) => {
|
|
16608
|
+
let verb;
|
|
16609
|
+
let length;
|
|
16610
|
+
const fixed = /* @__PURE__ */ new Map();
|
|
16611
|
+
for (const conjunct of conjuncts) {
|
|
16612
|
+
const constraint = constraintOf(conjunct);
|
|
16613
|
+
if (!constraint) continue;
|
|
16614
|
+
if ("verb" in constraint) verb = constraint.verb;
|
|
16615
|
+
else if ("length" in constraint) length = constraint.length;
|
|
16616
|
+
else fixed.set(constraint.index, constraint.literal);
|
|
16617
|
+
}
|
|
16618
|
+
if (verb === void 0) return void 0;
|
|
16619
|
+
const count = length ?? (fixed.size > 0 ? Math.max(...fixed.keys()) + 1 : 0);
|
|
16620
|
+
const segments = [];
|
|
16621
|
+
for (let index = 0; index < count; index++) segments.push(fixed.get(index) ?? "*");
|
|
16622
|
+
return { verb, segments, exactLength: length !== void 0 };
|
|
16623
|
+
};
|
|
16624
|
+
const collectRoutes = (fn) => {
|
|
16625
|
+
const routes = /* @__PURE__ */ new Map();
|
|
16626
|
+
const walk2 = (node, enclosing) => {
|
|
16627
|
+
if (namedFunctionName(node) !== void 0) return;
|
|
16628
|
+
if (ts.isIfStatement(node)) {
|
|
16629
|
+
const guarded = [...enclosing, ...conjunctsOf(node.expression)];
|
|
16630
|
+
const route = routeOf(guarded);
|
|
16631
|
+
if (route) routes.set(`${route.verb} /${route.segments.join("/")}${route.exactLength ? "" : "/\u2026"}`, route);
|
|
16632
|
+
walk2(node.thenStatement, guarded);
|
|
16633
|
+
if (node.elseStatement) walk2(node.elseStatement, enclosing);
|
|
16634
|
+
return;
|
|
16635
|
+
}
|
|
16636
|
+
ts.forEachChild(node, (child) => walk2(child, enclosing));
|
|
16637
|
+
};
|
|
16638
|
+
if (fn.body) walk2(fn.body, []);
|
|
16639
|
+
return routes;
|
|
16640
|
+
};
|
|
16441
16641
|
const visit = (node) => {
|
|
16442
16642
|
const fnName = namedFunctionName(node);
|
|
16443
16643
|
if (fnName && node.body) {
|
|
@@ -16449,6 +16649,12 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
16449
16649
|
const signatures = functionParams.get(fnName) ?? [];
|
|
16450
16650
|
signatures.push(declaredParameters(node));
|
|
16451
16651
|
functionParams.set(fnName, signatures);
|
|
16652
|
+
const routes = collectRoutes(node);
|
|
16653
|
+
if (routes.size > 0) {
|
|
16654
|
+
const known = functionRoutes.get(fnName) ?? /* @__PURE__ */ new Map();
|
|
16655
|
+
for (const [key, route] of routes) known.set(key, route);
|
|
16656
|
+
functionRoutes.set(fnName, known);
|
|
16657
|
+
}
|
|
16452
16658
|
}
|
|
16453
16659
|
if (ts.isPropertyDeclaration(node)) recordFieldType(node.name, node.type);
|
|
16454
16660
|
else if (ts.isParameter(node) && node.parent && ts.isConstructorDeclaration(node.parent) && isParameterProperty(node)) {
|
|
@@ -16556,7 +16762,7 @@ function walkExact(ts, sourceText, fileName) {
|
|
|
16556
16762
|
typeShapes.set(alias.name, { origin: "derived", fields: schemaMembers(literal), methods: [] });
|
|
16557
16763
|
}
|
|
16558
16764
|
const reexportOnly = sf.statements.length > 0 && sf.statements.every((st) => ts.isExportDeclaration(st) && !!st.moduleSpecifier);
|
|
16559
|
-
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, mutableBindings, reexportOnly };
|
|
16765
|
+
return { declared, anchors, exported, imports, reexports, starExports, namedReexports, complexity, calls, importBindings, typeOnlyBindings, fieldTypes, localTypes, typeShapes, functionParams, functionRoutes, mutableBindings, reexportOnly };
|
|
16560
16766
|
}
|
|
16561
16767
|
function resolveRelativeModule(fromFile, specifier) {
|
|
16562
16768
|
if (!specifier.startsWith(".")) return null;
|
|
@@ -16740,6 +16946,7 @@ function buildCodeModel(implementations, types, projectRoot2, sourceRoots = [],
|
|
|
16740
16946
|
localTypes: Object.fromEntries([...facts.localTypes].map(([k, v]) => [k, [...v]])),
|
|
16741
16947
|
typeShapes: Object.fromEntries(facts.typeShapes),
|
|
16742
16948
|
functionParams: Object.fromEntries(facts.functionParams),
|
|
16949
|
+
functionRoutes: Object.fromEntries([...facts.functionRoutes].map(([k, v]) => [k, [...v.values()]])),
|
|
16743
16950
|
topLevelMutableBindings: [...facts.mutableBindings],
|
|
16744
16951
|
reexportOnly: facts.reexportOnly
|
|
16745
16952
|
};
|
|
@@ -33580,7 +33787,7 @@ async function runDomainsAdd(options = {}) {
|
|
|
33580
33787
|
type: "input",
|
|
33581
33788
|
name: "domainName",
|
|
33582
33789
|
message: "Display name:",
|
|
33583
|
-
default: (
|
|
33790
|
+
default: (answers2) => answers2.domainId.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ")
|
|
33584
33791
|
},
|
|
33585
33792
|
{
|
|
33586
33793
|
type: "input",
|
|
@@ -33592,7 +33799,7 @@ async function runDomainsAdd(options = {}) {
|
|
|
33592
33799
|
type: "input",
|
|
33593
33800
|
name: "ownedPathsRaw",
|
|
33594
33801
|
message: "Owned path globs (comma-separated):",
|
|
33595
|
-
default: (
|
|
33802
|
+
default: (answers2) => answers2.domainPath ? `${answers2.domainPath}/**` : "",
|
|
33596
33803
|
validate: (v) => v.trim().length > 0 || "At least one owned path is required"
|
|
33597
33804
|
}
|
|
33598
33805
|
]);
|
|
@@ -41209,12 +41416,12 @@ var ApprovalRegistry = class {
|
|
|
41209
41416
|
`Approval request "${id}" is ${current2.status}, not approved; only an approved request can be completed.`
|
|
41210
41417
|
);
|
|
41211
41418
|
}
|
|
41212
|
-
const
|
|
41419
|
+
const completed2 = { ...current2, status: "completed" };
|
|
41213
41420
|
const next = [...requests];
|
|
41214
|
-
next[idx] =
|
|
41421
|
+
next[idx] = completed2;
|
|
41215
41422
|
persistRequests(this.dataDir, next);
|
|
41216
41423
|
this.store.replaceAll(next);
|
|
41217
|
-
return
|
|
41424
|
+
return completed2;
|
|
41218
41425
|
}
|
|
41219
41426
|
/**
|
|
41220
41427
|
* Flip every pending request whose expiresAt is at or before the supplied
|
|
@@ -45509,6 +45716,21 @@ function adminPlaceProject(cfg, sessionId, projectId, unitId) {
|
|
|
45509
45716
|
function adminRemoveUnit(cfg, sessionId, unitId, disposition) {
|
|
45510
45717
|
removeUnit(cfg, sessionId, unitId, disposition);
|
|
45511
45718
|
}
|
|
45719
|
+
function adminGetLandscape(cfg, sessionId) {
|
|
45720
|
+
return generateLandscape(cfg, sessionId);
|
|
45721
|
+
}
|
|
45722
|
+
function adminGetHealth(cfg, sessionId) {
|
|
45723
|
+
return getHealthReport(cfg, sessionId);
|
|
45724
|
+
}
|
|
45725
|
+
function adminGetUsage(cfg, sessionId) {
|
|
45726
|
+
return getUsage(cfg, sessionId);
|
|
45727
|
+
}
|
|
45728
|
+
function adminListApprovals(cfg, sessionId) {
|
|
45729
|
+
return listPendingRequests(cfg, sessionId);
|
|
45730
|
+
}
|
|
45731
|
+
function adminDecideApproval(cfg, sessionId, decision) {
|
|
45732
|
+
return decideRequest(cfg, sessionId, decision);
|
|
45733
|
+
}
|
|
45512
45734
|
function bodyScope(body) {
|
|
45513
45735
|
return {
|
|
45514
45736
|
scopeKind: body?.scopeKind ? String(body.scopeKind) : void 0,
|
|
@@ -46100,16 +46322,16 @@ async function handleWebRequest(cfg, req, res, body, url, ctx) {
|
|
|
46100
46322
|
return sendJson(res, 200, { entries: adminGetShareAccessLog(cfg, sessionId, url.searchParams.get("linkId") ?? "", limit) });
|
|
46101
46323
|
}
|
|
46102
46324
|
if (req.method === "GET" && parts.length === 3 && parts[2] === "landscape") {
|
|
46103
|
-
return sendJson(res, 200,
|
|
46325
|
+
return sendJson(res, 200, adminGetLandscape(cfg, sessionId));
|
|
46104
46326
|
}
|
|
46105
46327
|
if (req.method === "GET" && parts.length === 3 && parts[2] === "health") {
|
|
46106
|
-
return sendJson(res, 200,
|
|
46328
|
+
return sendJson(res, 200, adminGetHealth(cfg, sessionId));
|
|
46107
46329
|
}
|
|
46108
46330
|
if (req.method === "GET" && parts.length === 3 && parts[2] === "usage") {
|
|
46109
|
-
return sendJson(res, 200, { usage:
|
|
46331
|
+
return sendJson(res, 200, { usage: adminGetUsage(cfg, sessionId) });
|
|
46110
46332
|
}
|
|
46111
46333
|
if (req.method === "GET" && parts.length === 3 && parts[2] === "approvals") {
|
|
46112
|
-
return sendJson(res, 200, { requests:
|
|
46334
|
+
return sendJson(res, 200, { requests: adminListApprovals(cfg, sessionId) });
|
|
46113
46335
|
}
|
|
46114
46336
|
if (req.method === "POST" && parts.length === 4 && parts[2] === "approvals" && parts[3] === "decide") {
|
|
46115
46337
|
const decision = {
|
|
@@ -46121,7 +46343,7 @@ async function handleWebRequest(cfg, req, res, body, url, ctx) {
|
|
|
46121
46343
|
decidedAt: ""
|
|
46122
46344
|
// set server-side
|
|
46123
46345
|
};
|
|
46124
|
-
return sendJson(res, 200,
|
|
46346
|
+
return sendJson(res, 200, adminDecideApproval(cfg, sessionId, decision));
|
|
46125
46347
|
}
|
|
46126
46348
|
}
|
|
46127
46349
|
sendJson(res, 404, { error: "not found" });
|
|
@@ -47022,6 +47244,7 @@ var WEB_MUTATION_PATHS = /* @__PURE__ */ new Set([
|
|
|
47022
47244
|
"/web/admin/share/refresh",
|
|
47023
47245
|
"/web/admin/share/update",
|
|
47024
47246
|
"/web/admin/share/remove",
|
|
47247
|
+
"/web/admin/approvals/decide",
|
|
47025
47248
|
"/web/projects/packs",
|
|
47026
47249
|
"/web/projects/packs/upload",
|
|
47027
47250
|
"/web/projects/packs/remove",
|