@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.
Files changed (101) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +27 -9
  3. package/TECHNICAL-NOTE.md +36 -0
  4. package/dist/chunk-3N3B5KHV.js +19596 -0
  5. package/dist/chunk-3N3B5KHV.js.map +1 -0
  6. package/dist/cli.js +2645 -521
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.d.ts +67 -2
  9. package/dist/index.js +1 -1
  10. package/package.json +1 -1
  11. package/src/cli/001-index-summary.ts +22 -0
  12. package/src/cli/003-doctor-package-resolution.ts +68 -0
  13. package/src/cli/doctor.ts +25 -14
  14. package/src/cli.ts +151 -87
  15. package/src/db/000-call-fact-repository.ts +499 -340
  16. package/src/db/001-fact-lifecycle.ts +60 -30
  17. package/src/db/002-fact-json-inventory.ts +169 -0
  18. package/src/db/003-current-fact-semantics.ts +699 -0
  19. package/src/db/004-package-target-invalidation.ts +183 -0
  20. package/src/db/005-schema-structure.ts +201 -0
  21. package/src/db/006-relative-symbol-resolution.ts +464 -0
  22. package/src/db/007-package-fact-semantics.ts +573 -0
  23. package/src/db/008-relative-fact-semantics.ts +210 -0
  24. package/src/db/009-binding-fact-semantics.ts +352 -0
  25. package/src/db/010-package-symbol-surface-semantics.ts +320 -0
  26. package/src/db/011-symbol-call-semantics.ts +144 -0
  27. package/src/db/012-binding-reference-proof.ts +268 -0
  28. package/src/db/013-index-publication-failure.ts +91 -0
  29. package/src/db/014-binding-helper-provenance.ts +17 -0
  30. package/src/db/migrations.ts +16 -3
  31. package/src/db/repositories.ts +130 -6
  32. package/src/db/schema.ts +4 -2
  33. package/src/index.ts +12 -0
  34. package/src/indexer/cds-extension-resolver.ts +27 -3
  35. package/src/indexer/repository-indexer.ts +135 -13
  36. package/src/indexer/workspace-indexer.ts +237 -34
  37. package/src/linker/003-package-import-symbol-resolver.ts +363 -131
  38. package/src/linker/004-event-subscription-handler-linker.ts +34 -11
  39. package/src/linker/005-odata-path-structure.ts +371 -0
  40. package/src/linker/006-event-template-link.ts +72 -0
  41. package/src/linker/007-call-edge-insertion.ts +568 -0
  42. package/src/linker/cross-repo-linker.ts +4 -166
  43. package/src/linker/odata-path-normalizer.ts +273 -180
  44. package/src/linker/service-resolver.ts +197 -77
  45. package/src/parsers/000-direct-query-execution.ts +11 -0
  46. package/src/parsers/002-symbol-import-bindings.ts +516 -0
  47. package/src/parsers/003-package-public-surface.ts +661 -0
  48. package/src/parsers/004-fact-identity.ts +108 -0
  49. package/src/parsers/005-event-subscription-facts.ts +281 -0
  50. package/src/parsers/006-binding-identity.ts +348 -0
  51. package/src/parsers/007-source-fact-reconciliation.ts +105 -0
  52. package/src/parsers/008-package-surface-publication.ts +82 -0
  53. package/src/parsers/009-symbol-call-facts.ts +528 -0
  54. package/src/parsers/010-package-public-surface-analysis.ts +352 -0
  55. package/src/parsers/011-binding-lexical-scope.ts +583 -0
  56. package/src/parsers/012-package-fact-contract.ts +306 -0
  57. package/src/parsers/013-executable-body-eligibility.ts +35 -0
  58. package/src/parsers/014-service-binding-helper-flow.ts +306 -0
  59. package/src/parsers/015-service-binding-collector.ts +693 -0
  60. package/src/parsers/016-local-symbol-reference.ts +261 -0
  61. package/src/parsers/017-symbol-derived-contexts.ts +268 -0
  62. package/src/parsers/018-package-commonjs-syntax.ts +142 -0
  63. package/src/parsers/019-binding-assignment-targets.ts +76 -0
  64. package/src/parsers/020-stable-local-value.ts +217 -0
  65. package/src/parsers/021-binding-visibility.ts +168 -0
  66. package/src/parsers/022-outbound-expression-analysis.ts +700 -0
  67. package/src/parsers/023-outbound-call-classifier.ts +692 -0
  68. package/src/parsers/operation-path-analysis.ts +6 -1
  69. package/src/parsers/outbound-call-parser.ts +162 -512
  70. package/src/parsers/package-json-parser.ts +45 -3
  71. package/src/parsers/service-binding-parser-helpers.ts +86 -15
  72. package/src/parsers/service-binding-parser.ts +147 -597
  73. package/src/parsers/symbol-parser.ts +513 -352
  74. package/src/trace/002-trace-diagnostics.ts +36 -1
  75. package/src/trace/007-implementation-start-diagnostic.ts +1 -0
  76. package/src/trace/011-event-subscriber-traversal.ts +100 -8
  77. package/src/trace/013-trace-root-scopes.ts +2 -3
  78. package/src/trace/014-compact-contract.ts +6 -0
  79. package/src/trace/015-trace-edge-recorder.ts +61 -4
  80. package/src/trace/016-compact-projector.ts +15 -17
  81. package/src/trace/019-trace-edge-semantics.ts +6 -10
  82. package/src/trace/020-compact-field-projection.ts +122 -38
  83. package/src/trace/021-compact-decision-normalization.ts +171 -0
  84. package/src/trace/022-trace-fact-preflight.ts +21 -0
  85. package/src/trace/023-nested-event-scopes.ts +23 -0
  86. package/src/trace/024-compact-observation-decision.ts +81 -0
  87. package/src/trace/025-trace-implementation-scope.ts +123 -0
  88. package/src/trace/026-trace-start-scope.ts +336 -0
  89. package/src/trace/027-trace-scope-execution.ts +566 -0
  90. package/src/trace/028-trace-operation-execution.ts +336 -0
  91. package/src/trace/029-trace-start-implementation.ts +172 -0
  92. package/src/trace/030-event-runtime-resolution.ts +151 -0
  93. package/src/trace/031-local-call-expansion.ts +37 -0
  94. package/src/trace/implementation-hints.ts +9 -6
  95. package/src/trace/selectors.ts +1 -0
  96. package/src/trace/trace-engine.ts +122 -624
  97. package/src/types.ts +57 -0
  98. package/src/utils/001-placeholders.ts +188 -10
  99. package/src/version.ts +1 -1
  100. package/dist/chunk-ZQABU7MR.js +0 -12151
  101. package/dist/chunk-ZQABU7MR.js.map +0 -1
@@ -0,0 +1,352 @@
1
+ import { posix } from 'node:path';
2
+ import {
3
+ moduleInfoMap,
4
+ PACKAGE_PUBLIC_SURFACE_RECORD_CAP,
5
+ PACKAGE_PUBLIC_SURFACE_SCHEMA,
6
+ resolveModule,
7
+ } from './003-package-public-surface.js';
8
+ import type {
9
+ ModuleInfo,
10
+ ModuleResolution,
11
+ PackagePublicEntry,
12
+ PackagePublicScope,
13
+ PackagePublicSurfaceAnalysis,
14
+ PackagePublicSurfaceFact,
15
+ PackagePublicSurfaceStatus,
16
+ PackageSourceModule,
17
+ ResolvedExport,
18
+ SymbolPublicSurfaceEvidence,
19
+ } from './003-package-public-surface.js';
20
+ import type { PackageEntrypointManifest } from './package-json-parser.js';
21
+
22
+ interface EntryResolution {
23
+ entries: PackagePublicEntry[];
24
+ status: 'complete' | 'incomplete' | 'unsupported';
25
+ reason?: string;
26
+ }
27
+
28
+ interface RetainedSurface {
29
+ entries: PackagePublicEntry[];
30
+ scopes: PackagePublicScope[];
31
+ total: number;
32
+ shown: number;
33
+ }
34
+
35
+ function compareBinary(left: string, right: string): number {
36
+ return left < right ? -1 : left > right ? 1 : 0;
37
+ }
38
+
39
+ function targetKey(target: SymbolPublicSurfaceEvidence['target']): string {
40
+ return [
41
+ target.sourceFile, target.startOffset, target.endOffset,
42
+ target.kind, target.qualifiedName,
43
+ ].join('\0');
44
+ }
45
+
46
+ function canonicalModulePath(sourceFile: string): string {
47
+ return sourceFile.replace(/\\/g, '/').replace(/\.(?:d\.)?(?:ts|js)$/, '');
48
+ }
49
+
50
+ function validEntry(entry: string): boolean {
51
+ return entry === '.' || (
52
+ entry.startsWith('./') && !entry.includes('*')
53
+ && !entry.split('/').some((part) => part === '..')
54
+ );
55
+ }
56
+
57
+ function entryModuleCandidates(
58
+ specifier: string,
59
+ infos: Map<string, ModuleInfo[]>,
60
+ ): string[] {
61
+ if (!specifier.startsWith('./') || specifier.includes('*')) return [];
62
+ const normalized = posix.normalize(specifier.slice(2));
63
+ if (!normalized || normalized === '..' || normalized.startsWith('../'))
64
+ return [];
65
+ const base = canonicalModulePath(normalized);
66
+ const hasExtension = /\.(?:d\.)?(?:ts|js)$/.test(normalized);
67
+ const candidates = hasExtension ? [base] : [base, `${base}/index`];
68
+ return candidates.filter((item, index, values) => {
69
+ const rows = infos.get(item) ?? [];
70
+ return values.indexOf(item) === index && rows.length === 1
71
+ && (!hasExtension || rows[0]?.sourceFile === normalized);
72
+ });
73
+ }
74
+
75
+ function resolveEntry(
76
+ entry: string,
77
+ target: string,
78
+ infos: Map<string, ModuleInfo[]>,
79
+ ): PackagePublicEntry | undefined {
80
+ const candidates = entryModuleCandidates(target, infos);
81
+ return candidates.length === 1 && candidates[0]
82
+ ? { entry, modulePath: candidates[0] }
83
+ : undefined;
84
+ }
85
+
86
+ function resolvedEntryPairs(
87
+ pairs: Array<[string, string]>,
88
+ infos: Map<string, ModuleInfo[]>,
89
+ ): EntryResolution {
90
+ const entries: PackagePublicEntry[] = [];
91
+ pairs.sort((left, right) => compareBinary(left[0], right[0]));
92
+ for (const [entry, target] of pairs) {
93
+ const resolved = resolveEntry(entry, target, infos);
94
+ if (!resolved) return {
95
+ entries: [], status: 'incomplete',
96
+ reason: 'public_surface_entry_target_not_indexed',
97
+ };
98
+ entries.push(resolved);
99
+ }
100
+ return { entries, status: 'complete' };
101
+ }
102
+
103
+ function explicitExportEntries(
104
+ manifest: PackageEntrypointManifest,
105
+ infos: Map<string, ModuleInfo[]>,
106
+ ): EntryResolution {
107
+ const value = manifest.exportsValue;
108
+ const pairs: Array<[string, string]> = [];
109
+ if (typeof value === 'string') pairs.push(['.', value]);
110
+ else if (value && typeof value === 'object' && !Array.isArray(value)) {
111
+ for (const [entry, target] of Object.entries(value)) {
112
+ if (!validEntry(entry) || typeof target !== 'string'
113
+ || target.includes('*')) return {
114
+ entries: [], status: 'unsupported',
115
+ reason: 'unsupported_exports_map_shape',
116
+ };
117
+ pairs.push([entry, target]);
118
+ }
119
+ } else return {
120
+ entries: [], status: 'unsupported',
121
+ reason: 'unsupported_exports_map_shape',
122
+ };
123
+ return resolvedEntryPairs(pairs, infos);
124
+ }
125
+
126
+ function hasUnsupportedLegacyEntrypoint(
127
+ manifest: PackageEntrypointManifest,
128
+ ): boolean {
129
+ return (manifest.mainPresent && !manifest.main)
130
+ || (manifest.modulePresent && !manifest.module);
131
+ }
132
+
133
+ function legacyRootEntry(
134
+ manifest: PackageEntrypointManifest,
135
+ infos: Map<string, ModuleInfo[]>,
136
+ ): EntryResolution {
137
+ if (hasUnsupportedLegacyEntrypoint(manifest)) return {
138
+ entries: [], status: 'unsupported',
139
+ reason: 'unsupported_package_entrypoint_shape',
140
+ };
141
+ const targets = [manifest.module, manifest.main]
142
+ .filter((value): value is string => typeof value === 'string');
143
+ if (targets.length === 0) return (infos.get('index') ?? []).length === 1
144
+ ? { entries: [{ entry: '.', modulePath: 'index' }], status: 'complete' }
145
+ : { entries: [], status: 'complete' };
146
+ const resolved = targets.map((target) => resolveEntry('.', target, infos));
147
+ const paths = new Set(resolved.flatMap((item) =>
148
+ item ? [item.modulePath] : []));
149
+ if (resolved.some((item) => !item) || paths.size !== 1) return {
150
+ entries: [], status: 'incomplete',
151
+ reason: paths.size > 1
152
+ ? 'public_surface_main_module_conflict'
153
+ : 'public_surface_entry_target_not_indexed',
154
+ };
155
+ const [modulePath] = paths;
156
+ return modulePath
157
+ ? { entries: [{ entry: '.', modulePath }], status: 'complete' }
158
+ : { entries: [], status: 'complete' };
159
+ }
160
+
161
+ function legacyPhysicalEntries(
162
+ infos: Map<string, ModuleInfo[]>,
163
+ ): PackagePublicEntry[] {
164
+ const byEntry = new Map<string, Set<string>>();
165
+ for (const [modulePath, rows] of infos) {
166
+ if (rows.length !== 1) continue;
167
+ const entries = [`./${modulePath}`];
168
+ if (modulePath.endsWith('/index'))
169
+ entries.push(`./${modulePath.slice(0, -'/index'.length)}`);
170
+ for (const entry of entries) {
171
+ const paths = byEntry.get(entry) ?? new Set<string>();
172
+ paths.add(modulePath);
173
+ byEntry.set(entry, paths);
174
+ }
175
+ }
176
+ return [...byEntry.entries()].flatMap(([entry, paths]) =>
177
+ paths.size === 1 ? [{ entry, modulePath: [...paths][0] ?? '' }] : [])
178
+ .filter((item) => item.modulePath)
179
+ .sort((left, right) => compareBinary(left.entry, right.entry));
180
+ }
181
+
182
+ function entryResolution(
183
+ manifest: PackageEntrypointManifest,
184
+ infos: Map<string, ModuleInfo[]>,
185
+ ): EntryResolution {
186
+ if (manifest.exportsPresent) return explicitExportEntries(manifest, infos);
187
+ const root = legacyRootEntry(manifest, infos);
188
+ if (root.status !== 'complete') return root;
189
+ const byEntry = new Map(
190
+ [...root.entries, ...legacyPhysicalEntries(infos)]
191
+ .map((item) => [item.entry, item]),
192
+ );
193
+ return {
194
+ entries: [...byEntry.values()].sort((left, right) =>
195
+ compareBinary(left.entry, right.entry)),
196
+ status: 'complete',
197
+ };
198
+ }
199
+
200
+ function scopeFromExport(
201
+ entry: PackagePublicEntry,
202
+ publicName: string,
203
+ value: ResolvedExport,
204
+ ): PackagePublicScope {
205
+ const eligible = value.targets.filter((item) =>
206
+ item.bodyEligibility.eligible);
207
+ const candidateCount = value.targets.length + value.declarationOnlyCount;
208
+ return {
209
+ ...entry, publicName, candidateCount,
210
+ eligibleCandidateCount: eligible.length,
211
+ selectedCandidateCount: eligible.length === 1 ? 1 : 0,
212
+ candidateSetComplete: true, targets: value.targets,
213
+ };
214
+ }
215
+
216
+ function allScopes(
217
+ entries: readonly PackagePublicEntry[],
218
+ infos: Map<string, ModuleInfo[]>,
219
+ ): { scopes: PackagePublicScope[]; reason?: string } {
220
+ const memo = new Map<string, ModuleResolution>();
221
+ const scopes: PackagePublicScope[] = [];
222
+ for (const entry of entries) {
223
+ const resolved = resolveModule(entry.modulePath, infos, memo);
224
+ if (!resolved.complete) return { scopes: [], reason: resolved.reason };
225
+ for (const [publicName, value] of resolved.exports)
226
+ scopes.push(scopeFromExport(entry, publicName, value));
227
+ }
228
+ scopes.sort((left, right) => compareBinary(
229
+ `${left.entry}\0${left.publicName}`,
230
+ `${right.entry}\0${right.publicName}`,
231
+ ));
232
+ return { scopes };
233
+ }
234
+
235
+ function retainSurface(
236
+ entries: readonly PackagePublicEntry[],
237
+ scopes: readonly PackagePublicScope[],
238
+ ): RetainedSurface {
239
+ let remaining = PACKAGE_PUBLIC_SURFACE_RECORD_CAP;
240
+ const retainedEntries = entries.slice(0, remaining);
241
+ remaining -= retainedEntries.length;
242
+ const entryNames = new Set(retainedEntries.map((item) => item.entry));
243
+ const retainedScopes: PackagePublicScope[] = [];
244
+ for (const scope of scopes) {
245
+ const cost = 1 + scope.targets.length;
246
+ if (!entryNames.has(scope.entry) || cost > remaining) continue;
247
+ retainedScopes.push(scope);
248
+ remaining -= cost;
249
+ }
250
+ const total = entries.length + scopes.reduce(
251
+ (sum, scope) => sum + 1 + scope.targets.length, 0,
252
+ );
253
+ return {
254
+ entries: retainedEntries, scopes: retainedScopes, total,
255
+ shown: PACKAGE_PUBLIC_SURFACE_RECORD_CAP - remaining,
256
+ };
257
+ }
258
+
259
+ function symbolEvidence(
260
+ scopes: readonly PackagePublicScope[],
261
+ ): SymbolPublicSurfaceEvidence[] {
262
+ const result = new Map<string, SymbolPublicSurfaceEvidence>();
263
+ for (const scope of scopes) for (const target of scope.targets) {
264
+ const key = targetKey(target);
265
+ const current = result.get(key) ?? {
266
+ target,
267
+ exposures: [],
268
+ exposureTotal: 0,
269
+ shownExposureCount: 0,
270
+ omittedExposureCount: 0,
271
+ };
272
+ current.exposures.push({
273
+ entry: scope.entry, modulePath: scope.modulePath,
274
+ publicName: scope.publicName,
275
+ });
276
+ result.set(key, current);
277
+ }
278
+ return [...result.values()].map((item) => {
279
+ const exposures = item.exposures.sort((left, right) => compareBinary(
280
+ `${left.entry}\0${left.publicName}`,
281
+ `${right.entry}\0${right.publicName}`,
282
+ ));
283
+ return {
284
+ ...item,
285
+ exposures,
286
+ exposureTotal: exposures.length,
287
+ shownExposureCount: exposures.length,
288
+ omittedExposureCount: 0,
289
+ };
290
+ }).sort((left, right) =>
291
+ compareBinary(targetKey(left.target), targetKey(right.target)));
292
+ }
293
+
294
+ function baseSurface(
295
+ packageName: string | undefined,
296
+ manifest: PackageEntrypointManifest,
297
+ status: PackagePublicSurfaceStatus,
298
+ reason: string | null,
299
+ ): PackagePublicSurfaceFact {
300
+ return {
301
+ schema: PACKAGE_PUBLIC_SURFACE_SCHEMA, status, reason,
302
+ recordCap: PACKAGE_PUBLIC_SURFACE_RECORD_CAP,
303
+ total: 0, shown: 0, omitted: 0, packageName: packageName ?? null,
304
+ exportsPresent: manifest.exportsPresent,
305
+ exportsAuthoritative: manifest.exportsPresent,
306
+ main: manifest.main, module: manifest.module, entries: [], scopes: [],
307
+ };
308
+ }
309
+
310
+ function moduleFailureStatus(
311
+ reason: string,
312
+ ): Extract<PackagePublicSurfaceStatus, 'incomplete' | 'unsupported'> {
313
+ return reason.startsWith('unsupported_')
314
+ || reason === 'anonymous_default_export_without_symbol_identity'
315
+ ? 'unsupported'
316
+ : 'incomplete';
317
+ }
318
+
319
+ export function analyzePackagePublicSurface(
320
+ packageName: string | undefined,
321
+ manifest: PackageEntrypointManifest,
322
+ modules: readonly PackageSourceModule[],
323
+ ): PackagePublicSurfaceAnalysis {
324
+ if (!packageName) return {
325
+ surface: baseSurface(packageName, manifest, 'not_applicable', null),
326
+ symbols: [],
327
+ };
328
+ const infos = moduleInfoMap(modules);
329
+ const entries = entryResolution(manifest, infos);
330
+ if (entries.status !== 'complete') return {
331
+ surface: baseSurface(
332
+ packageName, manifest, entries.status, entries.reason ?? null,
333
+ ),
334
+ symbols: [],
335
+ };
336
+ const resolved = allScopes(entries.entries, infos);
337
+ if (resolved.reason) return {
338
+ surface: baseSurface(
339
+ packageName, manifest,
340
+ moduleFailureStatus(resolved.reason), resolved.reason,
341
+ ),
342
+ symbols: [],
343
+ };
344
+ const retained = retainSurface(entries.entries, resolved.scopes);
345
+ const surface = baseSurface(packageName, manifest, 'complete', null);
346
+ Object.assign(surface, {
347
+ entries: retained.entries, scopes: retained.scopes,
348
+ total: retained.total, shown: retained.shown,
349
+ omitted: Math.max(0, retained.total - retained.shown),
350
+ });
351
+ return { surface, symbols: symbolEvidence(retained.scopes) };
352
+ }