@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/adapter.d.ts.map +1 -1
- package/dist/adapter.js +47 -3
- package/dist/adapter.js.map +1 -1
- package/dist/client-base.d.ts +114 -0
- package/dist/client-base.d.ts.map +1 -0
- package/dist/client-base.js +102 -0
- package/dist/client-base.js.map +1 -0
- package/dist/client.d.ts +28 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +83 -4
- package/dist/client.js.map +1 -1
- package/dist/dto.d.ts +86 -0
- package/dist/dto.d.ts.map +1 -0
- package/dist/dto.js +198 -0
- package/dist/dto.js.map +1 -0
- package/dist/external.d.ts +169 -0
- package/dist/external.d.ts.map +1 -0
- package/dist/external.js +262 -0
- package/dist/external.js.map +1 -0
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +403 -4
- package/dist/extract.js.map +1 -1
- package/dist/parse.d.ts +32 -0
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +118 -6
- package/dist/parse.js.map +1 -1
- package/package.json +5 -5
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
|
package/dist/dto.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dto.js","sourceRoot":"","sources":["../src/dto.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AAIH,kFAAkF;AAClF,MAAM,CAAC,MAAM,MAAM,GAAG,QAAQ,CAAC;AAE/B,wEAAwE;AACxE,MAAM,cAAc,GAAG,aAAa,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,gBAAgB;IAChB,YAAY;IACZ,MAAM;IACN,MAAM;IACN,mBAAmB;IACnB,UAAU;IACV,MAAM;IACN,KAAK;IACL,KAAK;IACL,YAAY;IACZ,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,MAAM;IACN,OAAO;IACP,KAAK;IACL,MAAM;IACN,OAAO;IACP,QAAQ;IACR,SAAS;IACT,MAAM;IACN,MAAM;IACN,OAAO;IACP,SAAS;IACT,MAAM;IACN,OAAO;IACP,QAAQ;IACR,SAAS;IACT,WAAW;IACX,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,MAAM;IACN,SAAS;IACT,WAAW;IACX,eAAe;CAChB,CAAC,CAAC;AAcH,uFAAuF;AACvF,SAAS,cAAc,CAAC,IAAc;IACpC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,IAAI,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC9D,CAAC;AAED,oFAAoF;AACpF,SAAS,SAAS,CAAC,MAAkB;IACnC,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAC5B,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,gBAAgB,IAAI,sCAAsC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CACnG,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,SAAS,SAAS,CAAC,IAA+B;IAChD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IACzE,OAAO,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAClD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAA0B;IAE1B,uEAAuE;IACvE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAClD,sEAAsE;IACtE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;IAEtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,eAAe,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;YACnD,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAClD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAAuC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,MAA0B,EAAE,IAA4B,EAAQ,EAAE;QAChF,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO;QACjC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjC,yEAAyE;QACzE,6DAA6D;QAC7D,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO;QAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,QAAQ,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;YACvD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;gBAAE,SAAS;YACpC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAClC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;oBAAE,SAAS;gBAEjC,sEAAsE;gBACtE,uEAAuE;gBACvE,mCAAmC;gBACnC,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC;gBACnC,IAAI,QAAQ,CAAC,IAAI,KAAK,OAAO;oBAAE,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,CAAC;gBAE5E,uEAAuE;gBACvE,kEAAkE;gBAClE,mEAAmE;gBACnE,6DAA6D;gBAC7D,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,oBAAoB,EAAE,CAAC;oBAC9D,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,cAAc,CAAC;wBAAE,SAAS;oBACxE,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;gBACxD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IACjD,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,KAAK,EAAE,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;YACd,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;YAC/B,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE;SAC9B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -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"}
|