@agent-surface/cli 0.11.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/bin.js +14 -6
- package/dist/bin.js.map +1 -1
- package/dist/check-POE5VRSE.js +183 -0
- package/dist/check-POE5VRSE.js.map +1 -0
- package/dist/chunk-J2NN3J5N.js +541 -0
- package/dist/chunk-J2NN3J5N.js.map +1 -0
- package/dist/{chunk-2FG527AM.js → chunk-Q5WOLWEW.js} +269 -25
- package/dist/chunk-Q5WOLWEW.js.map +1 -0
- package/dist/{config-DqK8YqyQ.d.ts → config-rHD2wEws.d.ts} +8 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.js.map +1 -1
- package/dist/{init-ODFEGU3P.js → init-LFQ5R3G7.js} +2 -2
- package/dist/{inspect-6XKULUH2.js → inspect-XMDZBSK2.js} +33 -22
- package/dist/inspect-XMDZBSK2.js.map +1 -0
- package/dist/{snapshot-YGEDJVTG.js → snapshot-FLXE5UC6.js} +9 -7
- package/dist/{snapshot-YGEDJVTG.js.map → snapshot-FLXE5UC6.js.map} +1 -1
- package/dist/vitest.d.ts +1 -1
- package/package.json +4 -4
- package/dist/check-XU3FYNGJ.js +0 -122
- package/dist/check-XU3FYNGJ.js.map +0 -1
- package/dist/chunk-2FG527AM.js.map +0 -1
- package/dist/chunk-AFLVTBI6.js +0 -316
- package/dist/chunk-AFLVTBI6.js.map +0 -1
- package/dist/inspect-6XKULUH2.js.map +0 -1
- /package/dist/{init-ODFEGU3P.js.map → init-LFQ5R3G7.js.map} +0 -0
|
@@ -4,24 +4,40 @@ import {
|
|
|
4
4
|
} from "./chunk-QIVOZAWX.js";
|
|
5
5
|
|
|
6
6
|
// src/baseline.ts
|
|
7
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
-
import { dirname, join, resolve } from "path";
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { dirname, join, resolve, sep } from "path";
|
|
9
9
|
import { serializeSurfaceSnapshot } from "@agent-surface/testing";
|
|
10
10
|
var DEFAULT_BASELINE_DIR = ".agent-surface";
|
|
11
|
+
var SCENARIO_MANIFEST_FILE = "scenarios.json";
|
|
11
12
|
function baselineDirFor(configPath, configured) {
|
|
12
13
|
return resolve(dirname(configPath), configured ?? DEFAULT_BASELINE_DIR);
|
|
13
14
|
}
|
|
14
15
|
function baselinePath(dir, scenario) {
|
|
15
|
-
|
|
16
|
+
const reserved = /* @__PURE__ */ new Set(["scenarios", "coverage-allow", "unresolved-allow"]);
|
|
17
|
+
if (scenario.length === 0 || scenario === "." || scenario === ".." || scenario.includes("/") || scenario.includes("\\") || scenario.includes("\0") || reserved.has(scenario)) {
|
|
18
|
+
throw new Error(`invalid scenario name ${JSON.stringify(scenario)} \u2014 use a filename-safe name`);
|
|
19
|
+
}
|
|
20
|
+
const root = resolve(dir);
|
|
21
|
+
const path = resolve(root, `${scenario}.json`);
|
|
22
|
+
if (!path.startsWith(`${root}${sep}`)) {
|
|
23
|
+
throw new Error(`scenario ${JSON.stringify(scenario)} escapes the baseline directory`);
|
|
24
|
+
}
|
|
25
|
+
return path;
|
|
26
|
+
}
|
|
27
|
+
function scenarioManifestPath(dir) {
|
|
28
|
+
return join(dir, SCENARIO_MANIFEST_FILE);
|
|
16
29
|
}
|
|
17
30
|
function normalize(snapshot) {
|
|
18
31
|
return serializeSurfaceSnapshot(snapshot);
|
|
19
32
|
}
|
|
20
33
|
function readBaseline(path) {
|
|
34
|
+
if (!existsSync(path)) return void 0;
|
|
21
35
|
try {
|
|
22
36
|
return JSON.parse(readFileSync(path, "utf8"));
|
|
23
|
-
} catch {
|
|
24
|
-
|
|
37
|
+
} catch (error) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`could not read baseline ${path}: ${error instanceof Error ? error.message : String(error)}`
|
|
40
|
+
);
|
|
25
41
|
}
|
|
26
42
|
}
|
|
27
43
|
function writeBaseline(path, value) {
|
|
@@ -29,6 +45,18 @@ function writeBaseline(path, value) {
|
|
|
29
45
|
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
30
46
|
`, "utf8");
|
|
31
47
|
}
|
|
48
|
+
function readScenarioManifest(dir) {
|
|
49
|
+
const path = scenarioManifestPath(dir);
|
|
50
|
+
const value = readBaseline(path);
|
|
51
|
+
if (value === void 0) return void 0;
|
|
52
|
+
if (typeof value !== "object" || value === null || !Array.isArray(value.scenarios) || !value.scenarios.every((name) => typeof name === "string")) {
|
|
53
|
+
throw new Error(`${path} must contain { "scenarios": string[] }`);
|
|
54
|
+
}
|
|
55
|
+
return [...value.scenarios].sort();
|
|
56
|
+
}
|
|
57
|
+
function writeScenarioManifest(dir, scenarios) {
|
|
58
|
+
writeBaseline(scenarioManifestPath(dir), { scenarios: [...scenarios].sort() });
|
|
59
|
+
}
|
|
32
60
|
var PATH_SEGMENT = /([^.[\]]+)|\[(\d+)\]/g;
|
|
33
61
|
function subjectFor(document, path) {
|
|
34
62
|
let node = document;
|
|
@@ -97,7 +125,7 @@ function formatValue(value) {
|
|
|
97
125
|
}
|
|
98
126
|
|
|
99
127
|
// src/coverage.ts
|
|
100
|
-
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
128
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
101
129
|
import { join as join2 } from "path";
|
|
102
130
|
var ALLOWLIST_FILE = "coverage-allow.json";
|
|
103
131
|
var UNREAD_ALLOWLIST_FILE = "unresolved-allow.json";
|
|
@@ -108,7 +136,7 @@ function unreadAllowlistPathFor(baselineDir) {
|
|
|
108
136
|
return join2(baselineDir, UNREAD_ALLOWLIST_FILE);
|
|
109
137
|
}
|
|
110
138
|
function readAllowlist(path, keyName = "capabilityId") {
|
|
111
|
-
if (!
|
|
139
|
+
if (!existsSync2(path)) return {};
|
|
112
140
|
let parsed;
|
|
113
141
|
try {
|
|
114
142
|
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
@@ -130,7 +158,7 @@ function readAllowlist(path, keyName = "capabilityId") {
|
|
|
130
158
|
return allowlist;
|
|
131
159
|
}
|
|
132
160
|
function unreadKey(entry) {
|
|
133
|
-
return `${entry.origin.file}#${entry.reason ?? "unknown"}`;
|
|
161
|
+
return `${entry.origin.file}#${entry.reason ?? "unknown"}#${entry.origin.site}`;
|
|
134
162
|
}
|
|
135
163
|
function buildCoverageReport(input) {
|
|
136
164
|
const unreached = [];
|
|
@@ -155,7 +183,8 @@ function buildCoverageReport(input) {
|
|
|
155
183
|
const stillUnread = new Set(input.unresolved.map(unreadKey));
|
|
156
184
|
const staleUnreadAllowlist = Object.keys(unreadAllowlist).filter((key) => !stillUnread.has(key)).sort();
|
|
157
185
|
const unaccounted = [...input.reachedIds].filter((id) => !input.authored.has(id)).sort();
|
|
158
|
-
const domainReached =
|
|
186
|
+
const domainReached = [...input.reachedIds].filter((id) => id.startsWith("domain:")).sort();
|
|
187
|
+
const unmanifestedDomain = input.domainAuthoritative ? unaccounted.filter((id) => id.startsWith("domain:")) : [];
|
|
159
188
|
const undeclared = unaccounted.filter((id) => !id.startsWith("domain:"));
|
|
160
189
|
return {
|
|
161
190
|
authored: input.authored.size,
|
|
@@ -166,6 +195,8 @@ function buildCoverageReport(input) {
|
|
|
166
195
|
unreached,
|
|
167
196
|
undeclared,
|
|
168
197
|
domainReached,
|
|
198
|
+
unmanifestedDomain,
|
|
199
|
+
domainAuthoritative: input.domainAuthoritative === true,
|
|
169
200
|
unresolved: unread,
|
|
170
201
|
allowed,
|
|
171
202
|
staleAllowlist,
|
|
@@ -177,6 +208,7 @@ function buildCoverageReport(input) {
|
|
|
177
208
|
}
|
|
178
209
|
function coverageExitCode(report, options = {}) {
|
|
179
210
|
if (report.unreached.length > 0) return 1;
|
|
211
|
+
if (report.unmanifestedDomain.length > 0) return 1;
|
|
180
212
|
if (report.unresolved.length > 0 && !options.allowUnresolved) return 1;
|
|
181
213
|
if (report.staleAllowlist.length > 0) return 1;
|
|
182
214
|
if (report.staleUnreadAllowlist.length > 0) return 1;
|
|
@@ -184,13 +216,34 @@ function coverageExitCode(report, options = {}) {
|
|
|
184
216
|
}
|
|
185
217
|
|
|
186
218
|
// src/extract.ts
|
|
187
|
-
import {
|
|
219
|
+
import { createHash } from "crypto";
|
|
220
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
188
221
|
import { dirname as dirname2, isAbsolute, join as join3, relative, resolve as resolve2 } from "path";
|
|
189
222
|
import ts from "typescript";
|
|
190
223
|
var UNRESOLVED_ID = "<unresolved>";
|
|
191
224
|
function findTsconfig(from) {
|
|
192
225
|
return ts.findConfigFile(resolve2(from), ts.sys.fileExists, "tsconfig.json");
|
|
193
226
|
}
|
|
227
|
+
function readLiteralConfigScope(configPath) {
|
|
228
|
+
const source = ts.createSourceFile(
|
|
229
|
+
configPath,
|
|
230
|
+
readFileSync3(configPath, "utf8"),
|
|
231
|
+
ts.ScriptTarget.Latest,
|
|
232
|
+
true,
|
|
233
|
+
configPath.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
234
|
+
);
|
|
235
|
+
let scope;
|
|
236
|
+
const visit = (node) => {
|
|
237
|
+
if (scope) return;
|
|
238
|
+
if (ts.isPropertyAssignment(node) && propertyName(node.name) === "scope" && ts.isArrayLiteralExpression(node.initializer)) {
|
|
239
|
+
const values = node.initializer.elements.map((entry) => literalText(entry));
|
|
240
|
+
if (values.every((value) => value !== void 0)) scope = values;
|
|
241
|
+
}
|
|
242
|
+
ts.forEachChild(node, visit);
|
|
243
|
+
};
|
|
244
|
+
visit(source);
|
|
245
|
+
return scope;
|
|
246
|
+
}
|
|
194
247
|
function readProgramFiles(tsconfigPath) {
|
|
195
248
|
const read = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
196
249
|
if (read.error) {
|
|
@@ -210,9 +263,65 @@ function readProgramFiles(tsconfigPath) {
|
|
|
210
263
|
}
|
|
211
264
|
return { fileNames: parsed.fileNames, options: parsed.options };
|
|
212
265
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
266
|
+
var REGISTRATION_HOOKS = /* @__PURE__ */ new Set(["useAgentComponent", "useAgentAction", "useAgentObservation"]);
|
|
267
|
+
function isRegistrationModule(specifier) {
|
|
268
|
+
return specifier.startsWith("@agent-surface/");
|
|
269
|
+
}
|
|
270
|
+
var NO_IMPORTS = { locals: /* @__PURE__ */ new Map(), namespaces: /* @__PURE__ */ new Set() };
|
|
271
|
+
function importedRegistrations(source) {
|
|
272
|
+
const locals = /* @__PURE__ */ new Map();
|
|
273
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
274
|
+
for (const statement of source.statements) {
|
|
275
|
+
if (!ts.isImportDeclaration(statement)) continue;
|
|
276
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
277
|
+
if (!isRegistrationModule(statement.moduleSpecifier.text)) continue;
|
|
278
|
+
const clause = statement.importClause;
|
|
279
|
+
if (!clause || clause.isTypeOnly || !clause.namedBindings) continue;
|
|
280
|
+
if (ts.isNamespaceImport(clause.namedBindings)) {
|
|
281
|
+
namespaces.add(clause.namedBindings.name.text);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
for (const element of clause.namedBindings.elements) {
|
|
285
|
+
if (element.isTypeOnly) continue;
|
|
286
|
+
const imported = (element.propertyName ?? element.name).text;
|
|
287
|
+
if (REGISTRATION_HOOKS.has(imported)) locals.set(element.name.text, imported);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return { locals, namespaces };
|
|
291
|
+
}
|
|
292
|
+
function renamedRegistrationExports(source, imports) {
|
|
293
|
+
const renamed = [];
|
|
294
|
+
for (const statement of source.statements) {
|
|
295
|
+
if (!ts.isExportDeclaration(statement) || statement.isTypeOnly) continue;
|
|
296
|
+
const clause = statement.exportClause;
|
|
297
|
+
if (!clause || !ts.isNamedExports(clause)) continue;
|
|
298
|
+
const from = statement.moduleSpecifier;
|
|
299
|
+
const fromOurs = from !== void 0 && ts.isStringLiteral(from) && isRegistrationModule(from.text);
|
|
300
|
+
if (from && !fromOurs) continue;
|
|
301
|
+
for (const element of clause.elements) {
|
|
302
|
+
if (element.isTypeOnly) continue;
|
|
303
|
+
const local = (element.propertyName ?? element.name).text;
|
|
304
|
+
const hook = fromOurs ? REGISTRATION_HOOKS.has(local) ? local : void 0 : imports.locals.get(local);
|
|
305
|
+
if (hook === void 0 || element.name.text === hook) continue;
|
|
306
|
+
renamed.push({ node: element, hook, exported: element.name.text });
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return renamed;
|
|
310
|
+
}
|
|
311
|
+
function namespaceMember(object, member, imports) {
|
|
312
|
+
return ts.isIdentifier(object) && imports.namespaces.has(object.text) && REGISTRATION_HOOKS.has(member);
|
|
313
|
+
}
|
|
314
|
+
function calleeName(call, imports = NO_IMPORTS) {
|
|
315
|
+
const callee = call.expression;
|
|
316
|
+
if (ts.isIdentifier(callee)) return imports.locals.get(callee.text) ?? callee.text;
|
|
317
|
+
if (ts.isPropertyAccessExpression(callee)) {
|
|
318
|
+
if (namespaceMember(callee.expression, callee.name.text, imports)) return callee.name.text;
|
|
319
|
+
return callee.name.text;
|
|
320
|
+
}
|
|
321
|
+
if (ts.isElementAccessExpression(callee)) {
|
|
322
|
+
const member = literalText(callee.argumentExpression);
|
|
323
|
+
if (member !== void 0 && namespaceMember(callee.expression, member, imports)) return member;
|
|
324
|
+
}
|
|
216
325
|
return void 0;
|
|
217
326
|
}
|
|
218
327
|
function propertyName(name) {
|
|
@@ -375,9 +484,22 @@ function capabilitiesFromGroup(group, kind, componentType, componentPartial, emi
|
|
|
375
484
|
emit.push(capability);
|
|
376
485
|
}
|
|
377
486
|
}
|
|
378
|
-
function visitCall(call, emit, source, deferred, enclosing) {
|
|
379
|
-
const callee = calleeName(call);
|
|
380
|
-
if (callee === void 0)
|
|
487
|
+
function visitCall(call, emit, source, imports, deferred, enclosing) {
|
|
488
|
+
const callee = calleeName(call, imports);
|
|
489
|
+
if (callee === void 0) {
|
|
490
|
+
const object = ts.isElementAccessExpression(call.expression) ? call.expression.expression : void 0;
|
|
491
|
+
if (object && ts.isIdentifier(object) && imports.namespaces.has(object.text)) {
|
|
492
|
+
emit.push({
|
|
493
|
+
capabilityId: UNRESOLVED_ID,
|
|
494
|
+
kind: "action",
|
|
495
|
+
origin: emit.origin(call),
|
|
496
|
+
resolution: "unresolved",
|
|
497
|
+
reason: "dynamic-callee",
|
|
498
|
+
note: `a call reads a computed member of \`${object.text}\`, a namespace of this library \u2014 which export it calls, and so whether it registers anything, cannot be read here`
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
381
503
|
if (GRANULAR_HOOKS.has(callee)) {
|
|
382
504
|
emit.push({
|
|
383
505
|
capabilityId: UNRESOLVED_ID,
|
|
@@ -502,10 +624,84 @@ function callsWrapper(site, wrapper, compilerOptions) {
|
|
|
502
624
|
}
|
|
503
625
|
return false;
|
|
504
626
|
}
|
|
627
|
+
function normalizedText(node, source) {
|
|
628
|
+
return node.getText(source).replace(/\s+/g, " ").trim();
|
|
629
|
+
}
|
|
630
|
+
function siteIdentity(source, node) {
|
|
631
|
+
const labels = [];
|
|
632
|
+
let enclosingCall = "";
|
|
633
|
+
let scope;
|
|
634
|
+
for (let parent = node.parent; parent && parent !== source; parent = parent.parent) {
|
|
635
|
+
if (!enclosingCall && ts.isCallExpression(parent)) {
|
|
636
|
+
enclosingCall = normalizedText(parent, source);
|
|
637
|
+
}
|
|
638
|
+
const named = (ts.isFunctionDeclaration(parent) || ts.isMethodDeclaration(parent)) && parent.name && (ts.isIdentifier(parent.name) || ts.isStringLiteral(parent.name)) ? parent.name.text : ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name) ? parent.name.text : void 0;
|
|
639
|
+
if (named !== void 0) {
|
|
640
|
+
labels.push(named);
|
|
641
|
+
scope ??= parent;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return { labels: labels.reverse(), enclosingCall, scope: scope ?? source };
|
|
645
|
+
}
|
|
646
|
+
function occurrence(scope, node, source) {
|
|
647
|
+
const text = normalizedText(node, source);
|
|
648
|
+
const start = node.getStart(source);
|
|
649
|
+
let rank = 0;
|
|
650
|
+
const visit = (candidate) => {
|
|
651
|
+
if (candidate.kind === node.kind && candidate.getStart(source) < start && normalizedText(candidate, source) === text) {
|
|
652
|
+
rank += 1;
|
|
653
|
+
}
|
|
654
|
+
ts.forEachChild(candidate, visit);
|
|
655
|
+
};
|
|
656
|
+
ts.forEachChild(scope, visit);
|
|
657
|
+
return rank;
|
|
658
|
+
}
|
|
659
|
+
function stableSite(source, node) {
|
|
660
|
+
const { labels, enclosingCall, scope } = siteIdentity(source, node);
|
|
661
|
+
return createHash("sha256").update(
|
|
662
|
+
`${labels.join("/")}\0${enclosingCall}\0${normalizedText(node, source)}\0${occurrence(
|
|
663
|
+
scope,
|
|
664
|
+
node,
|
|
665
|
+
source
|
|
666
|
+
)}`
|
|
667
|
+
).digest("hex").slice(0, 12);
|
|
668
|
+
}
|
|
669
|
+
var packageNameCache = /* @__PURE__ */ new Map();
|
|
670
|
+
function packageNameFor(file) {
|
|
671
|
+
let dir = dirname2(file);
|
|
672
|
+
for (; ; ) {
|
|
673
|
+
if (packageNameCache.has(dir)) return packageNameCache.get(dir);
|
|
674
|
+
const packagePath = join3(dir, "package.json");
|
|
675
|
+
if (existsSync3(packagePath)) {
|
|
676
|
+
let name;
|
|
677
|
+
try {
|
|
678
|
+
const parsed = JSON.parse(readFileSync3(packagePath, "utf8"));
|
|
679
|
+
if (typeof parsed.name === "string") name = parsed.name;
|
|
680
|
+
} catch {
|
|
681
|
+
}
|
|
682
|
+
packageNameCache.set(dir, name);
|
|
683
|
+
return name;
|
|
684
|
+
}
|
|
685
|
+
const parent = dirname2(dir);
|
|
686
|
+
if (parent === dir) return void 0;
|
|
687
|
+
dir = parent;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
var IMPLEMENTATION_PACKAGES = /* @__PURE__ */ new Set([
|
|
691
|
+
"@agent-surface/core",
|
|
692
|
+
"@agent-surface/react",
|
|
693
|
+
"@agent-surface/orpc",
|
|
694
|
+
"@agent-surface/testing",
|
|
695
|
+
"@agent-surface/webmcp",
|
|
696
|
+
"@agent-surface/cli"
|
|
697
|
+
]);
|
|
698
|
+
function isAgentSurfaceImplementation(file) {
|
|
699
|
+
return IMPLEMENTATION_PACKAGES.has(packageNameFor(file) ?? "");
|
|
700
|
+
}
|
|
505
701
|
function extractCapabilities(options) {
|
|
506
702
|
const root = resolve2(options.root);
|
|
507
703
|
const tsconfigPath = options.tsconfig ? isAbsolute(options.tsconfig) ? options.tsconfig : join3(root, options.tsconfig) : findTsconfig(root);
|
|
508
|
-
if (!tsconfigPath || !
|
|
704
|
+
if (!tsconfigPath || !existsSync3(tsconfigPath)) {
|
|
509
705
|
throw new Error(
|
|
510
706
|
`no tsconfig.json found from ${root} \u2014 \`capabilities\` reads the TypeScript program, so it needs one (pass --tsconfig to point at it)`
|
|
511
707
|
);
|
|
@@ -520,7 +716,7 @@ function extractCapabilities(options) {
|
|
|
520
716
|
for (const source of program.getSourceFiles()) {
|
|
521
717
|
if (source.isDeclarationFile) continue;
|
|
522
718
|
if (source.fileName.includes("/node_modules/")) continue;
|
|
523
|
-
if (!isInside(root, source.fileName)) {
|
|
719
|
+
if (!isInside(root, source.fileName) && isAgentSurfaceImplementation(source.fileName)) {
|
|
524
720
|
filesOutsideRoot += 1;
|
|
525
721
|
continue;
|
|
526
722
|
}
|
|
@@ -529,9 +725,21 @@ function extractCapabilities(options) {
|
|
|
529
725
|
push: (capability) => capabilities.push(capability),
|
|
530
726
|
origin: (node) => ({
|
|
531
727
|
file: relative(root, source.fileName),
|
|
532
|
-
line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1
|
|
728
|
+
line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1,
|
|
729
|
+
site: stableSite(source, node)
|
|
533
730
|
})
|
|
534
731
|
};
|
|
732
|
+
const imports = importedRegistrations(source);
|
|
733
|
+
for (const renamed of renamedRegistrationExports(source, imports)) {
|
|
734
|
+
emit.push({
|
|
735
|
+
capabilityId: UNRESOLVED_ID,
|
|
736
|
+
kind: "action",
|
|
737
|
+
origin: emit.origin(renamed.node),
|
|
738
|
+
resolution: "unresolved",
|
|
739
|
+
reason: "dynamic-callee",
|
|
740
|
+
note: `${renamed.hook}() leaves this module as \`${renamed.exported}\`, so nothing at its call sites elsewhere proves they register anything \u2014 whatever they author is not in this catalog`
|
|
741
|
+
});
|
|
742
|
+
}
|
|
535
743
|
let pendingName;
|
|
536
744
|
const visit = (node, enclosing) => {
|
|
537
745
|
const fn = functionLike(node);
|
|
@@ -543,7 +751,7 @@ function extractCapabilities(options) {
|
|
|
543
751
|
pendingName = node.name.text;
|
|
544
752
|
}
|
|
545
753
|
if (ts.isCallExpression(node)) {
|
|
546
|
-
visitCall(node, emit, source, deferred, enclosing);
|
|
754
|
+
visitCall(node, emit, source, imports, deferred, enclosing);
|
|
547
755
|
if (ts.isIdentifier(node.expression)) {
|
|
548
756
|
const name = node.expression.text;
|
|
549
757
|
const sites = callsByName.get(name) ?? [];
|
|
@@ -634,6 +842,9 @@ function readInventory(options) {
|
|
|
634
842
|
...options.tsconfig ? { tsconfig: options.tsconfig } : {}
|
|
635
843
|
});
|
|
636
844
|
}
|
|
845
|
+
function staticConfigScope(options) {
|
|
846
|
+
return options.scope ?? readLiteralConfigScope(options.configPath);
|
|
847
|
+
}
|
|
637
848
|
async function mountScenarios(options, onEach) {
|
|
638
849
|
if (options.depth === "static") return void 0;
|
|
639
850
|
const runner = await createSurfaceRunner(options.configPath);
|
|
@@ -644,6 +855,7 @@ async function mountScenarios(options, onEach) {
|
|
|
644
855
|
);
|
|
645
856
|
}
|
|
646
857
|
const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;
|
|
858
|
+
const effectiveScope = options.scope ?? runner.config.scope;
|
|
647
859
|
const results = [];
|
|
648
860
|
const failures = [];
|
|
649
861
|
for (const scenario of scenarios) {
|
|
@@ -665,12 +877,16 @@ async function mountScenarios(options, onEach) {
|
|
|
665
877
|
}
|
|
666
878
|
return {
|
|
667
879
|
scenarios,
|
|
880
|
+
declaredScenarios: runner.scenarioNames,
|
|
668
881
|
results,
|
|
669
882
|
failures,
|
|
670
883
|
baselineDir: baselineDirFor(
|
|
671
884
|
options.configPath,
|
|
672
885
|
options.baselineDir ?? runner.config.baselineDir
|
|
673
|
-
)
|
|
886
|
+
),
|
|
887
|
+
...effectiveScope ? { scope: effectiveScope } : {},
|
|
888
|
+
domainCapabilities: Object.keys(runner.config.manifest?.tools ?? {}).map((path) => `domain:${path}`).sort(),
|
|
889
|
+
domainManifestConfigured: runner.config.manifest !== void 0
|
|
674
890
|
};
|
|
675
891
|
} finally {
|
|
676
892
|
await runner.close();
|
|
@@ -681,10 +897,23 @@ function componentTypeOf(capabilityId) {
|
|
|
681
897
|
const dot = withoutPlane.lastIndexOf(".");
|
|
682
898
|
return dot === -1 ? withoutPlane : withoutPlane.slice(0, dot);
|
|
683
899
|
}
|
|
900
|
+
function scopeInventory(inventory, scope) {
|
|
901
|
+
if (!inventory || !scope) return inventory;
|
|
902
|
+
return {
|
|
903
|
+
...inventory,
|
|
904
|
+
capabilities: inventory.capabilities.filter(
|
|
905
|
+
(capability) => capability.resolution === "unresolved" || matchesScope(componentTypeOf(capability.capabilityId), scope)
|
|
906
|
+
)
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function scopeCapabilityIds(ids, scope) {
|
|
910
|
+
return scope ? ids.filter((id) => matchesScope(componentTypeOf(id), scope)) : ids;
|
|
911
|
+
}
|
|
684
912
|
function joinCoverage(inventory, runtime, options) {
|
|
685
913
|
if (!inventory || !runtime) return void 0;
|
|
686
914
|
if (runtime.failures.length > 0) return void 0;
|
|
687
|
-
const
|
|
915
|
+
const effectiveScope = options.scope ?? runtime.scope;
|
|
916
|
+
const inScope = (capabilityId) => matchesScope(componentTypeOf(capabilityId), effectiveScope);
|
|
688
917
|
const origins = /* @__PURE__ */ new Map();
|
|
689
918
|
for (const capability of inventory.capabilities) {
|
|
690
919
|
if (!origins.has(capability.capabilityId)) {
|
|
@@ -692,6 +921,12 @@ function joinCoverage(inventory, runtime, options) {
|
|
|
692
921
|
}
|
|
693
922
|
}
|
|
694
923
|
const authored = new Set([...authoredIds(inventory)].filter(inScope));
|
|
924
|
+
for (const capabilityId of runtime.domainCapabilities) {
|
|
925
|
+
if (inScope(capabilityId)) authored.add(capabilityId);
|
|
926
|
+
if (!origins.has(capabilityId)) {
|
|
927
|
+
origins.set(capabilityId, { file: "oRPC manifest", line: 0 });
|
|
928
|
+
}
|
|
929
|
+
}
|
|
695
930
|
const reachedIds = /* @__PURE__ */ new Set();
|
|
696
931
|
for (const result of runtime.results) {
|
|
697
932
|
for (const capability of result.explanation.capabilities) {
|
|
@@ -705,13 +940,14 @@ function joinCoverage(inventory, runtime, options) {
|
|
|
705
940
|
);
|
|
706
941
|
const unreadAllowlistPath = unreadAllowlistPathFor(runtime.baselineDir);
|
|
707
942
|
return buildCoverageReport({
|
|
708
|
-
unreadAllowlist: readAllowlist(unreadAllowlistPath, "file#reason"),
|
|
943
|
+
unreadAllowlist: readAllowlist(unreadAllowlistPath, "file#reason#site"),
|
|
709
944
|
unreadAllowlistPath,
|
|
945
|
+
domainAuthoritative: runtime.domainManifestConfigured,
|
|
710
946
|
authored,
|
|
711
947
|
origins,
|
|
712
948
|
reachedIds,
|
|
713
949
|
scenarios: runtime.scenarios,
|
|
714
|
-
...
|
|
950
|
+
...effectiveScope ? { scope: effectiveScope } : {},
|
|
715
951
|
unresolved: unresolved(inventory),
|
|
716
952
|
allowlist,
|
|
717
953
|
allowlistOutOfScope: Object.keys(wholeAllowlist).length - Object.keys(allowlist).length,
|
|
@@ -720,13 +956,18 @@ function joinCoverage(inventory, runtime, options) {
|
|
|
720
956
|
}
|
|
721
957
|
|
|
722
958
|
export {
|
|
959
|
+
SCENARIO_MANIFEST_FILE,
|
|
723
960
|
baselinePath,
|
|
724
961
|
normalize,
|
|
725
962
|
readBaseline,
|
|
726
963
|
writeBaseline,
|
|
964
|
+
readScenarioManifest,
|
|
965
|
+
writeScenarioManifest,
|
|
727
966
|
annotate,
|
|
728
967
|
diff,
|
|
729
968
|
formatValue,
|
|
969
|
+
ALLOWLIST_FILE,
|
|
970
|
+
UNREAD_ALLOWLIST_FILE,
|
|
730
971
|
unreadKey,
|
|
731
972
|
coverageExitCode,
|
|
732
973
|
findTsconfig,
|
|
@@ -734,7 +975,10 @@ export {
|
|
|
734
975
|
authoredIds,
|
|
735
976
|
unresolved,
|
|
736
977
|
readInventory,
|
|
978
|
+
staticConfigScope,
|
|
737
979
|
mountScenarios,
|
|
980
|
+
scopeInventory,
|
|
981
|
+
scopeCapabilityIds,
|
|
738
982
|
joinCoverage
|
|
739
983
|
};
|
|
740
|
-
//# sourceMappingURL=chunk-
|
|
984
|
+
//# sourceMappingURL=chunk-Q5WOLWEW.js.map
|