@aroman22/codegraph-vba 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -3
- package/dist/bin/daemon-release.d.ts +7 -0
- package/dist/db/queries.d.ts +41 -0
- package/dist/extraction/access-erd-extractor.d.ts +57 -0
- package/dist/extraction/extraction-version.d.ts +1 -1
- package/dist/extraction/grammars.d.ts +20 -0
- package/dist/extraction/index.d.ts +1 -0
- package/dist/extraction/parse-pool.d.ts +8 -3
- package/dist/extraction/sql-query-extractor.d.ts +16 -14
- package/dist/extraction/sql-table-scan.d.ts +184 -0
- package/dist/extraction/tree-sitter.d.ts +10 -1
- package/dist/extraction/vba/call-sweep.d.ts +2 -7
- package/dist/extraction/vba/calls.d.ts +24 -4
- package/dist/extraction/vba/constants.d.ts +7 -14
- package/dist/extraction/vba/context.d.ts +443 -7
- package/dist/extraction/vba/controls.d.ts +18 -1
- package/dist/extraction/vba/declarations.d.ts +1 -6
- package/dist/extraction/vba/dims.d.ts +11 -7
- package/dist/extraction/vba/docmd.d.ts +29 -2
- package/dist/extraction/vba/enums-consts.d.ts +7 -14
- package/dist/extraction/vba/error-channel.d.ts +57 -0
- package/dist/extraction/vba/errors.d.ts +64 -0
- package/dist/extraction/vba/filesystem-statements.d.ts +23 -0
- package/dist/extraction/vba/implements.d.ts +1 -6
- package/dist/extraction/vba/labels.d.ts +26 -0
- package/dist/extraction/vba/module-vars.d.ts +36 -0
- package/dist/extraction/vba/options.d.ts +88 -0
- package/dist/extraction/vba/parameters.d.ts +35 -0
- package/dist/extraction/vba/procedures.d.ts +1 -9
- package/dist/extraction/vba/rules.d.ts +10 -5
- package/dist/extraction/vba/runtime-objects.d.ts +59 -0
- package/dist/extraction/vba/signature.d.ts +57 -0
- package/dist/extraction/vba/sql-wrapper.d.ts +76 -3
- package/dist/extraction/vba/text-utils.d.ts +62 -2
- package/dist/extraction/vba-extractor.d.ts +18 -1
- package/dist/extraction/vba-form-extractor.d.ts +28 -14
- package/dist/extraction/vba-preprocess.d.ts +89 -3
- package/dist/extraction/vba-source.d.ts +0 -10
- package/dist/extraction/vba-test-manifest-extractor.d.ts +0 -6
- package/dist/graph/behavior-evidence.d.ts +162 -0
- package/dist/index.d.ts +22 -0
- package/dist/mcp/daemon-paths.d.ts +6 -0
- package/dist/mcp/daemon-registry.d.ts +82 -7
- package/dist/mcp/daemon-watchdog.d.ts +12 -0
- package/dist/mcp/daemon.d.ts +20 -1
- package/dist/mcp/proxy.d.ts +32 -0
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/project-config.d.ts +43 -2
- package/dist/resolution/index.d.ts +47 -2
- package/dist/resolution/name-matcher.d.ts +25 -0
- package/dist/resolution/vba-runtime-objects.d.ts +11 -11
- package/dist/types.d.ts +13 -5
- package/dist/utils/backtrace-helpers.d.ts +14 -2
- package/package.json +7 -7
|
@@ -1,7 +1,68 @@
|
|
|
1
|
-
import { VbaExtractorContext } from './context';
|
|
2
|
-
export declare function scanSqlInLine(ctx: VbaExtractorContext, line: string, lineNum: number, dedupe: Set<string>, sqlVariables: Map<string, string>): void;
|
|
1
|
+
import { VbaExtractorContext, ProcInfo } from './context';
|
|
3
2
|
/**
|
|
4
|
-
* #
|
|
3
|
+
* Issue #252 — `metadata.synthesizedBy` for a table reached through SQL
|
|
4
|
+
* assigned to a binding property at RUNTIME. Deliberately distinct from the
|
|
5
|
+
* `.form.txt` sweep's static `vba-row-source` so an audit can tell the two
|
|
6
|
+
* provenances apart: one is what the designer stored, the other is what the
|
|
7
|
+
* code actually binds.
|
|
8
|
+
*/
|
|
9
|
+
export declare const RUNTIME_BINDING_SYNTHESIZER = "vba-row-source-dynamic";
|
|
10
|
+
/**
|
|
11
|
+
* Issue #244 — the wrapper list used when `codegraph.json` →
|
|
12
|
+
* `vba.sqlWrappers` is absent. A STRICT SUPERSET of the pre-#244 behaviour:
|
|
13
|
+
*
|
|
14
|
+
* - `db` covers every receiver the old `…db\b` regex reached (see
|
|
15
|
+
* {@link receiverMatches} for the suffix arm that preserves it) plus the
|
|
16
|
+
* `db`-prefixed locals it missed (`dbUse`, `dbToUse`, `m_dbLanzadera`).
|
|
17
|
+
* - `getdb` covers the per-backend accessor family (`getdbHPS()`,
|
|
18
|
+
* `getdbExpedientes()`, `getdbLanzadera()`) the issue measured at 26-36%
|
|
19
|
+
* of all execution sites.
|
|
20
|
+
* - `CurrentDb` / `DBEngine` are the Access built-ins.
|
|
21
|
+
* - `qd` / `qdf` are the conventional DAO `QueryDef` receivers.
|
|
22
|
+
* - `DoCmd.RunSQL` is the Access statement form (both the literal and the
|
|
23
|
+
* variable spelling, previously two dedicated regexes).
|
|
24
|
+
* - `Connection.Execute` / `Recordset.Open` are the ADO pair.
|
|
25
|
+
*
|
|
26
|
+
* Project-specific accessors that do not fit these names are added through
|
|
27
|
+
* `vba.sqlWrappers`, which EXTENDS this list rather than replacing it.
|
|
28
|
+
*/
|
|
29
|
+
export declare const DEFAULT_SQL_WRAPPERS: readonly string[];
|
|
30
|
+
/** One parsed wrapper entry. Both fields are lowercase — VBA is case-insensitive. */
|
|
31
|
+
interface SqlWrapperMatcher {
|
|
32
|
+
/** Receiver name fragment, e.g. `getdb` from `"getdb"` or `cnn` from `"cnn.Execute"`. */
|
|
33
|
+
receiver: string;
|
|
34
|
+
/** Method name, or `null` for a bare entry (which implies {@link DEFAULT_WRAPPER_METHODS}). */
|
|
35
|
+
method: string | null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The per-extractor compiled wrapper state. Built ONCE per `VbaExtractor`
|
|
39
|
+
* (see `vba-extractor.ts`) and cached on `VbaExtractorContext` — the two
|
|
40
|
+
* RegExps are stateful (`/g`) and `scanSqlInLine` runs on every line of every
|
|
41
|
+
* file, so re-compiling them per line (which is what the pre-#244 code did)
|
|
42
|
+
* is pure waste on the hottest path in VBA extraction.
|
|
43
|
+
*/
|
|
44
|
+
export interface CompiledSqlWrappers {
|
|
45
|
+
/** Literal form — `<receiver>[()].<method> "…"`. Stateful; reset before use. */
|
|
46
|
+
readonly literalRe: RegExp;
|
|
47
|
+
/** Variable form — `<receiver>[()].<method> <identifier>`. Stateful; reset before use. */
|
|
48
|
+
readonly varRe: RegExp;
|
|
49
|
+
/** Parsed entries, defaults first, project entries appended. */
|
|
50
|
+
readonly matchers: readonly SqlWrapperMatcher[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse the plain-string wrapper entries into matchers and pair them with a
|
|
54
|
+
* fresh pair of scanning RegExps.
|
|
55
|
+
*
|
|
56
|
+
* `configured` entries are APPENDED to {@link DEFAULT_SQL_WRAPPERS}, never
|
|
57
|
+
* substituted for them: a project that names its own accessor must not lose
|
|
58
|
+
* `CurrentDb` in the trade. Entries are accepted in exactly two forms — a
|
|
59
|
+
* bare identifier fragment (`"getdb"`) or a `receiver.method` pair
|
|
60
|
+
* (`"cnn.Execute"`). Anything else is dropped here; `project-config.ts`
|
|
61
|
+
* already warned about it at load time.
|
|
62
|
+
*/
|
|
63
|
+
export declare function compileSqlWrappers(configured?: readonly string[]): CompiledSqlWrappers;
|
|
64
|
+
/**
|
|
65
|
+
* Issue #13: `sql = sql & "..."` (self-referential concatenation) must
|
|
5
66
|
* ACCUMULATE the new fragment onto whatever was already tracked for
|
|
6
67
|
* `varName`, not overwrite it. Overwriting silently dropped earlier
|
|
7
68
|
* fragments' tables — typically the initial `FROM <table>` in
|
|
@@ -14,4 +75,16 @@ export declare function scanSqlInLine(ctx: VbaExtractorContext, line: string, li
|
|
|
14
75
|
* behavior is unchanged.
|
|
15
76
|
*/
|
|
16
77
|
export declare function trackSqlVariableAssignment(lines: string[], lineIndex: number, sqlVariables: Map<string, string>): void;
|
|
78
|
+
/**
|
|
79
|
+
* Issue #255: scan one line for domain-aggregate calls and emit the saved-query
|
|
80
|
+
* reference their second argument names.
|
|
81
|
+
*
|
|
82
|
+
* Deliberately NOT handled: a domain spelled as a full SQL statement
|
|
83
|
+
* (`DLookup("N", "SELECT Id FROM T")`), which Access also accepts. It is
|
|
84
|
+
* rejected by `looksLikeSavedQueryName`'s verb gate and produces nothing —
|
|
85
|
+
* the same silence T11 chose for every literal it cannot classify.
|
|
86
|
+
*/
|
|
87
|
+
export declare function scanDomainFunctionsInLine(ctx: VbaExtractorContext, line: string, lineNum: number, dedupe: Set<string>, caller: ProcInfo): void;
|
|
88
|
+
export declare function scanSqlInLine(ctx: VbaExtractorContext, line: string, lineNum: number, dedupe: Set<string>, sqlVariables: Map<string, string>, caller?: ProcInfo): void;
|
|
89
|
+
export {};
|
|
17
90
|
//# sourceMappingURL=sql-wrapper.d.ts.map
|
|
@@ -11,8 +11,17 @@ export declare function foldVisibility(raw: string): 'public' | 'private';
|
|
|
11
11
|
* call-site patterns inside `"..."` spans are invisible to CALL_RE and
|
|
12
12
|
* the statement-form detectors. Column positions are preserved (each
|
|
13
13
|
* character is replaced 1-for-1) so any col-based metadata stays correct.
|
|
14
|
+
*
|
|
15
|
+
* Issue #265: `filler` selects the replacement character. The default `' '`
|
|
16
|
+
* is what every column-sensitive scanner wants — a masked literal reads as
|
|
17
|
+
* blank space. The statement-call detectors need the opposite: they must
|
|
18
|
+
* still be able to tell `MsgBox "x"` (an argument list, therefore
|
|
19
|
+
* unambiguously a call) from a bare `MsgBox` read, and a space-masked line
|
|
20
|
+
* collapses the two. Passing `'_'` keeps the literal *visible* as an opaque
|
|
21
|
+
* token while preserving both the 1-for-1 column mapping and the property
|
|
22
|
+
* that nothing inside the literal can be parsed as VBA syntax.
|
|
14
23
|
*/
|
|
15
|
-
export declare function maskStringContent(line: string): string;
|
|
24
|
+
export declare function maskStringContent(line: string, filler?: string): string;
|
|
16
25
|
/**
|
|
17
26
|
* #13 helper: escape a variable name for safe interpolation into the
|
|
18
27
|
* self-reference RegExp built by `trackSqlVariableAssignment`. VBA
|
|
@@ -26,7 +35,6 @@ export declare function parseConstDeclarations(body: string): Array<{
|
|
|
26
35
|
asType: string;
|
|
27
36
|
value: string | null;
|
|
28
37
|
}>;
|
|
29
|
-
export declare function splitOutsideVbaStrings(value: string, separator: string): string[];
|
|
30
38
|
/**
|
|
31
39
|
* Issue #50 helper: return true iff `line.charAt(fromIndex..)` (after the
|
|
32
40
|
* matched TempVars site, including any trailing whitespace) holds an `=`
|
|
@@ -65,4 +73,56 @@ export declare function parseEventHandlerName(name: string): {
|
|
|
65
73
|
controlName: string;
|
|
66
74
|
eventName: string;
|
|
67
75
|
} | null;
|
|
76
|
+
/**
|
|
77
|
+
* Issue #247 helper: parse a FORM-LEVEL Access event-handler Sub name.
|
|
78
|
+
*
|
|
79
|
+
* `parseEventHandlerName` above deliberately refuses `Form_*` because a
|
|
80
|
+
* form-level event fires on the form object, not on a control — routing it
|
|
81
|
+
* through the control path would synthesize a bogus `form-instance-control`
|
|
82
|
+
* node literally named `Form`. This helper is the other half of that
|
|
83
|
+
* decision: it recognises exactly the names the control parser rejects and
|
|
84
|
+
* hands them to the caller so they can be wired to the sibling
|
|
85
|
+
* `form-layout` / `report-layout` node instead.
|
|
86
|
+
*
|
|
87
|
+
* The owner segment must be exactly `Form` or `Report` (case-insensitive,
|
|
88
|
+
* matching VBA's case-insensitive identifiers), and the suffix must be a
|
|
89
|
+
* known Access event name. That second gate is what separates the real
|
|
90
|
+
* lifecycle handler `Form_Load` from an ordinary class method that merely
|
|
91
|
+
* happens to be called `Form_Helper`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function parseFormLevelEventHandlerName(name: string): {
|
|
94
|
+
ownerName: string;
|
|
95
|
+
eventName: string;
|
|
96
|
+
} | null;
|
|
97
|
+
/**
|
|
98
|
+
* Sibling layout extension implied by an Access code-behind prefix.
|
|
99
|
+
* `Form_*` binds to a `.form.txt`; `Report_*` binds to a `.report.txt`.
|
|
100
|
+
*/
|
|
101
|
+
export type CodeBehindExt = '.form.txt' | '.report.txt';
|
|
102
|
+
/**
|
|
103
|
+
* Issue #249 helper: the `Form_` / `Report_` code-behind prefix carried by a
|
|
104
|
+
* module's RESOLVED `Attribute VB_Name`, used as the fallback when the file
|
|
105
|
+
* on disk does not carry the prefix in its basename.
|
|
106
|
+
*
|
|
107
|
+
* Both binding sites (the event-handler synthesis in `procedures.ts` and the
|
|
108
|
+
* `Me.<Control>` sweep in `controls.ts`) keep their own basename check as the
|
|
109
|
+
* fast path and consult this only on a basename miss, so the common case —
|
|
110
|
+
* a Dysflow export whose filename and `VB_Name` agree — is byte-for-byte
|
|
111
|
+
* unchanged.
|
|
112
|
+
*
|
|
113
|
+
* `classNamePrefix` is `null` for `.bas` modules and holds the resolved
|
|
114
|
+
* `VB_Name` (or the extension-stripped basename when the module carries no
|
|
115
|
+
* `VB_Name`) for `.cls` modules, so this fallback can only ever fire for a
|
|
116
|
+
* class module. That is deliberate: the guard that keeps a plain service
|
|
117
|
+
* class such as `InformeRiesgoPDFServicio.cls` — whose methods
|
|
118
|
+
* (`GenerarHTML_Principal`, `GetEstilosCSS_PDF`) look like event handlers to
|
|
119
|
+
* a naive `<X>_<Y>` split — from synthesizing hundreds of spurious
|
|
120
|
+
* `form-instance-control` stubs is that NEITHER its filename NOR its
|
|
121
|
+
* `VB_Name` starts with `Form_` / `Report_`. Widening the prefix test would
|
|
122
|
+
* reopen exactly that hole, so the test stays the same canonical Access
|
|
123
|
+
* naming convention; only the string it is applied to widens.
|
|
124
|
+
*
|
|
125
|
+
* The trailing `.+` is load-bearing: a bare `Form_` names no form.
|
|
126
|
+
*/
|
|
127
|
+
export declare function codeBehindExtFromVbName(classNamePrefix: string | null): CodeBehindExt | null;
|
|
68
128
|
//# sourceMappingURL=text-utils.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ExtractionResult } from '../types';
|
|
2
|
+
import type { VbaExtractionOptions } from './vba/options';
|
|
2
3
|
import type { VbaExtractionRule } from './vba/rules';
|
|
3
4
|
/**
|
|
4
5
|
* Issue #152: per-file fanout cap for `RaiseEvent <EventName>` edges. An
|
|
@@ -62,7 +63,6 @@ export declare class VbaExtractor {
|
|
|
62
63
|
private filePath;
|
|
63
64
|
private source;
|
|
64
65
|
private ctx;
|
|
65
|
-
private vbaTargets?;
|
|
66
66
|
/**
|
|
67
67
|
* Issue #152: per-file fanout cap for `RaiseEvent <EventName>` edges.
|
|
68
68
|
* When a single event is raised more than this many times in one file,
|
|
@@ -72,6 +72,23 @@ export declare class VbaExtractor {
|
|
|
72
72
|
* the orchestrator: callers that don't pass a value get the default.
|
|
73
73
|
*/
|
|
74
74
|
private maxRaiseFanout;
|
|
75
|
+
/**
|
|
76
|
+
* Issue #243: the full options object, retained so classifiers that grow
|
|
77
|
+
* a new knob read it from one place instead of a new private field per
|
|
78
|
+
* knob. `targets`/`maxRaiseFanout` keep their dedicated fields because
|
|
79
|
+
* they are read on hot paths.
|
|
80
|
+
*/
|
|
81
|
+
private options;
|
|
82
|
+
/**
|
|
83
|
+
* Issue #243 — the options-object form. Every in-repo call site uses this.
|
|
84
|
+
*/
|
|
85
|
+
constructor(filePath: string, source: string, options?: VbaExtractionOptions);
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated Pass a {@link VbaExtractionOptions} object instead:
|
|
88
|
+
* `new VbaExtractor(filePath, source, { targets, maxRaiseFanout })`.
|
|
89
|
+
* The positional 3rd/4th parameters are kept working for one release so
|
|
90
|
+
* out-of-repo callers are not broken by #243; they will be removed after.
|
|
91
|
+
*/
|
|
75
92
|
constructor(filePath: string, source: string, vbaTargets?: Record<string, boolean>, maxRaiseFanout?: number);
|
|
76
93
|
extract(): ExtractionResult;
|
|
77
94
|
/**
|
|
@@ -17,6 +17,14 @@ export declare class VbaFormExtractor {
|
|
|
17
17
|
* stays self-contained per the project's per-extractor state rule).
|
|
18
18
|
*/
|
|
19
19
|
private synthClassNodeIds;
|
|
20
|
+
/**
|
|
21
|
+
* Issue #256: external-backend (`IN "<path>"`) node ids already emitted
|
|
22
|
+
* for this form. Same per-extractor de-dup discipline as
|
|
23
|
+
* `synthClassNodeIds`, keyed on the node id — which is derived from the
|
|
24
|
+
* NORMALIZED backend path, so two bindings naming the same `.accdb`
|
|
25
|
+
* share one node while each still emits its own edge.
|
|
26
|
+
*/
|
|
27
|
+
private externalBackendNodeIds;
|
|
20
28
|
constructor(filePath: string, source: string);
|
|
21
29
|
extract(): ExtractionResult;
|
|
22
30
|
private createFileNode;
|
|
@@ -63,23 +71,11 @@ export declare class VbaFormExtractor {
|
|
|
63
71
|
private emitExpressionHandlers;
|
|
64
72
|
private emitExpressionHandler;
|
|
65
73
|
private emitSourceObjectReference;
|
|
66
|
-
/**
|
|
67
|
-
* Issue #49 — copy of `VbaExtractor.SQL_TABLE_RE`. Same source / flags:
|
|
68
|
-
* captures the table name that follows `FROM`/`JOIN`/`INTO`/`UPDATE`,
|
|
69
|
-
* tolerates bracketed `[Order Details]` identifiers and `\p{L}` Unicode
|
|
70
|
-
* identifiers, and allows an optional schema prefix (`[dbo].[tblA]`).
|
|
71
|
-
*
|
|
72
|
-
* We duplicate the regex (rather than exporting it from `VbaExtractor`)
|
|
73
|
-
* because `VbaExtractor.SQL_TABLE_RE` is `private static` and the
|
|
74
|
-
* project's per-extractor state rule keeps each extractor's helpers
|
|
75
|
-
* self-contained — see the file-level JSDoc on `VbaExtractor`.
|
|
76
|
-
*/
|
|
77
|
-
private static readonly SQL_TABLE_RE;
|
|
78
74
|
/**
|
|
79
75
|
* Issue #49 — classify a RecordSource/RowSource value as SQL.
|
|
80
76
|
* Anything starting with a SQL keyword (`SELECT`, `PARAMETERS`, `WITH`,
|
|
81
77
|
* `UPDATE`, `INSERT`, `DELETE`) — case-insensitive — is treated as a
|
|
82
|
-
* SQL statement and run through `
|
|
78
|
+
* SQL statement and run through `scanSqlTables`. Anything else is a
|
|
83
79
|
* bare table-or-query name and emitted as a single reference.
|
|
84
80
|
*
|
|
85
81
|
* `WITH` is included because Access/JET supports CTE-style `WITH` queries
|
|
@@ -103,6 +99,20 @@ export declare class VbaFormExtractor {
|
|
|
103
99
|
* name — same dual-match `vba-sql-impact`'s `extractFormBindings` does).
|
|
104
100
|
*/
|
|
105
101
|
private emitTableReference;
|
|
102
|
+
/**
|
|
103
|
+
* Issue #256 — emit one `references` edge from `sourceNodeId` to the
|
|
104
|
+
* external database file an Access `IN "<path>"` clause points at.
|
|
105
|
+
*
|
|
106
|
+
* The target node is built by the shared `buildExternalBackendNode` so
|
|
107
|
+
* it is byte-identical to the node the in-code SQL sweep and the saved-
|
|
108
|
+
* query extractor emit for the same path: the graph ends up with ONE
|
|
109
|
+
* node per external backend, however many places name it. The edge
|
|
110
|
+
* carries only `synthesizedBy` — the `external` / `backendPath` facts
|
|
111
|
+
* describe the node, and live there.
|
|
112
|
+
*
|
|
113
|
+
* `backendPath` is already normalized by `scanSqlExternalBackends`.
|
|
114
|
+
*/
|
|
115
|
+
private emitExternalBackendReference;
|
|
106
116
|
/**
|
|
107
117
|
* Link a bound control to its enclosing form/report's single bare table.
|
|
108
118
|
* Expressions and SQL/absent RecordSource values stay metadata-only: column
|
|
@@ -112,7 +122,7 @@ export declare class VbaFormExtractor {
|
|
|
112
122
|
private emitControlSourceReference;
|
|
113
123
|
/**
|
|
114
124
|
* Issue #49 — dispatch the value of a RecordSource/RowSource binding.
|
|
115
|
-
* If SQL, run `
|
|
125
|
+
* If SQL, run `scanSqlTables` over the value and emit one edge per
|
|
116
126
|
* distinct table (within-value dedup so `FROM tblA JOIN tblA` emits a
|
|
117
127
|
* single edge). If a bare name, emit a single edge with the name as-is
|
|
118
128
|
* — the resolver handles the dual-match against `query` and `class`
|
|
@@ -122,6 +132,10 @@ export declare class VbaFormExtractor {
|
|
|
122
132
|
* the regex capture already includes them as part of the value, so a
|
|
123
133
|
* single `.replace(/""/g, '"')` collapses them — same technique the
|
|
124
134
|
* `extractStringLiterals` helper uses for its emitted `text` field.
|
|
135
|
+
*
|
|
136
|
+
* Issue #203: `scanSqlTables` also drops SQL reserved-word captures
|
|
137
|
+
* (`WHERE`, `ORDER`, `SET`, …) so a malformed SQL string never
|
|
138
|
+
* poisons the graph with phantom `class` nodes.
|
|
125
139
|
*/
|
|
126
140
|
private emitBinding;
|
|
127
141
|
}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
*
|
|
33
33
|
* Source unchanged guarantee: every helper treats `src` as read-only.
|
|
34
34
|
*/
|
|
35
|
+
import type { ExtractionError } from '../types';
|
|
35
36
|
/**
|
|
36
37
|
* Collapse VBA line continuations: any line whose last non-newline character is
|
|
37
38
|
* `_` (preceded by exactly one space, per VBA convention) is joined to the
|
|
@@ -93,8 +94,36 @@ export declare function stripVbaComments(src: string): string;
|
|
|
93
94
|
* promote this evaluator to full integer arithmetic.
|
|
94
95
|
*
|
|
95
96
|
* Directives and inactive branch lines are replaced with empty strings so
|
|
96
|
-
* downstream extraction keeps source-line parity.
|
|
97
|
-
*
|
|
97
|
+
* downstream extraction keeps source-line parity.
|
|
98
|
+
*
|
|
99
|
+
* **Issue #206 contract change.** Previously the docstring read
|
|
100
|
+
* *"Unsupported/unsafe expressions evaluate to false rather than
|
|
101
|
+
* throwing"* — accurate, but it framed silent whole-branch source
|
|
102
|
+
* deletion as a safety property. It was not: false-negative extraction is
|
|
103
|
+
* indistinguishable from correct extraction to every downstream consumer
|
|
104
|
+
* (an `#If MODE = "DEBUG" Then` whose `"` throws silently blanks the
|
|
105
|
+
* branch and the symbol vanishes from the graph).
|
|
106
|
+
*
|
|
107
|
+
* The new contract:
|
|
108
|
+
*
|
|
109
|
+
* 1. **Unterminated `#If`/`#ElseIf`**: if the directive stack is
|
|
110
|
+
* non-empty after the loop ends, an `ExtractionError` (severity
|
|
111
|
+
* `warning`, code `unterminated_if`) is pushed onto the optional
|
|
112
|
+
* `errors` array AND the un-blanked lines from the earliest
|
|
113
|
+
* unclosed `#If` are re-emitted. An unterminated `#If` is far
|
|
114
|
+
* more likely a typo than a genuinely dead file tail.
|
|
115
|
+
* 2. **Could-not-evaluate expressions**: `evaluateConditionalExpression`
|
|
116
|
+
* now distinguishes `evaluated: false` (a definitive false — e.g.
|
|
117
|
+
* `#If 0 Then`, unknown identifier falling through to 0) from
|
|
118
|
+
* `could-not-evaluate` (the lexer/parser hit an unsupported token,
|
|
119
|
+
* or `tokenize`/`parse` threw). On could-not-evaluate, the branch
|
|
120
|
+
* is **kept active** (conservative for a code-intelligence tool —
|
|
121
|
+
* a spurious symbol is recoverable, a missing one is invisible)
|
|
122
|
+
* and a warning is emitted through the same `errors` channel.
|
|
123
|
+
*
|
|
124
|
+
* The `errors` channel is OPTIONAL — every existing call site (and
|
|
125
|
+
* existing test) continues to work without it. The orchestrator can
|
|
126
|
+
* supply it to surface the warnings through `ctx.errors`.
|
|
98
127
|
*/
|
|
99
128
|
export declare function preprocessConditionalCompilation(src: string, customTargets?: Record<string, boolean>,
|
|
100
129
|
/**
|
|
@@ -106,7 +135,64 @@ export declare function preprocessConditionalCompilation(src: string, customTarg
|
|
|
106
135
|
* inner sub-totals. Undefined → no-op, zero cost (just the
|
|
107
136
|
* `if (timings)` null-check on every CC line).
|
|
108
137
|
*/
|
|
109
|
-
timings?: Map<string, number> | null
|
|
138
|
+
timings?: Map<string, number> | null,
|
|
139
|
+
/**
|
|
140
|
+
* Issue #206: optional `ExtractionError` sink for diagnostics the
|
|
141
|
+
* preprocessor wants to surface to extraction callers:
|
|
142
|
+
* - `unterminated_if` (warning) — `#If` / `#ElseIf` pushed but
|
|
143
|
+
* never closed by `#End If` (a likely typo).
|
|
144
|
+
* - `unparseable_expression` (warning) — a `#If` / `#ElseIf`
|
|
145
|
+
* expression the lexer/parser could not handle (the branch is
|
|
146
|
+
* still kept active under the conservative contract).
|
|
147
|
+
*
|
|
148
|
+
* Undefined → no-op, zero cost (the `if (errors)` null-check on
|
|
149
|
+
* every warning site). The orchestrator passes `this.ctx.errors` so
|
|
150
|
+
* the warnings route through `ExtractionResult.errors`.
|
|
151
|
+
*/
|
|
152
|
+
errors?: ExtractionError[],
|
|
153
|
+
/**
|
|
154
|
+
* Issue #206: optional file path included on every pushed error
|
|
155
|
+
* when the caller wants telemetry to know which file the warning
|
|
156
|
+
* came from. Independent of `errors` so a caller can attach the
|
|
157
|
+
* path even when collecting warnings elsewhere.
|
|
158
|
+
*/
|
|
159
|
+
filePath?: string): string;
|
|
160
|
+
/**
|
|
161
|
+
* Issue #206 discriminated return value for `evaluateConditionalExpression`.
|
|
162
|
+
*
|
|
163
|
+
* - `{ kind: 'evaluated', value }` — the lexer+parser produced a
|
|
164
|
+
* definitive answer. `value` is the truthiness of the expression
|
|
165
|
+
* under VBA semantics (non-zero = true).
|
|
166
|
+
* - `{ kind: 'could-not-evaluate' }` — the lexer/parser threw
|
|
167
|
+
* (unsupported token: string literal, floating-point literal,
|
|
168
|
+
* unrecognized character, mismatched paren, etc.). The caller MUST
|
|
169
|
+
* treat this as ACTIVE under the conservative contract from
|
|
170
|
+
* Issue #206 — see `preprocessConditionalCompilation`.
|
|
171
|
+
*/
|
|
172
|
+
export type ConditionalEvaluation = {
|
|
173
|
+
readonly kind: 'evaluated';
|
|
174
|
+
readonly value: boolean;
|
|
175
|
+
} | {
|
|
176
|
+
readonly kind: 'could-not-evaluate';
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Issue #206: marker error class for explicitly-unsupported VBA
|
|
180
|
+
* conditional-compilation tokens. The legacy `throw new Error(...)`
|
|
181
|
+
* from `tokenize` was indistinguishable from a real bug
|
|
182
|
+
* (`Unexpected trailing token`, mismatched paren); both bubbled into
|
|
183
|
+
* the same `catch { return false }`. The new helper carries the
|
|
184
|
+
* specific reason so the caller can attribute warnings precisely and
|
|
185
|
+
* distinguish "the lexer simply cannot handle this token" from a
|
|
186
|
+
* legitimate parse failure (which is also routed to
|
|
187
|
+
* could-not-evaluate, but with the original message preserved).
|
|
188
|
+
*/
|
|
189
|
+
export declare class UnparseableExpressionError extends Error {
|
|
190
|
+
/** Coarse category: `string_literal`, `float_literal`, `unsupported_character`. */
|
|
191
|
+
readonly reason: string;
|
|
192
|
+
/** The verbatim fragment that triggered the throw (for diagnostics). */
|
|
193
|
+
readonly fragment: string;
|
|
194
|
+
constructor(reason: string, fragment: string);
|
|
195
|
+
}
|
|
110
196
|
export interface StringLiteralSpan {
|
|
111
197
|
/** The literal content (no surrounding quotes; `""` collapsed to `"`). */
|
|
112
198
|
text: string;
|
|
@@ -45,14 +45,4 @@ export interface ReadVbaSourceResult {
|
|
|
45
45
|
* @param opts optional dependencies; see {@link ReadVbaSourceOptions}.
|
|
46
46
|
*/
|
|
47
47
|
export declare function readVbaSource(filePath: string, opts?: ReadVbaSourceOptions): ReadVbaSourceResult;
|
|
48
|
-
/**
|
|
49
|
-
* String-level BOM strip — survives when the upstream `fsp.readFile(path, 'utf-8')`
|
|
50
|
-
* already decoded the bytes (it's the only way the `\uFEFF` char survives in
|
|
51
|
-
* a string). Used as a defensive last resort when a read site has NOT gone
|
|
52
|
-
* through `readVbaSource` and the source text starts with the BOM marker.
|
|
53
|
-
*
|
|
54
|
-
* Most call sites should prefer `readVbaSource` for fresh reads; this is
|
|
55
|
-
* a string post-process for callers that already have a `string` in hand.
|
|
56
|
-
*/
|
|
57
|
-
export declare function stripUtf8Bom(text: string): string;
|
|
58
48
|
//# sourceMappingURL=vba-source.d.ts.map
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { ExtractionResult } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
* Content-shape gate: is `parsed` a VBA test manifest — a top-level `tests`
|
|
4
|
-
* array with at least one item carrying a string `procedure`? Pure; the file's
|
|
5
|
-
* basename is gated separately by `isVbaTestManifestFile` in `grammars.ts`.
|
|
6
|
-
*/
|
|
7
|
-
export declare function isVbaTestManifestShape(parsed: unknown): boolean;
|
|
8
2
|
export declare class VbaTestManifestExtractor {
|
|
9
3
|
private filePath;
|
|
10
4
|
private source;
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { QueryBuilder } from '../db/queries';
|
|
2
|
+
/**
|
|
3
|
+
* The consumer compatibility payload.
|
|
4
|
+
*
|
|
5
|
+
* Field types are fixed by the consumer that reads them (Dysflow's
|
|
6
|
+
* `CodeGraphBehaviorEvidence`) and must not be widened here.
|
|
7
|
+
*/
|
|
8
|
+
export interface CodeGraphBehaviorEvidence {
|
|
9
|
+
/** Name of the procedure the event is bound to. */
|
|
10
|
+
handler: string;
|
|
11
|
+
/**
|
|
12
|
+
* One root-to-leaf execution path, handler first, callees in the order the
|
|
13
|
+
* graph stores them (by node id, so the output is stable across runs).
|
|
14
|
+
* Distinct branches are separate entries — they are never concatenated into
|
|
15
|
+
* a single sequence the runtime would not take. A path that re-enters a
|
|
16
|
+
* procedure already on it ends there, repeating that name once.
|
|
17
|
+
*/
|
|
18
|
+
callPath: string[];
|
|
19
|
+
/** Tables reached along this path, directly or through a saved query. */
|
|
20
|
+
tables?: string[];
|
|
21
|
+
/** See {@link BEHAVIOR_EFFECT_VOCABULARY}. */
|
|
22
|
+
effects?: string[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The closed vocabulary of `effects` strings. Anything outside this list is a
|
|
26
|
+
* bug, not a new fact.
|
|
27
|
+
*
|
|
28
|
+
* - `read:<name>` / `write:<name>` — a data reference the extractor typed.
|
|
29
|
+
* - `data-access:<name>` — a data reference whose direction is unknown.
|
|
30
|
+
* NOT a read and NOT a write; it means the index cannot say.
|
|
31
|
+
* - `opens-form:<Name>` / `opens-report:<Name>` — an Access object opened.
|
|
32
|
+
* - `raises-event:<Name>` — an event raised from the path.
|
|
33
|
+
*/
|
|
34
|
+
export declare const BEHAVIOR_EFFECT_VOCABULARY: readonly ["read:<name>", "write:<name>", "data-access:<name>", "opens-form:<Name>", "opens-report:<Name>", "raises-event:<Name>"];
|
|
35
|
+
/** How a handler is bound to the thing it answers for. */
|
|
36
|
+
export type BehaviorBindingScope =
|
|
37
|
+
/** `<Control>_<Event>` code-behind on a control. */
|
|
38
|
+
'control'
|
|
39
|
+
/** The form's or report's own lifecycle event. */
|
|
40
|
+
| 'form'
|
|
41
|
+
/** An `=Expression()` property on the control or layout. */
|
|
42
|
+
| 'expression'
|
|
43
|
+
/** The request named a procedure directly; no binding was involved. */
|
|
44
|
+
| 'direct';
|
|
45
|
+
export interface BehaviorEvidenceRequest {
|
|
46
|
+
/** Stable node id. The unambiguous form — always prefer it. */
|
|
47
|
+
nodeId?: string;
|
|
48
|
+
/** Control, handler or layout name. Requires `layout` when not unique. */
|
|
49
|
+
name?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Layout context for `name`: a form/report name or its layout file name.
|
|
52
|
+
* Without it an ambiguous name is REFUSED, never silently narrowed to the
|
|
53
|
+
* first match.
|
|
54
|
+
*/
|
|
55
|
+
layout?: string;
|
|
56
|
+
/** Call-path depth budget. Default 5, clamped to 1..20. */
|
|
57
|
+
maxCallDepth?: number;
|
|
58
|
+
/** Maximum evidence entries. Default 50, clamped to 1..500. */
|
|
59
|
+
maxResults?: number;
|
|
60
|
+
}
|
|
61
|
+
export interface BehaviorEvidenceTarget {
|
|
62
|
+
id: string;
|
|
63
|
+
name: string;
|
|
64
|
+
kind: string;
|
|
65
|
+
filePath: string;
|
|
66
|
+
/** Owning form/report, from the layout's `contains` edge. */
|
|
67
|
+
layout: string | null;
|
|
68
|
+
}
|
|
69
|
+
export interface BehaviorHandlerContext {
|
|
70
|
+
handler: string;
|
|
71
|
+
handlerId: string;
|
|
72
|
+
/** Access event name (`Click`, `Load`, …), or null when not bound. */
|
|
73
|
+
event: string | null;
|
|
74
|
+
scope: BehaviorBindingScope;
|
|
75
|
+
provenance: string;
|
|
76
|
+
/** `file:line` of the wiring site. */
|
|
77
|
+
location: string;
|
|
78
|
+
/** `metadata.synthesizedBy` of the binding edge, when present. */
|
|
79
|
+
wiredBy: string | null;
|
|
80
|
+
}
|
|
81
|
+
export interface BehaviorDataEvidence {
|
|
82
|
+
/** Procedure the reference was attributed to. */
|
|
83
|
+
procedure: string;
|
|
84
|
+
name: string;
|
|
85
|
+
/** Node kind of the referenced object (`class`, `table`, `query`, …). */
|
|
86
|
+
targetKind: string;
|
|
87
|
+
access: 'read' | 'write' | 'unknown';
|
|
88
|
+
/** `metadata.synthesizedBy` of the reference edge. */
|
|
89
|
+
via: string | null;
|
|
90
|
+
/** Set when the table was reached through a saved query. */
|
|
91
|
+
throughQuery?: string;
|
|
92
|
+
/**
|
|
93
|
+
* `edge-source` — the reference edge starts at the procedure itself.
|
|
94
|
+
* `source-line` — it starts at the module node and was attributed to this
|
|
95
|
+
* procedure because the edge's line falls inside its range.
|
|
96
|
+
*/
|
|
97
|
+
attributedBy: 'edge-source' | 'source-line';
|
|
98
|
+
location: string;
|
|
99
|
+
}
|
|
100
|
+
export interface BehaviorAmbiguity {
|
|
101
|
+
name: string;
|
|
102
|
+
matches: Array<{
|
|
103
|
+
id: string;
|
|
104
|
+
kind: string;
|
|
105
|
+
filePath: string;
|
|
106
|
+
layout: string | null;
|
|
107
|
+
}>;
|
|
108
|
+
}
|
|
109
|
+
export interface BehaviorUnresolvedReference {
|
|
110
|
+
/** Procedure the unresolved reference sits in, when it could be attributed. */
|
|
111
|
+
procedure: string | null;
|
|
112
|
+
referenceName: string;
|
|
113
|
+
referenceKind: string;
|
|
114
|
+
location: string;
|
|
115
|
+
}
|
|
116
|
+
export interface BehaviorEvidenceResult {
|
|
117
|
+
/** What the request resolved to, or null when it resolved to nothing. */
|
|
118
|
+
target: BehaviorEvidenceTarget | null;
|
|
119
|
+
/** The consumer compatibility payload. */
|
|
120
|
+
evidence: CodeGraphBehaviorEvidence[];
|
|
121
|
+
context: {
|
|
122
|
+
handlers: BehaviorHandlerContext[];
|
|
123
|
+
data: BehaviorDataEvidence[];
|
|
124
|
+
unresolved: BehaviorUnresolvedReference[];
|
|
125
|
+
/** Populated only when a name matched more than one node. */
|
|
126
|
+
ambiguous: BehaviorAmbiguity[];
|
|
127
|
+
truncated: {
|
|
128
|
+
/** A path hit `maxCallDepth` and was cut short. */
|
|
129
|
+
callDepth: boolean;
|
|
130
|
+
/** Entries were dropped at `maxResults`. */
|
|
131
|
+
results: boolean;
|
|
132
|
+
/** A path re-entered a procedure already on it. */
|
|
133
|
+
cycle: boolean;
|
|
134
|
+
};
|
|
135
|
+
/** See {@link BEHAVIOR_EVIDENCE_NOTES}. */
|
|
136
|
+
notes: string[];
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The closed vocabulary of `context.notes`.
|
|
141
|
+
*
|
|
142
|
+
* - `MISSING_TARGET_SELECTOR` — neither `nodeId` nor `name` was given.
|
|
143
|
+
* - `TARGET_NOT_FOUND` — nothing in the index matches. A lookup miss, not
|
|
144
|
+
* proof the control has no behavior.
|
|
145
|
+
* - `AMBIGUOUS_TARGET` — the name matches several nodes; see
|
|
146
|
+
* `context.ambiguous`. No evidence is returned, and nothing is guessed.
|
|
147
|
+
* - `NO_HANDLER_BOUND` — the target exists but no handler is wired to it in
|
|
148
|
+
* the index. Access macros and `[Event Procedure]` entries with no
|
|
149
|
+
* code-behind land here.
|
|
150
|
+
* - `STATIC_SOURCE_EVIDENCE` — always present. The answer comes from indexed
|
|
151
|
+
* exported source: it does not prove the code ran, and it says nothing
|
|
152
|
+
* about whether the `.accdb` binary matches the export.
|
|
153
|
+
*/
|
|
154
|
+
export declare const BEHAVIOR_EVIDENCE_NOTES: readonly ["MISSING_TARGET_SELECTOR", "TARGET_NOT_FOUND", "AMBIGUOUS_TARGET", "NO_HANDLER_BOUND", "STATIC_SOURCE_EVIDENCE"];
|
|
155
|
+
/**
|
|
156
|
+
* Assembles behavior evidence for one control, layout or handler.
|
|
157
|
+
*
|
|
158
|
+
* Pure read path over {@link QueryBuilder}: no new SQL, no second traversal
|
|
159
|
+
* implementation, no writes.
|
|
160
|
+
*/
|
|
161
|
+
export declare function buildBehaviorEvidence(queries: QueryBuilder, request: BehaviorEvidenceRequest): BehaviorEvidenceResult;
|
|
162
|
+
//# sourceMappingURL=behavior-evidence.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* knowledge graph from any codebase.
|
|
6
6
|
*/
|
|
7
7
|
import { Node, Edge, FileRecord, ExtractionResult, Subgraph, TraversalOptions, SearchOptions, SearchResult, SegmentMatch, Context, GraphStats, TaskInput, TaskContext, BuildContextOptions, FindRelevantContextOptions } from './types';
|
|
8
|
+
import { BehaviorEvidenceRequest, BehaviorEvidenceResult } from './graph/behavior-evidence';
|
|
8
9
|
import { IndexProgress, IndexResult, SyncResult } from './extraction';
|
|
9
10
|
import { ResolutionResult } from './resolution';
|
|
10
11
|
import { WatchOptions, PendingFile } from './sync';
|
|
@@ -16,6 +17,7 @@ export { IndexProgress, IndexResult, SyncResult } from './extraction';
|
|
|
16
17
|
export { detectLanguage, isLanguageSupported, isGrammarLoaded, getSupportedLanguages, initGrammars, loadGrammarsForLanguages, loadAllGrammars } from './extraction';
|
|
17
18
|
export { ResolutionResult } from './resolution';
|
|
18
19
|
export { CodeGraphError, FileError, ParseError, DatabaseError, SearchError, VectorError, ConfigError, Logger, setLogger, getLogger, silentLogger, defaultLogger, } from './errors';
|
|
20
|
+
export { buildBehaviorEvidence, BEHAVIOR_EFFECT_VOCABULARY, BEHAVIOR_EVIDENCE_NOTES, CodeGraphBehaviorEvidence, BehaviorEvidenceRequest, BehaviorEvidenceResult, BehaviorEvidenceTarget, BehaviorHandlerContext, BehaviorDataEvidence, BehaviorAmbiguity, BehaviorUnresolvedReference, BehaviorBindingScope, } from './graph/behavior-evidence';
|
|
19
21
|
export { Mutex, FileLock, processInBatches, debounce, throttle, MemoryMonitor } from './utils';
|
|
20
22
|
export { FileWatcher, WatchOptions, PendingFile, LockUnavailableError } from './sync';
|
|
21
23
|
export { MCPServer } from './mcp';
|
|
@@ -527,6 +529,26 @@ export declare class CodeGraph {
|
|
|
527
529
|
* @returns Subgraph containing potentially impacted nodes
|
|
528
530
|
*/
|
|
529
531
|
getImpactRadius(nodeId: string, maxDepth?: number): Subgraph;
|
|
532
|
+
/**
|
|
533
|
+
* Assemble bounded behavior evidence for an Access control, layout or
|
|
534
|
+
* handler (issue #299).
|
|
535
|
+
*
|
|
536
|
+
* Read-only: it joins facts that are already indexed — the event binding,
|
|
537
|
+
* the call paths under it, and the tables/effects those procedures reach —
|
|
538
|
+
* into one typed payload, so a consumer does not re-implement the graph
|
|
539
|
+
* semantics for itself. It never parses source, opens Access, or writes to
|
|
540
|
+
* the index.
|
|
541
|
+
*
|
|
542
|
+
* Identify the target by `nodeId` whenever you have one. A `name` needs a
|
|
543
|
+
* `layout` as soon as it is not unique: an ambiguous name is REFUSED with
|
|
544
|
+
* the candidates listed, never narrowed to an arbitrary first match.
|
|
545
|
+
*
|
|
546
|
+
* The answer is static evidence from exported source. An empty `tables` or
|
|
547
|
+
* `effects` list means the index holds no such fact — not that the code has
|
|
548
|
+
* no runtime effect. See {@link BehaviorEvidenceResult.context} for what
|
|
549
|
+
* could not be answered.
|
|
550
|
+
*/
|
|
551
|
+
getBehaviorEvidence(request: BehaviorEvidenceRequest): BehaviorEvidenceResult;
|
|
530
552
|
/**
|
|
531
553
|
* Find the shortest path between two nodes
|
|
532
554
|
*
|
|
@@ -52,6 +52,12 @@ export declare function getDaemonSocketCandidates(projectRoot: string): string[]
|
|
|
52
52
|
export declare function getDaemonSocketPath(projectRoot: string): string;
|
|
53
53
|
/** Absolute path to the daemon pid lockfile for `projectRoot`. */
|
|
54
54
|
export declare function getDaemonPidPath(projectRoot: string): string;
|
|
55
|
+
/** Root-scoped lease held while intentional release owns lifecycle cleanup. */
|
|
56
|
+
export declare function getDaemonReleaseLeasePath(projectRoot: string): string;
|
|
57
|
+
/** Exclusive marker for release-lease recovery/heartbeat publication. */
|
|
58
|
+
export declare function getDaemonReleaseRecoveryPath(projectRoot: string): string;
|
|
59
|
+
/** Short root-scoped arbitration lock shared by startup and release publication. */
|
|
60
|
+
export declare function getDaemonLifecyclePath(projectRoot: string): string;
|
|
55
61
|
/** Structured contents of the pid lockfile. */
|
|
56
62
|
export interface DaemonLockInfo {
|
|
57
63
|
pid: number;
|