@saptools/service-flow 0.1.67 → 0.1.69
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/CHANGELOG.md +20 -0
- package/README.md +27 -9
- package/TECHNICAL-NOTE.md +36 -0
- package/dist/chunk-3N3B5KHV.js +19596 -0
- package/dist/chunk-3N3B5KHV.js.map +1 -0
- package/dist/cli.js +2645 -521
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +67 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/cli/001-index-summary.ts +22 -0
- package/src/cli/003-doctor-package-resolution.ts +68 -0
- package/src/cli/doctor.ts +25 -14
- package/src/cli.ts +151 -87
- package/src/db/000-call-fact-repository.ts +499 -340
- package/src/db/001-fact-lifecycle.ts +60 -30
- package/src/db/002-fact-json-inventory.ts +169 -0
- package/src/db/003-current-fact-semantics.ts +699 -0
- package/src/db/004-package-target-invalidation.ts +183 -0
- package/src/db/005-schema-structure.ts +201 -0
- package/src/db/006-relative-symbol-resolution.ts +464 -0
- package/src/db/007-package-fact-semantics.ts +573 -0
- package/src/db/008-relative-fact-semantics.ts +210 -0
- package/src/db/009-binding-fact-semantics.ts +352 -0
- package/src/db/010-package-symbol-surface-semantics.ts +320 -0
- package/src/db/011-symbol-call-semantics.ts +144 -0
- package/src/db/012-binding-reference-proof.ts +268 -0
- package/src/db/013-index-publication-failure.ts +91 -0
- package/src/db/014-binding-helper-provenance.ts +17 -0
- package/src/db/migrations.ts +16 -3
- package/src/db/repositories.ts +130 -6
- package/src/db/schema.ts +4 -2
- package/src/index.ts +12 -0
- package/src/indexer/cds-extension-resolver.ts +27 -3
- package/src/indexer/repository-indexer.ts +135 -13
- package/src/indexer/workspace-indexer.ts +237 -34
- package/src/linker/003-package-import-symbol-resolver.ts +363 -131
- package/src/linker/004-event-subscription-handler-linker.ts +34 -11
- package/src/linker/005-odata-path-structure.ts +371 -0
- package/src/linker/006-event-template-link.ts +72 -0
- package/src/linker/007-call-edge-insertion.ts +568 -0
- package/src/linker/cross-repo-linker.ts +4 -166
- package/src/linker/odata-path-normalizer.ts +273 -180
- package/src/linker/service-resolver.ts +197 -77
- package/src/parsers/000-direct-query-execution.ts +11 -0
- package/src/parsers/002-symbol-import-bindings.ts +516 -0
- package/src/parsers/003-package-public-surface.ts +661 -0
- package/src/parsers/004-fact-identity.ts +108 -0
- package/src/parsers/005-event-subscription-facts.ts +281 -0
- package/src/parsers/006-binding-identity.ts +348 -0
- package/src/parsers/007-source-fact-reconciliation.ts +105 -0
- package/src/parsers/008-package-surface-publication.ts +82 -0
- package/src/parsers/009-symbol-call-facts.ts +528 -0
- package/src/parsers/010-package-public-surface-analysis.ts +352 -0
- package/src/parsers/011-binding-lexical-scope.ts +583 -0
- package/src/parsers/012-package-fact-contract.ts +306 -0
- package/src/parsers/013-executable-body-eligibility.ts +35 -0
- package/src/parsers/014-service-binding-helper-flow.ts +306 -0
- package/src/parsers/015-service-binding-collector.ts +693 -0
- package/src/parsers/016-local-symbol-reference.ts +261 -0
- package/src/parsers/017-symbol-derived-contexts.ts +268 -0
- package/src/parsers/018-package-commonjs-syntax.ts +142 -0
- package/src/parsers/019-binding-assignment-targets.ts +76 -0
- package/src/parsers/020-stable-local-value.ts +217 -0
- package/src/parsers/021-binding-visibility.ts +168 -0
- package/src/parsers/022-outbound-expression-analysis.ts +700 -0
- package/src/parsers/023-outbound-call-classifier.ts +692 -0
- package/src/parsers/operation-path-analysis.ts +6 -1
- package/src/parsers/outbound-call-parser.ts +162 -512
- package/src/parsers/package-json-parser.ts +45 -3
- package/src/parsers/service-binding-parser-helpers.ts +86 -15
- package/src/parsers/service-binding-parser.ts +147 -597
- package/src/parsers/symbol-parser.ts +513 -352
- package/src/trace/002-trace-diagnostics.ts +36 -1
- package/src/trace/007-implementation-start-diagnostic.ts +1 -0
- package/src/trace/011-event-subscriber-traversal.ts +100 -8
- package/src/trace/013-trace-root-scopes.ts +2 -3
- package/src/trace/014-compact-contract.ts +6 -0
- package/src/trace/015-trace-edge-recorder.ts +61 -4
- package/src/trace/016-compact-projector.ts +15 -17
- package/src/trace/019-trace-edge-semantics.ts +6 -10
- package/src/trace/020-compact-field-projection.ts +122 -38
- package/src/trace/021-compact-decision-normalization.ts +171 -0
- package/src/trace/022-trace-fact-preflight.ts +21 -0
- package/src/trace/023-nested-event-scopes.ts +23 -0
- package/src/trace/024-compact-observation-decision.ts +81 -0
- package/src/trace/025-trace-implementation-scope.ts +123 -0
- package/src/trace/026-trace-start-scope.ts +336 -0
- package/src/trace/027-trace-scope-execution.ts +566 -0
- package/src/trace/028-trace-operation-execution.ts +336 -0
- package/src/trace/029-trace-start-implementation.ts +172 -0
- package/src/trace/030-event-runtime-resolution.ts +151 -0
- package/src/trace/031-local-call-expansion.ts +37 -0
- package/src/trace/implementation-hints.ts +9 -6
- package/src/trace/selectors.ts +1 -0
- package/src/trace/trace-engine.ts +122 -624
- package/src/types.ts +57 -0
- package/src/utils/001-placeholders.ts +188 -10
- package/src/version.ts +1 -1
- package/dist/chunk-ZQABU7MR.js +0 -12151
- package/dist/chunk-ZQABU7MR.js.map +0 -1
package/src/types.ts
CHANGED
|
@@ -160,8 +160,64 @@ export interface ServiceBindingFact {
|
|
|
160
160
|
placeholders: string[];
|
|
161
161
|
sourceFile: string;
|
|
162
162
|
sourceLine: number;
|
|
163
|
+
bindingSiteStartOffset?: number;
|
|
164
|
+
bindingSiteEndOffset?: number;
|
|
165
|
+
sourceSymbolQualifiedName?: string;
|
|
166
|
+
ownerResolution?: ServiceBindingOwnerResolution;
|
|
163
167
|
helperChain?: Array<Record<string, unknown>>;
|
|
164
168
|
}
|
|
169
|
+
export type ServiceBindingOwnerResolution =
|
|
170
|
+
| 'owned_exact'
|
|
171
|
+
| 'ownerless_file_scope'
|
|
172
|
+
| 'legacy_unknown';
|
|
173
|
+
export type ServiceBindingReferenceStatus =
|
|
174
|
+
| 'resolved_exact'
|
|
175
|
+
| 'ambiguous'
|
|
176
|
+
| 'unresolved'
|
|
177
|
+
| 'not_applicable';
|
|
178
|
+
export type ServiceBindingReferenceReason =
|
|
179
|
+
| 'binding_not_found'
|
|
180
|
+
| 'binding_declared_after_call'
|
|
181
|
+
| 'binding_scope_ambiguous'
|
|
182
|
+
| 'scope_chain_limit_exceeded'
|
|
183
|
+
| 'unsupported_reaching_assignment'
|
|
184
|
+
| 'unsupported_var_binding'
|
|
185
|
+
| 'binding_flow_unsupported';
|
|
186
|
+
export interface LexicalScopeFact {
|
|
187
|
+
kind: 'source_file'
|
|
188
|
+
| 'module_block'
|
|
189
|
+
| 'function'
|
|
190
|
+
| 'class'
|
|
191
|
+
| 'loop'
|
|
192
|
+
| 'case_block'
|
|
193
|
+
| 'block'
|
|
194
|
+
| 'catch';
|
|
195
|
+
startOffset: number;
|
|
196
|
+
endOffset: number;
|
|
197
|
+
}
|
|
198
|
+
export interface ServiceBindingReference {
|
|
199
|
+
status: ServiceBindingReferenceStatus;
|
|
200
|
+
variableName?: string;
|
|
201
|
+
bindingSourceFile?: string;
|
|
202
|
+
bindingSiteStartOffset?: number;
|
|
203
|
+
bindingSiteEndOffset?: number;
|
|
204
|
+
resolutionStrategy?: 'lexical_declaration'
|
|
205
|
+
| 'lexical_alias_declaration'
|
|
206
|
+
| 'deterministic_reaching_assignment'
|
|
207
|
+
| 'single_hop_helper_return';
|
|
208
|
+
lexicalScopeChain?: LexicalScopeFact[];
|
|
209
|
+
bindingScopeIndex?: number;
|
|
210
|
+
scopeChainTotal: number;
|
|
211
|
+
scopeChainShown: number;
|
|
212
|
+
scopeChainOmitted: number;
|
|
213
|
+
reason?: ServiceBindingReferenceReason;
|
|
214
|
+
}
|
|
215
|
+
export type HandlerReferenceStatus =
|
|
216
|
+
| 'role_required'
|
|
217
|
+
| 'unsupported_inline'
|
|
218
|
+
| 'unsupported_wrapper'
|
|
219
|
+
| 'unsupported_reference_shape'
|
|
220
|
+
| 'missing_argument';
|
|
165
221
|
export interface OutboundCallFact {
|
|
166
222
|
callType: CallType;
|
|
167
223
|
sourceSymbolQualifiedName?: string;
|
|
@@ -178,6 +234,7 @@ export interface OutboundCallFact {
|
|
|
178
234
|
sourceLine: number;
|
|
179
235
|
callSiteStartOffset?: number;
|
|
180
236
|
callSiteEndOffset?: number;
|
|
237
|
+
serviceBindingReference?: ServiceBindingReference;
|
|
181
238
|
confidence: number;
|
|
182
239
|
unresolvedReason?: string;
|
|
183
240
|
evidence?: Record<string, unknown>;
|
|
@@ -1,26 +1,80 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
|
|
1
3
|
export interface PlaceholderSpan {
|
|
2
4
|
readonly start: number;
|
|
3
5
|
readonly end: number;
|
|
4
6
|
readonly key: string;
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
export
|
|
9
|
+
export type PlaceholderScanReason =
|
|
10
|
+
| 'placeholder_expression_empty'
|
|
11
|
+
| 'placeholder_delimiter_mismatch'
|
|
12
|
+
| 'placeholder_token_unterminated'
|
|
13
|
+
| 'placeholder_unterminated';
|
|
14
|
+
|
|
15
|
+
export interface PlaceholderScanResult {
|
|
16
|
+
readonly status: 'balanced' | 'malformed';
|
|
17
|
+
readonly spans: readonly PlaceholderSpan[];
|
|
18
|
+
readonly reason?: PlaceholderScanReason;
|
|
19
|
+
readonly failureOffset?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type Delimiter = 'brace' | 'bracket' | 'paren' | 'template';
|
|
23
|
+
|
|
24
|
+
interface PlaceholderMatch {
|
|
25
|
+
readonly span?: PlaceholderSpan;
|
|
26
|
+
readonly reason?: PlaceholderScanReason;
|
|
27
|
+
readonly failureOffset?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const expressionEndTokens = new Set<ts.SyntaxKind>([
|
|
31
|
+
ts.SyntaxKind.Identifier,
|
|
32
|
+
ts.SyntaxKind.PrivateIdentifier,
|
|
33
|
+
ts.SyntaxKind.NumericLiteral,
|
|
34
|
+
ts.SyntaxKind.BigIntLiteral,
|
|
35
|
+
ts.SyntaxKind.StringLiteral,
|
|
36
|
+
ts.SyntaxKind.RegularExpressionLiteral,
|
|
37
|
+
ts.SyntaxKind.NoSubstitutionTemplateLiteral,
|
|
38
|
+
ts.SyntaxKind.TemplateTail,
|
|
39
|
+
ts.SyntaxKind.TrueKeyword,
|
|
40
|
+
ts.SyntaxKind.FalseKeyword,
|
|
41
|
+
ts.SyntaxKind.NullKeyword,
|
|
42
|
+
ts.SyntaxKind.ThisKeyword,
|
|
43
|
+
ts.SyntaxKind.SuperKeyword,
|
|
44
|
+
ts.SyntaxKind.CloseBraceToken,
|
|
45
|
+
ts.SyntaxKind.CloseBracketToken,
|
|
46
|
+
ts.SyntaxKind.CloseParenToken,
|
|
47
|
+
ts.SyntaxKind.PlusPlusToken,
|
|
48
|
+
ts.SyntaxKind.MinusMinusToken,
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
export function scanPlaceholderStructure(
|
|
52
|
+
value: string | undefined,
|
|
53
|
+
): PlaceholderScanResult {
|
|
8
54
|
const input = value ?? '';
|
|
9
55
|
const spans: PlaceholderSpan[] = [];
|
|
10
56
|
let cursor = 0;
|
|
11
57
|
while (cursor < input.length) {
|
|
12
58
|
const start = input.indexOf('${', cursor);
|
|
13
59
|
if (start < 0) break;
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
60
|
+
const match = scanPlaceholderAt(input, start);
|
|
61
|
+
if (!match.span) {
|
|
62
|
+
return {
|
|
63
|
+
status: 'malformed', spans: [], reason: match.reason,
|
|
64
|
+
failureOffset: match.failureOffset,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
spans.push(match.span);
|
|
68
|
+
cursor = match.span.end;
|
|
22
69
|
}
|
|
23
|
-
return spans;
|
|
70
|
+
return { status: 'balanced', spans };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function scanPlaceholders(
|
|
74
|
+
value: string | undefined,
|
|
75
|
+
): readonly PlaceholderSpan[] {
|
|
76
|
+
const result = scanPlaceholderStructure(value);
|
|
77
|
+
return result.status === 'balanced' ? result.spans : [];
|
|
24
78
|
}
|
|
25
79
|
|
|
26
80
|
export function extractPlaceholderKeys(value: string | undefined): string[] {
|
|
@@ -28,3 +82,127 @@ export function extractPlaceholderKeys(value: string | undefined): string[] {
|
|
|
28
82
|
.map((span) => span.key.trim())
|
|
29
83
|
.filter(Boolean);
|
|
30
84
|
}
|
|
85
|
+
|
|
86
|
+
function scanPlaceholderAt(input: string, start: number): PlaceholderMatch {
|
|
87
|
+
const bodyOffset = start + 2;
|
|
88
|
+
if (input.indexOf('}', bodyOffset) < 0)
|
|
89
|
+
return { reason: 'placeholder_unterminated', failureOffset: start };
|
|
90
|
+
const scanner = ts.createScanner(
|
|
91
|
+
ts.ScriptTarget.Latest,
|
|
92
|
+
false,
|
|
93
|
+
ts.LanguageVariant.Standard,
|
|
94
|
+
input.slice(bodyOffset),
|
|
95
|
+
);
|
|
96
|
+
const delimiters: Delimiter[] = [];
|
|
97
|
+
let previousEndsExpression = false;
|
|
98
|
+
while (true) {
|
|
99
|
+
const scanned = scanToken(scanner, previousEndsExpression);
|
|
100
|
+
if (scanner.isUnterminated())
|
|
101
|
+
return failedMatch('placeholder_token_unterminated', bodyOffset, scanner);
|
|
102
|
+
if (scanned === ts.SyntaxKind.EndOfFileToken)
|
|
103
|
+
return failedMatch('placeholder_unterminated', bodyOffset, scanner);
|
|
104
|
+
if (isTrivia(scanned)) continue;
|
|
105
|
+
const result = consumeDelimiter(scanner, scanned, delimiters);
|
|
106
|
+
if (result.reason)
|
|
107
|
+
return failedMatch(result.reason, bodyOffset, scanner);
|
|
108
|
+
if (result.closed) {
|
|
109
|
+
const end = bodyOffset + scanner.getTextPos();
|
|
110
|
+
const key = input.slice(bodyOffset, end - 1);
|
|
111
|
+
return key.trim()
|
|
112
|
+
? { span: { start, end, key } }
|
|
113
|
+
: { reason: 'placeholder_expression_empty', failureOffset: start };
|
|
114
|
+
}
|
|
115
|
+
previousEndsExpression = tokenEndsExpression(
|
|
116
|
+
result.token, previousEndsExpression,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function scanToken(
|
|
122
|
+
scanner: ts.Scanner,
|
|
123
|
+
previousEndsExpression: boolean,
|
|
124
|
+
): ts.SyntaxKind {
|
|
125
|
+
const token = scanner.scan();
|
|
126
|
+
if ((token === ts.SyntaxKind.SlashToken
|
|
127
|
+
|| token === ts.SyntaxKind.SlashEqualsToken)
|
|
128
|
+
&& !previousEndsExpression)
|
|
129
|
+
return scanner.reScanSlashToken();
|
|
130
|
+
return token;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function consumeDelimiter(
|
|
134
|
+
scanner: ts.Scanner,
|
|
135
|
+
token: ts.SyntaxKind,
|
|
136
|
+
stack: Delimiter[],
|
|
137
|
+
): { token: ts.SyntaxKind; closed?: boolean; reason?: PlaceholderScanReason } {
|
|
138
|
+
if (token === ts.SyntaxKind.TemplateHead) stack.push('template');
|
|
139
|
+
else if (token === ts.SyntaxKind.OpenBraceToken) stack.push('brace');
|
|
140
|
+
else if (token === ts.SyntaxKind.OpenBracketToken) stack.push('bracket');
|
|
141
|
+
else if (token === ts.SyntaxKind.OpenParenToken) stack.push('paren');
|
|
142
|
+
else if (token === ts.SyntaxKind.CloseBracketToken)
|
|
143
|
+
return closeDelimiter(token, stack, 'bracket');
|
|
144
|
+
else if (token === ts.SyntaxKind.CloseParenToken)
|
|
145
|
+
return closeDelimiter(token, stack, 'paren');
|
|
146
|
+
else if (token === ts.SyntaxKind.CloseBraceToken)
|
|
147
|
+
return closeBrace(scanner, stack);
|
|
148
|
+
return { token };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function closeDelimiter(
|
|
152
|
+
token: ts.SyntaxKind,
|
|
153
|
+
stack: Delimiter[],
|
|
154
|
+
expected: Delimiter,
|
|
155
|
+
): { token: ts.SyntaxKind; reason?: PlaceholderScanReason } {
|
|
156
|
+
if (stack.at(-1) !== expected)
|
|
157
|
+
return { token, reason: 'placeholder_delimiter_mismatch' };
|
|
158
|
+
stack.pop();
|
|
159
|
+
return { token };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function closeBrace(
|
|
163
|
+
scanner: ts.Scanner,
|
|
164
|
+
stack: Delimiter[],
|
|
165
|
+
): { token: ts.SyntaxKind; closed?: boolean; reason?: PlaceholderScanReason } {
|
|
166
|
+
const current = stack.at(-1);
|
|
167
|
+
if (!current) return { token: ts.SyntaxKind.CloseBraceToken, closed: true };
|
|
168
|
+
if (current === 'brace') {
|
|
169
|
+
stack.pop();
|
|
170
|
+
return { token: ts.SyntaxKind.CloseBraceToken };
|
|
171
|
+
}
|
|
172
|
+
if (current !== 'template')
|
|
173
|
+
return {
|
|
174
|
+
token: ts.SyntaxKind.CloseBraceToken,
|
|
175
|
+
reason: 'placeholder_delimiter_mismatch',
|
|
176
|
+
};
|
|
177
|
+
const token = scanner.reScanTemplateToken(false);
|
|
178
|
+
if (token === ts.SyntaxKind.TemplateTail) stack.pop();
|
|
179
|
+
if (token !== ts.SyntaxKind.TemplateTail
|
|
180
|
+
&& token !== ts.SyntaxKind.TemplateMiddle)
|
|
181
|
+
return { token, reason: 'placeholder_token_unterminated' };
|
|
182
|
+
return { token };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function failedMatch(
|
|
186
|
+
reason: PlaceholderScanReason,
|
|
187
|
+
bodyOffset: number,
|
|
188
|
+
scanner: ts.Scanner,
|
|
189
|
+
): PlaceholderMatch {
|
|
190
|
+
return {
|
|
191
|
+
reason,
|
|
192
|
+
failureOffset: bodyOffset + scanner.getTokenPos(),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function tokenEndsExpression(
|
|
197
|
+
token: ts.SyntaxKind,
|
|
198
|
+
previousEndsExpression: boolean,
|
|
199
|
+
): boolean {
|
|
200
|
+
if (token === ts.SyntaxKind.ExclamationToken)
|
|
201
|
+
return previousEndsExpression;
|
|
202
|
+
return expressionEndTokens.has(token);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function isTrivia(token: ts.SyntaxKind): boolean {
|
|
206
|
+
return token >= ts.SyntaxKind.FirstTriviaToken
|
|
207
|
+
&& token <= ts.SyntaxKind.LastTriviaToken;
|
|
208
|
+
}
|
package/src/version.ts
CHANGED