@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,661 @@
1
+ import { posix } from 'node:path';
2
+ import ts from 'typescript';
3
+ import {
4
+ hasNestedCommonJsMutation,
5
+ isUnshadowedCommonJsExportExpression,
6
+ unsupportedCommonJsMutation,
7
+ } from './018-package-commonjs-syntax.js';
8
+ import { stableLocalValueReference } from './020-stable-local-value.js';
9
+
10
+ export const PACKAGE_PUBLIC_SURFACE_SCHEMA =
11
+ 'service-flow/package-public-surface@1';
12
+ export const PACKAGE_PUBLIC_SURFACE_RECORD_CAP = 256;
13
+ export const PACKAGE_PUBLIC_SURFACE_EXPORT_DEPTH = 16;
14
+
15
+ export type PackagePublicSurfaceStatus =
16
+ | 'complete'
17
+ | 'incomplete'
18
+ | 'unsupported'
19
+ | 'not_applicable';
20
+ export type ExecutableBodyReason =
21
+ | 'body_present'
22
+ | 'declaration_only'
23
+ | 'ambient_declaration'
24
+ | 'abstract_bodyless'
25
+ | 'overload_signature';
26
+
27
+ export interface PackageSourceModule {
28
+ sourceFile: string;
29
+ source: ts.SourceFile;
30
+ }
31
+ export interface ExecutableBodyEligibility {
32
+ eligible: boolean;
33
+ reason: ExecutableBodyReason;
34
+ }
35
+ export interface PublicSurfaceTarget {
36
+ sourceFile: string;
37
+ kind: 'function' | 'method' | 'object_method';
38
+ localName: string;
39
+ qualifiedName: string;
40
+ startOffset: number;
41
+ endOffset: number;
42
+ bodyEligibility: ExecutableBodyEligibility;
43
+ }
44
+ export interface PackagePublicEntry {
45
+ entry: string;
46
+ modulePath: string;
47
+ }
48
+ export interface PackagePublicScope {
49
+ entry: string;
50
+ modulePath: string;
51
+ publicName: string;
52
+ candidateCount: number;
53
+ eligibleCandidateCount: number;
54
+ selectedCandidateCount: 0 | 1;
55
+ candidateSetComplete: true;
56
+ targets: PublicSurfaceTarget[];
57
+ }
58
+ export interface PackagePublicSurfaceFact {
59
+ schema: typeof PACKAGE_PUBLIC_SURFACE_SCHEMA;
60
+ status: PackagePublicSurfaceStatus;
61
+ reason: string | null;
62
+ recordCap: typeof PACKAGE_PUBLIC_SURFACE_RECORD_CAP;
63
+ total: number;
64
+ shown: number;
65
+ omitted: number;
66
+ packageName: string | null;
67
+ exportsPresent: boolean;
68
+ exportsAuthoritative: boolean;
69
+ main: string | null;
70
+ module: string | null;
71
+ entries: PackagePublicEntry[];
72
+ scopes: PackagePublicScope[];
73
+ }
74
+ export interface SymbolPublicExposure {
75
+ entry: string;
76
+ modulePath: string;
77
+ publicName: string;
78
+ }
79
+ export interface SymbolPublicSurfaceEvidence {
80
+ target: PublicSurfaceTarget;
81
+ exposures: SymbolPublicExposure[];
82
+ exposureTotal: number;
83
+ shownExposureCount: number;
84
+ omittedExposureCount: number;
85
+ }
86
+ export interface PackagePublicSurfaceAnalysis {
87
+ surface: PackagePublicSurfaceFact;
88
+ symbols: SymbolPublicSurfaceEvidence[];
89
+ }
90
+
91
+ interface LocalValue {
92
+ callable: PublicSurfaceTarget[];
93
+ members: Map<string, PublicSurfaceTarget[]>;
94
+ declarationOnlyCount: number;
95
+ }
96
+ interface LocalExport {
97
+ publicName: string;
98
+ localName: string;
99
+ typeOnly: boolean;
100
+ }
101
+ interface ReExport extends LocalExport {
102
+ importedName: string;
103
+ specifier: string;
104
+ }
105
+ export interface ModuleInfo {
106
+ modulePath: string;
107
+ sourceFile: string;
108
+ values: Map<string, LocalValue>;
109
+ localExports: LocalExport[];
110
+ reExports: ReExport[];
111
+ starExports: string[];
112
+ unstableValues: Set<string>;
113
+ unsupportedReason?: string;
114
+ }
115
+ export interface ResolvedExport {
116
+ targets: PublicSurfaceTarget[];
117
+ declarationOnlyCount: number;
118
+ }
119
+ export interface ModuleResolution {
120
+ exports: Map<string, ResolvedExport>;
121
+ complete: boolean;
122
+ reason?: string;
123
+ }
124
+ const extensionPattern = /\.(?:d\.)?(?:ts|js)$/;
125
+
126
+ function compareBinary(left: string, right: string): number {
127
+ return left < right ? -1 : left > right ? 1 : 0;
128
+ }
129
+
130
+ function hasModifier(node: ts.Node, kind: ts.SyntaxKind): boolean {
131
+ return ts.canHaveModifiers(node)
132
+ && Boolean(ts.getModifiers(node)?.some((item) => item.kind === kind));
133
+ }
134
+
135
+ function canonicalModulePath(sourceFile: string): string {
136
+ return sourceFile.replace(/\\/g, '/').replace(extensionPattern, '');
137
+ }
138
+
139
+ function targetKey(target: PublicSurfaceTarget): string {
140
+ return [
141
+ target.sourceFile, target.startOffset, target.endOffset,
142
+ target.kind, target.qualifiedName,
143
+ ].join('\0');
144
+ }
145
+
146
+ function bodyEligibility(
147
+ node: ts.FunctionLikeDeclaration,
148
+ source: ts.SourceFile,
149
+ ): ExecutableBodyEligibility {
150
+ if (node.body) return { eligible: true, reason: 'body_present' };
151
+ if (hasModifier(node, ts.SyntaxKind.AbstractKeyword))
152
+ return { eligible: false, reason: 'abstract_bodyless' };
153
+ if (hasModifier(node, ts.SyntaxKind.DeclareKeyword))
154
+ return { eligible: false, reason: 'ambient_declaration' };
155
+ return {
156
+ eligible: false,
157
+ reason: source.isDeclarationFile
158
+ ? 'declaration_only'
159
+ : 'overload_signature',
160
+ };
161
+ }
162
+
163
+ function functionTarget(
164
+ info: ModuleInfo,
165
+ source: ts.SourceFile,
166
+ node: ts.FunctionLikeDeclaration,
167
+ localName: string,
168
+ kind: PublicSurfaceTarget['kind'] = 'function',
169
+ qualifiedName = localName,
170
+ ): PublicSurfaceTarget {
171
+ return {
172
+ sourceFile: info.sourceFile, kind, localName, qualifiedName,
173
+ startOffset: node.getStart(source), endOffset: node.getEnd(),
174
+ bodyEligibility: bodyEligibility(node, source),
175
+ };
176
+ }
177
+
178
+ function emptyLocalValue(declarationOnlyCount = 0): LocalValue {
179
+ return { callable: [], members: new Map(), declarationOnlyCount };
180
+ }
181
+
182
+ function localValue(values: Map<string, LocalValue>, name: string): LocalValue {
183
+ const value = values.get(name) ?? emptyLocalValue();
184
+ values.set(name, value);
185
+ return value;
186
+ }
187
+
188
+ function addMember(
189
+ value: LocalValue,
190
+ name: string,
191
+ target: PublicSurfaceTarget,
192
+ ): void {
193
+ const targets = value.members.get(name) ?? [];
194
+ targets.push(target);
195
+ value.members.set(name, targets);
196
+ }
197
+
198
+ function propertyName(node: ts.PropertyName | undefined): string | undefined {
199
+ if (!node) return undefined;
200
+ if (ts.isIdentifier(node) || ts.isStringLiteralLike(node)
201
+ || ts.isNumericLiteral(node)) return node.text;
202
+ return undefined;
203
+ }
204
+
205
+ function isPublicStaticMember(member: ts.ClassElement): boolean {
206
+ if (!hasModifier(member, ts.SyntaxKind.StaticKeyword)) return false;
207
+ if (hasModifier(member, ts.SyntaxKind.PrivateKeyword)
208
+ || hasModifier(member, ts.SyntaxKind.ProtectedKeyword)) return false;
209
+ return !member.name || !ts.isPrivateIdentifier(member.name);
210
+ }
211
+
212
+ function collectFunction(
213
+ info: ModuleInfo,
214
+ source: ts.SourceFile,
215
+ node: ts.FunctionDeclaration,
216
+ ): void {
217
+ if (!node.name) {
218
+ if (hasModifier(node, ts.SyntaxKind.ExportKeyword))
219
+ info.unsupportedReason = 'anonymous_default_export_without_symbol_identity';
220
+ return;
221
+ }
222
+ localValue(info.values, node.name.text).callable.push(
223
+ functionTarget(info, source, node, node.name.text),
224
+ );
225
+ }
226
+
227
+ function collectClassMember(
228
+ info: ModuleInfo,
229
+ source: ts.SourceFile,
230
+ className: string,
231
+ value: LocalValue,
232
+ member: ts.ClassElement,
233
+ ): void {
234
+ if (!isPublicStaticMember(member)) return;
235
+ const name = propertyName(member.name);
236
+ if (!name) return;
237
+ if (ts.isMethodDeclaration(member)) {
238
+ addMember(value, name, functionTarget(
239
+ info, source, member, name, 'method', `${className}.${name}`,
240
+ ));
241
+ return;
242
+ }
243
+ if (!ts.isPropertyDeclaration(member) || !member.initializer
244
+ || (!ts.isArrowFunction(member.initializer)
245
+ && !ts.isFunctionExpression(member.initializer))) return;
246
+ addMember(value, name, functionTarget(
247
+ info, source, member.initializer, name, 'method', `${className}.${name}`,
248
+ ));
249
+ }
250
+
251
+ function collectClassMembers(
252
+ info: ModuleInfo,
253
+ source: ts.SourceFile,
254
+ node: ts.ClassDeclaration,
255
+ ): void {
256
+ if (!node.name) return;
257
+ if (!stableLocalValueReference(source, node.name)) {
258
+ info.unstableValues.add(node.name.text);
259
+ return;
260
+ }
261
+ const value = localValue(info.values, node.name.text);
262
+ for (const member of node.members)
263
+ collectClassMember(info, source, node.name.text, value, member);
264
+ }
265
+
266
+ function collectObjectMembers(
267
+ info: ModuleInfo,
268
+ source: ts.SourceFile,
269
+ objectName: string,
270
+ object: ts.ObjectLiteralExpression,
271
+ ): void {
272
+ const value = localValue(info.values, objectName);
273
+ for (const member of object.properties) {
274
+ const name = propertyName(member.name);
275
+ if (!name) continue;
276
+ const callable = ts.isMethodDeclaration(member)
277
+ ? member
278
+ : ts.isPropertyAssignment(member)
279
+ && (ts.isArrowFunction(member.initializer)
280
+ || ts.isFunctionExpression(member.initializer))
281
+ ? member.initializer
282
+ : undefined;
283
+ if (callable) addMember(value, name, functionTarget(
284
+ info, source, callable, `${objectName}.${name}`,
285
+ 'object_method', `${objectName}.${name}`,
286
+ ));
287
+ }
288
+ }
289
+
290
+ function collectVariables(
291
+ info: ModuleInfo,
292
+ source: ts.SourceFile,
293
+ statement: ts.VariableStatement,
294
+ ): void {
295
+ const immutable = (statement.declarationList.flags & ts.NodeFlags.Const) !== 0;
296
+ for (const item of statement.declarationList.declarations) {
297
+ if (!ts.isIdentifier(item.name)) continue;
298
+ const name = item.name.text;
299
+ if (!immutable || !stableLocalValueReference(source, item.name)) {
300
+ info.unstableValues.add(name);
301
+ continue;
302
+ }
303
+ if (!item.initializer) continue;
304
+ if (ts.isArrowFunction(item.initializer)
305
+ || ts.isFunctionExpression(item.initializer))
306
+ localValue(info.values, name).callable.push(
307
+ functionTarget(info, source, item.initializer, name),
308
+ );
309
+ if (ts.isObjectLiteralExpression(item.initializer))
310
+ collectObjectMembers(info, source, name, item.initializer);
311
+ }
312
+ }
313
+
314
+ function pushLocalExport(
315
+ info: ModuleInfo,
316
+ publicName: string,
317
+ localName: string,
318
+ typeOnly = false,
319
+ ): void {
320
+ const rootName = localName.split('.')[0] ?? localName;
321
+ if (info.unstableValues.has(rootName))
322
+ info.unsupportedReason = 'unsupported_mutable_export_binding';
323
+ info.localExports.push({ publicName, localName, typeOnly });
324
+ }
325
+
326
+ function addDirectExports(info: ModuleInfo, statement: ts.Statement): void {
327
+ if (!hasModifier(statement, ts.SyntaxKind.ExportKeyword)) return;
328
+ const exportedName = (name: string): string =>
329
+ hasModifier(statement, ts.SyntaxKind.DefaultKeyword) ? 'default' : name;
330
+ if (ts.isFunctionDeclaration(statement) && statement.name)
331
+ pushLocalExport(info, exportedName(statement.name.text), statement.name.text);
332
+ if (ts.isClassDeclaration(statement) && statement.name)
333
+ pushLocalExport(info, exportedName(statement.name.text), statement.name.text);
334
+ if (ts.isVariableStatement(statement))
335
+ for (const item of statement.declarationList.declarations)
336
+ if (ts.isIdentifier(item.name))
337
+ pushLocalExport(info, item.name.text, item.name.text);
338
+ }
339
+
340
+ function addNamedExports(
341
+ info: ModuleInfo,
342
+ statement: ts.ExportDeclaration,
343
+ ): void {
344
+ const clause = statement.exportClause;
345
+ if (!clause || !ts.isNamedExports(clause)) return;
346
+ const module = statement.moduleSpecifier;
347
+ const specifier = module && ts.isStringLiteralLike(module)
348
+ ? module.text
349
+ : undefined;
350
+ for (const item of clause.elements) {
351
+ const importedName = item.propertyName?.text ?? item.name.text;
352
+ const typeOnly = statement.isTypeOnly || item.isTypeOnly;
353
+ if (specifier) info.reExports.push({
354
+ publicName: item.name.text, localName: importedName,
355
+ importedName, specifier, typeOnly,
356
+ });
357
+ else pushLocalExport(info, item.name.text, importedName, typeOnly);
358
+ }
359
+ }
360
+
361
+ function addExportDeclaration(
362
+ info: ModuleInfo,
363
+ statement: ts.ExportDeclaration,
364
+ ): void {
365
+ if (statement.exportClause) {
366
+ if (ts.isNamedExports(statement.exportClause))
367
+ addNamedExports(info, statement);
368
+ else info.unsupportedReason = 'unsupported_namespace_reexport';
369
+ return;
370
+ }
371
+ const module = statement.moduleSpecifier;
372
+ if (!module || !ts.isStringLiteralLike(module)) {
373
+ info.unsupportedReason = 'unsupported_export_declaration';
374
+ return;
375
+ }
376
+ info.starExports.push(module.text);
377
+ }
378
+
379
+ function addExportAssignment(
380
+ info: ModuleInfo,
381
+ statement: ts.ExportAssignment,
382
+ ): void {
383
+ if (!statement.isExportEquals && ts.isIdentifier(statement.expression))
384
+ pushLocalExport(info, 'default', statement.expression.text);
385
+ else info.unsupportedReason = 'unsupported_export_assignment';
386
+ }
387
+
388
+ function addCjsObjectExports(
389
+ info: ModuleInfo,
390
+ object: ts.ObjectLiteralExpression,
391
+ ): void {
392
+ for (const item of object.properties) {
393
+ if (ts.isShorthandPropertyAssignment(item))
394
+ pushLocalExport(info, item.name.text, item.name.text);
395
+ else if (ts.isPropertyAssignment(item)
396
+ && ts.isIdentifier(item.initializer)) {
397
+ const name = propertyName(item.name);
398
+ if (name) pushLocalExport(info, name, item.initializer.text);
399
+ } else info.unsupportedReason = 'unsupported_commonjs_export_shape';
400
+ }
401
+ }
402
+
403
+ function addCjsNamespaceExport(info: ModuleInfo, localName: string): void {
404
+ const value = info.values.get(localName);
405
+ if (!value) {
406
+ info.unsupportedReason = 'unsupported_commonjs_export_shape';
407
+ return;
408
+ }
409
+ if (value.callable.length > 0) pushLocalExport(info, 'default', localName);
410
+ for (const member of value.members.keys()) {
411
+ pushLocalExport(info, member, `${localName}.${member}`);
412
+ pushLocalExport(info, `default.${member}`, `${localName}.${member}`);
413
+ }
414
+ }
415
+
416
+ function cjsBinaryAssignment(
417
+ statement: ts.ExpressionStatement,
418
+ ): ts.BinaryExpression | undefined {
419
+ const value = statement.expression;
420
+ if (!ts.isBinaryExpression(value)) return undefined;
421
+ return value.operatorToken.kind === ts.SyntaxKind.EqualsToken
422
+ ? value
423
+ : undefined;
424
+ }
425
+
426
+ function cjsAssignment(
427
+ info: ModuleInfo,
428
+ statement: ts.ExpressionStatement,
429
+ ): void {
430
+ const value = cjsBinaryAssignment(statement);
431
+ if (!value) return;
432
+ const left = value.left.getText();
433
+ if (!isUnshadowedCommonJsExportExpression(value.left)) return;
434
+ if (left === 'module.exports' && ts.isObjectLiteralExpression(value.right))
435
+ return addCjsObjectExports(info, value.right);
436
+ if (left === 'module.exports' && ts.isIdentifier(value.right))
437
+ return addCjsNamespaceExport(info, value.right.text);
438
+ const match = /^(?:exports|module\.exports)\.([A-Za-z_$][\w$]*)$/.exec(left);
439
+ if (match?.[1] && ts.isIdentifier(value.right))
440
+ return pushLocalExport(info, match[1], value.right.text);
441
+ info.unsupportedReason = 'unsupported_commonjs_export_shape';
442
+ }
443
+
444
+ function collectTypeDeclaration(
445
+ info: ModuleInfo,
446
+ statement: ts.Statement,
447
+ ): void {
448
+ if (!(ts.isInterfaceDeclaration(statement)
449
+ || ts.isTypeAliasDeclaration(statement))
450
+ || !hasModifier(statement, ts.SyntaxKind.ExportKeyword)) return;
451
+ localValue(info.values, statement.name.text).declarationOnlyCount += 1;
452
+ pushLocalExport(info, statement.name.text, statement.name.text, true);
453
+ }
454
+
455
+ function moduleInfo(module: PackageSourceModule): ModuleInfo {
456
+ const info: ModuleInfo = {
457
+ modulePath: canonicalModulePath(module.sourceFile),
458
+ sourceFile: module.sourceFile.replace(/\\/g, '/'),
459
+ values: new Map(), localExports: [], reExports: [], starExports: [],
460
+ unstableValues: new Set(),
461
+ };
462
+ for (const statement of module.source.statements) {
463
+ if (ts.isFunctionDeclaration(statement))
464
+ collectFunction(info, module.source, statement);
465
+ if (ts.isClassDeclaration(statement))
466
+ collectClassMembers(info, module.source, statement);
467
+ if (ts.isVariableStatement(statement))
468
+ collectVariables(info, module.source, statement);
469
+ addDirectExports(info, statement);
470
+ if (ts.isExportDeclaration(statement))
471
+ addExportDeclaration(info, statement);
472
+ if (ts.isExportAssignment(statement))
473
+ addExportAssignment(info, statement);
474
+ if (ts.isExpressionStatement(statement)) {
475
+ cjsAssignment(info, statement);
476
+ if (unsupportedCommonJsMutation(statement))
477
+ info.unsupportedReason = 'unsupported_commonjs_export_shape';
478
+ }
479
+ collectTypeDeclaration(info, statement);
480
+ }
481
+ if (hasNestedCommonJsMutation(module.source))
482
+ info.unsupportedReason = 'unsupported_commonjs_export_shape';
483
+ return info;
484
+ }
485
+
486
+ function mergeTargets(
487
+ current: readonly PublicSurfaceTarget[],
488
+ incoming: readonly PublicSurfaceTarget[],
489
+ ): PublicSurfaceTarget[] {
490
+ const byKey = new Map(current.map((item) => [targetKey(item), item]));
491
+ for (const item of incoming) byKey.set(targetKey(item), item);
492
+ return [...byKey.values()].sort((left, right) =>
493
+ compareBinary(targetKey(left), targetKey(right)));
494
+ }
495
+
496
+ function mergeExport(
497
+ exports: Map<string, ResolvedExport>,
498
+ publicName: string,
499
+ incoming: ResolvedExport,
500
+ ): void {
501
+ const current = exports.get(publicName)
502
+ ?? { targets: [], declarationOnlyCount: 0 };
503
+ exports.set(publicName, {
504
+ targets: mergeTargets(current.targets, incoming.targets),
505
+ declarationOnlyCount:
506
+ current.declarationOnlyCount + incoming.declarationOnlyCount,
507
+ });
508
+ }
509
+
510
+ function localExportValue(
511
+ info: ModuleInfo,
512
+ localName: string,
513
+ ): LocalValue | undefined {
514
+ const direct = info.values.get(localName);
515
+ if (direct) return direct;
516
+ const separator = localName.lastIndexOf('.');
517
+ if (separator < 1) return undefined;
518
+ const owner = info.values.get(localName.slice(0, separator));
519
+ const targets = owner?.members.get(localName.slice(separator + 1));
520
+ return targets
521
+ ? { callable: targets, members: new Map(), declarationOnlyCount: 0 }
522
+ : undefined;
523
+ }
524
+
525
+ function applyLocalExport(
526
+ exports: Map<string, ResolvedExport>,
527
+ info: ModuleInfo,
528
+ item: LocalExport,
529
+ ): void {
530
+ const value = localExportValue(info, item.localName);
531
+ if (!value || item.typeOnly) {
532
+ mergeExport(exports, item.publicName, {
533
+ targets: [], declarationOnlyCount: value?.declarationOnlyCount ?? 1,
534
+ });
535
+ return;
536
+ }
537
+ if (value.callable.length > 0 || value.declarationOnlyCount > 0)
538
+ mergeExport(exports, item.publicName, {
539
+ targets: value.callable,
540
+ declarationOnlyCount: value.declarationOnlyCount,
541
+ });
542
+ for (const [member, targets] of value.members)
543
+ mergeExport(exports, `${item.publicName}.${member}`, {
544
+ targets, declarationOnlyCount: 0,
545
+ });
546
+ }
547
+
548
+ function requestedModuleCandidates(
549
+ fromModule: string,
550
+ specifier: string,
551
+ infos: Map<string, ModuleInfo[]>,
552
+ ): string[] {
553
+ if (!specifier.startsWith('.')) return [];
554
+ const joined = posix.normalize(posix.join(posix.dirname(fromModule), specifier));
555
+ if (joined === '..' || joined.startsWith('../')) return [];
556
+ const base = canonicalModulePath(joined.replace(/^\.\//, ''));
557
+ return [base, `${base}/index`].filter((item, index, values) =>
558
+ values.indexOf(item) === index && infos.get(item)?.length === 1);
559
+ }
560
+
561
+ function renameExport(
562
+ exports: Map<string, ResolvedExport>,
563
+ source: Map<string, ResolvedExport>,
564
+ item: ReExport,
565
+ ): void {
566
+ const exact = source.get(item.importedName);
567
+ const typeOnly = (value: ResolvedExport): ResolvedExport => item.typeOnly
568
+ ? { targets: [], declarationOnlyCount: Math.max(
569
+ 1, value.declarationOnlyCount,
570
+ ) }
571
+ : value;
572
+ if (exact) mergeExport(exports, item.publicName, typeOnly(exact));
573
+ for (const [name, value] of source) {
574
+ if (!name.startsWith(`${item.importedName}.`)) continue;
575
+ mergeExport(exports, `${item.publicName}${name.slice(
576
+ item.importedName.length,
577
+ )}`, typeOnly(value));
578
+ }
579
+ }
580
+
581
+ function moduleFailure(reason: string): ModuleResolution {
582
+ return { exports: new Map(), complete: false, reason };
583
+ }
584
+
585
+ function nestedResolution(
586
+ info: ModuleInfo,
587
+ specifier: string,
588
+ infos: Map<string, ModuleInfo[]>,
589
+ memo: Map<string, ModuleResolution>,
590
+ stack: readonly string[],
591
+ ): ModuleResolution {
592
+ if (!specifier.startsWith('.'))
593
+ return moduleFailure('unsupported_external_reexport');
594
+ const candidates = requestedModuleCandidates(info.modulePath, specifier, infos);
595
+ if (candidates.length !== 1 || !candidates[0])
596
+ return moduleFailure(candidates.length > 1
597
+ ? 'public_surface_module_resolution_ambiguous'
598
+ : 'public_surface_module_not_indexed');
599
+ return resolveModule(candidates[0], infos, memo, stack);
600
+ }
601
+
602
+ function resolveModuleInfo(
603
+ info: ModuleInfo,
604
+ infos: Map<string, ModuleInfo[]>,
605
+ memo: Map<string, ModuleResolution>,
606
+ stack: readonly string[],
607
+ ): ModuleResolution {
608
+ const exports = new Map<string, ResolvedExport>();
609
+ for (const item of info.localExports) applyLocalExport(exports, info, item);
610
+ if (info.unsupportedReason)
611
+ return { exports, complete: false, reason: info.unsupportedReason };
612
+ for (const item of info.reExports) {
613
+ const nested = nestedResolution(info, item.specifier, infos, memo, stack);
614
+ if (!nested.complete) return nested;
615
+ renameExport(exports, nested.exports, item);
616
+ }
617
+ for (const specifier of info.starExports) {
618
+ const nested = nestedResolution(info, specifier, infos, memo, stack);
619
+ if (!nested.complete) return nested;
620
+ for (const [name, value] of nested.exports)
621
+ if (name !== 'default' && !name.startsWith('default.'))
622
+ mergeExport(exports, name, value);
623
+ }
624
+ return { exports, complete: true };
625
+ }
626
+
627
+ export function resolveModule(
628
+ modulePath: string,
629
+ infos: Map<string, ModuleInfo[]>,
630
+ memo: Map<string, ModuleResolution>,
631
+ stack: readonly string[] = [],
632
+ ): ModuleResolution {
633
+ const cached = memo.get(modulePath);
634
+ if (cached) return cached;
635
+ if (stack.includes(modulePath))
636
+ return moduleFailure('public_surface_reexport_cycle');
637
+ if (stack.length >= PACKAGE_PUBLIC_SURFACE_EXPORT_DEPTH)
638
+ return moduleFailure('public_surface_reexport_depth_exceeded');
639
+ const rows = infos.get(modulePath) ?? [];
640
+ if (rows.length !== 1 || !rows[0])
641
+ return moduleFailure(rows.length > 1
642
+ ? 'public_surface_module_resolution_ambiguous'
643
+ : 'public_surface_module_not_indexed');
644
+ const result = resolveModuleInfo(rows[0], infos, memo, [...stack, modulePath]);
645
+ if (result.complete) memo.set(modulePath, result);
646
+ return result;
647
+ }
648
+
649
+ export function moduleInfoMap(
650
+ modules: readonly PackageSourceModule[],
651
+ ): Map<string, ModuleInfo[]> {
652
+ const result = new Map<string, ModuleInfo[]>();
653
+ for (const module of modules) {
654
+ const info = moduleInfo(module);
655
+ result.set(info.modulePath, [...(result.get(info.modulePath) ?? []), info]);
656
+ }
657
+ return result;
658
+ }
659
+
660
+ export { analyzePackagePublicSurface } from
661
+ './010-package-public-surface-analysis.js';