@fairfox/polly 0.7.1 → 0.7.4
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/src/background/index.d.ts +1 -1
- package/dist/src/background/index.js +10 -3
- package/dist/src/background/index.js.map +9 -9
- package/dist/src/background/message-router.js +10 -3
- package/dist/src/background/message-router.js.map +8 -8
- package/dist/src/index.d.ts +9 -9
- package/dist/src/index.js +74 -67
- package/dist/src/index.js.map +12 -12
- package/dist/src/shared/adapters/chrome/context-menus.chrome.d.ts +1 -1
- package/dist/src/shared/adapters/chrome/tabs.chrome.d.ts +2 -2
- package/dist/src/shared/adapters/context-menus.adapter.d.ts +1 -1
- package/dist/src/shared/adapters/index.d.ts +4 -4
- package/dist/src/shared/adapters/index.js +9 -2
- package/dist/src/shared/adapters/index.js.map +6 -6
- package/dist/src/shared/adapters/tabs.adapter.d.ts +2 -2
- package/dist/src/shared/lib/context-helpers.js +10 -3
- package/dist/src/shared/lib/context-helpers.js.map +8 -8
- package/dist/src/shared/lib/message-bus.js +10 -3
- package/dist/src/shared/lib/message-bus.js.map +7 -7
- package/dist/src/shared/lib/state.js +10 -3
- package/dist/src/shared/lib/state.js.map +8 -8
- package/dist/src/shared/state/app-state.js +10 -3
- package/dist/src/shared/state/app-state.js.map +8 -8
- package/dist/tools/init/src/cli.js +17 -2
- package/dist/tools/init/src/cli.js.map +4 -4
- package/dist/tools/init/templates/pwa/package.json.template +3 -3
- package/dist/tools/teach/src/cli.js +2712 -2442
- package/dist/tools/teach/src/cli.js.map +11 -11
- package/dist/tools/teach/src/index.js +1379 -1379
- package/dist/tools/teach/src/index.js.map +10 -10
- package/dist/tools/test/src/adapters/index.d.ts +8 -8
- package/dist/tools/test/src/adapters/index.js +3 -2
- package/dist/tools/test/src/adapters/index.js.map +6 -6
- package/dist/tools/test/src/index.d.ts +3 -3
- package/dist/tools/test/src/index.js +3 -2
- package/dist/tools/test/src/index.js.map +6 -6
- package/dist/tools/verify/src/cli.js +190 -68
- package/dist/tools/verify/src/cli.js.map +8 -8
- package/dist/tools/visualize/src/cli.js +18 -3
- package/dist/tools/visualize/src/cli.js.map +4 -4
- package/package.json +27 -16
|
@@ -47,8 +47,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
47
47
|
return require.apply(this, arguments);
|
|
48
48
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
49
49
|
});
|
|
50
|
-
// tools/analysis/src/types/index.ts
|
|
51
|
-
var init_types = () => {};
|
|
52
50
|
|
|
53
51
|
// node:path
|
|
54
52
|
var exports_path = {};
|
|
@@ -390,6 +388,150 @@ var init_path = __esm(() => {
|
|
|
390
388
|
path_default = posix;
|
|
391
389
|
});
|
|
392
390
|
|
|
391
|
+
// tools/analysis/src/extract/adr.ts
|
|
392
|
+
var fs = (() => ({}));
|
|
393
|
+
|
|
394
|
+
class ADRExtractor {
|
|
395
|
+
projectRoot;
|
|
396
|
+
constructor(projectRoot) {
|
|
397
|
+
this.projectRoot = projectRoot;
|
|
398
|
+
}
|
|
399
|
+
extract() {
|
|
400
|
+
const adrDir = this.findADRDirectory();
|
|
401
|
+
if (!adrDir || !fs.existsSync(adrDir)) {
|
|
402
|
+
return {
|
|
403
|
+
adrs: [],
|
|
404
|
+
directory: adrDir || join(this.projectRoot, "docs", "adr")
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const files = fs.readdirSync(adrDir).filter((file) => file.endsWith(".md")).map((file) => join(adrDir, file));
|
|
408
|
+
const adrs = [];
|
|
409
|
+
for (const file of files) {
|
|
410
|
+
try {
|
|
411
|
+
const adr = this.parseADR(file);
|
|
412
|
+
if (adr) {
|
|
413
|
+
adrs.push(adr);
|
|
414
|
+
}
|
|
415
|
+
} catch (error) {
|
|
416
|
+
if (process.env["POLLY_DEBUG"]) {
|
|
417
|
+
console.log(`[DEBUG] Failed to parse ADR file ${file}: ${error}`);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
adrs.sort((a, b) => a.id.localeCompare(b.id));
|
|
422
|
+
return {
|
|
423
|
+
adrs,
|
|
424
|
+
directory: adrDir
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
findADRDirectory() {
|
|
428
|
+
const candidates = [
|
|
429
|
+
join(this.projectRoot, "docs", "adr"),
|
|
430
|
+
join(this.projectRoot, "docs", "architecture", "decisions"),
|
|
431
|
+
join(this.projectRoot, "adr"),
|
|
432
|
+
join(this.projectRoot, "architecture", "decisions")
|
|
433
|
+
];
|
|
434
|
+
for (const candidate of candidates) {
|
|
435
|
+
if (fs.existsSync(candidate)) {
|
|
436
|
+
return candidate;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
parseADR(filePath) {
|
|
442
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
443
|
+
const fileName = basename(filePath, ".md");
|
|
444
|
+
const idMatch = fileName.match(/^(\d+)/);
|
|
445
|
+
const id = idMatch?.[1] ?? fileName;
|
|
446
|
+
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
447
|
+
const title = titleMatch?.[1]?.trim() ?? fileName;
|
|
448
|
+
const status = this.extractStatus(content);
|
|
449
|
+
const date = this.extractDate(content);
|
|
450
|
+
const context = this.extractSection(content, "Context");
|
|
451
|
+
const decision = this.extractSection(content, "Decision");
|
|
452
|
+
const consequences = this.extractSection(content, "Consequences");
|
|
453
|
+
const alternativesSection = this.extractSection(content, "Alternatives");
|
|
454
|
+
const alternatives = alternativesSection ? alternativesSection.split(`
|
|
455
|
+
`).filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim()) : undefined;
|
|
456
|
+
const links = this.extractLinks(content);
|
|
457
|
+
if (!context || !decision || !consequences) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
id,
|
|
462
|
+
title,
|
|
463
|
+
status,
|
|
464
|
+
date,
|
|
465
|
+
context,
|
|
466
|
+
decision,
|
|
467
|
+
consequences,
|
|
468
|
+
...alternatives && alternatives.length > 0 ? { alternatives } : {},
|
|
469
|
+
...links.length > 0 ? { links } : {},
|
|
470
|
+
source: filePath
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
extractStatus(content) {
|
|
474
|
+
const statusMatch = content.match(/Status:\s*(\w+)/i);
|
|
475
|
+
if (!statusMatch)
|
|
476
|
+
return "accepted";
|
|
477
|
+
const status = statusMatch[1]?.toLowerCase();
|
|
478
|
+
if (status && ["proposed", "accepted", "deprecated", "superseded"].includes(status)) {
|
|
479
|
+
return status;
|
|
480
|
+
}
|
|
481
|
+
return "accepted";
|
|
482
|
+
}
|
|
483
|
+
extractDate(content) {
|
|
484
|
+
const dateMatch = content.match(/Date:\s*(\d{4}-\d{2}-\d{2})/i) || content.match(/(\d{4}-\d{2}-\d{2})/i);
|
|
485
|
+
if (dateMatch?.[1]) {
|
|
486
|
+
return dateMatch[1];
|
|
487
|
+
}
|
|
488
|
+
const isoDate = new Date().toISOString().split("T")[0];
|
|
489
|
+
return isoDate || new Date().toLocaleDateString("en-CA");
|
|
490
|
+
}
|
|
491
|
+
extractSection(content, sectionName) {
|
|
492
|
+
const regex = new RegExp(`##\\s+${sectionName}\\s*\\n([\\s\\S]*?)(?=\\n##|$)`, "i");
|
|
493
|
+
const match = content.match(regex);
|
|
494
|
+
return match?.[1]?.trim() ?? "";
|
|
495
|
+
}
|
|
496
|
+
extractLinks(content) {
|
|
497
|
+
const links = [];
|
|
498
|
+
const supersedesMatch = content.match(/Supersedes:\s*ADR-(\d+)/gi);
|
|
499
|
+
if (supersedesMatch) {
|
|
500
|
+
for (const match of supersedesMatch) {
|
|
501
|
+
const idMatch = match.match(/ADR-(\d+)/);
|
|
502
|
+
const id = idMatch?.[1];
|
|
503
|
+
if (id) {
|
|
504
|
+
links.push({
|
|
505
|
+
type: "supersedes",
|
|
506
|
+
adrId: id
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const supersededByMatch = content.match(/Superseded by:\s*ADR-(\d+)/gi);
|
|
512
|
+
if (supersededByMatch) {
|
|
513
|
+
for (const match of supersededByMatch) {
|
|
514
|
+
const idMatch = match.match(/ADR-(\d+)/);
|
|
515
|
+
const id = idMatch?.[1];
|
|
516
|
+
if (id) {
|
|
517
|
+
links.push({
|
|
518
|
+
type: "superseded-by",
|
|
519
|
+
adrId: id
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return links;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
function extractADRs(projectRoot) {
|
|
528
|
+
const extractor = new ADRExtractor(projectRoot);
|
|
529
|
+
return extractor.extract();
|
|
530
|
+
}
|
|
531
|
+
var init_adr = __esm(() => {
|
|
532
|
+
init_path();
|
|
533
|
+
});
|
|
534
|
+
|
|
393
535
|
// node:os
|
|
394
536
|
var exports_os = {};
|
|
395
537
|
__export(exports_os, {
|
|
@@ -20018,10 +20160,10 @@ var require_typescript = __commonJS((exports, module) => {
|
|
|
20018
20160
|
function and(f, g) {
|
|
20019
20161
|
return (arg) => f(arg) && g(arg);
|
|
20020
20162
|
}
|
|
20021
|
-
function or(...
|
|
20163
|
+
function or(...fs2) {
|
|
20022
20164
|
return (...args) => {
|
|
20023
20165
|
let lastResult;
|
|
20024
|
-
for (const f of
|
|
20166
|
+
for (const f of fs2) {
|
|
20025
20167
|
lastResult = f(...args);
|
|
20026
20168
|
if (lastResult) {
|
|
20027
20169
|
return lastResult;
|
|
@@ -21550,7 +21692,7 @@ ${lanes.join(`
|
|
|
21550
21692
|
init_ts2();
|
|
21551
21693
|
init_ts_performance();
|
|
21552
21694
|
((tracingEnabled2) => {
|
|
21553
|
-
let
|
|
21695
|
+
let fs2;
|
|
21554
21696
|
let traceCount = 0;
|
|
21555
21697
|
let traceFd = 0;
|
|
21556
21698
|
let mode;
|
|
@@ -21559,9 +21701,9 @@ ${lanes.join(`
|
|
|
21559
21701
|
const legend = [];
|
|
21560
21702
|
function startTracing2(tracingMode, traceDir, configFilePath) {
|
|
21561
21703
|
Debug.assert(!tracing, "Tracing already started");
|
|
21562
|
-
if (
|
|
21704
|
+
if (fs2 === undefined) {
|
|
21563
21705
|
try {
|
|
21564
|
-
|
|
21706
|
+
fs2 = (()=>{throw new Error("Cannot require module "+"fs");})();
|
|
21565
21707
|
} catch (e) {
|
|
21566
21708
|
throw new Error(`tracing requires having fs
|
|
21567
21709
|
(original error: ${e.message || e})`);
|
|
@@ -21572,8 +21714,8 @@ ${lanes.join(`
|
|
|
21572
21714
|
if (legendPath === undefined) {
|
|
21573
21715
|
legendPath = combinePaths(traceDir, "legend.json");
|
|
21574
21716
|
}
|
|
21575
|
-
if (!
|
|
21576
|
-
|
|
21717
|
+
if (!fs2.existsSync(traceDir)) {
|
|
21718
|
+
fs2.mkdirSync(traceDir, { recursive: true });
|
|
21577
21719
|
}
|
|
21578
21720
|
const countPart = mode === "build" ? `.${process.pid}-${++traceCount}` : mode === "server" ? `.${process.pid}` : ``;
|
|
21579
21721
|
const tracePath = combinePaths(traceDir, `trace${countPart}.json`);
|
|
@@ -21583,10 +21725,10 @@ ${lanes.join(`
|
|
|
21583
21725
|
tracePath,
|
|
21584
21726
|
typesPath
|
|
21585
21727
|
});
|
|
21586
|
-
traceFd =
|
|
21728
|
+
traceFd = fs2.openSync(tracePath, "w");
|
|
21587
21729
|
tracing = tracingEnabled2;
|
|
21588
21730
|
const meta = { cat: "__metadata", ph: "M", ts: 1000 * timestamp2(), pid: 1, tid: 1 };
|
|
21589
|
-
|
|
21731
|
+
fs2.writeSync(traceFd, `[
|
|
21590
21732
|
` + [{ name: "process_name", args: { name: "tsc" }, ...meta }, { name: "thread_name", args: { name: "Main" }, ...meta }, { name: "TracingStartedInBrowser", ...meta, cat: "disabled-by-default-devtools.timeline" }].map((v) => JSON.stringify(v)).join(`,
|
|
21591
21733
|
`));
|
|
21592
21734
|
}
|
|
@@ -21594,10 +21736,10 @@ ${lanes.join(`
|
|
|
21594
21736
|
function stopTracing() {
|
|
21595
21737
|
Debug.assert(tracing, "Tracing is not in progress");
|
|
21596
21738
|
Debug.assert(!!typeCatalog.length === (mode !== "server"));
|
|
21597
|
-
|
|
21739
|
+
fs2.writeSync(traceFd, `
|
|
21598
21740
|
]
|
|
21599
21741
|
`);
|
|
21600
|
-
|
|
21742
|
+
fs2.closeSync(traceFd);
|
|
21601
21743
|
tracing = undefined;
|
|
21602
21744
|
if (typeCatalog.length) {
|
|
21603
21745
|
dumpTypes(typeCatalog);
|
|
@@ -21662,13 +21804,13 @@ ${lanes.join(`
|
|
|
21662
21804
|
if (mode === "server" && phase === "checkTypes")
|
|
21663
21805
|
return;
|
|
21664
21806
|
mark("beginTracing");
|
|
21665
|
-
|
|
21807
|
+
fs2.writeSync(traceFd, `,
|
|
21666
21808
|
{"pid":1,"tid":1,"ph":"${eventType}","cat":"${phase}","ts":${time},"name":"${name}"`);
|
|
21667
21809
|
if (extras)
|
|
21668
|
-
|
|
21810
|
+
fs2.writeSync(traceFd, `,${extras}`);
|
|
21669
21811
|
if (args)
|
|
21670
|
-
|
|
21671
|
-
|
|
21812
|
+
fs2.writeSync(traceFd, `,"args":${JSON.stringify(args)}`);
|
|
21813
|
+
fs2.writeSync(traceFd, `}`);
|
|
21672
21814
|
mark("endTracing");
|
|
21673
21815
|
measure("Tracing", "beginTracing", "endTracing");
|
|
21674
21816
|
}
|
|
@@ -21690,9 +21832,9 @@ ${lanes.join(`
|
|
|
21690
21832
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
|
|
21691
21833
|
mark("beginDumpTypes");
|
|
21692
21834
|
const typesPath = legend[legend.length - 1].typesPath;
|
|
21693
|
-
const typesFd =
|
|
21835
|
+
const typesFd = fs2.openSync(typesPath, "w");
|
|
21694
21836
|
const recursionIdentityMap = /* @__PURE__ */ new Map;
|
|
21695
|
-
|
|
21837
|
+
fs2.writeSync(typesFd, "[");
|
|
21696
21838
|
const numTypes = types2.length;
|
|
21697
21839
|
for (let i2 = 0;i2 < numTypes; i2++) {
|
|
21698
21840
|
const type2 = types2[i2];
|
|
@@ -21788,15 +21930,15 @@ ${lanes.join(`
|
|
|
21788
21930
|
flags: Debug.formatTypeFlags(type2.flags).split("|"),
|
|
21789
21931
|
display
|
|
21790
21932
|
};
|
|
21791
|
-
|
|
21933
|
+
fs2.writeSync(typesFd, JSON.stringify(descriptor));
|
|
21792
21934
|
if (i2 < numTypes - 1) {
|
|
21793
|
-
|
|
21935
|
+
fs2.writeSync(typesFd, `,
|
|
21794
21936
|
`);
|
|
21795
21937
|
}
|
|
21796
21938
|
}
|
|
21797
|
-
|
|
21939
|
+
fs2.writeSync(typesFd, `]
|
|
21798
21940
|
`);
|
|
21799
|
-
|
|
21941
|
+
fs2.closeSync(typesFd);
|
|
21800
21942
|
mark("endDumpTypes");
|
|
21801
21943
|
measure("Dump types", "beginDumpTypes", "endDumpTypes");
|
|
21802
21944
|
}
|
|
@@ -21804,7 +21946,7 @@ ${lanes.join(`
|
|
|
21804
21946
|
if (!legendPath) {
|
|
21805
21947
|
return;
|
|
21806
21948
|
}
|
|
21807
|
-
|
|
21949
|
+
fs2.writeFileSync(legendPath, JSON.stringify(legend));
|
|
21808
21950
|
}
|
|
21809
21951
|
tracingEnabled2.dumpLegend = dumpLegend;
|
|
21810
21952
|
})(tracingEnabled || (tracingEnabled = {}));
|
|
@@ -21817,7 +21959,7 @@ ${lanes.join(`
|
|
|
21817
21959
|
return lowerCase ? name.toLowerCase() : name;
|
|
21818
21960
|
}
|
|
21819
21961
|
var SyntaxKind, NodeFlags, ModifierFlags, JsxFlags, RelationComparisonResult, GeneratedIdentifierFlags, TokenFlags, FlowFlags, CommentDirectiveType, OperationCanceledException, FileIncludeKind, FilePreprocessingDiagnosticsKind, EmitOnly, StructureIsReused, ExitStatus, MemberOverrideStatus, UnionReduction, ContextFlags, NodeBuilderFlags, TypeFormatFlags, SymbolFormatFlags, SymbolAccessibility, SyntheticSymbolKind, TypePredicateKind, TypeReferenceSerializationKind, SymbolFlags, EnumKind, CheckFlags, InternalSymbolName, NodeCheckFlags, TypeFlags, ObjectFlags, VarianceFlags, ElementFlags, AccessFlags, IndexFlags, JsxReferenceKind, SignatureKind, SignatureFlags, IndexKind, TypeMapKind, InferencePriority, InferenceFlags, Ternary, AssignmentDeclarationKind, DiagnosticCategory, ModuleResolutionKind, ModuleDetectionKind, WatchFileKind, WatchDirectoryKind, PollingWatchKind, ModuleKind, JsxEmit, ImportsNotUsedAsValues, NewLineKind, ScriptKind, ScriptTarget, LanguageVariant, WatchDirectoryFlags, CharacterCodes, Extension, TransformFlags, SnippetKind, EmitFlags, InternalEmitFlags, ExternalEmitHelpers, EmitHint, OuterExpressionKinds, LexicalEnvironmentFlags, BundleFileSectionKind, ListFormat, PragmaKindFlags, commentPragmas, JSDocParsingMode;
|
|
21820
|
-
var
|
|
21962
|
+
var init_types = __esm2({
|
|
21821
21963
|
"src/compiler/types.ts"() {
|
|
21822
21964
|
SyntaxKind = /* @__PURE__ */ ((SyntaxKind5) => {
|
|
21823
21965
|
SyntaxKind5[SyntaxKind5["Unknown"] = 0] = "Unknown";
|
|
@@ -25138,7 +25280,7 @@ ${lanes.join(`
|
|
|
25138
25280
|
var Diagnostics;
|
|
25139
25281
|
var init_diagnosticInformationMap_generated = __esm2({
|
|
25140
25282
|
"src/compiler/diagnosticInformationMap.generated.ts"() {
|
|
25141
|
-
|
|
25283
|
+
init_types();
|
|
25142
25284
|
Diagnostics = {
|
|
25143
25285
|
Unterminated_string_literal: diag(1002, 1, "Unterminated_string_literal_1002", "Unterminated string literal."),
|
|
25144
25286
|
Identifier_expected: diag(1003, 1, "Identifier_expected_1003", "Identifier expected."),
|
|
@@ -126749,7 +126891,7 @@ ${lanes.join(`
|
|
|
126749
126891
|
init_performanceCore();
|
|
126750
126892
|
init_perfLogger();
|
|
126751
126893
|
init_tracing();
|
|
126752
|
-
|
|
126894
|
+
init_types();
|
|
126753
126895
|
init_sys();
|
|
126754
126896
|
init_path2();
|
|
126755
126897
|
init_diagnosticInformationMap_generated();
|
|
@@ -126859,13 +127001,13 @@ ${lanes.join(`
|
|
|
126859
127001
|
`;
|
|
126860
127002
|
}
|
|
126861
127003
|
});
|
|
126862
|
-
var
|
|
127004
|
+
var init_types2 = __esm2({
|
|
126863
127005
|
"src/jsTyping/types.ts"() {}
|
|
126864
127006
|
});
|
|
126865
127007
|
var init_ts_server = __esm2({
|
|
126866
127008
|
"src/jsTyping/_namespaces/ts.server.ts"() {
|
|
126867
127009
|
init_shared();
|
|
126868
|
-
|
|
127010
|
+
init_types2();
|
|
126869
127011
|
}
|
|
126870
127012
|
});
|
|
126871
127013
|
function isTypingUpToDate(cachedTyping, availableTypingVersions) {
|
|
@@ -212515,31 +212657,31 @@ interface CSSNumericArray{[Symbol.iterator]():IterableIterator<CSSNumericValue>;
|
|
|
212515
212657
|
yield path2;
|
|
212516
212658
|
}
|
|
212517
212659
|
}
|
|
212518
|
-
var
|
|
212660
|
+
var fs2 = runtime.fs;
|
|
212519
212661
|
|
|
212520
212662
|
class RealFileSystemHost {
|
|
212521
212663
|
async delete(path2) {
|
|
212522
212664
|
try {
|
|
212523
|
-
await
|
|
212665
|
+
await fs2.delete(path2);
|
|
212524
212666
|
} catch (err) {
|
|
212525
212667
|
throw this.#getFileNotFoundErrorIfNecessary(err, path2);
|
|
212526
212668
|
}
|
|
212527
212669
|
}
|
|
212528
212670
|
deleteSync(path2) {
|
|
212529
212671
|
try {
|
|
212530
|
-
|
|
212672
|
+
fs2.deleteSync(path2);
|
|
212531
212673
|
} catch (err) {
|
|
212532
212674
|
throw this.#getFileNotFoundErrorIfNecessary(err, path2);
|
|
212533
212675
|
}
|
|
212534
212676
|
}
|
|
212535
212677
|
readDirSync(dirPath) {
|
|
212536
212678
|
try {
|
|
212537
|
-
const entries =
|
|
212679
|
+
const entries = fs2.readDirSync(dirPath);
|
|
212538
212680
|
for (const entry of entries) {
|
|
212539
212681
|
entry.name = FileUtils.pathJoin(dirPath, entry.name);
|
|
212540
212682
|
if (entry.isSymlink) {
|
|
212541
212683
|
try {
|
|
212542
|
-
const info =
|
|
212684
|
+
const info = fs2.statSync(entry.name);
|
|
212543
212685
|
if (info != null) {
|
|
212544
212686
|
entry.isDirectory = info.isDirectory();
|
|
212545
212687
|
entry.isFile = info.isFile();
|
|
@@ -212554,84 +212696,84 @@ interface CSSNumericArray{[Symbol.iterator]():IterableIterator<CSSNumericValue>;
|
|
|
212554
212696
|
}
|
|
212555
212697
|
async readFile(filePath, encoding = "utf-8") {
|
|
212556
212698
|
try {
|
|
212557
|
-
return await
|
|
212699
|
+
return await fs2.readFile(filePath, encoding);
|
|
212558
212700
|
} catch (err) {
|
|
212559
212701
|
throw this.#getFileNotFoundErrorIfNecessary(err, filePath);
|
|
212560
212702
|
}
|
|
212561
212703
|
}
|
|
212562
212704
|
readFileSync(filePath, encoding = "utf-8") {
|
|
212563
212705
|
try {
|
|
212564
|
-
return
|
|
212706
|
+
return fs2.readFileSync(filePath, encoding);
|
|
212565
212707
|
} catch (err) {
|
|
212566
212708
|
throw this.#getFileNotFoundErrorIfNecessary(err, filePath);
|
|
212567
212709
|
}
|
|
212568
212710
|
}
|
|
212569
212711
|
async writeFile(filePath, fileText) {
|
|
212570
|
-
return
|
|
212712
|
+
return fs2.writeFile(filePath, fileText);
|
|
212571
212713
|
}
|
|
212572
212714
|
writeFileSync(filePath, fileText) {
|
|
212573
|
-
|
|
212715
|
+
fs2.writeFileSync(filePath, fileText);
|
|
212574
212716
|
}
|
|
212575
212717
|
mkdir(dirPath) {
|
|
212576
|
-
return
|
|
212718
|
+
return fs2.mkdir(dirPath);
|
|
212577
212719
|
}
|
|
212578
212720
|
mkdirSync(dirPath) {
|
|
212579
|
-
|
|
212721
|
+
fs2.mkdirSync(dirPath);
|
|
212580
212722
|
}
|
|
212581
212723
|
move(srcPath, destPath) {
|
|
212582
|
-
return
|
|
212724
|
+
return fs2.move(srcPath, destPath);
|
|
212583
212725
|
}
|
|
212584
212726
|
moveSync(srcPath, destPath) {
|
|
212585
|
-
|
|
212727
|
+
fs2.moveSync(srcPath, destPath);
|
|
212586
212728
|
}
|
|
212587
212729
|
copy(srcPath, destPath) {
|
|
212588
|
-
return
|
|
212730
|
+
return fs2.copy(srcPath, destPath);
|
|
212589
212731
|
}
|
|
212590
212732
|
copySync(srcPath, destPath) {
|
|
212591
|
-
|
|
212733
|
+
fs2.copySync(srcPath, destPath);
|
|
212592
212734
|
}
|
|
212593
212735
|
async fileExists(filePath) {
|
|
212594
212736
|
try {
|
|
212595
|
-
return (await
|
|
212737
|
+
return (await fs2.stat(filePath))?.isFile() ?? false;
|
|
212596
212738
|
} catch {
|
|
212597
212739
|
return false;
|
|
212598
212740
|
}
|
|
212599
212741
|
}
|
|
212600
212742
|
fileExistsSync(filePath) {
|
|
212601
212743
|
try {
|
|
212602
|
-
return
|
|
212744
|
+
return fs2.statSync(filePath)?.isFile() ?? false;
|
|
212603
212745
|
} catch {
|
|
212604
212746
|
return false;
|
|
212605
212747
|
}
|
|
212606
212748
|
}
|
|
212607
212749
|
async directoryExists(dirPath) {
|
|
212608
212750
|
try {
|
|
212609
|
-
return (await
|
|
212751
|
+
return (await fs2.stat(dirPath))?.isDirectory() ?? false;
|
|
212610
212752
|
} catch {
|
|
212611
212753
|
return false;
|
|
212612
212754
|
}
|
|
212613
212755
|
}
|
|
212614
212756
|
directoryExistsSync(dirPath) {
|
|
212615
212757
|
try {
|
|
212616
|
-
return
|
|
212758
|
+
return fs2.statSync(dirPath)?.isDirectory() ?? false;
|
|
212617
212759
|
} catch {
|
|
212618
212760
|
return false;
|
|
212619
212761
|
}
|
|
212620
212762
|
}
|
|
212621
212763
|
realpathSync(path2) {
|
|
212622
|
-
return
|
|
212764
|
+
return fs2.realpathSync(path2);
|
|
212623
212765
|
}
|
|
212624
212766
|
getCurrentDirectory() {
|
|
212625
|
-
return FileUtils.standardizeSlashes(
|
|
212767
|
+
return FileUtils.standardizeSlashes(fs2.getCurrentDirectory());
|
|
212626
212768
|
}
|
|
212627
212769
|
glob(patterns) {
|
|
212628
|
-
return
|
|
212770
|
+
return fs2.glob(backSlashesToForward(patterns));
|
|
212629
212771
|
}
|
|
212630
212772
|
globSync(patterns) {
|
|
212631
|
-
return
|
|
212773
|
+
return fs2.globSync(backSlashesToForward(patterns));
|
|
212632
212774
|
}
|
|
212633
212775
|
isCaseSensitive() {
|
|
212634
|
-
return
|
|
212776
|
+
return fs2.isCaseSensitive();
|
|
212635
212777
|
}
|
|
212636
212778
|
#getDirectoryNotFoundErrorIfNecessary(err, path2) {
|
|
212637
212779
|
return FileUtils.isNotExistsError(err) ? new exports.errors.DirectoryNotFoundError(FileUtils.getStandardizedAbsolutePath(this, path2)) : err;
|
|
@@ -235621,6 +235763,479 @@ Node text: ${this.#forgottenText}`;
|
|
|
235621
235763
|
exports.setScopeForNode = setScopeForNode;
|
|
235622
235764
|
});
|
|
235623
235765
|
|
|
235766
|
+
// tools/analysis/src/extract/contexts.ts
|
|
235767
|
+
class ContextAnalyzer {
|
|
235768
|
+
project;
|
|
235769
|
+
constructor(tsConfigPath) {
|
|
235770
|
+
this.project = new import_ts_morph.Project({
|
|
235771
|
+
tsConfigFilePath: tsConfigPath
|
|
235772
|
+
});
|
|
235773
|
+
}
|
|
235774
|
+
analyzeContext(contextType, entryPoint, handlers) {
|
|
235775
|
+
const sourceFile = this.project.getSourceFile(entryPoint);
|
|
235776
|
+
if (!sourceFile) {
|
|
235777
|
+
throw new Error(`Could not find source file: ${entryPoint}`);
|
|
235778
|
+
}
|
|
235779
|
+
const chromeAPIs = this.extractChromeAPIs(sourceFile);
|
|
235780
|
+
const dependencies = this.extractDependencies(sourceFile);
|
|
235781
|
+
const description = this.extractDescription(sourceFile);
|
|
235782
|
+
const components = this.isUIContext(contextType) ? this.extractComponents(sourceFile) : undefined;
|
|
235783
|
+
const contextHandlers = handlers.filter((h) => h.node === contextType);
|
|
235784
|
+
return {
|
|
235785
|
+
type: contextType,
|
|
235786
|
+
entryPoint,
|
|
235787
|
+
handlers: contextHandlers,
|
|
235788
|
+
chromeAPIs,
|
|
235789
|
+
externalAPIs: [],
|
|
235790
|
+
...components ? { components } : {},
|
|
235791
|
+
dependencies,
|
|
235792
|
+
...description ? { description } : {}
|
|
235793
|
+
};
|
|
235794
|
+
}
|
|
235795
|
+
extractChromeAPIs(sourceFile) {
|
|
235796
|
+
const apis = new Set;
|
|
235797
|
+
sourceFile.forEachDescendant((node) => {
|
|
235798
|
+
if (import_ts_morph.Node.isPropertyAccessExpression(node)) {
|
|
235799
|
+
const text = node.getText();
|
|
235800
|
+
this.detectAPIPattern(text, apis);
|
|
235801
|
+
}
|
|
235802
|
+
});
|
|
235803
|
+
return Array.from(apis).sort();
|
|
235804
|
+
}
|
|
235805
|
+
detectAPIPattern(text, apis) {
|
|
235806
|
+
if (text.startsWith("chrome.")) {
|
|
235807
|
+
const api = this.extractAPIFromPrefix(text, "chrome");
|
|
235808
|
+
if (api)
|
|
235809
|
+
apis.add(api);
|
|
235810
|
+
return;
|
|
235811
|
+
}
|
|
235812
|
+
if (text.startsWith("browser.")) {
|
|
235813
|
+
const api = this.extractAPIFromPrefix(text, "browser");
|
|
235814
|
+
if (api)
|
|
235815
|
+
apis.add(api);
|
|
235816
|
+
return;
|
|
235817
|
+
}
|
|
235818
|
+
if (text.includes("bus.adapters.")) {
|
|
235819
|
+
const api = this.extractAPIFromBusAdapter(text);
|
|
235820
|
+
if (api)
|
|
235821
|
+
apis.add(api);
|
|
235822
|
+
}
|
|
235823
|
+
}
|
|
235824
|
+
extractAPIFromPrefix(text, prefix) {
|
|
235825
|
+
const pattern = new RegExp(`^${prefix}\\.([^.(]+(?:\\.[^.(]+)?)`);
|
|
235826
|
+
const match = text.match(pattern);
|
|
235827
|
+
return match?.[1] || null;
|
|
235828
|
+
}
|
|
235829
|
+
extractAPIFromBusAdapter(text) {
|
|
235830
|
+
const match = text.match(/bus\.adapters\.([^.(]+)/);
|
|
235831
|
+
return match?.[1] || null;
|
|
235832
|
+
}
|
|
235833
|
+
extractDependencies(sourceFile) {
|
|
235834
|
+
const deps = [];
|
|
235835
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
235836
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
235837
|
+
deps.push(moduleSpecifier);
|
|
235838
|
+
}
|
|
235839
|
+
return deps;
|
|
235840
|
+
}
|
|
235841
|
+
extractDescription(sourceFile) {
|
|
235842
|
+
const firstStatement = sourceFile.getStatements()[0];
|
|
235843
|
+
if (!firstStatement)
|
|
235844
|
+
return;
|
|
235845
|
+
const leadingComments = firstStatement.getLeadingCommentRanges();
|
|
235846
|
+
if (leadingComments.length === 0)
|
|
235847
|
+
return;
|
|
235848
|
+
const comment = leadingComments[0].getText();
|
|
235849
|
+
const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
|
|
235850
|
+
if (descMatch) {
|
|
235851
|
+
return descMatch[1].trim();
|
|
235852
|
+
}
|
|
235853
|
+
const lines = comment.split(`
|
|
235854
|
+
`).map((l) => l.replace(/^[\s*]+/, "").trim()).filter((l) => l && !l.startsWith("@"));
|
|
235855
|
+
return lines[0] || undefined;
|
|
235856
|
+
}
|
|
235857
|
+
extractComponents(sourceFile) {
|
|
235858
|
+
const components = [];
|
|
235859
|
+
sourceFile.forEachDescendant((node) => {
|
|
235860
|
+
this.extractFunctionComponent(node, sourceFile, components);
|
|
235861
|
+
this.extractArrowFunctionComponent(node, sourceFile, components);
|
|
235862
|
+
this.extractClassComponent(node, sourceFile, components);
|
|
235863
|
+
});
|
|
235864
|
+
return components;
|
|
235865
|
+
}
|
|
235866
|
+
extractFunctionComponent(node, sourceFile, components) {
|
|
235867
|
+
if (!import_ts_morph.Node.isFunctionDeclaration(node))
|
|
235868
|
+
return;
|
|
235869
|
+
const name = node.getName();
|
|
235870
|
+
if (!name || !this.looksLikeComponent(name, node))
|
|
235871
|
+
return;
|
|
235872
|
+
const description = this.extractJSDocDescription(node);
|
|
235873
|
+
components.push({
|
|
235874
|
+
name,
|
|
235875
|
+
type: "function",
|
|
235876
|
+
filePath: sourceFile.getFilePath(),
|
|
235877
|
+
line: node.getStartLineNumber(),
|
|
235878
|
+
props: this.extractProps(node),
|
|
235879
|
+
...description ? { description } : {}
|
|
235880
|
+
});
|
|
235881
|
+
}
|
|
235882
|
+
extractArrowFunctionComponent(node, sourceFile, components) {
|
|
235883
|
+
if (!import_ts_morph.Node.isVariableDeclaration(node))
|
|
235884
|
+
return;
|
|
235885
|
+
const name = node.getName();
|
|
235886
|
+
const initializer = node.getInitializer();
|
|
235887
|
+
if (!name || !initializer)
|
|
235888
|
+
return;
|
|
235889
|
+
if (!import_ts_morph.Node.isArrowFunction(initializer) && !import_ts_morph.Node.isFunctionExpression(initializer))
|
|
235890
|
+
return;
|
|
235891
|
+
if (!this.looksLikeComponent(name, initializer))
|
|
235892
|
+
return;
|
|
235893
|
+
const description = this.extractJSDocDescription(node);
|
|
235894
|
+
components.push({
|
|
235895
|
+
name,
|
|
235896
|
+
type: "function",
|
|
235897
|
+
filePath: sourceFile.getFilePath(),
|
|
235898
|
+
line: node.getStartLineNumber(),
|
|
235899
|
+
props: this.extractProps(initializer),
|
|
235900
|
+
...description ? { description } : {}
|
|
235901
|
+
});
|
|
235902
|
+
}
|
|
235903
|
+
extractClassComponent(node, sourceFile, components) {
|
|
235904
|
+
if (!import_ts_morph.Node.isClassDeclaration(node))
|
|
235905
|
+
return;
|
|
235906
|
+
const name = node.getName();
|
|
235907
|
+
if (!name || !this.looksLikeClassComponent(node))
|
|
235908
|
+
return;
|
|
235909
|
+
const description = this.extractJSDocDescription(node);
|
|
235910
|
+
components.push({
|
|
235911
|
+
name,
|
|
235912
|
+
type: "class",
|
|
235913
|
+
filePath: sourceFile.getFilePath(),
|
|
235914
|
+
line: node.getStartLineNumber(),
|
|
235915
|
+
props: this.extractPropsFromClass(node),
|
|
235916
|
+
...description ? { description } : {}
|
|
235917
|
+
});
|
|
235918
|
+
}
|
|
235919
|
+
looksLikeComponent(name, node) {
|
|
235920
|
+
if (!/^[A-Z]/.test(name))
|
|
235921
|
+
return false;
|
|
235922
|
+
const body = node.getBody();
|
|
235923
|
+
if (!body)
|
|
235924
|
+
return false;
|
|
235925
|
+
let hasJSX = false;
|
|
235926
|
+
if (import_ts_morph.Node.isBlock(body)) {
|
|
235927
|
+
body.forEachDescendant((child) => {
|
|
235928
|
+
if (import_ts_morph.Node.isJsxElement(child) || import_ts_morph.Node.isJsxSelfClosingElement(child)) {
|
|
235929
|
+
hasJSX = true;
|
|
235930
|
+
}
|
|
235931
|
+
});
|
|
235932
|
+
} else if (import_ts_morph.Node.isJsxElement(body) || import_ts_morph.Node.isJsxSelfClosingElement(body)) {
|
|
235933
|
+
hasJSX = true;
|
|
235934
|
+
}
|
|
235935
|
+
return hasJSX;
|
|
235936
|
+
}
|
|
235937
|
+
looksLikeClassComponent(node) {
|
|
235938
|
+
const extendedTypes = node.getExtends();
|
|
235939
|
+
if (!extendedTypes)
|
|
235940
|
+
return false;
|
|
235941
|
+
const extendsText = extendedTypes.getText();
|
|
235942
|
+
return /Component|PureComponent/.test(extendsText);
|
|
235943
|
+
}
|
|
235944
|
+
extractProps(node) {
|
|
235945
|
+
const params = node.getParameters();
|
|
235946
|
+
if (params.length === 0)
|
|
235947
|
+
return [];
|
|
235948
|
+
const propsParam = params[0];
|
|
235949
|
+
const type2 = propsParam.getType();
|
|
235950
|
+
const props = [];
|
|
235951
|
+
for (const prop of type2.getProperties()) {
|
|
235952
|
+
props.push(prop.getName());
|
|
235953
|
+
}
|
|
235954
|
+
return props;
|
|
235955
|
+
}
|
|
235956
|
+
extractPropsFromClass(node) {
|
|
235957
|
+
const extendedTypes = node.getExtends();
|
|
235958
|
+
if (!extendedTypes)
|
|
235959
|
+
return [];
|
|
235960
|
+
const typeArgs = extendedTypes.getType().getTypeArguments();
|
|
235961
|
+
if (typeArgs.length === 0)
|
|
235962
|
+
return [];
|
|
235963
|
+
const propsType = typeArgs[0];
|
|
235964
|
+
const props = [];
|
|
235965
|
+
for (const prop of propsType.getProperties()) {
|
|
235966
|
+
props.push(prop.getName());
|
|
235967
|
+
}
|
|
235968
|
+
return props;
|
|
235969
|
+
}
|
|
235970
|
+
extractJSDocDescription(node) {
|
|
235971
|
+
const jsDocs = node.getJsDocs();
|
|
235972
|
+
if (jsDocs.length === 0)
|
|
235973
|
+
return;
|
|
235974
|
+
const description = jsDocs[0].getDescription().trim();
|
|
235975
|
+
return description || undefined;
|
|
235976
|
+
}
|
|
235977
|
+
isUIContext(contextType) {
|
|
235978
|
+
return ["popup", "options", "devtools"].includes(contextType);
|
|
235979
|
+
}
|
|
235980
|
+
}
|
|
235981
|
+
var import_ts_morph;
|
|
235982
|
+
var init_contexts = __esm(() => {
|
|
235983
|
+
import_ts_morph = __toESM(require_ts_morph(), 1);
|
|
235984
|
+
});
|
|
235985
|
+
|
|
235986
|
+
// tools/analysis/src/extract/flows.ts
|
|
235987
|
+
class FlowAnalyzer {
|
|
235988
|
+
project;
|
|
235989
|
+
handlers;
|
|
235990
|
+
constructor(tsConfigPath, handlers) {
|
|
235991
|
+
this.project = new import_ts_morph2.Project({
|
|
235992
|
+
tsConfigFilePath: tsConfigPath
|
|
235993
|
+
});
|
|
235994
|
+
this.handlers = handlers;
|
|
235995
|
+
}
|
|
235996
|
+
analyzeFlows() {
|
|
235997
|
+
const flows = [];
|
|
235998
|
+
const handlersByType = new Map;
|
|
235999
|
+
for (const handler of this.handlers) {
|
|
236000
|
+
if (!handlersByType.has(handler.messageType)) {
|
|
236001
|
+
handlersByType.set(handler.messageType, []);
|
|
236002
|
+
}
|
|
236003
|
+
handlersByType.get(handler.messageType)?.push(handler);
|
|
236004
|
+
}
|
|
236005
|
+
for (const [messageType, handlers] of handlersByType) {
|
|
236006
|
+
const senders = this.findMessageSenders(messageType);
|
|
236007
|
+
for (const sender of senders) {
|
|
236008
|
+
const recipients = handlers.map((h) => h.node);
|
|
236009
|
+
const sequence = this.buildSequence(messageType, sender, handlers);
|
|
236010
|
+
const flowMetadata = this.extractFlowMetadata(sender.file, sender.line);
|
|
236011
|
+
flows.push({
|
|
236012
|
+
messageType,
|
|
236013
|
+
from: sender.context,
|
|
236014
|
+
to: recipients,
|
|
236015
|
+
...flowMetadata.trigger ? { trigger: flowMetadata.trigger } : {},
|
|
236016
|
+
...flowMetadata.flowName ? { flowName: flowMetadata.flowName } : {},
|
|
236017
|
+
...flowMetadata.description ? { description: flowMetadata.description } : {},
|
|
236018
|
+
sequence
|
|
236019
|
+
});
|
|
236020
|
+
}
|
|
236021
|
+
}
|
|
236022
|
+
return flows;
|
|
236023
|
+
}
|
|
236024
|
+
findMessageSenders(messageType) {
|
|
236025
|
+
const senders = [];
|
|
236026
|
+
for (const sourceFile of this.project.getSourceFiles()) {
|
|
236027
|
+
const filePath = sourceFile.getFilePath();
|
|
236028
|
+
const context = this.inferContext(filePath);
|
|
236029
|
+
sourceFile.forEachDescendant((node) => {
|
|
236030
|
+
this.processMessageSender(node, messageType, context, filePath, senders);
|
|
236031
|
+
});
|
|
236032
|
+
}
|
|
236033
|
+
return senders;
|
|
236034
|
+
}
|
|
236035
|
+
processMessageSender(node, messageType, context, filePath, senders) {
|
|
236036
|
+
if (!import_ts_morph2.Node.isCallExpression(node)) {
|
|
236037
|
+
return;
|
|
236038
|
+
}
|
|
236039
|
+
const expression = node.getExpression();
|
|
236040
|
+
if (import_ts_morph2.Node.isPropertyAccessExpression(expression)) {
|
|
236041
|
+
this.processPropertyAccessSender(node, expression, messageType, context, filePath, senders);
|
|
236042
|
+
} else if (import_ts_morph2.Node.isIdentifier(expression)) {
|
|
236043
|
+
this.processIdentifierSender(node, expression, messageType, context, filePath, senders);
|
|
236044
|
+
}
|
|
236045
|
+
}
|
|
236046
|
+
processPropertyAccessSender(node, expression, messageType, context, filePath, senders) {
|
|
236047
|
+
if (!import_ts_morph2.Node.isPropertyAccessExpression(expression)) {
|
|
236048
|
+
return;
|
|
236049
|
+
}
|
|
236050
|
+
const methodName = expression.getName();
|
|
236051
|
+
if (!this.isMessageSendMethod(methodName)) {
|
|
236052
|
+
return;
|
|
236053
|
+
}
|
|
236054
|
+
const args = node.getArguments();
|
|
236055
|
+
if (args.length === 0) {
|
|
236056
|
+
return;
|
|
236057
|
+
}
|
|
236058
|
+
const msgType = this.extractMessageTypeFromArg(args[0]);
|
|
236059
|
+
if (msgType === messageType) {
|
|
236060
|
+
senders.push({
|
|
236061
|
+
context,
|
|
236062
|
+
file: filePath,
|
|
236063
|
+
line: node.getStartLineNumber()
|
|
236064
|
+
});
|
|
236065
|
+
}
|
|
236066
|
+
}
|
|
236067
|
+
processIdentifierSender(node, expression, messageType, context, filePath, senders) {
|
|
236068
|
+
if (!import_ts_morph2.Node.isIdentifier(expression)) {
|
|
236069
|
+
return;
|
|
236070
|
+
}
|
|
236071
|
+
if (expression.getText() !== "postMessage") {
|
|
236072
|
+
return;
|
|
236073
|
+
}
|
|
236074
|
+
const args = node.getArguments();
|
|
236075
|
+
if (args.length === 0) {
|
|
236076
|
+
return;
|
|
236077
|
+
}
|
|
236078
|
+
const msgType = this.extractMessageTypeFromArg(args[0]);
|
|
236079
|
+
if (msgType === messageType) {
|
|
236080
|
+
senders.push({
|
|
236081
|
+
context,
|
|
236082
|
+
file: filePath,
|
|
236083
|
+
line: node.getStartLineNumber()
|
|
236084
|
+
});
|
|
236085
|
+
}
|
|
236086
|
+
}
|
|
236087
|
+
isMessageSendMethod(methodName) {
|
|
236088
|
+
return methodName === "send" || methodName === "emit" || methodName === "postMessage" || methodName === "broadcast";
|
|
236089
|
+
}
|
|
236090
|
+
buildSequence(messageType, sender, handlers) {
|
|
236091
|
+
const steps = [];
|
|
236092
|
+
let stepNumber = 1;
|
|
236093
|
+
steps.push({
|
|
236094
|
+
step: stepNumber++,
|
|
236095
|
+
action: `${sender.context}.send(${messageType})`,
|
|
236096
|
+
context: sender.context,
|
|
236097
|
+
location: {
|
|
236098
|
+
file: sender.file,
|
|
236099
|
+
line: sender.line
|
|
236100
|
+
}
|
|
236101
|
+
});
|
|
236102
|
+
for (const handler of handlers) {
|
|
236103
|
+
steps.push({
|
|
236104
|
+
step: stepNumber++,
|
|
236105
|
+
action: `${handler.node}.handle(${messageType})`,
|
|
236106
|
+
context: handler.node,
|
|
236107
|
+
location: handler.location
|
|
236108
|
+
});
|
|
236109
|
+
const subsends = this.findMessagesInHandler(handler);
|
|
236110
|
+
for (const subsend of subsends) {
|
|
236111
|
+
steps.push({
|
|
236112
|
+
step: stepNumber++,
|
|
236113
|
+
action: `${handler.node}.send(${subsend.messageType})`,
|
|
236114
|
+
context: handler.node,
|
|
236115
|
+
location: subsend.location
|
|
236116
|
+
});
|
|
236117
|
+
}
|
|
236118
|
+
}
|
|
236119
|
+
return steps;
|
|
236120
|
+
}
|
|
236121
|
+
findMessagesInHandler(handler) {
|
|
236122
|
+
const sends = [];
|
|
236123
|
+
const sourceFile = this.project.getSourceFile(handler.location.file);
|
|
236124
|
+
if (!sourceFile)
|
|
236125
|
+
return sends;
|
|
236126
|
+
const targetLine = handler.location.line;
|
|
236127
|
+
sourceFile.forEachDescendant((node) => {
|
|
236128
|
+
this.processSendCall(node, targetLine, handler.location.file, sends);
|
|
236129
|
+
});
|
|
236130
|
+
return sends;
|
|
236131
|
+
}
|
|
236132
|
+
processSendCall(node, targetLine, filePath, sends) {
|
|
236133
|
+
if (!import_ts_morph2.Node.isCallExpression(node)) {
|
|
236134
|
+
return;
|
|
236135
|
+
}
|
|
236136
|
+
const line = node.getStartLineNumber();
|
|
236137
|
+
if (!this.isNearLine(line, targetLine)) {
|
|
236138
|
+
return;
|
|
236139
|
+
}
|
|
236140
|
+
const expression = node.getExpression();
|
|
236141
|
+
if (!this.isSendOrEmitCall(expression)) {
|
|
236142
|
+
return;
|
|
236143
|
+
}
|
|
236144
|
+
const args = node.getArguments();
|
|
236145
|
+
if (args.length === 0) {
|
|
236146
|
+
return;
|
|
236147
|
+
}
|
|
236148
|
+
const messageType = this.extractMessageTypeFromArg(args[0]);
|
|
236149
|
+
if (messageType) {
|
|
236150
|
+
sends.push({
|
|
236151
|
+
messageType,
|
|
236152
|
+
location: { file: filePath, line }
|
|
236153
|
+
});
|
|
236154
|
+
}
|
|
236155
|
+
}
|
|
236156
|
+
isNearLine(line, targetLine) {
|
|
236157
|
+
return Math.abs(line - targetLine) < 20;
|
|
236158
|
+
}
|
|
236159
|
+
isSendOrEmitCall(expression) {
|
|
236160
|
+
if (!import_ts_morph2.Node.isPropertyAccessExpression(expression)) {
|
|
236161
|
+
return false;
|
|
236162
|
+
}
|
|
236163
|
+
const methodName = expression.getName();
|
|
236164
|
+
return methodName === "send" || methodName === "emit";
|
|
236165
|
+
}
|
|
236166
|
+
extractMessageTypeFromArg(arg) {
|
|
236167
|
+
if (import_ts_morph2.Node.isStringLiteral(arg)) {
|
|
236168
|
+
return arg.getLiteralValue();
|
|
236169
|
+
}
|
|
236170
|
+
if (import_ts_morph2.Node.isObjectLiteralExpression(arg)) {
|
|
236171
|
+
return this.extractMessageTypeFromObject(arg);
|
|
236172
|
+
}
|
|
236173
|
+
return;
|
|
236174
|
+
}
|
|
236175
|
+
extractMessageTypeFromObject(obj) {
|
|
236176
|
+
if (!import_ts_morph2.Node.isObjectLiteralExpression(obj)) {
|
|
236177
|
+
return;
|
|
236178
|
+
}
|
|
236179
|
+
const typeProperty = obj.getProperty("type");
|
|
236180
|
+
if (!typeProperty || !import_ts_morph2.Node.isPropertyAssignment(typeProperty)) {
|
|
236181
|
+
return;
|
|
236182
|
+
}
|
|
236183
|
+
const initializer = typeProperty.getInitializer();
|
|
236184
|
+
if (!initializer || !import_ts_morph2.Node.isStringLiteral(initializer)) {
|
|
236185
|
+
return;
|
|
236186
|
+
}
|
|
236187
|
+
return initializer.getLiteralValue();
|
|
236188
|
+
}
|
|
236189
|
+
extractFlowMetadata(filePath, lineNumber) {
|
|
236190
|
+
const sourceFile = this.project.getSourceFile(filePath);
|
|
236191
|
+
if (!sourceFile)
|
|
236192
|
+
return {};
|
|
236193
|
+
let targetNode = null;
|
|
236194
|
+
sourceFile.forEachDescendant((node) => {
|
|
236195
|
+
if (node.getStartLineNumber() === lineNumber) {
|
|
236196
|
+
targetNode = node;
|
|
236197
|
+
}
|
|
236198
|
+
});
|
|
236199
|
+
if (!targetNode)
|
|
236200
|
+
return {};
|
|
236201
|
+
const jsDocs = targetNode.getJsDocs?.() || [];
|
|
236202
|
+
if (jsDocs.length === 0)
|
|
236203
|
+
return {};
|
|
236204
|
+
const comment = jsDocs[0].getText();
|
|
236205
|
+
const flowMatch = comment.match(/@flow\s+([^\s]+)/);
|
|
236206
|
+
const flowName = flowMatch ? flowMatch[1] : undefined;
|
|
236207
|
+
const triggerMatch = comment.match(/@trigger\s+(.+?)(?:\n|$)/);
|
|
236208
|
+
const trigger = triggerMatch ? triggerMatch[1].trim() : undefined;
|
|
236209
|
+
const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
|
|
236210
|
+
const description = descMatch ? descMatch[1].trim() : undefined;
|
|
236211
|
+
return { trigger, flowName, description };
|
|
236212
|
+
}
|
|
236213
|
+
inferContext(filePath) {
|
|
236214
|
+
const path = filePath.toLowerCase();
|
|
236215
|
+
const contextPatterns = [
|
|
236216
|
+
{ context: "background", patterns: ["/background/", "\\background\\"] },
|
|
236217
|
+
{ context: "content", patterns: ["/content/", "\\content\\"] },
|
|
236218
|
+
{ context: "popup", patterns: ["/popup/", "\\popup\\"] },
|
|
236219
|
+
{ context: "devtools", patterns: ["/devtools/", "\\devtools\\"] },
|
|
236220
|
+
{ context: "options", patterns: ["/options/", "\\options\\"] },
|
|
236221
|
+
{ context: "offscreen", patterns: ["/offscreen/", "\\offscreen\\"] },
|
|
236222
|
+
{ context: "server", patterns: ["/server/", "\\server\\", "/server."] },
|
|
236223
|
+
{ context: "client", patterns: ["/client/", "\\client\\", "/client."] },
|
|
236224
|
+
{ context: "worker", patterns: ["/worker/", "\\worker\\", "service-worker"] }
|
|
236225
|
+
];
|
|
236226
|
+
for (const { context, patterns } of contextPatterns) {
|
|
236227
|
+
if (patterns.some((pattern) => path.includes(pattern))) {
|
|
236228
|
+
return context;
|
|
236229
|
+
}
|
|
236230
|
+
}
|
|
236231
|
+
return "unknown";
|
|
236232
|
+
}
|
|
236233
|
+
}
|
|
236234
|
+
var import_ts_morph2;
|
|
236235
|
+
var init_flows = __esm(() => {
|
|
236236
|
+
import_ts_morph2 = __toESM(require_ts_morph(), 1);
|
|
236237
|
+
});
|
|
236238
|
+
|
|
235624
236239
|
// tools/analysis/src/extract/relationships.ts
|
|
235625
236240
|
class RelationshipExtractor {
|
|
235626
236241
|
extractFromHandler(handlerNode, sourceFile, handlerName) {
|
|
@@ -235635,16 +236250,16 @@ class RelationshipExtractor {
|
|
|
235635
236250
|
});
|
|
235636
236251
|
}
|
|
235637
236252
|
processDescendantNode(descendant, sourceFile, handlerName, relationships, visited) {
|
|
235638
|
-
if (
|
|
236253
|
+
if (import_ts_morph3.Node.isCallExpression(descendant)) {
|
|
235639
236254
|
this.processCallExpression(descendant, sourceFile, handlerName, relationships, visited);
|
|
235640
236255
|
}
|
|
235641
|
-
if (
|
|
236256
|
+
if (import_ts_morph3.Node.isAwaitExpression(descendant)) {
|
|
235642
236257
|
const rel = this.extractFromDatabaseCall(descendant, handlerName);
|
|
235643
236258
|
if (rel) {
|
|
235644
236259
|
relationships.push(rel);
|
|
235645
236260
|
}
|
|
235646
236261
|
}
|
|
235647
|
-
if (
|
|
236262
|
+
if (import_ts_morph3.Node.isCallExpression(descendant) && descendant.getExpression().getText() === "fetch") {
|
|
235648
236263
|
const rel = this.extractFromFetchCall(descendant, handlerName);
|
|
235649
236264
|
if (rel) {
|
|
235650
236265
|
relationships.push(rel);
|
|
@@ -235653,11 +236268,11 @@ class RelationshipExtractor {
|
|
|
235653
236268
|
}
|
|
235654
236269
|
processCallExpression(callExpr, sourceFile, handlerName, relationships, visited) {
|
|
235655
236270
|
const expr = callExpr.getExpression();
|
|
235656
|
-
if (
|
|
236271
|
+
if (import_ts_morph3.Node.isIdentifier(expr)) {
|
|
235657
236272
|
this.processIdentifierCall(expr, sourceFile, handlerName, relationships, visited);
|
|
235658
236273
|
return;
|
|
235659
236274
|
}
|
|
235660
|
-
if (
|
|
236275
|
+
if (import_ts_morph3.Node.isPropertyAccessExpression(expr)) {
|
|
235661
236276
|
const rel2 = this.extractFromPropertyAccess(expr, handlerName);
|
|
235662
236277
|
if (rel2) {
|
|
235663
236278
|
relationships.push(rel2);
|
|
@@ -235738,7 +236353,7 @@ class RelationshipExtractor {
|
|
|
235738
236353
|
}
|
|
235739
236354
|
let functionName = exprText;
|
|
235740
236355
|
let targetComponent = null;
|
|
235741
|
-
if (
|
|
236356
|
+
if (import_ts_morph3.Node.isPropertyAccessExpression(expr)) {
|
|
235742
236357
|
const objectExpr = expr.getExpression();
|
|
235743
236358
|
const objectName = objectExpr.getText();
|
|
235744
236359
|
const methodName = expr.getName();
|
|
@@ -235763,13 +236378,13 @@ class RelationshipExtractor {
|
|
|
235763
236378
|
};
|
|
235764
236379
|
}
|
|
235765
236380
|
extractFromPropertyAccess(propAccess, handlerName) {
|
|
235766
|
-
if (!
|
|
236381
|
+
if (!import_ts_morph3.Node.isPropertyAccessExpression(propAccess)) {
|
|
235767
236382
|
return null;
|
|
235768
236383
|
}
|
|
235769
236384
|
const fullChain = propAccess.getText();
|
|
235770
236385
|
const methodName = propAccess.getName();
|
|
235771
236386
|
let rootObject = propAccess.getExpression();
|
|
235772
|
-
while (
|
|
236387
|
+
while (import_ts_morph3.Node.isPropertyAccessExpression(rootObject)) {
|
|
235773
236388
|
rootObject = rootObject.getExpression();
|
|
235774
236389
|
}
|
|
235775
236390
|
const objectName = rootObject.getText();
|
|
@@ -235787,11 +236402,11 @@ class RelationshipExtractor {
|
|
|
235787
236402
|
};
|
|
235788
236403
|
}
|
|
235789
236404
|
extractFromDatabaseCall(awaitExpr, handlerName) {
|
|
235790
|
-
if (!
|
|
236405
|
+
if (!import_ts_morph3.Node.isAwaitExpression(awaitExpr)) {
|
|
235791
236406
|
return null;
|
|
235792
236407
|
}
|
|
235793
236408
|
const innerExpr = awaitExpr.getExpression();
|
|
235794
|
-
if (!
|
|
236409
|
+
if (!import_ts_morph3.Node.isCallExpression(innerExpr)) {
|
|
235795
236410
|
return null;
|
|
235796
236411
|
}
|
|
235797
236412
|
const callExpr = innerExpr.getExpression().getText();
|
|
@@ -235975,7 +236590,7 @@ class RelationshipExtractor {
|
|
|
235975
236590
|
const variableDecl = moduleSpecifier.getVariableDeclaration(functionName);
|
|
235976
236591
|
if (variableDecl) {
|
|
235977
236592
|
const initializer = variableDecl.getInitializer();
|
|
235978
|
-
if (initializer && (
|
|
236593
|
+
if (initializer && (import_ts_morph3.Node.isArrowFunction(initializer) || import_ts_morph3.Node.isFunctionExpression(initializer))) {
|
|
235979
236594
|
return {
|
|
235980
236595
|
functionDecl: initializer,
|
|
235981
236596
|
sourceFile: moduleSpecifier
|
|
@@ -236009,9 +236624,9 @@ class RelationshipExtractor {
|
|
|
236009
236624
|
return unique;
|
|
236010
236625
|
}
|
|
236011
236626
|
}
|
|
236012
|
-
var
|
|
236627
|
+
var import_ts_morph3;
|
|
236013
236628
|
var init_relationships = __esm(() => {
|
|
236014
|
-
|
|
236629
|
+
import_ts_morph3 = __toESM(require_ts_morph(), 1);
|
|
236015
236630
|
});
|
|
236016
236631
|
|
|
236017
236632
|
// tools/analysis/src/extract/handlers.ts
|
|
@@ -236020,7 +236635,7 @@ class HandlerExtractor {
|
|
|
236020
236635
|
typeGuardCache;
|
|
236021
236636
|
relationshipExtractor;
|
|
236022
236637
|
constructor(tsConfigPath) {
|
|
236023
|
-
this.project = new
|
|
236638
|
+
this.project = new import_ts_morph4.Project({
|
|
236024
236639
|
tsConfigFilePath: tsConfigPath
|
|
236025
236640
|
});
|
|
236026
236641
|
this.typeGuardCache = new WeakMap;
|
|
@@ -236086,25 +236701,25 @@ class HandlerExtractor {
|
|
|
236086
236701
|
return handlers;
|
|
236087
236702
|
}
|
|
236088
236703
|
processNodeForHandlers(node, context, filePath, handlers) {
|
|
236089
|
-
if (
|
|
236704
|
+
if (import_ts_morph4.Node.isCallExpression(node)) {
|
|
236090
236705
|
this.processCallExpressionHandler(node, context, filePath, handlers);
|
|
236091
236706
|
}
|
|
236092
|
-
if (
|
|
236707
|
+
if (import_ts_morph4.Node.isSwitchStatement(node)) {
|
|
236093
236708
|
const switchHandlers = this.extractSwitchCaseHandlers(node, context, filePath);
|
|
236094
236709
|
handlers.push(...switchHandlers);
|
|
236095
236710
|
}
|
|
236096
|
-
if (
|
|
236711
|
+
if (import_ts_morph4.Node.isVariableDeclaration(node)) {
|
|
236097
236712
|
const mapHandlers = this.extractHandlerMapPattern(node, context, filePath);
|
|
236098
236713
|
handlers.push(...mapHandlers);
|
|
236099
236714
|
}
|
|
236100
|
-
if (
|
|
236715
|
+
if (import_ts_morph4.Node.isIfStatement(node) && !this.isElseIfStatement(node)) {
|
|
236101
236716
|
const typeGuardHandlers = this.extractTypeGuardHandlers(node, context, filePath);
|
|
236102
236717
|
handlers.push(...typeGuardHandlers);
|
|
236103
236718
|
}
|
|
236104
236719
|
}
|
|
236105
236720
|
processCallExpressionHandler(node, context, filePath, handlers) {
|
|
236106
236721
|
const expression = node.getExpression();
|
|
236107
|
-
if (
|
|
236722
|
+
if (import_ts_morph4.Node.isPropertyAccessExpression(expression)) {
|
|
236108
236723
|
const methodName = expression.getName();
|
|
236109
236724
|
if (methodName === "on" || methodName === "addEventListener") {
|
|
236110
236725
|
const handler = this.extractHandler(node, context, filePath);
|
|
@@ -236116,7 +236731,7 @@ class HandlerExtractor {
|
|
|
236116
236731
|
}
|
|
236117
236732
|
isElseIfStatement(node) {
|
|
236118
236733
|
const parent = node.getParent();
|
|
236119
|
-
return parent !== undefined &&
|
|
236734
|
+
return parent !== undefined && import_ts_morph4.Node.isIfStatement(parent);
|
|
236120
236735
|
}
|
|
236121
236736
|
extractHandler(callExpr, context, filePath) {
|
|
236122
236737
|
const args = callExpr.getArguments();
|
|
@@ -236125,9 +236740,9 @@ class HandlerExtractor {
|
|
|
236125
236740
|
}
|
|
236126
236741
|
const messageTypeArg = args[0];
|
|
236127
236742
|
let messageType = null;
|
|
236128
|
-
if (
|
|
236743
|
+
if (import_ts_morph4.Node.isStringLiteral(messageTypeArg)) {
|
|
236129
236744
|
messageType = messageTypeArg.getLiteralValue();
|
|
236130
|
-
} else if (
|
|
236745
|
+
} else if (import_ts_morph4.Node.isTemplateExpression(messageTypeArg)) {
|
|
236131
236746
|
messageType = messageTypeArg.getText().replace(/[`'"]/g, "");
|
|
236132
236747
|
}
|
|
236133
236748
|
if (!messageType) {
|
|
@@ -236137,7 +236752,7 @@ class HandlerExtractor {
|
|
|
236137
236752
|
const assignments = [];
|
|
236138
236753
|
const preconditions = [];
|
|
236139
236754
|
const postconditions = [];
|
|
236140
|
-
if (
|
|
236755
|
+
if (import_ts_morph4.Node.isArrowFunction(handlerArg) || import_ts_morph4.Node.isFunctionExpression(handlerArg)) {
|
|
236141
236756
|
this.extractAssignments(handlerArg, assignments);
|
|
236142
236757
|
this.extractVerificationConditions(handlerArg, preconditions, postconditions);
|
|
236143
236758
|
this.checkAsyncMutations(handlerArg, messageType);
|
|
@@ -236146,7 +236761,7 @@ class HandlerExtractor {
|
|
|
236146
236761
|
const sourceFile = callExpr.getSourceFile();
|
|
236147
236762
|
const handlerName = `${messageType}_handler`;
|
|
236148
236763
|
let relationships;
|
|
236149
|
-
if (
|
|
236764
|
+
if (import_ts_morph4.Node.isArrowFunction(handlerArg) || import_ts_morph4.Node.isFunctionExpression(handlerArg)) {
|
|
236150
236765
|
const detectedRelationships = this.relationshipExtractor.extractFromHandler(handlerArg, sourceFile, handlerName);
|
|
236151
236766
|
if (detectedRelationships.length > 0) {
|
|
236152
236767
|
relationships = detectedRelationships;
|
|
@@ -236167,16 +236782,16 @@ class HandlerExtractor {
|
|
|
236167
236782
|
}
|
|
236168
236783
|
extractAssignments(funcNode, assignments) {
|
|
236169
236784
|
funcNode.forEachDescendant((node) => {
|
|
236170
|
-
if (
|
|
236785
|
+
if (import_ts_morph4.Node.isBinaryExpression(node)) {
|
|
236171
236786
|
this.extractBinaryExpressionAssignment(node, assignments);
|
|
236172
236787
|
}
|
|
236173
|
-
if (
|
|
236788
|
+
if (import_ts_morph4.Node.isCallExpression(node)) {
|
|
236174
236789
|
this.extractArrayMutationAssignment(node, assignments);
|
|
236175
236790
|
}
|
|
236176
236791
|
});
|
|
236177
236792
|
}
|
|
236178
236793
|
extractBinaryExpressionAssignment(node, assignments) {
|
|
236179
|
-
if (!
|
|
236794
|
+
if (!import_ts_morph4.Node.isBinaryExpression(node))
|
|
236180
236795
|
return;
|
|
236181
236796
|
const operator = node.getOperatorToken().getText();
|
|
236182
236797
|
if (operator === "=") {
|
|
@@ -236186,18 +236801,18 @@ class HandlerExtractor {
|
|
|
236186
236801
|
}
|
|
236187
236802
|
}
|
|
236188
236803
|
extractSimpleOrElementAccessAssignment(node, assignments) {
|
|
236189
|
-
if (!
|
|
236804
|
+
if (!import_ts_morph4.Node.isBinaryExpression(node))
|
|
236190
236805
|
return;
|
|
236191
236806
|
const left = node.getLeft();
|
|
236192
236807
|
const right = node.getRight();
|
|
236193
|
-
if (
|
|
236808
|
+
if (import_ts_morph4.Node.isPropertyAccessExpression(left)) {
|
|
236194
236809
|
this.extractPropertyAccessAssignment(left, right, assignments);
|
|
236195
|
-
} else if (
|
|
236810
|
+
} else if (import_ts_morph4.Node.isElementAccessExpression(left)) {
|
|
236196
236811
|
this.extractElementAccessAssignment(left, right, assignments);
|
|
236197
236812
|
}
|
|
236198
236813
|
}
|
|
236199
236814
|
extractPropertyAccessAssignment(left, right, assignments) {
|
|
236200
|
-
if (!
|
|
236815
|
+
if (!import_ts_morph4.Node.isPropertyAccessExpression(left))
|
|
236201
236816
|
return;
|
|
236202
236817
|
const fieldPath = this.getPropertyPath(left);
|
|
236203
236818
|
if (fieldPath.startsWith("state.")) {
|
|
@@ -236209,10 +236824,10 @@ class HandlerExtractor {
|
|
|
236209
236824
|
}
|
|
236210
236825
|
}
|
|
236211
236826
|
extractElementAccessAssignment(left, right, assignments) {
|
|
236212
|
-
if (!
|
|
236827
|
+
if (!import_ts_morph4.Node.isElementAccessExpression(left))
|
|
236213
236828
|
return;
|
|
236214
236829
|
const expr = left.getExpression();
|
|
236215
|
-
if (!
|
|
236830
|
+
if (!import_ts_morph4.Node.isPropertyAccessExpression(expr))
|
|
236216
236831
|
return;
|
|
236217
236832
|
const fieldPath = this.getPropertyPath(expr);
|
|
236218
236833
|
if (!fieldPath.startsWith("state."))
|
|
@@ -236222,7 +236837,7 @@ class HandlerExtractor {
|
|
|
236222
236837
|
const index = indexExpr ? indexExpr.getText() : "0";
|
|
236223
236838
|
const value = this.extractValue(right);
|
|
236224
236839
|
if (value !== undefined) {
|
|
236225
|
-
const tlaIndex = this.isNumericLiteral(index) ? (Number.parseInt(index) + 1).toString() : `${index} + 1`;
|
|
236840
|
+
const tlaIndex = this.isNumericLiteral(index) ? (Number.parseInt(index, 10) + 1).toString() : `${index} + 1`;
|
|
236226
236841
|
assignments.push({
|
|
236227
236842
|
field: `${field}[${tlaIndex}]`,
|
|
236228
236843
|
value
|
|
@@ -236230,12 +236845,12 @@ class HandlerExtractor {
|
|
|
236230
236845
|
}
|
|
236231
236846
|
}
|
|
236232
236847
|
extractCompoundAssignment(node, assignments) {
|
|
236233
|
-
if (!
|
|
236848
|
+
if (!import_ts_morph4.Node.isBinaryExpression(node))
|
|
236234
236849
|
return;
|
|
236235
236850
|
const operator = node.getOperatorToken().getText();
|
|
236236
236851
|
const left = node.getLeft();
|
|
236237
236852
|
const right = node.getRight();
|
|
236238
|
-
if (
|
|
236853
|
+
if (import_ts_morph4.Node.isPropertyAccessExpression(left)) {
|
|
236239
236854
|
const fieldPath = this.getPropertyPath(left);
|
|
236240
236855
|
if (fieldPath.startsWith("state.")) {
|
|
236241
236856
|
const field = fieldPath.substring(6);
|
|
@@ -236249,14 +236864,14 @@ class HandlerExtractor {
|
|
|
236249
236864
|
}
|
|
236250
236865
|
}
|
|
236251
236866
|
extractArrayMutationAssignment(node, assignments) {
|
|
236252
|
-
if (!
|
|
236867
|
+
if (!import_ts_morph4.Node.isCallExpression(node))
|
|
236253
236868
|
return;
|
|
236254
236869
|
const expr = node.getExpression();
|
|
236255
|
-
if (!
|
|
236870
|
+
if (!import_ts_morph4.Node.isPropertyAccessExpression(expr))
|
|
236256
236871
|
return;
|
|
236257
236872
|
const methodName = expr.getName();
|
|
236258
236873
|
const object = expr.getExpression();
|
|
236259
|
-
if (
|
|
236874
|
+
if (import_ts_morph4.Node.isPropertyAccessExpression(object)) {
|
|
236260
236875
|
const fieldPath = this.getPropertyPath(object);
|
|
236261
236876
|
if (fieldPath.startsWith("state.")) {
|
|
236262
236877
|
const field = fieldPath.substring(6);
|
|
@@ -236288,13 +236903,13 @@ class HandlerExtractor {
|
|
|
236288
236903
|
}
|
|
236289
236904
|
}
|
|
236290
236905
|
checkAsyncMutations(funcNode, messageType) {
|
|
236291
|
-
const isAsync = funcNode.hasModifier?.(
|
|
236906
|
+
const isAsync = funcNode.hasModifier?.(import_ts_morph4.SyntaxKind.AsyncKeyword) || funcNode.getModifiers?.()?.some((m) => m.getKind() === import_ts_morph4.SyntaxKind.AsyncKeyword);
|
|
236292
236907
|
if (!isAsync) {
|
|
236293
236908
|
return;
|
|
236294
236909
|
}
|
|
236295
236910
|
const awaitExpressions = [];
|
|
236296
236911
|
funcNode.forEachDescendant((node) => {
|
|
236297
|
-
if (
|
|
236912
|
+
if (import_ts_morph4.Node.isAwaitExpression(node)) {
|
|
236298
236913
|
awaitExpressions.push(node);
|
|
236299
236914
|
}
|
|
236300
236915
|
});
|
|
@@ -236308,10 +236923,10 @@ class HandlerExtractor {
|
|
|
236308
236923
|
}
|
|
236309
236924
|
const firstAwaitPos = awaitExpressions[0].getStart();
|
|
236310
236925
|
funcNode.forEachDescendant((node) => {
|
|
236311
|
-
if (
|
|
236926
|
+
if (import_ts_morph4.Node.isBinaryExpression(node)) {
|
|
236312
236927
|
this.checkBinaryExpressionMutation(node, firstAwaitPos, mutations);
|
|
236313
236928
|
}
|
|
236314
|
-
if (
|
|
236929
|
+
if (import_ts_morph4.Node.isCallExpression(node)) {
|
|
236315
236930
|
this.checkCallExpressionMutation(node, firstAwaitPos, mutations);
|
|
236316
236931
|
}
|
|
236317
236932
|
});
|
|
@@ -236324,19 +236939,19 @@ class HandlerExtractor {
|
|
|
236324
236939
|
}
|
|
236325
236940
|
extractVerificationConditions(funcNode, preconditions, postconditions) {
|
|
236326
236941
|
const body = funcNode.getBody();
|
|
236327
|
-
const statements =
|
|
236942
|
+
const statements = import_ts_morph4.Node.isBlock(body) ? body.getStatements() : [body];
|
|
236328
236943
|
for (const statement of statements) {
|
|
236329
236944
|
this.processStatementForConditions(statement, preconditions, postconditions);
|
|
236330
236945
|
}
|
|
236331
236946
|
}
|
|
236332
236947
|
processStatementForConditions(statement, preconditions, postconditions) {
|
|
236333
|
-
if (!
|
|
236948
|
+
if (!import_ts_morph4.Node.isExpressionStatement(statement))
|
|
236334
236949
|
return;
|
|
236335
236950
|
const expr = statement.getExpression();
|
|
236336
|
-
if (!
|
|
236951
|
+
if (!import_ts_morph4.Node.isCallExpression(expr))
|
|
236337
236952
|
return;
|
|
236338
236953
|
const callee = expr.getExpression();
|
|
236339
|
-
if (!
|
|
236954
|
+
if (!import_ts_morph4.Node.isIdentifier(callee))
|
|
236340
236955
|
return;
|
|
236341
236956
|
const functionName = callee.getText();
|
|
236342
236957
|
if (functionName === "requires") {
|
|
@@ -236359,7 +236974,7 @@ class HandlerExtractor {
|
|
|
236359
236974
|
const conditionArg = args[0];
|
|
236360
236975
|
const expression = conditionArg.getText();
|
|
236361
236976
|
let message;
|
|
236362
|
-
if (args.length >= 2 &&
|
|
236977
|
+
if (args.length >= 2 && import_ts_morph4.Node.isStringLiteral(args[1])) {
|
|
236363
236978
|
message = args[1].getLiteralValue();
|
|
236364
236979
|
}
|
|
236365
236980
|
const line = callExpr.getStartLineNumber();
|
|
@@ -236376,29 +236991,29 @@ class HandlerExtractor {
|
|
|
236376
236991
|
getPropertyPath(node) {
|
|
236377
236992
|
const parts = [];
|
|
236378
236993
|
let current = node;
|
|
236379
|
-
while (
|
|
236994
|
+
while (import_ts_morph4.Node.isPropertyAccessExpression(current)) {
|
|
236380
236995
|
parts.unshift(current.getName());
|
|
236381
236996
|
current = current.getExpression();
|
|
236382
236997
|
}
|
|
236383
|
-
if (
|
|
236998
|
+
if (import_ts_morph4.Node.isIdentifier(current)) {
|
|
236384
236999
|
parts.unshift(current.getText());
|
|
236385
237000
|
}
|
|
236386
237001
|
return parts.join(".");
|
|
236387
237002
|
}
|
|
236388
237003
|
extractValue(node) {
|
|
236389
|
-
if (
|
|
237004
|
+
if (import_ts_morph4.Node.isStringLiteral(node)) {
|
|
236390
237005
|
return node.getLiteralValue();
|
|
236391
237006
|
}
|
|
236392
|
-
if (
|
|
237007
|
+
if (import_ts_morph4.Node.isNumericLiteral(node)) {
|
|
236393
237008
|
return node.getLiteralValue();
|
|
236394
237009
|
}
|
|
236395
|
-
if (node.getKind() ===
|
|
237010
|
+
if (node.getKind() === import_ts_morph4.SyntaxKind.TrueKeyword) {
|
|
236396
237011
|
return true;
|
|
236397
237012
|
}
|
|
236398
|
-
if (node.getKind() ===
|
|
237013
|
+
if (node.getKind() === import_ts_morph4.SyntaxKind.FalseKeyword) {
|
|
236399
237014
|
return false;
|
|
236400
237015
|
}
|
|
236401
|
-
if (node.getKind() ===
|
|
237016
|
+
if (node.getKind() === import_ts_morph4.SyntaxKind.NullKeyword) {
|
|
236402
237017
|
return null;
|
|
236403
237018
|
}
|
|
236404
237019
|
return;
|
|
@@ -236416,10 +237031,10 @@ class HandlerExtractor {
|
|
|
236416
237031
|
}
|
|
236417
237032
|
const caseClauses = switchNode.getClauses();
|
|
236418
237033
|
for (const clause of caseClauses) {
|
|
236419
|
-
if (
|
|
237034
|
+
if (import_ts_morph4.Node.isCaseClause(clause)) {
|
|
236420
237035
|
const caseExpr = clause.getExpression();
|
|
236421
237036
|
let messageType = null;
|
|
236422
|
-
if (
|
|
237037
|
+
if (import_ts_morph4.Node.isStringLiteral(caseExpr)) {
|
|
236423
237038
|
messageType = caseExpr.getLiteralValue();
|
|
236424
237039
|
}
|
|
236425
237040
|
if (messageType) {
|
|
@@ -236456,14 +237071,14 @@ class HandlerExtractor {
|
|
|
236456
237071
|
return handlers;
|
|
236457
237072
|
}
|
|
236458
237073
|
isHandlerMapInitializer(initializer, varDecl) {
|
|
236459
|
-
if (!initializer || !
|
|
237074
|
+
if (!initializer || !import_ts_morph4.Node.isObjectLiteralExpression(initializer)) {
|
|
236460
237075
|
return false;
|
|
236461
237076
|
}
|
|
236462
237077
|
const varName = varDecl.getName().toLowerCase();
|
|
236463
237078
|
return /(handler|listener|callback|event)s?/.test(varName);
|
|
236464
237079
|
}
|
|
236465
237080
|
extractHandlerFromProperty(prop, context, filePath) {
|
|
236466
|
-
if (!
|
|
237081
|
+
if (!import_ts_morph4.Node.isPropertyAssignment(prop)) {
|
|
236467
237082
|
return null;
|
|
236468
237083
|
}
|
|
236469
237084
|
const nameNode = prop.getNameNode();
|
|
@@ -236482,10 +237097,10 @@ class HandlerExtractor {
|
|
|
236482
237097
|
};
|
|
236483
237098
|
}
|
|
236484
237099
|
getMessageTypeFromPropertyName(nameNode) {
|
|
236485
|
-
if (
|
|
237100
|
+
if (import_ts_morph4.Node.isStringLiteral(nameNode)) {
|
|
236486
237101
|
return nameNode.getLiteralValue();
|
|
236487
237102
|
}
|
|
236488
|
-
if (
|
|
237103
|
+
if (import_ts_morph4.Node.isIdentifier(nameNode)) {
|
|
236489
237104
|
return nameNode.getText();
|
|
236490
237105
|
}
|
|
236491
237106
|
return null;
|
|
@@ -236530,7 +237145,7 @@ class HandlerExtractor {
|
|
|
236530
237145
|
this.debugLogFoundHandler(handler);
|
|
236531
237146
|
}
|
|
236532
237147
|
const elseStatement = ifStatement.getElseStatement();
|
|
236533
|
-
if (elseStatement &&
|
|
237148
|
+
if (elseStatement && import_ts_morph4.Node.isIfStatement(elseStatement)) {
|
|
236534
237149
|
ifStatement = elseStatement;
|
|
236535
237150
|
} else {
|
|
236536
237151
|
break;
|
|
@@ -236551,11 +237166,11 @@ class HandlerExtractor {
|
|
|
236551
237166
|
try {
|
|
236552
237167
|
const ifStmt = ifNode;
|
|
236553
237168
|
const condition = ifStmt.getExpression();
|
|
236554
|
-
if (!
|
|
237169
|
+
if (!import_ts_morph4.Node.isCallExpression(condition)) {
|
|
236555
237170
|
return null;
|
|
236556
237171
|
}
|
|
236557
237172
|
const funcExpr = condition.getExpression();
|
|
236558
|
-
const funcName =
|
|
237173
|
+
const funcName = import_ts_morph4.Node.isIdentifier(funcExpr) ? funcExpr.getText() : undefined;
|
|
236559
237174
|
this.debugLogProcessingFunction(funcName);
|
|
236560
237175
|
const messageType = this.resolveMessageType(funcExpr, funcName, typeGuards);
|
|
236561
237176
|
if (!messageType) {
|
|
@@ -236595,7 +237210,7 @@ class HandlerExtractor {
|
|
|
236595
237210
|
return guardType;
|
|
236596
237211
|
}
|
|
236597
237212
|
}
|
|
236598
|
-
if (
|
|
237213
|
+
if (import_ts_morph4.Node.isIdentifier(funcExpr)) {
|
|
236599
237214
|
this.debugLogTryingImportResolution(funcName);
|
|
236600
237215
|
return this.resolveImportedTypeGuard(funcExpr) ?? undefined;
|
|
236601
237216
|
}
|
|
@@ -236633,7 +237248,7 @@ class HandlerExtractor {
|
|
|
236633
237248
|
return;
|
|
236634
237249
|
}
|
|
236635
237250
|
const returnTypeNode = node.getReturnTypeNode();
|
|
236636
|
-
if (!returnTypeNode || !
|
|
237251
|
+
if (!returnTypeNode || !import_ts_morph4.Node.isTypePredicate(returnTypeNode)) {
|
|
236637
237252
|
return;
|
|
236638
237253
|
}
|
|
236639
237254
|
const functionName = this.extractFunctionName(node);
|
|
@@ -236646,17 +237261,17 @@ class HandlerExtractor {
|
|
|
236646
237261
|
}
|
|
236647
237262
|
}
|
|
236648
237263
|
extractFunctionName(node) {
|
|
236649
|
-
if (
|
|
237264
|
+
if (import_ts_morph4.Node.isFunctionDeclaration(node)) {
|
|
236650
237265
|
return node.getName();
|
|
236651
237266
|
}
|
|
236652
|
-
if (
|
|
237267
|
+
if (import_ts_morph4.Node.isFunctionExpression(node) || import_ts_morph4.Node.isArrowFunction(node)) {
|
|
236653
237268
|
return this.extractFunctionNameFromVariable(node);
|
|
236654
237269
|
}
|
|
236655
237270
|
return;
|
|
236656
237271
|
}
|
|
236657
237272
|
extractFunctionNameFromVariable(node) {
|
|
236658
237273
|
const parent = node.getParent();
|
|
236659
|
-
if (
|
|
237274
|
+
if (import_ts_morph4.Node.isVariableDeclaration(parent)) {
|
|
236660
237275
|
return parent.getName();
|
|
236661
237276
|
}
|
|
236662
237277
|
return;
|
|
@@ -236716,10 +237331,10 @@ class HandlerExtractor {
|
|
|
236716
237331
|
return this.extractFromFunctionBody(def, funcName);
|
|
236717
237332
|
}
|
|
236718
237333
|
isFunctionNode(node) {
|
|
236719
|
-
return
|
|
237334
|
+
return import_ts_morph4.Node.isFunctionDeclaration(node) || import_ts_morph4.Node.isFunctionExpression(node) || import_ts_morph4.Node.isArrowFunction(node);
|
|
236720
237335
|
}
|
|
236721
237336
|
extractFromTypePredicate(returnTypeNode, funcName) {
|
|
236722
|
-
if (!returnTypeNode || !
|
|
237337
|
+
if (!returnTypeNode || !import_ts_morph4.Node.isTypePredicate(returnTypeNode)) {
|
|
236723
237338
|
return null;
|
|
236724
237339
|
}
|
|
236725
237340
|
const typeNode = returnTypeNode.getTypeNode();
|
|
@@ -236758,7 +237373,7 @@ class HandlerExtractor {
|
|
|
236758
237373
|
const returnType = def.getReturnType().getText();
|
|
236759
237374
|
console.log(`[DEBUG] Function ${funcName} return type (resolved): ${returnType}`);
|
|
236760
237375
|
console.log(`[DEBUG] Has return type node: ${!!returnTypeNode}`);
|
|
236761
|
-
console.log(`[DEBUG] Is type predicate node: ${returnTypeNode &&
|
|
237376
|
+
console.log(`[DEBUG] Is type predicate node: ${returnTypeNode && import_ts_morph4.Node.isTypePredicate(returnTypeNode)}`);
|
|
236762
237377
|
}
|
|
236763
237378
|
}
|
|
236764
237379
|
debugLogTypePredicateResolution(funcName, messageType) {
|
|
@@ -236836,15 +237451,15 @@ class HandlerExtractor {
|
|
|
236836
237451
|
return null;
|
|
236837
237452
|
}
|
|
236838
237453
|
checkBinaryExpressionMutation(node, firstAwaitPos, mutations) {
|
|
236839
|
-
if (!
|
|
237454
|
+
if (!import_ts_morph4.Node.isBinaryExpression(node))
|
|
236840
237455
|
return;
|
|
236841
237456
|
const operator = node.getOperatorToken().getText();
|
|
236842
237457
|
if (operator !== "=" && !["+=", "-=", "*=", "/=", "%="].includes(operator))
|
|
236843
237458
|
return;
|
|
236844
237459
|
const left = node.getLeft();
|
|
236845
|
-
if (!
|
|
237460
|
+
if (!import_ts_morph4.Node.isPropertyAccessExpression(left) && !import_ts_morph4.Node.isElementAccessExpression(left))
|
|
236846
237461
|
return;
|
|
236847
|
-
const fieldPath =
|
|
237462
|
+
const fieldPath = import_ts_morph4.Node.isPropertyAccessExpression(left) ? this.getPropertyPath(left) : this.getPropertyPath(left.getExpression());
|
|
236848
237463
|
if (fieldPath.startsWith("state.")) {
|
|
236849
237464
|
const field = fieldPath.substring(6);
|
|
236850
237465
|
const line = node.getStartLineNumber();
|
|
@@ -236853,14 +237468,14 @@ class HandlerExtractor {
|
|
|
236853
237468
|
}
|
|
236854
237469
|
}
|
|
236855
237470
|
checkCallExpressionMutation(node, firstAwaitPos, mutations) {
|
|
236856
|
-
if (!
|
|
237471
|
+
if (!import_ts_morph4.Node.isCallExpression(node))
|
|
236857
237472
|
return;
|
|
236858
237473
|
const expr = node.getExpression();
|
|
236859
|
-
if (!
|
|
237474
|
+
if (!import_ts_morph4.Node.isPropertyAccessExpression(expr))
|
|
236860
237475
|
return;
|
|
236861
237476
|
const methodName = expr.getName();
|
|
236862
237477
|
const object = expr.getExpression();
|
|
236863
|
-
if (!
|
|
237478
|
+
if (!import_ts_morph4.Node.isPropertyAccessExpression(object))
|
|
236864
237479
|
return;
|
|
236865
237480
|
const fieldPath = this.getPropertyPath(object);
|
|
236866
237481
|
if (!fieldPath.startsWith("state."))
|
|
@@ -236877,442 +237492,252 @@ function extractHandlers(tsConfigPath) {
|
|
|
236877
237492
|
const extractor = new HandlerExtractor(tsConfigPath);
|
|
236878
237493
|
return extractor.extractHandlers();
|
|
236879
237494
|
}
|
|
236880
|
-
var
|
|
237495
|
+
var import_ts_morph4;
|
|
236881
237496
|
var init_handlers = __esm(() => {
|
|
236882
237497
|
init_relationships();
|
|
236883
|
-
|
|
237498
|
+
import_ts_morph4 = __toESM(require_ts_morph(), 1);
|
|
236884
237499
|
});
|
|
236885
237500
|
|
|
236886
|
-
// tools/analysis/src/extract/
|
|
236887
|
-
class
|
|
237501
|
+
// tools/analysis/src/extract/integrations.ts
|
|
237502
|
+
class IntegrationAnalyzer {
|
|
236888
237503
|
project;
|
|
236889
237504
|
constructor(tsConfigPath) {
|
|
236890
|
-
this.project = new
|
|
237505
|
+
this.project = new import_ts_morph5.Project({
|
|
236891
237506
|
tsConfigFilePath: tsConfigPath
|
|
236892
237507
|
});
|
|
236893
237508
|
}
|
|
236894
|
-
|
|
236895
|
-
const
|
|
236896
|
-
const
|
|
236897
|
-
const
|
|
236898
|
-
|
|
236899
|
-
const validMessageTypes = this.filterAndLogMessageTypes(messageTypes, handlerAnalysis.messageTypes);
|
|
236900
|
-
const validHandlers = this.filterAndLogHandlers(handlerAnalysis.handlers);
|
|
236901
|
-
return {
|
|
236902
|
-
stateType,
|
|
236903
|
-
messageTypes: validMessageTypes,
|
|
236904
|
-
fields,
|
|
236905
|
-
handlers: validHandlers
|
|
236906
|
-
};
|
|
236907
|
-
}
|
|
236908
|
-
extractHandlerAnalysis() {
|
|
236909
|
-
const configFilePath = this.project.getCompilerOptions()["configFilePath"];
|
|
236910
|
-
const tsConfigPath = typeof configFilePath === "string" ? configFilePath : "tsconfig.json";
|
|
236911
|
-
const handlerExtractor = new HandlerExtractor(tsConfigPath);
|
|
236912
|
-
return handlerExtractor.extractHandlers();
|
|
236913
|
-
}
|
|
236914
|
-
filterAndLogMessageTypes(messageTypes, handlerMessageTypes) {
|
|
236915
|
-
const allMessageTypes = Array.from(new Set([...messageTypes, ...handlerMessageTypes]));
|
|
236916
|
-
const validMessageTypes = [];
|
|
236917
|
-
const invalidMessageTypes = [];
|
|
236918
|
-
for (const msgType of allMessageTypes) {
|
|
236919
|
-
if (this.isValidTLAIdentifier(msgType)) {
|
|
236920
|
-
validMessageTypes.push(msgType);
|
|
236921
|
-
} else {
|
|
236922
|
-
invalidMessageTypes.push(msgType);
|
|
236923
|
-
}
|
|
237509
|
+
analyzeIntegrations() {
|
|
237510
|
+
const integrations = new Map;
|
|
237511
|
+
const fetchCalls = this.findFetchCalls();
|
|
237512
|
+
for (const call of fetchCalls) {
|
|
237513
|
+
this.addOrMergeIntegration(integrations, this.createAPIIntegration(call));
|
|
236924
237514
|
}
|
|
236925
|
-
this.
|
|
236926
|
-
|
|
236927
|
-
|
|
236928
|
-
logInvalidMessageTypes(invalidMessageTypes) {
|
|
236929
|
-
if (invalidMessageTypes.length === 0 || !process.env["POLLY_DEBUG"])
|
|
236930
|
-
return;
|
|
236931
|
-
console.log(`[WARN] Filtered out ${invalidMessageTypes.length} invalid message type(s):`);
|
|
236932
|
-
for (const invalid of invalidMessageTypes) {
|
|
236933
|
-
console.log(`[WARN] - "${invalid}" (not a valid TLA+ identifier)`);
|
|
237515
|
+
const websockets = this.findWebSockets();
|
|
237516
|
+
for (const ws of websockets) {
|
|
237517
|
+
this.addOrMergeIntegration(integrations, this.createWebSocketIntegration(ws));
|
|
236934
237518
|
}
|
|
236935
|
-
|
|
236936
|
-
|
|
236937
|
-
|
|
236938
|
-
this.logInvalidHandlers(handlers, validHandlers);
|
|
236939
|
-
return validHandlers;
|
|
236940
|
-
}
|
|
236941
|
-
logInvalidHandlers(allHandlers, validHandlers) {
|
|
236942
|
-
const filteredHandlerCount = allHandlers.length - validHandlers.length;
|
|
236943
|
-
if (filteredHandlerCount === 0 || !process.env["POLLY_DEBUG"])
|
|
236944
|
-
return;
|
|
236945
|
-
console.log(`[WARN] Filtered out ${filteredHandlerCount} handler(s) with invalid message types:`);
|
|
236946
|
-
for (const handler of allHandlers) {
|
|
236947
|
-
if (!this.isValidTLAIdentifier(handler.messageType)) {
|
|
236948
|
-
console.log(`[WARN] - Handler for "${handler.messageType}" at ${handler.location.file}:${handler.location.line}`);
|
|
236949
|
-
}
|
|
237519
|
+
const externalScripts = this.findExternalScripts();
|
|
237520
|
+
for (const script of externalScripts) {
|
|
237521
|
+
this.addOrMergeIntegration(integrations, script);
|
|
236950
237522
|
}
|
|
237523
|
+
return Array.from(integrations.values());
|
|
236951
237524
|
}
|
|
236952
|
-
|
|
236953
|
-
|
|
236954
|
-
|
|
237525
|
+
findFetchCalls() {
|
|
237526
|
+
const calls = [];
|
|
237527
|
+
for (const sourceFile of this.project.getSourceFiles()) {
|
|
237528
|
+
sourceFile.forEachDescendant((node) => {
|
|
237529
|
+
this.processFetchCall(node, sourceFile, calls);
|
|
237530
|
+
});
|
|
236955
237531
|
}
|
|
236956
|
-
return
|
|
237532
|
+
return calls;
|
|
236957
237533
|
}
|
|
236958
|
-
|
|
236959
|
-
|
|
236960
|
-
|
|
236961
|
-
return null;
|
|
236962
|
-
}
|
|
236963
|
-
const typeAlias = sourceFile.getTypeAlias("AppState") || sourceFile.getTypeAlias("State") || sourceFile.getTypeAliases()[0];
|
|
236964
|
-
if (!typeAlias) {
|
|
236965
|
-
return null;
|
|
237534
|
+
processFetchCall(node, sourceFile, calls) {
|
|
237535
|
+
if (!import_ts_morph5.Node.isCallExpression(node)) {
|
|
237536
|
+
return;
|
|
236966
237537
|
}
|
|
236967
|
-
const
|
|
236968
|
-
|
|
236969
|
-
|
|
236970
|
-
findStateType() {
|
|
236971
|
-
const stateFiles = this.project.getSourceFiles("**/state*.ts");
|
|
236972
|
-
for (const file of stateFiles) {
|
|
236973
|
-
const typeAlias = file.getTypeAlias("AppState") || file.getTypeAlias("State");
|
|
236974
|
-
if (typeAlias) {
|
|
236975
|
-
const type2 = typeAlias.getType();
|
|
236976
|
-
return this.convertType(type2, typeAlias.getName());
|
|
236977
|
-
}
|
|
237538
|
+
const expression = node.getExpression();
|
|
237539
|
+
if (!this.isFetchCall(expression)) {
|
|
237540
|
+
return;
|
|
236978
237541
|
}
|
|
236979
|
-
|
|
236980
|
-
|
|
236981
|
-
|
|
236982
|
-
const messageTypes = [];
|
|
236983
|
-
const warnings = [];
|
|
236984
|
-
const messageFiles = this.project.getSourceFiles("**/message*.ts");
|
|
236985
|
-
for (const file of messageFiles) {
|
|
236986
|
-
for (const typeAlias of file.getTypeAliases()) {
|
|
236987
|
-
const extractedTypes = this.extractMessageTypesFromType(typeAlias.getType(), typeAlias.getName(), file, warnings);
|
|
236988
|
-
messageTypes.push(...extractedTypes);
|
|
236989
|
-
}
|
|
237542
|
+
const args = node.getArguments();
|
|
237543
|
+
if (args.length === 0) {
|
|
237544
|
+
return;
|
|
236990
237545
|
}
|
|
236991
|
-
|
|
236992
|
-
|
|
236993
|
-
|
|
236994
|
-
console.log(`[WARN] ${warning}`);
|
|
236995
|
-
}
|
|
237546
|
+
const url = this.extractURLFromArg(args[0]);
|
|
237547
|
+
if (!url) {
|
|
237548
|
+
return;
|
|
236996
237549
|
}
|
|
236997
|
-
|
|
237550
|
+
const method = this.extractMethodFromOptions(args);
|
|
237551
|
+
const description = this.extractJSDocDescription(node);
|
|
237552
|
+
calls.push({
|
|
237553
|
+
url,
|
|
237554
|
+
method,
|
|
237555
|
+
file: sourceFile.getFilePath(),
|
|
237556
|
+
line: node.getStartLineNumber(),
|
|
237557
|
+
description
|
|
237558
|
+
});
|
|
236998
237559
|
}
|
|
236999
|
-
|
|
237000
|
-
|
|
237001
|
-
|
|
237002
|
-
|
|
237003
|
-
if (
|
|
237004
|
-
return
|
|
237005
|
-
}
|
|
237006
|
-
if (type2.isConditionalType?.()) {
|
|
237007
|
-
return this.extractFromConditionalType(type2, warnings);
|
|
237008
|
-
}
|
|
237009
|
-
if (type2.getText().includes("[K in ")) {
|
|
237010
|
-
return this.extractFromMappedType(type2, sourceFile, warnings);
|
|
237560
|
+
isFetchCall(expression) {
|
|
237561
|
+
return import_ts_morph5.Node.isIdentifier(expression) && expression.getText() === "fetch";
|
|
237562
|
+
}
|
|
237563
|
+
extractURLFromArg(urlArg) {
|
|
237564
|
+
if (import_ts_morph5.Node.isStringLiteral(urlArg)) {
|
|
237565
|
+
return urlArg.getLiteralValue();
|
|
237011
237566
|
}
|
|
237012
|
-
if (
|
|
237013
|
-
this.
|
|
237014
|
-
return [];
|
|
237567
|
+
if (import_ts_morph5.Node.isTemplateExpression(urlArg)) {
|
|
237568
|
+
return this.extractBaseURL(urlArg.getText());
|
|
237015
237569
|
}
|
|
237016
|
-
return
|
|
237570
|
+
return null;
|
|
237017
237571
|
}
|
|
237018
|
-
|
|
237019
|
-
const
|
|
237020
|
-
|
|
237021
|
-
|
|
237022
|
-
const extracted = this.extractFromUnionMember(unionType, typeName, sourceFile, warnings);
|
|
237023
|
-
messageTypes.push(...extracted);
|
|
237572
|
+
extractMethodFromOptions(args) {
|
|
237573
|
+
const defaultMethod = "GET";
|
|
237574
|
+
if (args.length <= 1) {
|
|
237575
|
+
return defaultMethod;
|
|
237024
237576
|
}
|
|
237025
|
-
|
|
237026
|
-
|
|
237027
|
-
|
|
237028
|
-
if (unionType.isObject()) {
|
|
237029
|
-
return this.extractFromDiscriminatedUnion(unionType, sourceFile, warnings);
|
|
237577
|
+
const optionsArg = args[1];
|
|
237578
|
+
if (!import_ts_morph5.Node.isObjectLiteralExpression(optionsArg)) {
|
|
237579
|
+
return defaultMethod;
|
|
237030
237580
|
}
|
|
237031
|
-
|
|
237032
|
-
|
|
237581
|
+
const methodProp = optionsArg.getProperty("method");
|
|
237582
|
+
if (!methodProp || !import_ts_morph5.Node.isPropertyAssignment(methodProp)) {
|
|
237583
|
+
return defaultMethod;
|
|
237033
237584
|
}
|
|
237034
|
-
|
|
237035
|
-
|
|
237036
|
-
|
|
237037
|
-
return this.extractMessageTypesFromType(aliasedType, typeName, sourceFile, warnings);
|
|
237038
|
-
}
|
|
237585
|
+
const initializer = methodProp.getInitializer();
|
|
237586
|
+
if (!initializer || !import_ts_morph5.Node.isStringLiteral(initializer)) {
|
|
237587
|
+
return defaultMethod;
|
|
237039
237588
|
}
|
|
237040
|
-
return
|
|
237589
|
+
return initializer.getLiteralValue().toUpperCase();
|
|
237041
237590
|
}
|
|
237042
|
-
|
|
237043
|
-
const
|
|
237044
|
-
|
|
237045
|
-
|
|
237046
|
-
|
|
237047
|
-
|
|
237048
|
-
|
|
237049
|
-
|
|
237050
|
-
|
|
237051
|
-
|
|
237052
|
-
|
|
237591
|
+
findWebSockets() {
|
|
237592
|
+
const websockets = [];
|
|
237593
|
+
for (const sourceFile of this.project.getSourceFiles()) {
|
|
237594
|
+
sourceFile.forEachDescendant((node) => {
|
|
237595
|
+
if (import_ts_morph5.Node.isNewExpression(node)) {
|
|
237596
|
+
const expression = node.getExpression();
|
|
237597
|
+
if (import_ts_morph5.Node.isIdentifier(expression) && expression.getText() === "WebSocket") {
|
|
237598
|
+
const args = node.getArguments();
|
|
237599
|
+
if (args.length > 0 && import_ts_morph5.Node.isStringLiteral(args[0])) {
|
|
237600
|
+
const url = args[0].getLiteralValue();
|
|
237601
|
+
const description = this.extractJSDocDescription(node);
|
|
237602
|
+
websockets.push({
|
|
237603
|
+
url,
|
|
237604
|
+
file: sourceFile.getFilePath(),
|
|
237605
|
+
line: node.getStartLineNumber(),
|
|
237606
|
+
description
|
|
237607
|
+
});
|
|
237608
|
+
}
|
|
237609
|
+
}
|
|
237610
|
+
}
|
|
237611
|
+
});
|
|
237053
237612
|
}
|
|
237054
|
-
return
|
|
237613
|
+
return websockets;
|
|
237055
237614
|
}
|
|
237056
|
-
|
|
237057
|
-
const
|
|
237058
|
-
|
|
237059
|
-
|
|
237615
|
+
findExternalScripts() {
|
|
237616
|
+
const scripts = [];
|
|
237617
|
+
const seen = new Set;
|
|
237618
|
+
for (const sourceFile of this.project.getSourceFiles()) {
|
|
237619
|
+
this.extractExternalImportsFromFile(sourceFile, scripts, seen);
|
|
237060
237620
|
}
|
|
237061
|
-
return
|
|
237621
|
+
return scripts;
|
|
237062
237622
|
}
|
|
237063
|
-
|
|
237064
|
-
|
|
237065
|
-
|
|
237066
|
-
|
|
237067
|
-
|
|
237068
|
-
|
|
237069
|
-
|
|
237070
|
-
|
|
237071
|
-
|
|
237072
|
-
for (const branch of branches) {
|
|
237073
|
-
const extracted = this.extractStringLiteralFromBranch(branch);
|
|
237074
|
-
if (extracted) {
|
|
237075
|
-
messageTypes.push(extracted);
|
|
237623
|
+
extractExternalImportsFromFile(sourceFile, scripts, seen) {
|
|
237624
|
+
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
237625
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
237626
|
+
if (this.isExternalImport(moduleSpecifier)) {
|
|
237627
|
+
const packageName = this.extractPackageName(moduleSpecifier);
|
|
237628
|
+
if (packageName && !seen.has(packageName)) {
|
|
237629
|
+
seen.add(packageName);
|
|
237630
|
+
scripts.push(this.createExternalScriptIntegration(packageName, sourceFile));
|
|
237631
|
+
}
|
|
237076
237632
|
}
|
|
237077
237633
|
}
|
|
237078
|
-
return messageTypes;
|
|
237079
237634
|
}
|
|
237080
|
-
|
|
237081
|
-
|
|
237082
|
-
const match = trimmed.match(/^["'](\w+)["']$/);
|
|
237083
|
-
return match?.[1] ?? null;
|
|
237635
|
+
isExternalImport(moduleSpecifier) {
|
|
237636
|
+
return !moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/");
|
|
237084
237637
|
}
|
|
237085
|
-
|
|
237086
|
-
|
|
237087
|
-
|
|
237088
|
-
if (!match?.[1]) {
|
|
237089
|
-
return [];
|
|
237090
|
-
}
|
|
237091
|
-
const keyTypeName = match[1].trim();
|
|
237092
|
-
const keyTypeAlias = sourceFile.getTypeAlias?.(keyTypeName);
|
|
237093
|
-
if (!keyTypeAlias) {
|
|
237094
|
-
return [];
|
|
237638
|
+
extractPackageName(moduleSpecifier) {
|
|
237639
|
+
if (moduleSpecifier.startsWith("@")) {
|
|
237640
|
+
return moduleSpecifier.split("/").slice(0, 2).join("/");
|
|
237095
237641
|
}
|
|
237096
|
-
|
|
237097
|
-
return this.extractMessageTypesFromType(keyType, keyTypeName, sourceFile, warnings);
|
|
237098
|
-
}
|
|
237099
|
-
warnTemplateLiteral(type2, warnings) {
|
|
237100
|
-
warnings.push(`Template literal type: ${type2.getText()} - this creates an unbounded set and cannot be fully extracted`);
|
|
237642
|
+
return moduleSpecifier.split("/")[0];
|
|
237101
237643
|
}
|
|
237102
|
-
|
|
237103
|
-
|
|
237104
|
-
|
|
237105
|
-
|
|
237106
|
-
|
|
237107
|
-
|
|
237108
|
-
|
|
237109
|
-
}
|
|
237110
|
-
if (type2.isString() || type2.isStringLiteral()) {
|
|
237111
|
-
return { name, kind: "string", nullable };
|
|
237112
|
-
}
|
|
237113
|
-
if (type2.isNumber() || type2.isNumberLiteral()) {
|
|
237114
|
-
return { name, kind: "number", nullable };
|
|
237115
|
-
}
|
|
237116
|
-
if (type2.isArray()) {
|
|
237117
|
-
return this.convertArrayType(type2, name, nullable);
|
|
237118
|
-
}
|
|
237119
|
-
const collectionType = this.tryConvertCollectionType(type2, name, nullable);
|
|
237120
|
-
if (collectionType) {
|
|
237121
|
-
return collectionType;
|
|
237122
|
-
}
|
|
237123
|
-
if (type2.isObject()) {
|
|
237124
|
-
return this.convertObjectType(type2, name, nullable);
|
|
237125
|
-
}
|
|
237126
|
-
if (type2.isNull()) {
|
|
237127
|
-
return { name, kind: "null", nullable: true };
|
|
237128
|
-
}
|
|
237129
|
-
return { name, kind: "unknown", nullable };
|
|
237644
|
+
createExternalScriptIntegration(packageName, sourceFile) {
|
|
237645
|
+
return {
|
|
237646
|
+
type: "external-script",
|
|
237647
|
+
name: packageName,
|
|
237648
|
+
technology: "npm package",
|
|
237649
|
+
usedIn: [sourceFile.getFilePath()],
|
|
237650
|
+
description: `External dependency: ${packageName}`
|
|
237651
|
+
};
|
|
237130
237652
|
}
|
|
237131
|
-
|
|
237132
|
-
const
|
|
237133
|
-
const
|
|
237134
|
-
if (allStringLiterals) {
|
|
237135
|
-
const enumValues = unionTypes.map((t) => t.getLiteralValue());
|
|
237136
|
-
return { name, kind: "enum", nullable, enumValues };
|
|
237137
|
-
}
|
|
237138
|
-
const nonNullTypes = unionTypes.filter((t) => !t.isNull() && !t.isUndefined());
|
|
237139
|
-
if (nonNullTypes.length === 1) {
|
|
237140
|
-
const firstType = nonNullTypes[0];
|
|
237141
|
-
if (firstType) {
|
|
237142
|
-
const baseType = this.convertType(firstType, name);
|
|
237143
|
-
return { ...baseType, nullable: true };
|
|
237144
|
-
}
|
|
237145
|
-
}
|
|
237653
|
+
createAPIIntegration(call) {
|
|
237654
|
+
const baseURL = this.extractBaseURL(call.url);
|
|
237655
|
+
const name = this.inferAPIName(baseURL);
|
|
237146
237656
|
return {
|
|
237657
|
+
type: "api",
|
|
237147
237658
|
name,
|
|
237148
|
-
|
|
237149
|
-
|
|
237150
|
-
|
|
237659
|
+
technology: "REST API",
|
|
237660
|
+
url: baseURL,
|
|
237661
|
+
usedIn: [call.file],
|
|
237662
|
+
description: call.description || `External API: ${name}`,
|
|
237663
|
+
calls: [
|
|
237664
|
+
{
|
|
237665
|
+
method: call.method,
|
|
237666
|
+
endpoint: call.url,
|
|
237667
|
+
location: {
|
|
237668
|
+
file: call.file,
|
|
237669
|
+
line: call.line
|
|
237670
|
+
},
|
|
237671
|
+
description: call.description
|
|
237672
|
+
}
|
|
237673
|
+
]
|
|
237151
237674
|
};
|
|
237152
237675
|
}
|
|
237153
|
-
|
|
237154
|
-
const
|
|
237676
|
+
createWebSocketIntegration(ws) {
|
|
237677
|
+
const name = this.inferAPIName(ws.url);
|
|
237155
237678
|
return {
|
|
237679
|
+
type: "websocket",
|
|
237156
237680
|
name,
|
|
237157
|
-
|
|
237158
|
-
|
|
237159
|
-
|
|
237681
|
+
technology: "WebSocket",
|
|
237682
|
+
url: ws.url,
|
|
237683
|
+
usedIn: [ws.file],
|
|
237684
|
+
description: ws.description || `WebSocket connection: ${name}`
|
|
237160
237685
|
};
|
|
237161
237686
|
}
|
|
237162
|
-
|
|
237163
|
-
|
|
237164
|
-
|
|
237165
|
-
|
|
237166
|
-
|
|
237167
|
-
|
|
237168
|
-
|
|
237169
|
-
|
|
237170
|
-
|
|
237171
|
-
kind: "map",
|
|
237172
|
-
nullable,
|
|
237173
|
-
valueType: typeArgs?.[1] ? this.convertType(typeArgs[1], `${name}_value`) : undefined
|
|
237174
|
-
};
|
|
237175
|
-
}
|
|
237176
|
-
if (symbolName === "Set") {
|
|
237177
|
-
const typeArgs = type2.getTypeArguments();
|
|
237178
|
-
return {
|
|
237179
|
-
name,
|
|
237180
|
-
kind: "set",
|
|
237181
|
-
nullable,
|
|
237182
|
-
elementType: typeArgs?.[0] ? this.convertType(typeArgs[0], `${name}_element`) : undefined
|
|
237183
|
-
};
|
|
237184
|
-
}
|
|
237185
|
-
return null;
|
|
237186
|
-
}
|
|
237187
|
-
convertObjectType(type2, name, nullable) {
|
|
237188
|
-
const properties = {};
|
|
237189
|
-
const sourceFile = this.project.getSourceFiles()[0];
|
|
237190
|
-
if (sourceFile) {
|
|
237191
|
-
for (const prop of type2.getProperties()) {
|
|
237192
|
-
const propName = prop.getName();
|
|
237193
|
-
const propType = prop.getTypeAtLocation(sourceFile);
|
|
237194
|
-
properties[propName] = this.convertType(propType, propName);
|
|
237195
|
-
}
|
|
237687
|
+
extractBaseURL(url) {
|
|
237688
|
+
let cleanUrl = url.replace(/\$\{[^}]+\}/g, "");
|
|
237689
|
+
cleanUrl = cleanUrl.replace(/`/g, "");
|
|
237690
|
+
try {
|
|
237691
|
+
const parsed = new URL(cleanUrl);
|
|
237692
|
+
return `${parsed.protocol}//${parsed.host}`;
|
|
237693
|
+
} catch {
|
|
237694
|
+
const match = cleanUrl.match(/https?:\/\/([^/]+)/);
|
|
237695
|
+
return match ? match[0] : cleanUrl;
|
|
237196
237696
|
}
|
|
237197
|
-
return { name, kind: "object", nullable, properties };
|
|
237198
237697
|
}
|
|
237199
|
-
|
|
237200
|
-
|
|
237201
|
-
|
|
237202
|
-
|
|
237203
|
-
|
|
237204
|
-
|
|
237205
|
-
|
|
237206
|
-
}
|
|
237207
|
-
const analysis = this.analyzeField(path, propType);
|
|
237208
|
-
fields.push(analysis);
|
|
237209
|
-
}
|
|
237698
|
+
inferAPIName(url) {
|
|
237699
|
+
try {
|
|
237700
|
+
const parsed = new URL(url);
|
|
237701
|
+
const hostname2 = parsed.hostname;
|
|
237702
|
+
const cleanHost = hostname2.replace(/^www\./, "");
|
|
237703
|
+
const parts = cleanHost.split(".");
|
|
237704
|
+
if (parts.length > 0 && parts[0]) {
|
|
237705
|
+
return `${parts[0].charAt(0).toUpperCase() + parts[0].slice(1)} API`;
|
|
237210
237706
|
}
|
|
237211
|
-
}
|
|
237212
|
-
return
|
|
237213
|
-
}
|
|
237214
|
-
analyzeField(path, type2) {
|
|
237215
|
-
const analysis = {
|
|
237216
|
-
path,
|
|
237217
|
-
type: type2,
|
|
237218
|
-
confidence: "low",
|
|
237219
|
-
evidence: [],
|
|
237220
|
-
suggestions: [],
|
|
237221
|
-
bounds: {}
|
|
237222
|
-
};
|
|
237223
|
-
if (type2.kind === "boolean")
|
|
237224
|
-
return this.analyzeBooleanField(analysis);
|
|
237225
|
-
if (type2.kind === "enum")
|
|
237226
|
-
return this.analyzeEnumField(analysis, type2);
|
|
237227
|
-
if (type2.kind === "array")
|
|
237228
|
-
return this.analyzeArrayField(analysis);
|
|
237229
|
-
if (type2.kind === "number")
|
|
237230
|
-
return this.analyzeNumberField(analysis);
|
|
237231
|
-
if (type2.kind === "string")
|
|
237232
|
-
return this.analyzeStringField(analysis);
|
|
237233
|
-
if (type2.kind === "map" || type2.kind === "set")
|
|
237234
|
-
return this.analyzeMapSetField(analysis);
|
|
237235
|
-
return analysis;
|
|
237236
|
-
}
|
|
237237
|
-
analyzeBooleanField(analysis) {
|
|
237238
|
-
analysis.confidence = "high";
|
|
237239
|
-
analysis.evidence.push("Boolean type - auto-configured");
|
|
237240
|
-
return analysis;
|
|
237707
|
+
} catch {}
|
|
237708
|
+
return "External API";
|
|
237241
237709
|
}
|
|
237242
|
-
|
|
237243
|
-
|
|
237244
|
-
|
|
237245
|
-
|
|
237246
|
-
if (
|
|
237247
|
-
|
|
237710
|
+
addOrMergeIntegration(map, integration) {
|
|
237711
|
+
const key = `${integration.type}:${integration.name}`;
|
|
237712
|
+
if (map.has(key)) {
|
|
237713
|
+
const existing = map.get(key);
|
|
237714
|
+
if (!existing)
|
|
237715
|
+
return;
|
|
237716
|
+
existing.usedIn = [...new Set([...existing.usedIn, ...integration.usedIn])];
|
|
237717
|
+
if (integration.calls && existing.calls) {
|
|
237718
|
+
existing.calls.push(...integration.calls);
|
|
237719
|
+
} else if (integration.calls) {
|
|
237720
|
+
existing.calls = integration.calls;
|
|
237248
237721
|
}
|
|
237722
|
+
} else {
|
|
237723
|
+
map.set(key, integration);
|
|
237249
237724
|
}
|
|
237250
|
-
return analysis;
|
|
237251
|
-
}
|
|
237252
|
-
analyzeArrayField(analysis) {
|
|
237253
|
-
analysis.confidence = "low";
|
|
237254
|
-
analysis.suggestions.push("Choose maxLength: 5 (fast), 10 (balanced), or 20 (thorough)");
|
|
237255
|
-
if (analysis.bounds) {
|
|
237256
|
-
analysis.bounds.maxLength = undefined;
|
|
237257
|
-
}
|
|
237258
|
-
const foundBound = this.findArrayBound();
|
|
237259
|
-
if (foundBound && analysis.bounds) {
|
|
237260
|
-
analysis.confidence = "medium";
|
|
237261
|
-
analysis.evidence.push(`Found array check: ${foundBound.evidence}`);
|
|
237262
|
-
analysis.bounds.maxLength = foundBound.value;
|
|
237263
|
-
}
|
|
237264
|
-
return analysis;
|
|
237265
|
-
}
|
|
237266
|
-
analyzeNumberField(analysis) {
|
|
237267
|
-
analysis.confidence = "low";
|
|
237268
|
-
analysis.suggestions.push("Provide min and max values based on your application logic");
|
|
237269
|
-
if (analysis.bounds) {
|
|
237270
|
-
analysis.bounds.min = undefined;
|
|
237271
|
-
analysis.bounds.max = undefined;
|
|
237272
|
-
}
|
|
237273
|
-
const foundBound = this.findNumberBound();
|
|
237274
|
-
if (foundBound && analysis.bounds) {
|
|
237275
|
-
analysis.confidence = "high";
|
|
237276
|
-
analysis.evidence.push(`Found comparison: ${foundBound.evidence}`);
|
|
237277
|
-
analysis.bounds = { ...analysis.bounds, ...foundBound.bounds };
|
|
237278
|
-
}
|
|
237279
|
-
return analysis;
|
|
237280
|
-
}
|
|
237281
|
-
analyzeStringField(analysis) {
|
|
237282
|
-
analysis.confidence = "low";
|
|
237283
|
-
analysis.suggestions.push('Provide 2-3 example values: ["value1", "value2", "value3"]', "Or use { abstract: true } for symbolic verification");
|
|
237284
|
-
if (analysis.bounds) {
|
|
237285
|
-
analysis.bounds.values = undefined;
|
|
237286
|
-
}
|
|
237287
|
-
return analysis;
|
|
237288
|
-
}
|
|
237289
|
-
analyzeMapSetField(analysis) {
|
|
237290
|
-
analysis.confidence = "low";
|
|
237291
|
-
analysis.suggestions.push("Provide maxSize (recommended: 3-5)");
|
|
237292
|
-
if (analysis.bounds) {
|
|
237293
|
-
analysis.bounds.maxSize = undefined;
|
|
237294
|
-
}
|
|
237295
|
-
return analysis;
|
|
237296
237725
|
}
|
|
237297
|
-
|
|
237298
|
-
|
|
237299
|
-
|
|
237300
|
-
|
|
237301
|
-
|
|
237726
|
+
extractJSDocDescription(node) {
|
|
237727
|
+
const jsDocs = node.getJsDocs?.() || [];
|
|
237728
|
+
if (jsDocs.length === 0)
|
|
237729
|
+
return;
|
|
237730
|
+
const comment = jsDocs[0].getDescription().trim();
|
|
237731
|
+
return comment || undefined;
|
|
237302
237732
|
}
|
|
237303
237733
|
}
|
|
237304
|
-
|
|
237305
|
-
|
|
237306
|
-
|
|
237307
|
-
}
|
|
237308
|
-
var import_ts_morph3;
|
|
237309
|
-
var init_types2 = __esm(() => {
|
|
237310
|
-
init_handlers();
|
|
237311
|
-
import_ts_morph3 = __toESM(require_ts_morph(), 1);
|
|
237734
|
+
var import_ts_morph5;
|
|
237735
|
+
var init_integrations = __esm(() => {
|
|
237736
|
+
import_ts_morph5 = __toESM(require_ts_morph(), 1);
|
|
237312
237737
|
});
|
|
237313
237738
|
|
|
237314
237739
|
// tools/analysis/src/extract/manifest.ts
|
|
237315
|
-
var
|
|
237740
|
+
var fs2 = (() => ({}));
|
|
237316
237741
|
|
|
237317
237742
|
class ManifestParser {
|
|
237318
237743
|
manifestPath;
|
|
@@ -237321,7 +237746,7 @@ class ManifestParser {
|
|
|
237321
237746
|
constructor(projectRoot, optional = false) {
|
|
237322
237747
|
this.baseDir = projectRoot;
|
|
237323
237748
|
this.manifestPath = join(projectRoot, "manifest.json");
|
|
237324
|
-
if (!
|
|
237749
|
+
if (!fs2.existsSync(this.manifestPath)) {
|
|
237325
237750
|
if (optional) {
|
|
237326
237751
|
this.manifestData = null;
|
|
237327
237752
|
return;
|
|
@@ -237329,7 +237754,7 @@ class ManifestParser {
|
|
|
237329
237754
|
throw new Error(`manifest.json not found at ${this.manifestPath}`);
|
|
237330
237755
|
}
|
|
237331
237756
|
try {
|
|
237332
|
-
const content =
|
|
237757
|
+
const content = fs2.readFileSync(this.manifestPath, "utf-8");
|
|
237333
237758
|
this.manifestData = JSON.parse(content);
|
|
237334
237759
|
} catch (error) {
|
|
237335
237760
|
throw new Error(`Failed to parse manifest.json: ${error}`);
|
|
@@ -237505,27 +237930,27 @@ class ManifestParser {
|
|
|
237505
237930
|
join(this.baseDir, "src", manifestPath.replace(/\.js$/, ".tsx"))
|
|
237506
237931
|
];
|
|
237507
237932
|
for (const candidate of candidates) {
|
|
237508
|
-
if (
|
|
237933
|
+
if (fs2.existsSync(candidate)) {
|
|
237509
237934
|
return candidate;
|
|
237510
237935
|
}
|
|
237511
237936
|
}
|
|
237512
237937
|
return join(this.baseDir, manifestPath);
|
|
237513
237938
|
}
|
|
237514
237939
|
findAssociatedJS(htmlPath) {
|
|
237515
|
-
if (!
|
|
237940
|
+
if (!fs2.existsSync(htmlPath)) {
|
|
237516
237941
|
return null;
|
|
237517
237942
|
}
|
|
237518
|
-
const html =
|
|
237943
|
+
const html = fs2.readFileSync(htmlPath, "utf-8");
|
|
237519
237944
|
const scriptMatch = html.match(/<script[^>]+src=["']([^"']+)["']/i);
|
|
237520
237945
|
if (scriptMatch?.[1]) {
|
|
237521
237946
|
const scriptPath = scriptMatch[1];
|
|
237522
237947
|
const fullPath = resolve(dirname(htmlPath), scriptPath);
|
|
237523
|
-
if (
|
|
237948
|
+
if (fs2.existsSync(fullPath))
|
|
237524
237949
|
return fullPath;
|
|
237525
|
-
if (
|
|
237950
|
+
if (fs2.existsSync(fullPath.replace(/\.js$/, ".ts"))) {
|
|
237526
237951
|
return fullPath.replace(/\.js$/, ".ts");
|
|
237527
237952
|
}
|
|
237528
|
-
if (
|
|
237953
|
+
if (fs2.existsSync(fullPath.replace(/\.js$/, ".tsx"))) {
|
|
237529
237954
|
return fullPath.replace(/\.js$/, ".tsx");
|
|
237530
237955
|
}
|
|
237531
237956
|
}
|
|
@@ -237540,7 +237965,7 @@ class ManifestParser {
|
|
|
237540
237965
|
join(dir, "index.js")
|
|
237541
237966
|
];
|
|
237542
237967
|
for (const candidate of candidates) {
|
|
237543
|
-
if (
|
|
237968
|
+
if (fs2.existsSync(candidate)) {
|
|
237544
237969
|
return candidate;
|
|
237545
237970
|
}
|
|
237546
237971
|
}
|
|
@@ -237559,861 +237984,6 @@ var init_manifest = __esm(() => {
|
|
|
237559
237984
|
init_path();
|
|
237560
237985
|
});
|
|
237561
237986
|
|
|
237562
|
-
// tools/analysis/src/extract/contexts.ts
|
|
237563
|
-
class ContextAnalyzer {
|
|
237564
|
-
project;
|
|
237565
|
-
constructor(tsConfigPath) {
|
|
237566
|
-
this.project = new import_ts_morph4.Project({
|
|
237567
|
-
tsConfigFilePath: tsConfigPath
|
|
237568
|
-
});
|
|
237569
|
-
}
|
|
237570
|
-
analyzeContext(contextType, entryPoint, handlers) {
|
|
237571
|
-
const sourceFile = this.project.getSourceFile(entryPoint);
|
|
237572
|
-
if (!sourceFile) {
|
|
237573
|
-
throw new Error(`Could not find source file: ${entryPoint}`);
|
|
237574
|
-
}
|
|
237575
|
-
const chromeAPIs = this.extractChromeAPIs(sourceFile);
|
|
237576
|
-
const dependencies = this.extractDependencies(sourceFile);
|
|
237577
|
-
const description = this.extractDescription(sourceFile);
|
|
237578
|
-
const components = this.isUIContext(contextType) ? this.extractComponents(sourceFile) : undefined;
|
|
237579
|
-
const contextHandlers = handlers.filter((h) => h.node === contextType);
|
|
237580
|
-
return {
|
|
237581
|
-
type: contextType,
|
|
237582
|
-
entryPoint,
|
|
237583
|
-
handlers: contextHandlers,
|
|
237584
|
-
chromeAPIs,
|
|
237585
|
-
externalAPIs: [],
|
|
237586
|
-
...components ? { components } : {},
|
|
237587
|
-
dependencies,
|
|
237588
|
-
...description ? { description } : {}
|
|
237589
|
-
};
|
|
237590
|
-
}
|
|
237591
|
-
extractChromeAPIs(sourceFile) {
|
|
237592
|
-
const apis = new Set;
|
|
237593
|
-
sourceFile.forEachDescendant((node) => {
|
|
237594
|
-
if (import_ts_morph4.Node.isPropertyAccessExpression(node)) {
|
|
237595
|
-
const text = node.getText();
|
|
237596
|
-
this.detectAPIPattern(text, apis);
|
|
237597
|
-
}
|
|
237598
|
-
});
|
|
237599
|
-
return Array.from(apis).sort();
|
|
237600
|
-
}
|
|
237601
|
-
detectAPIPattern(text, apis) {
|
|
237602
|
-
if (text.startsWith("chrome.")) {
|
|
237603
|
-
const api = this.extractAPIFromPrefix(text, "chrome");
|
|
237604
|
-
if (api)
|
|
237605
|
-
apis.add(api);
|
|
237606
|
-
return;
|
|
237607
|
-
}
|
|
237608
|
-
if (text.startsWith("browser.")) {
|
|
237609
|
-
const api = this.extractAPIFromPrefix(text, "browser");
|
|
237610
|
-
if (api)
|
|
237611
|
-
apis.add(api);
|
|
237612
|
-
return;
|
|
237613
|
-
}
|
|
237614
|
-
if (text.includes("bus.adapters.")) {
|
|
237615
|
-
const api = this.extractAPIFromBusAdapter(text);
|
|
237616
|
-
if (api)
|
|
237617
|
-
apis.add(api);
|
|
237618
|
-
}
|
|
237619
|
-
}
|
|
237620
|
-
extractAPIFromPrefix(text, prefix) {
|
|
237621
|
-
const pattern = new RegExp(`^${prefix}\\.([^.(]+(?:\\.[^.(]+)?)`);
|
|
237622
|
-
const match = text.match(pattern);
|
|
237623
|
-
return match?.[1] || null;
|
|
237624
|
-
}
|
|
237625
|
-
extractAPIFromBusAdapter(text) {
|
|
237626
|
-
const match = text.match(/bus\.adapters\.([^.(]+)/);
|
|
237627
|
-
return match?.[1] || null;
|
|
237628
|
-
}
|
|
237629
|
-
extractDependencies(sourceFile) {
|
|
237630
|
-
const deps = [];
|
|
237631
|
-
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
237632
|
-
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
237633
|
-
deps.push(moduleSpecifier);
|
|
237634
|
-
}
|
|
237635
|
-
return deps;
|
|
237636
|
-
}
|
|
237637
|
-
extractDescription(sourceFile) {
|
|
237638
|
-
const firstStatement = sourceFile.getStatements()[0];
|
|
237639
|
-
if (!firstStatement)
|
|
237640
|
-
return;
|
|
237641
|
-
const leadingComments = firstStatement.getLeadingCommentRanges();
|
|
237642
|
-
if (leadingComments.length === 0)
|
|
237643
|
-
return;
|
|
237644
|
-
const comment = leadingComments[0].getText();
|
|
237645
|
-
const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
|
|
237646
|
-
if (descMatch) {
|
|
237647
|
-
return descMatch[1].trim();
|
|
237648
|
-
}
|
|
237649
|
-
const lines = comment.split(`
|
|
237650
|
-
`).map((l) => l.replace(/^[\s*]+/, "").trim()).filter((l) => l && !l.startsWith("@"));
|
|
237651
|
-
return lines[0] || undefined;
|
|
237652
|
-
}
|
|
237653
|
-
extractComponents(sourceFile) {
|
|
237654
|
-
const components = [];
|
|
237655
|
-
sourceFile.forEachDescendant((node) => {
|
|
237656
|
-
this.extractFunctionComponent(node, sourceFile, components);
|
|
237657
|
-
this.extractArrowFunctionComponent(node, sourceFile, components);
|
|
237658
|
-
this.extractClassComponent(node, sourceFile, components);
|
|
237659
|
-
});
|
|
237660
|
-
return components;
|
|
237661
|
-
}
|
|
237662
|
-
extractFunctionComponent(node, sourceFile, components) {
|
|
237663
|
-
if (!import_ts_morph4.Node.isFunctionDeclaration(node))
|
|
237664
|
-
return;
|
|
237665
|
-
const name = node.getName();
|
|
237666
|
-
if (!name || !this.looksLikeComponent(name, node))
|
|
237667
|
-
return;
|
|
237668
|
-
const description = this.extractJSDocDescription(node);
|
|
237669
|
-
components.push({
|
|
237670
|
-
name,
|
|
237671
|
-
type: "function",
|
|
237672
|
-
filePath: sourceFile.getFilePath(),
|
|
237673
|
-
line: node.getStartLineNumber(),
|
|
237674
|
-
props: this.extractProps(node),
|
|
237675
|
-
...description ? { description } : {}
|
|
237676
|
-
});
|
|
237677
|
-
}
|
|
237678
|
-
extractArrowFunctionComponent(node, sourceFile, components) {
|
|
237679
|
-
if (!import_ts_morph4.Node.isVariableDeclaration(node))
|
|
237680
|
-
return;
|
|
237681
|
-
const name = node.getName();
|
|
237682
|
-
const initializer = node.getInitializer();
|
|
237683
|
-
if (!name || !initializer)
|
|
237684
|
-
return;
|
|
237685
|
-
if (!import_ts_morph4.Node.isArrowFunction(initializer) && !import_ts_morph4.Node.isFunctionExpression(initializer))
|
|
237686
|
-
return;
|
|
237687
|
-
if (!this.looksLikeComponent(name, initializer))
|
|
237688
|
-
return;
|
|
237689
|
-
const description = this.extractJSDocDescription(node);
|
|
237690
|
-
components.push({
|
|
237691
|
-
name,
|
|
237692
|
-
type: "function",
|
|
237693
|
-
filePath: sourceFile.getFilePath(),
|
|
237694
|
-
line: node.getStartLineNumber(),
|
|
237695
|
-
props: this.extractProps(initializer),
|
|
237696
|
-
...description ? { description } : {}
|
|
237697
|
-
});
|
|
237698
|
-
}
|
|
237699
|
-
extractClassComponent(node, sourceFile, components) {
|
|
237700
|
-
if (!import_ts_morph4.Node.isClassDeclaration(node))
|
|
237701
|
-
return;
|
|
237702
|
-
const name = node.getName();
|
|
237703
|
-
if (!name || !this.looksLikeClassComponent(node))
|
|
237704
|
-
return;
|
|
237705
|
-
const description = this.extractJSDocDescription(node);
|
|
237706
|
-
components.push({
|
|
237707
|
-
name,
|
|
237708
|
-
type: "class",
|
|
237709
|
-
filePath: sourceFile.getFilePath(),
|
|
237710
|
-
line: node.getStartLineNumber(),
|
|
237711
|
-
props: this.extractPropsFromClass(node),
|
|
237712
|
-
...description ? { description } : {}
|
|
237713
|
-
});
|
|
237714
|
-
}
|
|
237715
|
-
looksLikeComponent(name, node) {
|
|
237716
|
-
if (!/^[A-Z]/.test(name))
|
|
237717
|
-
return false;
|
|
237718
|
-
const body = node.getBody();
|
|
237719
|
-
if (!body)
|
|
237720
|
-
return false;
|
|
237721
|
-
let hasJSX = false;
|
|
237722
|
-
if (import_ts_morph4.Node.isBlock(body)) {
|
|
237723
|
-
body.forEachDescendant((child) => {
|
|
237724
|
-
if (import_ts_morph4.Node.isJsxElement(child) || import_ts_morph4.Node.isJsxSelfClosingElement(child)) {
|
|
237725
|
-
hasJSX = true;
|
|
237726
|
-
}
|
|
237727
|
-
});
|
|
237728
|
-
} else if (import_ts_morph4.Node.isJsxElement(body) || import_ts_morph4.Node.isJsxSelfClosingElement(body)) {
|
|
237729
|
-
hasJSX = true;
|
|
237730
|
-
}
|
|
237731
|
-
return hasJSX;
|
|
237732
|
-
}
|
|
237733
|
-
looksLikeClassComponent(node) {
|
|
237734
|
-
const extendedTypes = node.getExtends();
|
|
237735
|
-
if (!extendedTypes)
|
|
237736
|
-
return false;
|
|
237737
|
-
const extendsText = extendedTypes.getText();
|
|
237738
|
-
return /Component|PureComponent/.test(extendsText);
|
|
237739
|
-
}
|
|
237740
|
-
extractProps(node) {
|
|
237741
|
-
const params = node.getParameters();
|
|
237742
|
-
if (params.length === 0)
|
|
237743
|
-
return [];
|
|
237744
|
-
const propsParam = params[0];
|
|
237745
|
-
const type2 = propsParam.getType();
|
|
237746
|
-
const props = [];
|
|
237747
|
-
for (const prop of type2.getProperties()) {
|
|
237748
|
-
props.push(prop.getName());
|
|
237749
|
-
}
|
|
237750
|
-
return props;
|
|
237751
|
-
}
|
|
237752
|
-
extractPropsFromClass(node) {
|
|
237753
|
-
const extendedTypes = node.getExtends();
|
|
237754
|
-
if (!extendedTypes)
|
|
237755
|
-
return [];
|
|
237756
|
-
const typeArgs = extendedTypes.getType().getTypeArguments();
|
|
237757
|
-
if (typeArgs.length === 0)
|
|
237758
|
-
return [];
|
|
237759
|
-
const propsType = typeArgs[0];
|
|
237760
|
-
const props = [];
|
|
237761
|
-
for (const prop of propsType.getProperties()) {
|
|
237762
|
-
props.push(prop.getName());
|
|
237763
|
-
}
|
|
237764
|
-
return props;
|
|
237765
|
-
}
|
|
237766
|
-
extractJSDocDescription(node) {
|
|
237767
|
-
const jsDocs = node.getJsDocs();
|
|
237768
|
-
if (jsDocs.length === 0)
|
|
237769
|
-
return;
|
|
237770
|
-
const description = jsDocs[0].getDescription().trim();
|
|
237771
|
-
return description || undefined;
|
|
237772
|
-
}
|
|
237773
|
-
isUIContext(contextType) {
|
|
237774
|
-
return ["popup", "options", "devtools"].includes(contextType);
|
|
237775
|
-
}
|
|
237776
|
-
}
|
|
237777
|
-
var import_ts_morph4;
|
|
237778
|
-
var init_contexts = __esm(() => {
|
|
237779
|
-
import_ts_morph4 = __toESM(require_ts_morph(), 1);
|
|
237780
|
-
});
|
|
237781
|
-
|
|
237782
|
-
// tools/analysis/src/extract/flows.ts
|
|
237783
|
-
class FlowAnalyzer {
|
|
237784
|
-
project;
|
|
237785
|
-
handlers;
|
|
237786
|
-
constructor(tsConfigPath, handlers) {
|
|
237787
|
-
this.project = new import_ts_morph5.Project({
|
|
237788
|
-
tsConfigFilePath: tsConfigPath
|
|
237789
|
-
});
|
|
237790
|
-
this.handlers = handlers;
|
|
237791
|
-
}
|
|
237792
|
-
analyzeFlows() {
|
|
237793
|
-
const flows = [];
|
|
237794
|
-
const handlersByType = new Map;
|
|
237795
|
-
for (const handler of this.handlers) {
|
|
237796
|
-
if (!handlersByType.has(handler.messageType)) {
|
|
237797
|
-
handlersByType.set(handler.messageType, []);
|
|
237798
|
-
}
|
|
237799
|
-
handlersByType.get(handler.messageType)?.push(handler);
|
|
237800
|
-
}
|
|
237801
|
-
for (const [messageType, handlers] of handlersByType) {
|
|
237802
|
-
const senders = this.findMessageSenders(messageType);
|
|
237803
|
-
for (const sender of senders) {
|
|
237804
|
-
const recipients = handlers.map((h) => h.node);
|
|
237805
|
-
const sequence = this.buildSequence(messageType, sender, handlers);
|
|
237806
|
-
const flowMetadata = this.extractFlowMetadata(sender.file, sender.line);
|
|
237807
|
-
flows.push({
|
|
237808
|
-
messageType,
|
|
237809
|
-
from: sender.context,
|
|
237810
|
-
to: recipients,
|
|
237811
|
-
...flowMetadata.trigger ? { trigger: flowMetadata.trigger } : {},
|
|
237812
|
-
...flowMetadata.flowName ? { flowName: flowMetadata.flowName } : {},
|
|
237813
|
-
...flowMetadata.description ? { description: flowMetadata.description } : {},
|
|
237814
|
-
sequence
|
|
237815
|
-
});
|
|
237816
|
-
}
|
|
237817
|
-
}
|
|
237818
|
-
return flows;
|
|
237819
|
-
}
|
|
237820
|
-
findMessageSenders(messageType) {
|
|
237821
|
-
const senders = [];
|
|
237822
|
-
for (const sourceFile of this.project.getSourceFiles()) {
|
|
237823
|
-
const filePath = sourceFile.getFilePath();
|
|
237824
|
-
const context = this.inferContext(filePath);
|
|
237825
|
-
sourceFile.forEachDescendant((node) => {
|
|
237826
|
-
this.processMessageSender(node, messageType, context, filePath, senders);
|
|
237827
|
-
});
|
|
237828
|
-
}
|
|
237829
|
-
return senders;
|
|
237830
|
-
}
|
|
237831
|
-
processMessageSender(node, messageType, context, filePath, senders) {
|
|
237832
|
-
if (!import_ts_morph5.Node.isCallExpression(node)) {
|
|
237833
|
-
return;
|
|
237834
|
-
}
|
|
237835
|
-
const expression = node.getExpression();
|
|
237836
|
-
if (import_ts_morph5.Node.isPropertyAccessExpression(expression)) {
|
|
237837
|
-
this.processPropertyAccessSender(node, expression, messageType, context, filePath, senders);
|
|
237838
|
-
} else if (import_ts_morph5.Node.isIdentifier(expression)) {
|
|
237839
|
-
this.processIdentifierSender(node, expression, messageType, context, filePath, senders);
|
|
237840
|
-
}
|
|
237841
|
-
}
|
|
237842
|
-
processPropertyAccessSender(node, expression, messageType, context, filePath, senders) {
|
|
237843
|
-
if (!import_ts_morph5.Node.isPropertyAccessExpression(expression)) {
|
|
237844
|
-
return;
|
|
237845
|
-
}
|
|
237846
|
-
const methodName = expression.getName();
|
|
237847
|
-
if (!this.isMessageSendMethod(methodName)) {
|
|
237848
|
-
return;
|
|
237849
|
-
}
|
|
237850
|
-
const args = node.getArguments();
|
|
237851
|
-
if (args.length === 0) {
|
|
237852
|
-
return;
|
|
237853
|
-
}
|
|
237854
|
-
const msgType = this.extractMessageTypeFromArg(args[0]);
|
|
237855
|
-
if (msgType === messageType) {
|
|
237856
|
-
senders.push({
|
|
237857
|
-
context,
|
|
237858
|
-
file: filePath,
|
|
237859
|
-
line: node.getStartLineNumber()
|
|
237860
|
-
});
|
|
237861
|
-
}
|
|
237862
|
-
}
|
|
237863
|
-
processIdentifierSender(node, expression, messageType, context, filePath, senders) {
|
|
237864
|
-
if (!import_ts_morph5.Node.isIdentifier(expression)) {
|
|
237865
|
-
return;
|
|
237866
|
-
}
|
|
237867
|
-
if (expression.getText() !== "postMessage") {
|
|
237868
|
-
return;
|
|
237869
|
-
}
|
|
237870
|
-
const args = node.getArguments();
|
|
237871
|
-
if (args.length === 0) {
|
|
237872
|
-
return;
|
|
237873
|
-
}
|
|
237874
|
-
const msgType = this.extractMessageTypeFromArg(args[0]);
|
|
237875
|
-
if (msgType === messageType) {
|
|
237876
|
-
senders.push({
|
|
237877
|
-
context,
|
|
237878
|
-
file: filePath,
|
|
237879
|
-
line: node.getStartLineNumber()
|
|
237880
|
-
});
|
|
237881
|
-
}
|
|
237882
|
-
}
|
|
237883
|
-
isMessageSendMethod(methodName) {
|
|
237884
|
-
return methodName === "send" || methodName === "emit" || methodName === "postMessage" || methodName === "broadcast";
|
|
237885
|
-
}
|
|
237886
|
-
buildSequence(messageType, sender, handlers) {
|
|
237887
|
-
const steps = [];
|
|
237888
|
-
let stepNumber = 1;
|
|
237889
|
-
steps.push({
|
|
237890
|
-
step: stepNumber++,
|
|
237891
|
-
action: `${sender.context}.send(${messageType})`,
|
|
237892
|
-
context: sender.context,
|
|
237893
|
-
location: {
|
|
237894
|
-
file: sender.file,
|
|
237895
|
-
line: sender.line
|
|
237896
|
-
}
|
|
237897
|
-
});
|
|
237898
|
-
for (const handler of handlers) {
|
|
237899
|
-
steps.push({
|
|
237900
|
-
step: stepNumber++,
|
|
237901
|
-
action: `${handler.node}.handle(${messageType})`,
|
|
237902
|
-
context: handler.node,
|
|
237903
|
-
location: handler.location
|
|
237904
|
-
});
|
|
237905
|
-
const subsends = this.findMessagesInHandler(handler);
|
|
237906
|
-
for (const subsend of subsends) {
|
|
237907
|
-
steps.push({
|
|
237908
|
-
step: stepNumber++,
|
|
237909
|
-
action: `${handler.node}.send(${subsend.messageType})`,
|
|
237910
|
-
context: handler.node,
|
|
237911
|
-
location: subsend.location
|
|
237912
|
-
});
|
|
237913
|
-
}
|
|
237914
|
-
}
|
|
237915
|
-
return steps;
|
|
237916
|
-
}
|
|
237917
|
-
findMessagesInHandler(handler) {
|
|
237918
|
-
const sends = [];
|
|
237919
|
-
const sourceFile = this.project.getSourceFile(handler.location.file);
|
|
237920
|
-
if (!sourceFile)
|
|
237921
|
-
return sends;
|
|
237922
|
-
const targetLine = handler.location.line;
|
|
237923
|
-
sourceFile.forEachDescendant((node) => {
|
|
237924
|
-
this.processSendCall(node, targetLine, handler.location.file, sends);
|
|
237925
|
-
});
|
|
237926
|
-
return sends;
|
|
237927
|
-
}
|
|
237928
|
-
processSendCall(node, targetLine, filePath, sends) {
|
|
237929
|
-
if (!import_ts_morph5.Node.isCallExpression(node)) {
|
|
237930
|
-
return;
|
|
237931
|
-
}
|
|
237932
|
-
const line = node.getStartLineNumber();
|
|
237933
|
-
if (!this.isNearLine(line, targetLine)) {
|
|
237934
|
-
return;
|
|
237935
|
-
}
|
|
237936
|
-
const expression = node.getExpression();
|
|
237937
|
-
if (!this.isSendOrEmitCall(expression)) {
|
|
237938
|
-
return;
|
|
237939
|
-
}
|
|
237940
|
-
const args = node.getArguments();
|
|
237941
|
-
if (args.length === 0) {
|
|
237942
|
-
return;
|
|
237943
|
-
}
|
|
237944
|
-
const messageType = this.extractMessageTypeFromArg(args[0]);
|
|
237945
|
-
if (messageType) {
|
|
237946
|
-
sends.push({
|
|
237947
|
-
messageType,
|
|
237948
|
-
location: { file: filePath, line }
|
|
237949
|
-
});
|
|
237950
|
-
}
|
|
237951
|
-
}
|
|
237952
|
-
isNearLine(line, targetLine) {
|
|
237953
|
-
return Math.abs(line - targetLine) < 20;
|
|
237954
|
-
}
|
|
237955
|
-
isSendOrEmitCall(expression) {
|
|
237956
|
-
if (!import_ts_morph5.Node.isPropertyAccessExpression(expression)) {
|
|
237957
|
-
return false;
|
|
237958
|
-
}
|
|
237959
|
-
const methodName = expression.getName();
|
|
237960
|
-
return methodName === "send" || methodName === "emit";
|
|
237961
|
-
}
|
|
237962
|
-
extractMessageTypeFromArg(arg) {
|
|
237963
|
-
if (import_ts_morph5.Node.isStringLiteral(arg)) {
|
|
237964
|
-
return arg.getLiteralValue();
|
|
237965
|
-
}
|
|
237966
|
-
if (import_ts_morph5.Node.isObjectLiteralExpression(arg)) {
|
|
237967
|
-
return this.extractMessageTypeFromObject(arg);
|
|
237968
|
-
}
|
|
237969
|
-
return;
|
|
237970
|
-
}
|
|
237971
|
-
extractMessageTypeFromObject(obj) {
|
|
237972
|
-
if (!import_ts_morph5.Node.isObjectLiteralExpression(obj)) {
|
|
237973
|
-
return;
|
|
237974
|
-
}
|
|
237975
|
-
const typeProperty = obj.getProperty("type");
|
|
237976
|
-
if (!typeProperty || !import_ts_morph5.Node.isPropertyAssignment(typeProperty)) {
|
|
237977
|
-
return;
|
|
237978
|
-
}
|
|
237979
|
-
const initializer = typeProperty.getInitializer();
|
|
237980
|
-
if (!initializer || !import_ts_morph5.Node.isStringLiteral(initializer)) {
|
|
237981
|
-
return;
|
|
237982
|
-
}
|
|
237983
|
-
return initializer.getLiteralValue();
|
|
237984
|
-
}
|
|
237985
|
-
extractFlowMetadata(filePath, lineNumber) {
|
|
237986
|
-
const sourceFile = this.project.getSourceFile(filePath);
|
|
237987
|
-
if (!sourceFile)
|
|
237988
|
-
return {};
|
|
237989
|
-
let targetNode = null;
|
|
237990
|
-
sourceFile.forEachDescendant((node) => {
|
|
237991
|
-
if (node.getStartLineNumber() === lineNumber) {
|
|
237992
|
-
targetNode = node;
|
|
237993
|
-
}
|
|
237994
|
-
});
|
|
237995
|
-
if (!targetNode)
|
|
237996
|
-
return {};
|
|
237997
|
-
const jsDocs = targetNode.getJsDocs?.() || [];
|
|
237998
|
-
if (jsDocs.length === 0)
|
|
237999
|
-
return {};
|
|
238000
|
-
const comment = jsDocs[0].getText();
|
|
238001
|
-
const flowMatch = comment.match(/@flow\s+([^\s]+)/);
|
|
238002
|
-
const flowName = flowMatch ? flowMatch[1] : undefined;
|
|
238003
|
-
const triggerMatch = comment.match(/@trigger\s+(.+?)(?:\n|$)/);
|
|
238004
|
-
const trigger = triggerMatch ? triggerMatch[1].trim() : undefined;
|
|
238005
|
-
const descMatch = comment.match(/@description\s+(.+?)(?:\n|$)/s);
|
|
238006
|
-
const description = descMatch ? descMatch[1].trim() : undefined;
|
|
238007
|
-
return { trigger, flowName, description };
|
|
238008
|
-
}
|
|
238009
|
-
inferContext(filePath) {
|
|
238010
|
-
const path = filePath.toLowerCase();
|
|
238011
|
-
const contextPatterns = [
|
|
238012
|
-
{ context: "background", patterns: ["/background/", "\\background\\"] },
|
|
238013
|
-
{ context: "content", patterns: ["/content/", "\\content\\"] },
|
|
238014
|
-
{ context: "popup", patterns: ["/popup/", "\\popup\\"] },
|
|
238015
|
-
{ context: "devtools", patterns: ["/devtools/", "\\devtools\\"] },
|
|
238016
|
-
{ context: "options", patterns: ["/options/", "\\options\\"] },
|
|
238017
|
-
{ context: "offscreen", patterns: ["/offscreen/", "\\offscreen\\"] },
|
|
238018
|
-
{ context: "server", patterns: ["/server/", "\\server\\", "/server."] },
|
|
238019
|
-
{ context: "client", patterns: ["/client/", "\\client\\", "/client."] },
|
|
238020
|
-
{ context: "worker", patterns: ["/worker/", "\\worker\\", "service-worker"] }
|
|
238021
|
-
];
|
|
238022
|
-
for (const { context, patterns } of contextPatterns) {
|
|
238023
|
-
if (patterns.some((pattern) => path.includes(pattern))) {
|
|
238024
|
-
return context;
|
|
238025
|
-
}
|
|
238026
|
-
}
|
|
238027
|
-
return "unknown";
|
|
238028
|
-
}
|
|
238029
|
-
}
|
|
238030
|
-
var import_ts_morph5;
|
|
238031
|
-
var init_flows = __esm(() => {
|
|
238032
|
-
import_ts_morph5 = __toESM(require_ts_morph(), 1);
|
|
238033
|
-
});
|
|
238034
|
-
|
|
238035
|
-
// tools/analysis/src/extract/integrations.ts
|
|
238036
|
-
class IntegrationAnalyzer {
|
|
238037
|
-
project;
|
|
238038
|
-
constructor(tsConfigPath) {
|
|
238039
|
-
this.project = new import_ts_morph6.Project({
|
|
238040
|
-
tsConfigFilePath: tsConfigPath
|
|
238041
|
-
});
|
|
238042
|
-
}
|
|
238043
|
-
analyzeIntegrations() {
|
|
238044
|
-
const integrations = new Map;
|
|
238045
|
-
const fetchCalls = this.findFetchCalls();
|
|
238046
|
-
for (const call of fetchCalls) {
|
|
238047
|
-
this.addOrMergeIntegration(integrations, this.createAPIIntegration(call));
|
|
238048
|
-
}
|
|
238049
|
-
const websockets = this.findWebSockets();
|
|
238050
|
-
for (const ws of websockets) {
|
|
238051
|
-
this.addOrMergeIntegration(integrations, this.createWebSocketIntegration(ws));
|
|
238052
|
-
}
|
|
238053
|
-
const externalScripts = this.findExternalScripts();
|
|
238054
|
-
for (const script of externalScripts) {
|
|
238055
|
-
this.addOrMergeIntegration(integrations, script);
|
|
238056
|
-
}
|
|
238057
|
-
return Array.from(integrations.values());
|
|
238058
|
-
}
|
|
238059
|
-
findFetchCalls() {
|
|
238060
|
-
const calls = [];
|
|
238061
|
-
for (const sourceFile of this.project.getSourceFiles()) {
|
|
238062
|
-
sourceFile.forEachDescendant((node) => {
|
|
238063
|
-
this.processFetchCall(node, sourceFile, calls);
|
|
238064
|
-
});
|
|
238065
|
-
}
|
|
238066
|
-
return calls;
|
|
238067
|
-
}
|
|
238068
|
-
processFetchCall(node, sourceFile, calls) {
|
|
238069
|
-
if (!import_ts_morph6.Node.isCallExpression(node)) {
|
|
238070
|
-
return;
|
|
238071
|
-
}
|
|
238072
|
-
const expression = node.getExpression();
|
|
238073
|
-
if (!this.isFetchCall(expression)) {
|
|
238074
|
-
return;
|
|
238075
|
-
}
|
|
238076
|
-
const args = node.getArguments();
|
|
238077
|
-
if (args.length === 0) {
|
|
238078
|
-
return;
|
|
238079
|
-
}
|
|
238080
|
-
const url = this.extractURLFromArg(args[0]);
|
|
238081
|
-
if (!url) {
|
|
238082
|
-
return;
|
|
238083
|
-
}
|
|
238084
|
-
const method = this.extractMethodFromOptions(args);
|
|
238085
|
-
const description = this.extractJSDocDescription(node);
|
|
238086
|
-
calls.push({
|
|
238087
|
-
url,
|
|
238088
|
-
method,
|
|
238089
|
-
file: sourceFile.getFilePath(),
|
|
238090
|
-
line: node.getStartLineNumber(),
|
|
238091
|
-
description
|
|
238092
|
-
});
|
|
238093
|
-
}
|
|
238094
|
-
isFetchCall(expression) {
|
|
238095
|
-
return import_ts_morph6.Node.isIdentifier(expression) && expression.getText() === "fetch";
|
|
238096
|
-
}
|
|
238097
|
-
extractURLFromArg(urlArg) {
|
|
238098
|
-
if (import_ts_morph6.Node.isStringLiteral(urlArg)) {
|
|
238099
|
-
return urlArg.getLiteralValue();
|
|
238100
|
-
}
|
|
238101
|
-
if (import_ts_morph6.Node.isTemplateExpression(urlArg)) {
|
|
238102
|
-
return this.extractBaseURL(urlArg.getText());
|
|
238103
|
-
}
|
|
238104
|
-
return null;
|
|
238105
|
-
}
|
|
238106
|
-
extractMethodFromOptions(args) {
|
|
238107
|
-
const defaultMethod = "GET";
|
|
238108
|
-
if (args.length <= 1) {
|
|
238109
|
-
return defaultMethod;
|
|
238110
|
-
}
|
|
238111
|
-
const optionsArg = args[1];
|
|
238112
|
-
if (!import_ts_morph6.Node.isObjectLiteralExpression(optionsArg)) {
|
|
238113
|
-
return defaultMethod;
|
|
238114
|
-
}
|
|
238115
|
-
const methodProp = optionsArg.getProperty("method");
|
|
238116
|
-
if (!methodProp || !import_ts_morph6.Node.isPropertyAssignment(methodProp)) {
|
|
238117
|
-
return defaultMethod;
|
|
238118
|
-
}
|
|
238119
|
-
const initializer = methodProp.getInitializer();
|
|
238120
|
-
if (!initializer || !import_ts_morph6.Node.isStringLiteral(initializer)) {
|
|
238121
|
-
return defaultMethod;
|
|
238122
|
-
}
|
|
238123
|
-
return initializer.getLiteralValue().toUpperCase();
|
|
238124
|
-
}
|
|
238125
|
-
findWebSockets() {
|
|
238126
|
-
const websockets = [];
|
|
238127
|
-
for (const sourceFile of this.project.getSourceFiles()) {
|
|
238128
|
-
sourceFile.forEachDescendant((node) => {
|
|
238129
|
-
if (import_ts_morph6.Node.isNewExpression(node)) {
|
|
238130
|
-
const expression = node.getExpression();
|
|
238131
|
-
if (import_ts_morph6.Node.isIdentifier(expression) && expression.getText() === "WebSocket") {
|
|
238132
|
-
const args = node.getArguments();
|
|
238133
|
-
if (args.length > 0 && import_ts_morph6.Node.isStringLiteral(args[0])) {
|
|
238134
|
-
const url = args[0].getLiteralValue();
|
|
238135
|
-
const description = this.extractJSDocDescription(node);
|
|
238136
|
-
websockets.push({
|
|
238137
|
-
url,
|
|
238138
|
-
file: sourceFile.getFilePath(),
|
|
238139
|
-
line: node.getStartLineNumber(),
|
|
238140
|
-
description
|
|
238141
|
-
});
|
|
238142
|
-
}
|
|
238143
|
-
}
|
|
238144
|
-
}
|
|
238145
|
-
});
|
|
238146
|
-
}
|
|
238147
|
-
return websockets;
|
|
238148
|
-
}
|
|
238149
|
-
findExternalScripts() {
|
|
238150
|
-
const scripts = [];
|
|
238151
|
-
const seen = new Set;
|
|
238152
|
-
for (const sourceFile of this.project.getSourceFiles()) {
|
|
238153
|
-
this.extractExternalImportsFromFile(sourceFile, scripts, seen);
|
|
238154
|
-
}
|
|
238155
|
-
return scripts;
|
|
238156
|
-
}
|
|
238157
|
-
extractExternalImportsFromFile(sourceFile, scripts, seen) {
|
|
238158
|
-
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
238159
|
-
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
238160
|
-
if (this.isExternalImport(moduleSpecifier)) {
|
|
238161
|
-
const packageName = this.extractPackageName(moduleSpecifier);
|
|
238162
|
-
if (packageName && !seen.has(packageName)) {
|
|
238163
|
-
seen.add(packageName);
|
|
238164
|
-
scripts.push(this.createExternalScriptIntegration(packageName, sourceFile));
|
|
238165
|
-
}
|
|
238166
|
-
}
|
|
238167
|
-
}
|
|
238168
|
-
}
|
|
238169
|
-
isExternalImport(moduleSpecifier) {
|
|
238170
|
-
return !moduleSpecifier.startsWith(".") && !moduleSpecifier.startsWith("/");
|
|
238171
|
-
}
|
|
238172
|
-
extractPackageName(moduleSpecifier) {
|
|
238173
|
-
if (moduleSpecifier.startsWith("@")) {
|
|
238174
|
-
return moduleSpecifier.split("/").slice(0, 2).join("/");
|
|
238175
|
-
}
|
|
238176
|
-
return moduleSpecifier.split("/")[0];
|
|
238177
|
-
}
|
|
238178
|
-
createExternalScriptIntegration(packageName, sourceFile) {
|
|
238179
|
-
return {
|
|
238180
|
-
type: "external-script",
|
|
238181
|
-
name: packageName,
|
|
238182
|
-
technology: "npm package",
|
|
238183
|
-
usedIn: [sourceFile.getFilePath()],
|
|
238184
|
-
description: `External dependency: ${packageName}`
|
|
238185
|
-
};
|
|
238186
|
-
}
|
|
238187
|
-
createAPIIntegration(call) {
|
|
238188
|
-
const baseURL = this.extractBaseURL(call.url);
|
|
238189
|
-
const name = this.inferAPIName(baseURL);
|
|
238190
|
-
return {
|
|
238191
|
-
type: "api",
|
|
238192
|
-
name,
|
|
238193
|
-
technology: "REST API",
|
|
238194
|
-
url: baseURL,
|
|
238195
|
-
usedIn: [call.file],
|
|
238196
|
-
description: call.description || `External API: ${name}`,
|
|
238197
|
-
calls: [
|
|
238198
|
-
{
|
|
238199
|
-
method: call.method,
|
|
238200
|
-
endpoint: call.url,
|
|
238201
|
-
location: {
|
|
238202
|
-
file: call.file,
|
|
238203
|
-
line: call.line
|
|
238204
|
-
},
|
|
238205
|
-
description: call.description
|
|
238206
|
-
}
|
|
238207
|
-
]
|
|
238208
|
-
};
|
|
238209
|
-
}
|
|
238210
|
-
createWebSocketIntegration(ws) {
|
|
238211
|
-
const name = this.inferAPIName(ws.url);
|
|
238212
|
-
return {
|
|
238213
|
-
type: "websocket",
|
|
238214
|
-
name,
|
|
238215
|
-
technology: "WebSocket",
|
|
238216
|
-
url: ws.url,
|
|
238217
|
-
usedIn: [ws.file],
|
|
238218
|
-
description: ws.description || `WebSocket connection: ${name}`
|
|
238219
|
-
};
|
|
238220
|
-
}
|
|
238221
|
-
extractBaseURL(url) {
|
|
238222
|
-
let cleanUrl = url.replace(/\$\{[^}]+\}/g, "");
|
|
238223
|
-
cleanUrl = cleanUrl.replace(/`/g, "");
|
|
238224
|
-
try {
|
|
238225
|
-
const parsed = new URL(cleanUrl);
|
|
238226
|
-
return `${parsed.protocol}//${parsed.host}`;
|
|
238227
|
-
} catch {
|
|
238228
|
-
const match = cleanUrl.match(/https?:\/\/([^/]+)/);
|
|
238229
|
-
return match ? match[0] : cleanUrl;
|
|
238230
|
-
}
|
|
238231
|
-
}
|
|
238232
|
-
inferAPIName(url) {
|
|
238233
|
-
try {
|
|
238234
|
-
const parsed = new URL(url);
|
|
238235
|
-
const hostname2 = parsed.hostname;
|
|
238236
|
-
const cleanHost = hostname2.replace(/^www\./, "");
|
|
238237
|
-
const parts = cleanHost.split(".");
|
|
238238
|
-
if (parts.length > 0 && parts[0]) {
|
|
238239
|
-
return `${parts[0].charAt(0).toUpperCase() + parts[0].slice(1)} API`;
|
|
238240
|
-
}
|
|
238241
|
-
} catch {}
|
|
238242
|
-
return "External API";
|
|
238243
|
-
}
|
|
238244
|
-
addOrMergeIntegration(map, integration) {
|
|
238245
|
-
const key = `${integration.type}:${integration.name}`;
|
|
238246
|
-
if (map.has(key)) {
|
|
238247
|
-
const existing = map.get(key);
|
|
238248
|
-
if (!existing)
|
|
238249
|
-
return;
|
|
238250
|
-
existing.usedIn = [...new Set([...existing.usedIn, ...integration.usedIn])];
|
|
238251
|
-
if (integration.calls && existing.calls) {
|
|
238252
|
-
existing.calls.push(...integration.calls);
|
|
238253
|
-
} else if (integration.calls) {
|
|
238254
|
-
existing.calls = integration.calls;
|
|
238255
|
-
}
|
|
238256
|
-
} else {
|
|
238257
|
-
map.set(key, integration);
|
|
238258
|
-
}
|
|
238259
|
-
}
|
|
238260
|
-
extractJSDocDescription(node) {
|
|
238261
|
-
const jsDocs = node.getJsDocs?.() || [];
|
|
238262
|
-
if (jsDocs.length === 0)
|
|
238263
|
-
return;
|
|
238264
|
-
const comment = jsDocs[0].getDescription().trim();
|
|
238265
|
-
return comment || undefined;
|
|
238266
|
-
}
|
|
238267
|
-
}
|
|
238268
|
-
var import_ts_morph6;
|
|
238269
|
-
var init_integrations = __esm(() => {
|
|
238270
|
-
import_ts_morph6 = __toESM(require_ts_morph(), 1);
|
|
238271
|
-
});
|
|
238272
|
-
|
|
238273
|
-
// tools/analysis/src/extract/adr.ts
|
|
238274
|
-
var fs2 = (() => ({}));
|
|
238275
|
-
|
|
238276
|
-
class ADRExtractor {
|
|
238277
|
-
projectRoot;
|
|
238278
|
-
constructor(projectRoot) {
|
|
238279
|
-
this.projectRoot = projectRoot;
|
|
238280
|
-
}
|
|
238281
|
-
extract() {
|
|
238282
|
-
const adrDir = this.findADRDirectory();
|
|
238283
|
-
if (!adrDir || !fs2.existsSync(adrDir)) {
|
|
238284
|
-
return {
|
|
238285
|
-
adrs: [],
|
|
238286
|
-
directory: adrDir || join(this.projectRoot, "docs", "adr")
|
|
238287
|
-
};
|
|
238288
|
-
}
|
|
238289
|
-
const files = fs2.readdirSync(adrDir).filter((file) => file.endsWith(".md")).map((file) => join(adrDir, file));
|
|
238290
|
-
const adrs = [];
|
|
238291
|
-
for (const file of files) {
|
|
238292
|
-
try {
|
|
238293
|
-
const adr2 = this.parseADR(file);
|
|
238294
|
-
if (adr2) {
|
|
238295
|
-
adrs.push(adr2);
|
|
238296
|
-
}
|
|
238297
|
-
} catch (error) {
|
|
238298
|
-
if (process.env["POLLY_DEBUG"]) {
|
|
238299
|
-
console.log(`[DEBUG] Failed to parse ADR file ${file}: ${error}`);
|
|
238300
|
-
}
|
|
238301
|
-
}
|
|
238302
|
-
}
|
|
238303
|
-
adrs.sort((a, b) => a.id.localeCompare(b.id));
|
|
238304
|
-
return {
|
|
238305
|
-
adrs,
|
|
238306
|
-
directory: adrDir
|
|
238307
|
-
};
|
|
238308
|
-
}
|
|
238309
|
-
findADRDirectory() {
|
|
238310
|
-
const candidates = [
|
|
238311
|
-
join(this.projectRoot, "docs", "adr"),
|
|
238312
|
-
join(this.projectRoot, "docs", "architecture", "decisions"),
|
|
238313
|
-
join(this.projectRoot, "adr"),
|
|
238314
|
-
join(this.projectRoot, "architecture", "decisions")
|
|
238315
|
-
];
|
|
238316
|
-
for (const candidate of candidates) {
|
|
238317
|
-
if (fs2.existsSync(candidate)) {
|
|
238318
|
-
return candidate;
|
|
238319
|
-
}
|
|
238320
|
-
}
|
|
238321
|
-
return null;
|
|
238322
|
-
}
|
|
238323
|
-
parseADR(filePath) {
|
|
238324
|
-
const content = fs2.readFileSync(filePath, "utf-8");
|
|
238325
|
-
const fileName = basename(filePath, ".md");
|
|
238326
|
-
const idMatch = fileName.match(/^(\d+)/);
|
|
238327
|
-
const id = idMatch?.[1] ?? fileName;
|
|
238328
|
-
const titleMatch = content.match(/^#\s+(.+)$/m);
|
|
238329
|
-
const title = titleMatch?.[1]?.trim() ?? fileName;
|
|
238330
|
-
const status = this.extractStatus(content);
|
|
238331
|
-
const date = this.extractDate(content);
|
|
238332
|
-
const context = this.extractSection(content, "Context");
|
|
238333
|
-
const decision = this.extractSection(content, "Decision");
|
|
238334
|
-
const consequences = this.extractSection(content, "Consequences");
|
|
238335
|
-
const alternativesSection = this.extractSection(content, "Alternatives");
|
|
238336
|
-
const alternatives = alternativesSection ? alternativesSection.split(`
|
|
238337
|
-
`).filter((line) => line.trim().startsWith("-")).map((line) => line.replace(/^-\s*/, "").trim()) : undefined;
|
|
238338
|
-
const links = this.extractLinks(content);
|
|
238339
|
-
if (!context || !decision || !consequences) {
|
|
238340
|
-
return null;
|
|
238341
|
-
}
|
|
238342
|
-
return {
|
|
238343
|
-
id,
|
|
238344
|
-
title,
|
|
238345
|
-
status,
|
|
238346
|
-
date,
|
|
238347
|
-
context,
|
|
238348
|
-
decision,
|
|
238349
|
-
consequences,
|
|
238350
|
-
...alternatives && alternatives.length > 0 ? { alternatives } : {},
|
|
238351
|
-
...links.length > 0 ? { links } : {},
|
|
238352
|
-
source: filePath
|
|
238353
|
-
};
|
|
238354
|
-
}
|
|
238355
|
-
extractStatus(content) {
|
|
238356
|
-
const statusMatch = content.match(/Status:\s*(\w+)/i);
|
|
238357
|
-
if (!statusMatch)
|
|
238358
|
-
return "accepted";
|
|
238359
|
-
const status = statusMatch[1]?.toLowerCase();
|
|
238360
|
-
if (status && ["proposed", "accepted", "deprecated", "superseded"].includes(status)) {
|
|
238361
|
-
return status;
|
|
238362
|
-
}
|
|
238363
|
-
return "accepted";
|
|
238364
|
-
}
|
|
238365
|
-
extractDate(content) {
|
|
238366
|
-
const dateMatch = content.match(/Date:\s*(\d{4}-\d{2}-\d{2})/i) || content.match(/(\d{4}-\d{2}-\d{2})/i);
|
|
238367
|
-
if (dateMatch?.[1]) {
|
|
238368
|
-
return dateMatch[1];
|
|
238369
|
-
}
|
|
238370
|
-
const isoDate = new Date().toISOString().split("T")[0];
|
|
238371
|
-
return isoDate || new Date().toLocaleDateString("en-CA");
|
|
238372
|
-
}
|
|
238373
|
-
extractSection(content, sectionName) {
|
|
238374
|
-
const regex = new RegExp(`##\\s+${sectionName}\\s*\\n([\\s\\S]*?)(?=\\n##|$)`, "i");
|
|
238375
|
-
const match = content.match(regex);
|
|
238376
|
-
return match?.[1]?.trim() ?? "";
|
|
238377
|
-
}
|
|
238378
|
-
extractLinks(content) {
|
|
238379
|
-
const links = [];
|
|
238380
|
-
const supersedesMatch = content.match(/Supersedes:\s*ADR-(\d+)/gi);
|
|
238381
|
-
if (supersedesMatch) {
|
|
238382
|
-
for (const match of supersedesMatch) {
|
|
238383
|
-
const idMatch = match.match(/ADR-(\d+)/);
|
|
238384
|
-
const id = idMatch?.[1];
|
|
238385
|
-
if (id) {
|
|
238386
|
-
links.push({
|
|
238387
|
-
type: "supersedes",
|
|
238388
|
-
adrId: id
|
|
238389
|
-
});
|
|
238390
|
-
}
|
|
238391
|
-
}
|
|
238392
|
-
}
|
|
238393
|
-
const supersededByMatch = content.match(/Superseded by:\s*ADR-(\d+)/gi);
|
|
238394
|
-
if (supersededByMatch) {
|
|
238395
|
-
for (const match of supersededByMatch) {
|
|
238396
|
-
const idMatch = match.match(/ADR-(\d+)/);
|
|
238397
|
-
const id = idMatch?.[1];
|
|
238398
|
-
if (id) {
|
|
238399
|
-
links.push({
|
|
238400
|
-
type: "superseded-by",
|
|
238401
|
-
adrId: id
|
|
238402
|
-
});
|
|
238403
|
-
}
|
|
238404
|
-
}
|
|
238405
|
-
}
|
|
238406
|
-
return links;
|
|
238407
|
-
}
|
|
238408
|
-
}
|
|
238409
|
-
function extractADRs(projectRoot) {
|
|
238410
|
-
const extractor = new ADRExtractor(projectRoot);
|
|
238411
|
-
return extractor.extract();
|
|
238412
|
-
}
|
|
238413
|
-
var init_adr = __esm(() => {
|
|
238414
|
-
init_path();
|
|
238415
|
-
});
|
|
238416
|
-
|
|
238417
237987
|
// tools/analysis/src/extract/project-detector.ts
|
|
238418
237988
|
var exports_project_detector = {};
|
|
238419
237989
|
__export(exports_project_detector, {
|
|
@@ -239052,18 +238622,448 @@ var init_architecture = __esm(() => {
|
|
|
239052
238622
|
init_manifest();
|
|
239053
238623
|
});
|
|
239054
238624
|
|
|
238625
|
+
// tools/analysis/src/extract/types.ts
|
|
238626
|
+
class TypeExtractor {
|
|
238627
|
+
project;
|
|
238628
|
+
constructor(tsConfigPath) {
|
|
238629
|
+
this.project = new import_ts_morph6.Project({
|
|
238630
|
+
tsConfigFilePath: tsConfigPath
|
|
238631
|
+
});
|
|
238632
|
+
}
|
|
238633
|
+
async analyzeCodebase(stateFilePath) {
|
|
238634
|
+
const stateType = stateFilePath ? this.extractStateType(stateFilePath) : this.findStateType();
|
|
238635
|
+
const messageTypes = this.findMessageTypes();
|
|
238636
|
+
const fields = stateType ? this.analyzeFields(stateType) : [];
|
|
238637
|
+
const handlerAnalysis = this.extractHandlerAnalysis();
|
|
238638
|
+
const validMessageTypes = this.filterAndLogMessageTypes(messageTypes, handlerAnalysis.messageTypes);
|
|
238639
|
+
const validHandlers = this.filterAndLogHandlers(handlerAnalysis.handlers);
|
|
238640
|
+
return {
|
|
238641
|
+
stateType,
|
|
238642
|
+
messageTypes: validMessageTypes,
|
|
238643
|
+
fields,
|
|
238644
|
+
handlers: validHandlers
|
|
238645
|
+
};
|
|
238646
|
+
}
|
|
238647
|
+
extractHandlerAnalysis() {
|
|
238648
|
+
const configFilePath = this.project.getCompilerOptions()["configFilePath"];
|
|
238649
|
+
const tsConfigPath = typeof configFilePath === "string" ? configFilePath : "tsconfig.json";
|
|
238650
|
+
const handlerExtractor = new HandlerExtractor(tsConfigPath);
|
|
238651
|
+
return handlerExtractor.extractHandlers();
|
|
238652
|
+
}
|
|
238653
|
+
filterAndLogMessageTypes(messageTypes, handlerMessageTypes) {
|
|
238654
|
+
const allMessageTypes = Array.from(new Set([...messageTypes, ...handlerMessageTypes]));
|
|
238655
|
+
const validMessageTypes = [];
|
|
238656
|
+
const invalidMessageTypes = [];
|
|
238657
|
+
for (const msgType of allMessageTypes) {
|
|
238658
|
+
if (this.isValidTLAIdentifier(msgType)) {
|
|
238659
|
+
validMessageTypes.push(msgType);
|
|
238660
|
+
} else {
|
|
238661
|
+
invalidMessageTypes.push(msgType);
|
|
238662
|
+
}
|
|
238663
|
+
}
|
|
238664
|
+
this.logInvalidMessageTypes(invalidMessageTypes);
|
|
238665
|
+
return validMessageTypes;
|
|
238666
|
+
}
|
|
238667
|
+
logInvalidMessageTypes(invalidMessageTypes) {
|
|
238668
|
+
if (invalidMessageTypes.length === 0 || !process.env["POLLY_DEBUG"])
|
|
238669
|
+
return;
|
|
238670
|
+
console.log(`[WARN] Filtered out ${invalidMessageTypes.length} invalid message type(s):`);
|
|
238671
|
+
for (const invalid of invalidMessageTypes) {
|
|
238672
|
+
console.log(`[WARN] - "${invalid}" (not a valid TLA+ identifier)`);
|
|
238673
|
+
}
|
|
238674
|
+
}
|
|
238675
|
+
filterAndLogHandlers(handlers) {
|
|
238676
|
+
const validHandlers = handlers.filter((h) => this.isValidTLAIdentifier(h.messageType));
|
|
238677
|
+
this.logInvalidHandlers(handlers, validHandlers);
|
|
238678
|
+
return validHandlers;
|
|
238679
|
+
}
|
|
238680
|
+
logInvalidHandlers(allHandlers, validHandlers) {
|
|
238681
|
+
const filteredHandlerCount = allHandlers.length - validHandlers.length;
|
|
238682
|
+
if (filteredHandlerCount === 0 || !process.env["POLLY_DEBUG"])
|
|
238683
|
+
return;
|
|
238684
|
+
console.log(`[WARN] Filtered out ${filteredHandlerCount} handler(s) with invalid message types:`);
|
|
238685
|
+
for (const handler of allHandlers) {
|
|
238686
|
+
if (!this.isValidTLAIdentifier(handler.messageType)) {
|
|
238687
|
+
console.log(`[WARN] - Handler for "${handler.messageType}" at ${handler.location.file}:${handler.location.line}`);
|
|
238688
|
+
}
|
|
238689
|
+
}
|
|
238690
|
+
}
|
|
238691
|
+
isValidTLAIdentifier(s) {
|
|
238692
|
+
if (!s || s.length === 0) {
|
|
238693
|
+
return false;
|
|
238694
|
+
}
|
|
238695
|
+
return /^[a-zA-Z][a-zA-Z0-9_]*$/.test(s);
|
|
238696
|
+
}
|
|
238697
|
+
extractStateType(filePath) {
|
|
238698
|
+
const sourceFile = this.project.getSourceFile(filePath);
|
|
238699
|
+
if (!sourceFile) {
|
|
238700
|
+
return null;
|
|
238701
|
+
}
|
|
238702
|
+
const typeAlias = sourceFile.getTypeAlias("AppState") || sourceFile.getTypeAlias("State") || sourceFile.getTypeAliases()[0];
|
|
238703
|
+
if (!typeAlias) {
|
|
238704
|
+
return null;
|
|
238705
|
+
}
|
|
238706
|
+
const type2 = typeAlias.getType();
|
|
238707
|
+
return this.convertType(type2, typeAlias.getName());
|
|
238708
|
+
}
|
|
238709
|
+
findStateType() {
|
|
238710
|
+
const stateFiles = this.project.getSourceFiles("**/state*.ts");
|
|
238711
|
+
for (const file of stateFiles) {
|
|
238712
|
+
const typeAlias = file.getTypeAlias("AppState") || file.getTypeAlias("State");
|
|
238713
|
+
if (typeAlias) {
|
|
238714
|
+
const type2 = typeAlias.getType();
|
|
238715
|
+
return this.convertType(type2, typeAlias.getName());
|
|
238716
|
+
}
|
|
238717
|
+
}
|
|
238718
|
+
return null;
|
|
238719
|
+
}
|
|
238720
|
+
findMessageTypes() {
|
|
238721
|
+
const messageTypes = [];
|
|
238722
|
+
const warnings = [];
|
|
238723
|
+
const messageFiles = this.project.getSourceFiles("**/message*.ts");
|
|
238724
|
+
for (const file of messageFiles) {
|
|
238725
|
+
for (const typeAlias of file.getTypeAliases()) {
|
|
238726
|
+
const extractedTypes = this.extractMessageTypesFromType(typeAlias.getType(), typeAlias.getName(), file, warnings);
|
|
238727
|
+
messageTypes.push(...extractedTypes);
|
|
238728
|
+
}
|
|
238729
|
+
}
|
|
238730
|
+
if (warnings.length > 0 && process.env["POLLY_DEBUG"]) {
|
|
238731
|
+
console.log("[WARN] Message type extraction warnings:");
|
|
238732
|
+
for (const warning of warnings) {
|
|
238733
|
+
console.log(`[WARN] ${warning}`);
|
|
238734
|
+
}
|
|
238735
|
+
}
|
|
238736
|
+
return [...new Set(messageTypes)];
|
|
238737
|
+
}
|
|
238738
|
+
extractMessageTypesFromType(type2, typeName, sourceFile, warnings) {
|
|
238739
|
+
if (type2.isUnion()) {
|
|
238740
|
+
return this.extractFromUnionType(type2, typeName, sourceFile, warnings);
|
|
238741
|
+
}
|
|
238742
|
+
if (type2.getAliasSymbol()) {
|
|
238743
|
+
return this.extractFromTypeAlias(type2, typeName, sourceFile, warnings);
|
|
238744
|
+
}
|
|
238745
|
+
if (type2.isConditionalType?.()) {
|
|
238746
|
+
return this.extractFromConditionalType(type2, warnings);
|
|
238747
|
+
}
|
|
238748
|
+
if (type2.getText().includes("[K in ")) {
|
|
238749
|
+
return this.extractFromMappedType(type2, sourceFile, warnings);
|
|
238750
|
+
}
|
|
238751
|
+
if (type2.getText().includes("`")) {
|
|
238752
|
+
this.warnTemplateLiteral(type2, warnings);
|
|
238753
|
+
return [];
|
|
238754
|
+
}
|
|
238755
|
+
return [];
|
|
238756
|
+
}
|
|
238757
|
+
extractFromUnionType(type2, typeName, sourceFile, warnings) {
|
|
238758
|
+
const messageTypes = [];
|
|
238759
|
+
const unionTypes = type2.getUnionTypes();
|
|
238760
|
+
for (const unionType of unionTypes) {
|
|
238761
|
+
const extracted = this.extractFromUnionMember(unionType, typeName, sourceFile, warnings);
|
|
238762
|
+
messageTypes.push(...extracted);
|
|
238763
|
+
}
|
|
238764
|
+
return messageTypes;
|
|
238765
|
+
}
|
|
238766
|
+
extractFromUnionMember(unionType, typeName, sourceFile, warnings) {
|
|
238767
|
+
if (unionType.isObject()) {
|
|
238768
|
+
return this.extractFromDiscriminatedUnion(unionType, sourceFile, warnings);
|
|
238769
|
+
}
|
|
238770
|
+
if (unionType.isStringLiteral()) {
|
|
238771
|
+
return [unionType.getLiteralValue()];
|
|
238772
|
+
}
|
|
238773
|
+
if (unionType.getAliasSymbol()) {
|
|
238774
|
+
const aliasedType = unionType.getAliasSymbol()?.getDeclaredType();
|
|
238775
|
+
if (aliasedType) {
|
|
238776
|
+
return this.extractMessageTypesFromType(aliasedType, typeName, sourceFile, warnings);
|
|
238777
|
+
}
|
|
238778
|
+
}
|
|
238779
|
+
return [];
|
|
238780
|
+
}
|
|
238781
|
+
extractFromDiscriminatedUnion(unionType, sourceFile, warnings) {
|
|
238782
|
+
const typeProperty = unionType.getProperty("type");
|
|
238783
|
+
if (!typeProperty) {
|
|
238784
|
+
return [];
|
|
238785
|
+
}
|
|
238786
|
+
const typeType = typeProperty.getTypeAtLocation(sourceFile);
|
|
238787
|
+
if (typeType.isStringLiteral()) {
|
|
238788
|
+
return [typeType.getLiteralValue()];
|
|
238789
|
+
}
|
|
238790
|
+
if (typeType.getText().includes("`")) {
|
|
238791
|
+
warnings.push(`Template literal type in discriminant: ${typeType.getText()} - may be unbounded`);
|
|
238792
|
+
}
|
|
238793
|
+
return [];
|
|
238794
|
+
}
|
|
238795
|
+
extractFromTypeAlias(type2, typeName, sourceFile, warnings) {
|
|
238796
|
+
const aliasedType = type2.getAliasSymbol()?.getDeclaredType();
|
|
238797
|
+
if (!aliasedType || aliasedType === type2) {
|
|
238798
|
+
return [];
|
|
238799
|
+
}
|
|
238800
|
+
return this.extractMessageTypesFromType(aliasedType, typeName, sourceFile, warnings);
|
|
238801
|
+
}
|
|
238802
|
+
extractFromConditionalType(type2, warnings) {
|
|
238803
|
+
warnings.push(`Conditional type detected: ${type2.getText()} - extracting conservatively`);
|
|
238804
|
+
const messageTypes = [];
|
|
238805
|
+
const typeText = type2.getText();
|
|
238806
|
+
const parts = typeText.split("?");
|
|
238807
|
+
if (parts.length < 2) {
|
|
238808
|
+
return messageTypes;
|
|
238809
|
+
}
|
|
238810
|
+
const branches = parts[1].split(":");
|
|
238811
|
+
for (const branch of branches) {
|
|
238812
|
+
const extracted = this.extractStringLiteralFromBranch(branch);
|
|
238813
|
+
if (extracted) {
|
|
238814
|
+
messageTypes.push(extracted);
|
|
238815
|
+
}
|
|
238816
|
+
}
|
|
238817
|
+
return messageTypes;
|
|
238818
|
+
}
|
|
238819
|
+
extractStringLiteralFromBranch(branch) {
|
|
238820
|
+
const trimmed = branch.trim();
|
|
238821
|
+
const match = trimmed.match(/^["'](\w+)["']$/);
|
|
238822
|
+
return match?.[1] ?? null;
|
|
238823
|
+
}
|
|
238824
|
+
extractFromMappedType(type2, sourceFile, warnings) {
|
|
238825
|
+
warnings.push(`Mapped type detected: ${type2.getText()} - attempting to extract keys`);
|
|
238826
|
+
const match = type2.getText().match(/\[K in ([^\]]+)\]/);
|
|
238827
|
+
if (!match?.[1]) {
|
|
238828
|
+
return [];
|
|
238829
|
+
}
|
|
238830
|
+
const keyTypeName = match[1].trim();
|
|
238831
|
+
const keyTypeAlias = sourceFile.getTypeAlias?.(keyTypeName);
|
|
238832
|
+
if (!keyTypeAlias) {
|
|
238833
|
+
return [];
|
|
238834
|
+
}
|
|
238835
|
+
const keyType = keyTypeAlias.getType();
|
|
238836
|
+
return this.extractMessageTypesFromType(keyType, keyTypeName, sourceFile, warnings);
|
|
238837
|
+
}
|
|
238838
|
+
warnTemplateLiteral(type2, warnings) {
|
|
238839
|
+
warnings.push(`Template literal type: ${type2.getText()} - this creates an unbounded set and cannot be fully extracted`);
|
|
238840
|
+
}
|
|
238841
|
+
convertType(type2, name) {
|
|
238842
|
+
const nullable = type2.isNullable();
|
|
238843
|
+
if (type2.isBoolean() || type2.isBooleanLiteral()) {
|
|
238844
|
+
return { name, kind: "boolean", nullable };
|
|
238845
|
+
}
|
|
238846
|
+
if (type2.isUnion()) {
|
|
238847
|
+
return this.convertUnionType(type2, name, nullable);
|
|
238848
|
+
}
|
|
238849
|
+
if (type2.isString() || type2.isStringLiteral()) {
|
|
238850
|
+
return { name, kind: "string", nullable };
|
|
238851
|
+
}
|
|
238852
|
+
if (type2.isNumber() || type2.isNumberLiteral()) {
|
|
238853
|
+
return { name, kind: "number", nullable };
|
|
238854
|
+
}
|
|
238855
|
+
if (type2.isArray()) {
|
|
238856
|
+
return this.convertArrayType(type2, name, nullable);
|
|
238857
|
+
}
|
|
238858
|
+
const collectionType = this.tryConvertCollectionType(type2, name, nullable);
|
|
238859
|
+
if (collectionType) {
|
|
238860
|
+
return collectionType;
|
|
238861
|
+
}
|
|
238862
|
+
if (type2.isObject()) {
|
|
238863
|
+
return this.convertObjectType(type2, name, nullable);
|
|
238864
|
+
}
|
|
238865
|
+
if (type2.isNull()) {
|
|
238866
|
+
return { name, kind: "null", nullable: true };
|
|
238867
|
+
}
|
|
238868
|
+
return { name, kind: "unknown", nullable };
|
|
238869
|
+
}
|
|
238870
|
+
convertUnionType(type2, name, nullable) {
|
|
238871
|
+
const unionTypes = type2.getUnionTypes();
|
|
238872
|
+
const allStringLiterals = unionTypes.every((t) => t.isStringLiteral());
|
|
238873
|
+
if (allStringLiterals) {
|
|
238874
|
+
const enumValues = unionTypes.map((t) => t.getLiteralValue());
|
|
238875
|
+
return { name, kind: "enum", nullable, enumValues };
|
|
238876
|
+
}
|
|
238877
|
+
const nonNullTypes = unionTypes.filter((t) => !t.isNull() && !t.isUndefined());
|
|
238878
|
+
if (nonNullTypes.length === 1) {
|
|
238879
|
+
const firstType = nonNullTypes[0];
|
|
238880
|
+
if (firstType) {
|
|
238881
|
+
const baseType = this.convertType(firstType, name);
|
|
238882
|
+
return { ...baseType, nullable: true };
|
|
238883
|
+
}
|
|
238884
|
+
}
|
|
238885
|
+
return {
|
|
238886
|
+
name,
|
|
238887
|
+
kind: "union",
|
|
238888
|
+
nullable,
|
|
238889
|
+
unionTypes: unionTypes.map((t, i2) => this.convertType(t, `${name}_${i2}`))
|
|
238890
|
+
};
|
|
238891
|
+
}
|
|
238892
|
+
convertArrayType(type2, name, nullable) {
|
|
238893
|
+
const elementType = type2.getArrayElementType();
|
|
238894
|
+
return {
|
|
238895
|
+
name,
|
|
238896
|
+
kind: "array",
|
|
238897
|
+
nullable,
|
|
238898
|
+
elementType: elementType ? this.convertType(elementType, `${name}_element`) : { name: "unknown", kind: "unknown", nullable: false }
|
|
238899
|
+
};
|
|
238900
|
+
}
|
|
238901
|
+
tryConvertCollectionType(type2, name, nullable) {
|
|
238902
|
+
const symbol = type2.getSymbol();
|
|
238903
|
+
if (!symbol)
|
|
238904
|
+
return null;
|
|
238905
|
+
const symbolName = symbol.getName();
|
|
238906
|
+
if (symbolName === "Map") {
|
|
238907
|
+
const typeArgs = type2.getTypeArguments();
|
|
238908
|
+
return {
|
|
238909
|
+
name,
|
|
238910
|
+
kind: "map",
|
|
238911
|
+
nullable,
|
|
238912
|
+
valueType: typeArgs?.[1] ? this.convertType(typeArgs[1], `${name}_value`) : undefined
|
|
238913
|
+
};
|
|
238914
|
+
}
|
|
238915
|
+
if (symbolName === "Set") {
|
|
238916
|
+
const typeArgs = type2.getTypeArguments();
|
|
238917
|
+
return {
|
|
238918
|
+
name,
|
|
238919
|
+
kind: "set",
|
|
238920
|
+
nullable,
|
|
238921
|
+
elementType: typeArgs?.[0] ? this.convertType(typeArgs[0], `${name}_element`) : undefined
|
|
238922
|
+
};
|
|
238923
|
+
}
|
|
238924
|
+
return null;
|
|
238925
|
+
}
|
|
238926
|
+
convertObjectType(type2, name, nullable) {
|
|
238927
|
+
const properties = {};
|
|
238928
|
+
const sourceFile = this.project.getSourceFiles()[0];
|
|
238929
|
+
if (sourceFile) {
|
|
238930
|
+
for (const prop of type2.getProperties()) {
|
|
238931
|
+
const propName = prop.getName();
|
|
238932
|
+
const propType = prop.getTypeAtLocation(sourceFile);
|
|
238933
|
+
properties[propName] = this.convertType(propType, propName);
|
|
238934
|
+
}
|
|
238935
|
+
}
|
|
238936
|
+
return { name, kind: "object", nullable, properties };
|
|
238937
|
+
}
|
|
238938
|
+
analyzeFields(stateType, prefix = "") {
|
|
238939
|
+
const fields = [];
|
|
238940
|
+
if (stateType.kind === "object" && stateType.properties) {
|
|
238941
|
+
for (const [key, propType] of Object.entries(stateType.properties)) {
|
|
238942
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
238943
|
+
if (propType.kind === "object") {
|
|
238944
|
+
fields.push(...this.analyzeFields(propType, path));
|
|
238945
|
+
} else {
|
|
238946
|
+
const analysis = this.analyzeField(path, propType);
|
|
238947
|
+
fields.push(analysis);
|
|
238948
|
+
}
|
|
238949
|
+
}
|
|
238950
|
+
}
|
|
238951
|
+
return fields;
|
|
238952
|
+
}
|
|
238953
|
+
analyzeField(path, type2) {
|
|
238954
|
+
const analysis = {
|
|
238955
|
+
path,
|
|
238956
|
+
type: type2,
|
|
238957
|
+
confidence: "low",
|
|
238958
|
+
evidence: [],
|
|
238959
|
+
suggestions: [],
|
|
238960
|
+
bounds: {}
|
|
238961
|
+
};
|
|
238962
|
+
if (type2.kind === "boolean")
|
|
238963
|
+
return this.analyzeBooleanField(analysis);
|
|
238964
|
+
if (type2.kind === "enum")
|
|
238965
|
+
return this.analyzeEnumField(analysis, type2);
|
|
238966
|
+
if (type2.kind === "array")
|
|
238967
|
+
return this.analyzeArrayField(analysis);
|
|
238968
|
+
if (type2.kind === "number")
|
|
238969
|
+
return this.analyzeNumberField(analysis);
|
|
238970
|
+
if (type2.kind === "string")
|
|
238971
|
+
return this.analyzeStringField(analysis);
|
|
238972
|
+
if (type2.kind === "map" || type2.kind === "set")
|
|
238973
|
+
return this.analyzeMapSetField(analysis);
|
|
238974
|
+
return analysis;
|
|
238975
|
+
}
|
|
238976
|
+
analyzeBooleanField(analysis) {
|
|
238977
|
+
analysis.confidence = "high";
|
|
238978
|
+
analysis.evidence.push("Boolean type - auto-configured");
|
|
238979
|
+
return analysis;
|
|
238980
|
+
}
|
|
238981
|
+
analyzeEnumField(analysis, type2) {
|
|
238982
|
+
if (type2.enumValues) {
|
|
238983
|
+
analysis.confidence = "high";
|
|
238984
|
+
analysis.evidence.push(`Enum with ${type2.enumValues.length} values`);
|
|
238985
|
+
if (analysis.bounds) {
|
|
238986
|
+
analysis.bounds.values = type2.enumValues;
|
|
238987
|
+
}
|
|
238988
|
+
}
|
|
238989
|
+
return analysis;
|
|
238990
|
+
}
|
|
238991
|
+
analyzeArrayField(analysis) {
|
|
238992
|
+
analysis.confidence = "low";
|
|
238993
|
+
analysis.suggestions.push("Choose maxLength: 5 (fast), 10 (balanced), or 20 (thorough)");
|
|
238994
|
+
if (analysis.bounds) {
|
|
238995
|
+
analysis.bounds.maxLength = undefined;
|
|
238996
|
+
}
|
|
238997
|
+
const foundBound = this.findArrayBound();
|
|
238998
|
+
if (foundBound && analysis.bounds) {
|
|
238999
|
+
analysis.confidence = "medium";
|
|
239000
|
+
analysis.evidence.push(`Found array check: ${foundBound.evidence}`);
|
|
239001
|
+
analysis.bounds.maxLength = foundBound.value;
|
|
239002
|
+
}
|
|
239003
|
+
return analysis;
|
|
239004
|
+
}
|
|
239005
|
+
analyzeNumberField(analysis) {
|
|
239006
|
+
analysis.confidence = "low";
|
|
239007
|
+
analysis.suggestions.push("Provide min and max values based on your application logic");
|
|
239008
|
+
if (analysis.bounds) {
|
|
239009
|
+
analysis.bounds.min = undefined;
|
|
239010
|
+
analysis.bounds.max = undefined;
|
|
239011
|
+
}
|
|
239012
|
+
const foundBound = this.findNumberBound();
|
|
239013
|
+
if (foundBound && analysis.bounds) {
|
|
239014
|
+
analysis.confidence = "high";
|
|
239015
|
+
analysis.evidence.push(`Found comparison: ${foundBound.evidence}`);
|
|
239016
|
+
analysis.bounds = { ...analysis.bounds, ...foundBound.bounds };
|
|
239017
|
+
}
|
|
239018
|
+
return analysis;
|
|
239019
|
+
}
|
|
239020
|
+
analyzeStringField(analysis) {
|
|
239021
|
+
analysis.confidence = "low";
|
|
239022
|
+
analysis.suggestions.push('Provide 2-3 example values: ["value1", "value2", "value3"]', "Or use { abstract: true } for symbolic verification");
|
|
239023
|
+
if (analysis.bounds) {
|
|
239024
|
+
analysis.bounds.values = undefined;
|
|
239025
|
+
}
|
|
239026
|
+
return analysis;
|
|
239027
|
+
}
|
|
239028
|
+
analyzeMapSetField(analysis) {
|
|
239029
|
+
analysis.confidence = "low";
|
|
239030
|
+
analysis.suggestions.push("Provide maxSize (recommended: 3-5)");
|
|
239031
|
+
if (analysis.bounds) {
|
|
239032
|
+
analysis.bounds.maxSize = undefined;
|
|
239033
|
+
}
|
|
239034
|
+
return analysis;
|
|
239035
|
+
}
|
|
239036
|
+
findArrayBound() {
|
|
239037
|
+
return null;
|
|
239038
|
+
}
|
|
239039
|
+
findNumberBound() {
|
|
239040
|
+
return null;
|
|
239041
|
+
}
|
|
239042
|
+
}
|
|
239043
|
+
async function analyzeCodebase(options) {
|
|
239044
|
+
const extractor = new TypeExtractor(options.tsConfigPath);
|
|
239045
|
+
return extractor.analyzeCodebase(options.stateFilePath);
|
|
239046
|
+
}
|
|
239047
|
+
var import_ts_morph6;
|
|
239048
|
+
var init_types = __esm(() => {
|
|
239049
|
+
init_handlers();
|
|
239050
|
+
import_ts_morph6 = __toESM(require_ts_morph(), 1);
|
|
239051
|
+
});
|
|
239052
|
+
|
|
239055
239053
|
// tools/analysis/src/extract/index.ts
|
|
239056
239054
|
var init_extract = __esm(() => {
|
|
239057
|
-
|
|
239058
|
-
|
|
239059
|
-
init_manifest();
|
|
239055
|
+
init_adr();
|
|
239056
|
+
init_architecture();
|
|
239060
239057
|
init_contexts();
|
|
239061
239058
|
init_flows();
|
|
239059
|
+
init_handlers();
|
|
239062
239060
|
init_integrations();
|
|
239063
|
-
|
|
239064
|
-
init_architecture();
|
|
239061
|
+
init_manifest();
|
|
239065
239062
|
init_project_detector();
|
|
239063
|
+
init_types();
|
|
239066
239064
|
});
|
|
239065
|
+
// tools/analysis/src/types/index.ts
|
|
239066
|
+
var init_types2 = () => {};
|
|
239067
239067
|
|
|
239068
239068
|
// tools/analysis/src/index.ts
|
|
239069
239069
|
var exports_src = {};
|
|
@@ -239086,8 +239086,8 @@ __export(exports_src, {
|
|
|
239086
239086
|
ADRExtractor: () => ADRExtractor
|
|
239087
239087
|
});
|
|
239088
239088
|
var init_src = __esm(() => {
|
|
239089
|
-
init_types();
|
|
239090
239089
|
init_extract();
|
|
239090
|
+
init_types2();
|
|
239091
239091
|
});
|
|
239092
239092
|
|
|
239093
239093
|
// tools/visualize/src/types/structurizr.ts
|
|
@@ -240436,4 +240436,4 @@ export {
|
|
|
240436
240436
|
generateTeachingMaterial
|
|
240437
240437
|
};
|
|
240438
240438
|
|
|
240439
|
-
//# debugId=
|
|
240439
|
+
//# debugId=E1CBC64B9CEE404064756E2164756E21
|