@descryy/adapter-java 0.4.0 → 0.5.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/dist/dto.d.ts ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * `DTO` for Java — the transport shapes a ROUTE declares, never a class that
3
+ * looks like one.
4
+ *
5
+ * ## The rule this keeps, and why the evidence is where it is
6
+ *
7
+ * DEC-043 refused `DTO` for the first real adapter because the corpus supplies
8
+ * three structurally identical classes separable only by name, and
9
+ * `*Request`/`*Response` -> `DTO` is a heuristic: a guessed node type is a
10
+ * false claim that caps nothing. That reasoning is intact and **nothing in
11
+ * this file reads the DTO class to decide whether it is one.**
12
+ *
13
+ * What was missing was a declaration somewhere else, and Spring has two, both
14
+ * written on the route:
15
+ *
16
+ * - `@RestController` is `@Controller + @ResponseBody`, so a handler's
17
+ * **return type is the response body** by the framework's own rule. A bare
18
+ * `@Controller` returns view names and declares nothing unless
19
+ * `@ResponseBody` opts in — the identical admission `spring.ts` already
20
+ * applies to accept the route at all, shared here rather than re-derived.
21
+ * - `@RequestBody` on a parameter states that the **parameter's type is the
22
+ * request body**.
23
+ *
24
+ * Both markers sit on the handler, so the entity beside them is untouched:
25
+ * `Order` keeps the same fields and the same entity-shaped name as
26
+ * `CreateOrderRequest` and stays a `CLASS`. That is the discrimination golden
27
+ * pattern 07 exists to test, and reading the route rather than the class is
28
+ * what preserves it. `test/dto.test.ts` fails if any of it slips.
29
+ *
30
+ * ## Refusals, each of them load-bearing
31
+ *
32
+ * - **A parameter annotation is not a body.** `@PathVariable`/`@RequestParam`
33
+ * sit in exactly the same position and declare a path segment or a query
34
+ * value. A reader taking "annotated parameter" for "request shape" claims
35
+ * `String`.
36
+ * - **A framework or built-in type names no shape here.** `ResponseEntity<T>`,
37
+ * `String`, `void`, a collection, a primitive: `ResponseEntity`'s payload is
38
+ * its type argument and resolving that is a generics read this file does not
39
+ * do, so it refuses rather than minting the wrapper.
40
+ * - **A type that resolves to no class in this repository is refused**, never
41
+ * minted as an empty node — a `DTO` with no shape joins nothing and
42
+ * disguises a missing read as a present one.
43
+ * - **A mapped entity stays a `MODEL`.** A wrong `MODEL` is a claim about a
44
+ * database and a wrong `DTO` a claim about a wire format; the mapping is the
45
+ * stronger, narrower evidence and wins, exactly as it does in
46
+ * `adapter-python`. Applied by the caller, which holds both maps.
47
+ *
48
+ * ## Field NAMES, and deliberately not field detail
49
+ *
50
+ * `attrs.fields` carries the names the class declares — a name-level fact,
51
+ * which rule 3 permits at R2. `attrs.fieldDetail` is **not** emitted and this
52
+ * file raises no node's resolution: whether a plain Java class body's field
53
+ * list is R3-grade evidence under DEC-202 is a separate question with
54
+ * fleet-wide blast radius (it would give `shape-reach.ts` its second fact kind
55
+ * and change what every Java finding may claim), it is filed, and it is not
56
+ * answered here. Golden 05's own note asks for exactly this shape — "an
57
+ * adapter stuck at R2 must emit the DTO nodes without field detail rather than
58
+ * guessing fields."
59
+ */
60
+ import type { JavaUnit } from "./parse.ts";
61
+ /** The declaration marker every `DTO` this file mints names in `attrs.schema`. */
62
+ export declare const SCHEMA = "spring";
63
+ /**
64
+ * A class this repository declares AND a route names as a transport shape.
65
+ * Keyed by `identityPackage::fqn`, the same key `readEntities` uses, so the
66
+ * caller can ask both maps with one lookup.
67
+ */
68
+ export interface TransportShape {
69
+ /** Field names the class declares, sorted. Names only — see the header. */
70
+ readonly fields: readonly string[];
71
+ /** Which directions a route declared for it: `request`, `response`, or both. */
72
+ readonly roles: readonly ("request" | "response")[];
73
+ }
74
+ /**
75
+ * Every class a Spring route declares as a request or response body, across
76
+ * the whole batch — cross-unit, because a controller and the shape it names
77
+ * are routinely different files.
78
+ *
79
+ * Resolution is by SIMPLE name against the set of declared types, which is the
80
+ * same reach the rest of this adapter has (R1/R2 name resolution, no checker).
81
+ * An ambiguous simple name — two classes with the same name in one batch —
82
+ * is refused rather than resolved by ranking: the wrong one would carry the
83
+ * wrong shape into a comparison, and rule 2 prefers the disclosed gap.
84
+ */
85
+ export declare function readTransportShapes(units: readonly JavaUnit[]): ReadonlyMap<string, TransportShape>;
86
+ //# sourceMappingURL=dto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../src/dto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAEH,OAAO,KAAK,EAAwB,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjE,kFAAkF;AAClF,eAAO,MAAM,MAAM,WAAW,CAAC;AAmD/B;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,SAAS,GAAG,UAAU,CAAC,EAAE,CAAC;CACrD;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,QAAQ,EAAE,GACzB,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CA0DrC"}
package/dist/dto.js ADDED
@@ -0,0 +1,198 @@
1
+ /**
2
+ * `DTO` for Java — the transport shapes a ROUTE declares, never a class that
3
+ * looks like one.
4
+ *
5
+ * ## The rule this keeps, and why the evidence is where it is
6
+ *
7
+ * DEC-043 refused `DTO` for the first real adapter because the corpus supplies
8
+ * three structurally identical classes separable only by name, and
9
+ * `*Request`/`*Response` -> `DTO` is a heuristic: a guessed node type is a
10
+ * false claim that caps nothing. That reasoning is intact and **nothing in
11
+ * this file reads the DTO class to decide whether it is one.**
12
+ *
13
+ * What was missing was a declaration somewhere else, and Spring has two, both
14
+ * written on the route:
15
+ *
16
+ * - `@RestController` is `@Controller + @ResponseBody`, so a handler's
17
+ * **return type is the response body** by the framework's own rule. A bare
18
+ * `@Controller` returns view names and declares nothing unless
19
+ * `@ResponseBody` opts in — the identical admission `spring.ts` already
20
+ * applies to accept the route at all, shared here rather than re-derived.
21
+ * - `@RequestBody` on a parameter states that the **parameter's type is the
22
+ * request body**.
23
+ *
24
+ * Both markers sit on the handler, so the entity beside them is untouched:
25
+ * `Order` keeps the same fields and the same entity-shaped name as
26
+ * `CreateOrderRequest` and stays a `CLASS`. That is the discrimination golden
27
+ * pattern 07 exists to test, and reading the route rather than the class is
28
+ * what preserves it. `test/dto.test.ts` fails if any of it slips.
29
+ *
30
+ * ## Refusals, each of them load-bearing
31
+ *
32
+ * - **A parameter annotation is not a body.** `@PathVariable`/`@RequestParam`
33
+ * sit in exactly the same position and declare a path segment or a query
34
+ * value. A reader taking "annotated parameter" for "request shape" claims
35
+ * `String`.
36
+ * - **A framework or built-in type names no shape here.** `ResponseEntity<T>`,
37
+ * `String`, `void`, a collection, a primitive: `ResponseEntity`'s payload is
38
+ * its type argument and resolving that is a generics read this file does not
39
+ * do, so it refuses rather than minting the wrapper.
40
+ * - **A type that resolves to no class in this repository is refused**, never
41
+ * minted as an empty node — a `DTO` with no shape joins nothing and
42
+ * disguises a missing read as a present one.
43
+ * - **A mapped entity stays a `MODEL`.** A wrong `MODEL` is a claim about a
44
+ * database and a wrong `DTO` a claim about a wire format; the mapping is the
45
+ * stronger, narrower evidence and wins, exactly as it does in
46
+ * `adapter-python`. Applied by the caller, which holds both maps.
47
+ *
48
+ * ## Field NAMES, and deliberately not field detail
49
+ *
50
+ * `attrs.fields` carries the names the class declares — a name-level fact,
51
+ * which rule 3 permits at R2. `attrs.fieldDetail` is **not** emitted and this
52
+ * file raises no node's resolution: whether a plain Java class body's field
53
+ * list is R3-grade evidence under DEC-202 is a separate question with
54
+ * fleet-wide blast radius (it would give `shape-reach.ts` its second fact kind
55
+ * and change what every Java finding may claim), it is filed, and it is not
56
+ * answered here. Golden 05's own note asks for exactly this shape — "an
57
+ * adapter stuck at R2 must emit the DTO nodes without field detail rather than
58
+ * guessing fields."
59
+ */
60
+ /** The declaration marker every `DTO` this file mints names in `attrs.schema`. */
61
+ export const SCHEMA = "spring";
62
+ /** Spring's own statement that a parameter carries the request body. */
63
+ const BODY_PARAMETER = "RequestBody";
64
+ /**
65
+ * Types that are framework plumbing or language built-ins rather than a
66
+ * transport shape. A return type here is refused outright: `ResponseEntity`'s
67
+ * real payload is its type argument, and this reader does not resolve generics
68
+ * — so it declines rather than minting the wrapper as the shape.
69
+ */
70
+ const NOT_A_SHAPE = new Set([
71
+ "ResponseEntity",
72
+ "HttpEntity",
73
+ "Mono",
74
+ "Flux",
75
+ "CompletableFuture",
76
+ "Optional",
77
+ "List",
78
+ "Set",
79
+ "Map",
80
+ "Collection",
81
+ "Iterable",
82
+ "String",
83
+ "Object",
84
+ "Void",
85
+ "byte",
86
+ "short",
87
+ "int",
88
+ "long",
89
+ "float",
90
+ "double",
91
+ "boolean",
92
+ "char",
93
+ "Byte",
94
+ "Short",
95
+ "Integer",
96
+ "Long",
97
+ "Float",
98
+ "Double",
99
+ "Boolean",
100
+ "Character",
101
+ "Number",
102
+ "BigDecimal",
103
+ "BigInteger",
104
+ "UUID",
105
+ "Instant",
106
+ "LocalDate",
107
+ "LocalDateTime",
108
+ ]);
109
+ /** `@RestController`, or `@Controller` that opted into a body. Mirrors `spring.ts`. */
110
+ function declaresBodies(type) {
111
+ const names = new Set(type.annotations.map((each) => each.name));
112
+ if (names.has("RestController"))
113
+ return true;
114
+ return names.has("Controller") && names.has("ResponseBody");
115
+ }
116
+ /** Is this method a Spring route handler at all? Any mapping annotation says so. */
117
+ function isHandler(method) {
118
+ return method.annotations.some((each) => each.name === "RequestMapping" || /^(Get|Post|Put|Patch|Delete)Mapping$/.test(each.name));
119
+ }
120
+ /** A simple type name this reader is willing to carry, or `undefined`. */
121
+ function shapeName(name) {
122
+ if (name === undefined || name === null || name === "")
123
+ return undefined;
124
+ return NOT_A_SHAPE.has(name) ? undefined : name;
125
+ }
126
+ /**
127
+ * Every class a Spring route declares as a request or response body, across
128
+ * the whole batch — cross-unit, because a controller and the shape it names
129
+ * are routinely different files.
130
+ *
131
+ * Resolution is by SIMPLE name against the set of declared types, which is the
132
+ * same reach the rest of this adapter has (R1/R2 name resolution, no checker).
133
+ * An ambiguous simple name — two classes with the same name in one batch —
134
+ * is refused rather than resolved by ranking: the wrong one would carry the
135
+ * wrong shape into a comparison, and rule 2 prefers the disclosed gap.
136
+ */
137
+ export function readTransportShapes(units) {
138
+ /** Simple name -> `identityPackage::fqn`, or `null` once ambiguous. */
139
+ const declared = new Map();
140
+ /** `identityPackage::fqn` -> the class's own declared field names. */
141
+ const fieldsOf = new Map();
142
+ for (const unit of units) {
143
+ for (const type of unit.types) {
144
+ const key = `${unit.identityPackage}::${type.fqn}`;
145
+ fieldsOf.set(key, [...type.fields.keys()].sort());
146
+ declared.set(type.simpleName, declared.has(type.simpleName) ? null : key);
147
+ }
148
+ }
149
+ const roles = new Map();
150
+ const record = (simple, role) => {
151
+ if (simple === undefined)
152
+ return;
153
+ const key = declared.get(simple);
154
+ // `undefined` — declared nowhere here; `null` — declared twice. Both are
155
+ // refusals: a DTO with no shape, or the wrong class's shape.
156
+ if (key === undefined || key === null)
157
+ return;
158
+ const existing = roles.get(key);
159
+ if (existing === undefined)
160
+ roles.set(key, new Set([role]));
161
+ else
162
+ existing.add(role);
163
+ };
164
+ for (const unit of units) {
165
+ for (const type of unit.types) {
166
+ if (!declaresBodies(type))
167
+ continue;
168
+ for (const method of type.methods) {
169
+ if (!isHandler(method))
170
+ continue;
171
+ // The return type IS the response body — that is what @RestController
172
+ // means. `TypeRead` keeps absent / void / unreadable apart, and only a
173
+ // written, named type is evidence.
174
+ const returned = method.returnType;
175
+ if (returned.kind === "named")
176
+ record(shapeName(returned.name), "response");
177
+ // ...and a @RequestBody parameter's type IS the request body. The type
178
+ // comes from `locals`, which the parse fills for every parameter;
179
+ // `null` there means the name was declared twice or with `var` and
180
+ // poisons itself, which `shapeName` passes straight through.
181
+ for (const [name, annotations] of method.parameterAnnotations) {
182
+ if (!annotations.some((each) => each.name === BODY_PARAMETER))
183
+ continue;
184
+ record(shapeName(method.locals.get(name)), "request");
185
+ }
186
+ }
187
+ }
188
+ }
189
+ const shapes = new Map();
190
+ for (const [key, directions] of roles) {
191
+ shapes.set(key, {
192
+ fields: fieldsOf.get(key) ?? [],
193
+ roles: [...directions].sort(),
194
+ });
195
+ }
196
+ return shapes;
197
+ }
198
+ //# sourceMappingURL=dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dto.js","sourceRoot":"","sources":["../src/dto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * The dependency boundary, Java side.
3
+ *
4
+ * `LoggerFactory.getLogger(Foo.class)` leaves the repository. Today that becomes
5
+ * a ledger row saying "names a type this run did not analyse" and the call graph
6
+ * stops. This module answers the one question that turns the boundary into a
7
+ * fact the graph can hold: **which package, and which symbol in it.**
8
+ *
9
+ * ## The rule everything here rests on: attribute only from a binding the
10
+ * developer wrote
11
+ *
12
+ * In Go (`adapter-go/src/external.ts`, the model for this file) that binding is
13
+ * the file's own `import` block. Java's is the same statement, and it is the
14
+ * cleanest of the ten: `import org.slf4j.Logger;` binds the simple name `Logger`
15
+ * to a fully-qualified name **written in the file**, and `import static
16
+ * org.junit.jupiter.api.Assertions.assertEquals;` binds a bare member name to
17
+ * the type that declares it. Nothing here reads `pom.xml`'s dependency list, a
18
+ * Gradle configuration, `~/.m2`, or a jar. Two reasons, the same two Go gives:
19
+ *
20
+ * 1. Most checkouts have no dependencies fetched, so an identity taken from a
21
+ * resolved classpath would exist for some runs and not others.
22
+ * 2. It would *move* the first time somebody ran `mvn dependency:resolve`.
23
+ *
24
+ * ## Why the Maven coordinate is not the identity
25
+ *
26
+ * `org.slf4j:slf4j-api` is the obvious answer and it is the one this module
27
+ * refuses. A jar's package-to-coordinate mapping lives in the jar, and the root
28
+ * `pom.xml`'s `<dependencies>` block binds a coordinate to no package at all —
29
+ * so there is no declared mapping to read without the resolved classpath. The
30
+ * **package** is written, in the import statement, by the developer, in the
31
+ * file. So the package is the identity, exactly as Go's import path is Go's and
32
+ * PHP's namespace is PHP's.
33
+ *
34
+ * ## The three shapes Java attributes, and what each costs
35
+ *
36
+ * - `import a.b.C;` … `C x` or `C.m()` — a single-type import. The direct
37
+ * analogue of Go's package-qualified call.
38
+ * - `import a.b.C;` … `C x; x.m()` — a call through a local or field whose
39
+ * **written type** is an imported name. Java writes its types down where Go
40
+ * and PHP do not, and this is the half that makes Java worth doing.
41
+ * - `import static a.b.C.m;` … `m()` — a bare name bound by a static import.
42
+ * This is how every JUnit assertion is written, so without it a test file's
43
+ * calls are almost entirely unattributable. Python's `from x import y` is
44
+ * the same shape and was the larger half there too.
45
+ *
46
+ * ## What Java makes harder than Go, and the refusals it earns
47
+ *
48
+ * Go has no fallback: an unqualified name is package-local, full stop. Java has
49
+ * three, and none of them is a binding a developer wrote:
50
+ *
51
+ * - **`java.lang` is imported implicitly.** `String`, `Integer`, `Exception`
52
+ * appear with no import statement anywhere. A language rule filling a
53
+ * silence is not a written binding, so they are declined — the same call
54
+ * PHP's `external.ts` makes about its global-namespace fallback.
55
+ * - **A wildcard import binds no name.** `import java.util.*;` then `List`
56
+ * could be `java.util.List` or a type from any other wildcard in the file,
57
+ * and telling them apart needs the classpath this module refuses to read.
58
+ * Python's `from x import *` refusal, for the same reason.
59
+ * - **Same-package types resolve without an import.** A name settled that way
60
+ * is ours by construction, and `resolveType` has already claimed it.
61
+ *
62
+ * One more refusal is Java-specific and is a **precision** refusal rather than a
63
+ * recall one: `extract.ts`'s `REASONS.outside` discloses that some `outside`
64
+ * rows are declarations in *another language in this same repository* — 238 of
65
+ * `okhttp`'s 458 are in `.kt` files. The own-package check below is what keeps
66
+ * those from being called dependencies, and it is whole-segment, so a Kotlin
67
+ * class in a package this repository's Java sources also declare is ours.
68
+ *
69
+ * **Everything this module does not attribute is a deliberate refusal.** Rule 2
70
+ * prices a wrong edge far above a missing one.
71
+ */
72
+ /** What one written name was bound by. Only a binding the developer wrote is
73
+ * attributed; `implicit` covers `java.lang`, a wildcard and the same package,
74
+ * which are language rules filling a silence. */
75
+ export type Binding = "single-type-import" | "written-fully-qualified" | "static-import";
76
+ export interface ExternalImports {
77
+ /** Simple name -> fully-qualified name, from `import a.b.C;`. */
78
+ readonly singleTypeImports: ReadonlyMap<string, string>;
79
+ /** Member name -> declaring type, from `import static a.b.C.m;`. */
80
+ readonly staticMemberImports: ReadonlyMap<string, string>;
81
+ }
82
+ export interface ExternalContext {
83
+ readonly imports: ExternalImports;
84
+ /**
85
+ * Packages declared by the repository's own compilation units. A type under
86
+ * one of them is OURS whether or not this run read it — calling unanalysed
87
+ * code of ours a dependency is the error that never surfaces.
88
+ */
89
+ readonly ownPackages: ReadonlySet<string>;
90
+ /** Fully-qualified names this run actually read. One of ours is not a boundary. */
91
+ readonly analysed: ReadonlySet<string>;
92
+ }
93
+ export interface ExternalAttribution {
94
+ /** The package **as written in the import statement** — this is the identity. */
95
+ readonly moduleOrNamespace: string;
96
+ /** The type chain, then any member named on it. Never a local alias; Java has none. */
97
+ readonly symbolPath: readonly string[];
98
+ /** Which written binding settled the name. Disclosure, never identity. */
99
+ readonly binding: Binding;
100
+ /**
101
+ * From a jar rather than the JDK, decided from the package name alone against
102
+ * `PLATFORM_BASIS`. Disclosure only — it never enters the node id, because a
103
+ * package that moved between the two (every `javax.*` that became `jakarta.*`
104
+ * did exactly this) would otherwise change identity with no line of source
105
+ * changing.
106
+ */
107
+ readonly thirdParty: boolean;
108
+ }
109
+ /**
110
+ * Provenance of the platform-package list, carried on every attributed node.
111
+ * A property of the language and its JDK, frozen into this source — never read
112
+ * from whatever JDK a developer happens to have installed, which would make the
113
+ * answer depend on the machine.
114
+ *
115
+ * `javax` is split rather than taken whole: `javax.swing` ships with the JDK and
116
+ * `javax.persistence` never did. Taking the whole prefix either way is a wrong
117
+ * disclosure on thousands of nodes, and the list is short enough to write down.
118
+ */
119
+ export declare const PLATFORM_BASIS = "jdk-21 java./jdk./sun./com.sun. roots, plus the JDK's own javax subpackages";
120
+ /**
121
+ * A fully-qualified name split into the package that names it and the symbol
122
+ * chain inside it: `java.util.Map.Entry` -> `java.util` + `Map.Entry`.
123
+ *
124
+ * `null` when the convention does not settle it — no lowercase prefix (a
125
+ * default-package name, or a name already inside a type) or no capitalised
126
+ * segment at all (`foo.bar.baz`, which is a value traversal and not a type).
127
+ */
128
+ export declare function splitQualifiedName(fqn: string): {
129
+ pkg: string;
130
+ symbols: string[];
131
+ } | null;
132
+ /** Generic arguments and array brackets are not part of a name's identity, and
133
+ * the type arguments inside them are separate references the extractor already
134
+ * files on their own. */
135
+ export declare function bareTypeName(written: string): string;
136
+ /**
137
+ * The package and type behind a **type reference** that left the repository, or
138
+ * `null` when no binding the developer wrote settles it.
139
+ *
140
+ * `written` is the name exactly as the source wrote it — `Logger`,
141
+ * `java.util.List`, `Map.Entry`.
142
+ */
143
+ export declare function attributeExternalType(written: string, context: ExternalContext): ExternalAttribution | null;
144
+ /**
145
+ * The package, type and member behind a **call** that left the repository, or
146
+ * `null` when no binding the developer wrote settles it.
147
+ *
148
+ * `receiverType` is the receiver's type **as the source wrote it**: the declared
149
+ * type of a local or field, or the type name itself for a static call. It is
150
+ * never inferred — a `var`, a chained call or a generic type variable writes no
151
+ * type at the reference and `extract.ts` hands `undefined` here for all three.
152
+ */
153
+ export declare function attributeExternalCall(receiverType: string | undefined, method: string, context: ExternalContext): ExternalAttribution | null;
154
+ /**
155
+ * The package, type and member behind an **unqualified call bound by a static
156
+ * import**, or `null` when no such binding exists.
157
+ *
158
+ * `assertEquals(a, b)` under `import static org.junit.jupiter.api.Assertions.assertEquals;`
159
+ * is a binding the developer wrote and the language guarantees it — following it
160
+ * is resolution, not inference. A bare name with no static import is declined:
161
+ * it is the enclosing type's own member, or an inherited one from a supertype
162
+ * this run did not read, and neither is a dependency.
163
+ *
164
+ * `import static a.b.C.*;` binds no name and is never recorded in
165
+ * `staticMemberImports`, so it cannot arrive here — Python's `import *` refusal
166
+ * in the form Java's parse already applies.
167
+ */
168
+ export declare function attributeExternalStaticCall(method: string, context: ExternalContext): ExternalAttribution | null;
169
+ //# sourceMappingURL=external.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../src/external.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH;;kDAEkD;AAClD,MAAM,MAAM,OAAO,GAAG,oBAAoB,GAAG,yBAAyB,GAAG,eAAe,CAAC;AAEzF,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,iBAAiB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxD,oEAAoE;IACpE,QAAQ,CAAC,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,mFAAmF;IACnF,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAClC,iFAAiF;IACjF,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,uFAAuF;IACvF,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,gFAAgF,CAAC;AAsD5G;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAOzF;AAED;;0BAE0B;AAC1B,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIpD;AA8DD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,GACvB,mBAAmB,GAAG,IAAI,CAI5B;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,mBAAmB,GAAG,IAAI,CAK5B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,eAAe,GACvB,mBAAmB,GAAG,IAAI,CAK5B"}