@descryy/adapter-python 0.1.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 +75 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +481 -0
- package/dist/adapter.js.map +1 -0
- package/dist/client.d.ts +117 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +347 -0
- package/dist/client.js.map +1 -0
- package/dist/extract.d.ts +231 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +2267 -0
- package/dist/extract.js.map +1 -0
- package/dist/extract.py +1319 -0
- package/dist/graphql.d.ts +197 -0
- package/dist/graphql.d.ts.map +1 -0
- package/dist/graphql.js +302 -0
- package/dist/graphql.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +99 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +211 -0
- package/dist/module.js.map +1 -0
- package/dist/orm.d.ts +102 -0
- package/dist/orm.d.ts.map +1 -0
- package/dist/orm.js +221 -0
- package/dist/orm.js.map +1 -0
- package/dist/pydantic.d.ts +157 -0
- package/dist/pydantic.d.ts.map +1 -0
- package/dist/pydantic.js +318 -0
- package/dist/pydantic.js.map +1 -0
- package/dist/pytest-config.d.ts +60 -0
- package/dist/pytest-config.d.ts.map +1 -0
- package/dist/pytest-config.js +147 -0
- package/dist/pytest-config.js.map +1 -0
- package/dist/routes.d.ts +249 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +511 -0
- package/dist/routes.js.map +1 -0
- package/package.json +34 -0
- package/src/extract.py +1319 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP calls made *from* Python — the caller half of the contract join.
|
|
3
|
+
*
|
|
4
|
+
* ```
|
|
5
|
+
* API_ROUTE ──SERVES_API──▶ API_ENDPOINT ◀──USES_API── caller
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* `routes.ts` produces the provider half. This produces the consumer half, and
|
|
9
|
+
* until it existed `adapter-python` declared no `USES_API` at all, so golden
|
|
10
|
+
* pattern 08 was skipped rather than passed — declared incapacity, per DEC-043.
|
|
11
|
+
*
|
|
12
|
+
* ## What counts as evidence
|
|
13
|
+
*
|
|
14
|
+
* **Provenance, never the name.** `requests.get(...)` is only an HTTP call when
|
|
15
|
+
* `requests` resolves, through this file's own import table, to the `requests`
|
|
16
|
+
* package. A local object called `requests`, or a `client.get()` on something
|
|
17
|
+
* this reader cannot trace, is not one — and `client.get()` is the single most
|
|
18
|
+
* common false positive available here, because every Django and Flask test
|
|
19
|
+
* suite in existence calls `client.get("/orders/1")` on a **test client** that
|
|
20
|
+
* makes no HTTP request at all. A census of the reference set found 1,970 such
|
|
21
|
+
* call sites in `django` and 310 in `flask`, against **one** file in either that
|
|
22
|
+
* imports `requests`. Matching on the verb name would have produced 2,280 edges
|
|
23
|
+
* to endpoints nothing serves.
|
|
24
|
+
*
|
|
25
|
+
* ## What is refused
|
|
26
|
+
*
|
|
27
|
+
* A path that is not a literal or an f-string · a path that is not
|
|
28
|
+
* repository-relative, which is an outbound call to somebody else's service and
|
|
29
|
+
* has no route on this side of the join · a `request()` whose verb is not a
|
|
30
|
+
* literal. Each is a ledger row.
|
|
31
|
+
*/
|
|
32
|
+
import type { PyCall } from "./extract.ts";
|
|
33
|
+
/** The packages whose module-level verbs are HTTP calls. */
|
|
34
|
+
export declare const CLIENT_MODULES: ReadonlySet<string>;
|
|
35
|
+
/** Constructors whose result is a reusable client bound to a local. */
|
|
36
|
+
export declare const SESSION_CONSTRUCTORS: ReadonlySet<string>;
|
|
37
|
+
/** Verb methods, by the name the package exports. */
|
|
38
|
+
export declare const CLIENT_VERBS: ReadonlyMap<string, string>;
|
|
39
|
+
export interface ClientCall {
|
|
40
|
+
readonly file: string;
|
|
41
|
+
readonly line: number;
|
|
42
|
+
readonly method: string;
|
|
43
|
+
/** The path, normalised to `{param}` for every interpolation. */
|
|
44
|
+
readonly path: string;
|
|
45
|
+
/** As written, for the ledger and for an adjudicator. */
|
|
46
|
+
readonly rawPath: string;
|
|
47
|
+
readonly scope: readonly string[];
|
|
48
|
+
/** `requests.get`, `session.post` — what the call site wrote. */
|
|
49
|
+
readonly client: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ClientRefusal {
|
|
52
|
+
readonly file: string;
|
|
53
|
+
readonly line: number;
|
|
54
|
+
readonly rawTarget: string;
|
|
55
|
+
readonly reason: "callPathNotLiteral" | "callPathNotRelative" | "callMethodNotLiteral" | "callBaseUnresolved" | "callBaseIsExternalHost" | "callPathFromLocal";
|
|
56
|
+
/** DEC-242's `attrs.blockedBy` — the literal blocking expression, or `null`. */
|
|
57
|
+
readonly blockedBy: string | null;
|
|
58
|
+
/** DEC-242's `attrs.refusalClass` — `undefined` means unclassified, never askable. */
|
|
59
|
+
readonly refusalClass: BaseRefusalClass | "out-of-scope" | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Diagnostic only, not part of DEC-242 — see `adapter-rust/src/client.ts`'s
|
|
62
|
+
* own doc for what this is and why it is not shipped as part of the
|
|
63
|
+
* contract. Present only on the generic fallthrough of `diagnoseArgument`.
|
|
64
|
+
*/
|
|
65
|
+
readonly argumentKind?: "field" | "call" | "concatenation" | "other";
|
|
66
|
+
}
|
|
67
|
+
export type BaseRefusalClass = "value-unknown" | "varies-per-call" | "capability-gap";
|
|
68
|
+
/**
|
|
69
|
+
* An f-string whose first element is an interpolation: `f"{BASE}/orders/{id}"`.
|
|
70
|
+
*
|
|
71
|
+
* **This is the shape a Python service actually writes**, and until this was
|
|
72
|
+
* added the reader refused every one of them as "not a literal". Counted across
|
|
73
|
+
* five reference repositories: 283 module-level `requests`/`httpx` verb calls,
|
|
74
|
+
* of which **not one** takes a repository-relative literal and **78** take this
|
|
75
|
+
* form. The extractor was built for a shape the corpus does not contain, and no
|
|
76
|
+
* precision draw could have said so — there were no edges to sample.
|
|
77
|
+
*
|
|
78
|
+
* Returns the base expression as written and the remainder as a template. The
|
|
79
|
+
* remainder must begin with `/`, because `f"{base}orders"` is a base whose own
|
|
80
|
+
* trailing slash decides the path and this reader cannot see it.
|
|
81
|
+
*/
|
|
82
|
+
export declare function assemblyOfSource(raw: string): {
|
|
83
|
+
base: string;
|
|
84
|
+
suffix: string;
|
|
85
|
+
} | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* A path argument's source text as a template, or `undefined`.
|
|
88
|
+
*
|
|
89
|
+
* `f"/orders/{order_id}"` becomes `/orders/{param}`, which is what
|
|
90
|
+
* `normaliseEndpointPath` produces from the provider's `/orders/{order_id}` —
|
|
91
|
+
* the two must agree or the join does not happen. `{{` is an escaped brace and
|
|
92
|
+
* is not an interpolation.
|
|
93
|
+
*
|
|
94
|
+
* Deliberately narrow: one string, no implicit concatenation, no `%` or
|
|
95
|
+
* `.format()`. Each of those is a path assembled somewhere else, and a template
|
|
96
|
+
* guessed from half of it is an endpoint that does not exist.
|
|
97
|
+
*/
|
|
98
|
+
export declare function templateOfSource(raw: string): string | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Locals holding a session or client constructed from one of these packages.
|
|
101
|
+
*
|
|
102
|
+
* `s = requests.Session()` and `c = httpx.Client()`. The construction is the
|
|
103
|
+
* evidence; a local *called* `session` is not.
|
|
104
|
+
*/
|
|
105
|
+
export declare function clientLocalsIn(calls: readonly PyCall[], localOfCall: (call: PyCall) => string | undefined, moduleOfImport: (name: string) => string | undefined): Map<string, string>;
|
|
106
|
+
/** Every HTTP call written in one file, and every one refused. */
|
|
107
|
+
export declare function clientCallsIn(file: string, calls: readonly PyCall[], moduleOfImport: (name: string) => string | undefined, clientLocals: ReadonlyMap<string, string>,
|
|
108
|
+
/** Module-scope `NAME = "literal"` in this file, for base resolution. */
|
|
109
|
+
constants?: ReadonlyMap<string, string>,
|
|
110
|
+
/** `scope::name` -> source text, for a path assembled a few lines earlier. */
|
|
111
|
+
localStrings?: ReadonlyMap<string, string>,
|
|
112
|
+
/** `scope.join(".")` -> parameter names, for DEC-242's varies-per-call. */
|
|
113
|
+
functionParams?: ReadonlyMap<string, ReadonlySet<string>>): {
|
|
114
|
+
readonly calls: ClientCall[];
|
|
115
|
+
readonly refusals: ClientRefusal[];
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP calls made *from* Python — the caller half of the contract join.
|
|
3
|
+
*
|
|
4
|
+
* ```
|
|
5
|
+
* API_ROUTE ──SERVES_API──▶ API_ENDPOINT ◀──USES_API── caller
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* `routes.ts` produces the provider half. This produces the consumer half, and
|
|
9
|
+
* until it existed `adapter-python` declared no `USES_API` at all, so golden
|
|
10
|
+
* pattern 08 was skipped rather than passed — declared incapacity, per DEC-043.
|
|
11
|
+
*
|
|
12
|
+
* ## What counts as evidence
|
|
13
|
+
*
|
|
14
|
+
* **Provenance, never the name.** `requests.get(...)` is only an HTTP call when
|
|
15
|
+
* `requests` resolves, through this file's own import table, to the `requests`
|
|
16
|
+
* package. A local object called `requests`, or a `client.get()` on something
|
|
17
|
+
* this reader cannot trace, is not one — and `client.get()` is the single most
|
|
18
|
+
* common false positive available here, because every Django and Flask test
|
|
19
|
+
* suite in existence calls `client.get("/orders/1")` on a **test client** that
|
|
20
|
+
* makes no HTTP request at all. A census of the reference set found 1,970 such
|
|
21
|
+
* call sites in `django` and 310 in `flask`, against **one** file in either that
|
|
22
|
+
* imports `requests`. Matching on the verb name would have produced 2,280 edges
|
|
23
|
+
* to endpoints nothing serves.
|
|
24
|
+
*
|
|
25
|
+
* ## What is refused
|
|
26
|
+
*
|
|
27
|
+
* A path that is not a literal or an f-string · a path that is not
|
|
28
|
+
* repository-relative, which is an outbound call to somebody else's service and
|
|
29
|
+
* has no route on this side of the join · a `request()` whose verb is not a
|
|
30
|
+
* literal. Each is a ledger row.
|
|
31
|
+
*/
|
|
32
|
+
/** The packages whose module-level verbs are HTTP calls. */
|
|
33
|
+
export const CLIENT_MODULES = new Set(["requests", "httpx", "aiohttp"]);
|
|
34
|
+
/** Constructors whose result is a reusable client bound to a local. */
|
|
35
|
+
export const SESSION_CONSTRUCTORS = new Set([
|
|
36
|
+
"Session",
|
|
37
|
+
"Client",
|
|
38
|
+
"AsyncClient",
|
|
39
|
+
"ClientSession",
|
|
40
|
+
]);
|
|
41
|
+
/** Verb methods, by the name the package exports. */
|
|
42
|
+
export const CLIENT_VERBS = new Map([
|
|
43
|
+
["get", "GET"],
|
|
44
|
+
["post", "POST"],
|
|
45
|
+
["put", "PUT"],
|
|
46
|
+
["patch", "PATCH"],
|
|
47
|
+
["delete", "DELETE"],
|
|
48
|
+
["head", "HEAD"],
|
|
49
|
+
["options", "OPTIONS"],
|
|
50
|
+
]);
|
|
51
|
+
const CAPABILITY_GAP = { blockedBy: null, refusalClass: "capability-gap" };
|
|
52
|
+
const VARIES_PER_CALL = { blockedBy: null, refusalClass: "varies-per-call" };
|
|
53
|
+
/** `os.environ["X"]`/`os.environ['X']`, `os.environ.get("X")`, `os.getenv("X")`. */
|
|
54
|
+
const ENV_READ_PATTERNS = [
|
|
55
|
+
/^os\.environ\[\s*['"][^'"]*['"]\s*\]$/,
|
|
56
|
+
/^os\.environ\.get\(\s*['"][^'"]*['"].*\)$/,
|
|
57
|
+
/^os\.getenv\(\s*['"][^'"]*['"].*\)$/,
|
|
58
|
+
];
|
|
59
|
+
/** Same heuristic as every other adapter's — see `adapter-rust/src/client.ts`'s doc. */
|
|
60
|
+
function argumentKindOf(text) {
|
|
61
|
+
if (text.endsWith(")") && text.includes("("))
|
|
62
|
+
return "call";
|
|
63
|
+
if (text.includes("+"))
|
|
64
|
+
return "concatenation";
|
|
65
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(text))
|
|
66
|
+
return "field";
|
|
67
|
+
return "other";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Why an argument (a direct path argument, or an f-string's interpolated
|
|
71
|
+
* base) could not be read as a literal — a bare identifier that is the
|
|
72
|
+
* enclosing function's own parameter, a known environment-read expression,
|
|
73
|
+
* or something this reader does not trace.
|
|
74
|
+
*/
|
|
75
|
+
function diagnoseArgument(text, parameterNames) {
|
|
76
|
+
if (text === undefined)
|
|
77
|
+
return CAPABILITY_GAP;
|
|
78
|
+
const trimmed = text.trim();
|
|
79
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(trimmed) && parameterNames.has(trimmed))
|
|
80
|
+
return VARIES_PER_CALL;
|
|
81
|
+
if (ENV_READ_PATTERNS.some((pattern) => pattern.test(trimmed))) {
|
|
82
|
+
return { blockedBy: trimmed, refusalClass: "value-unknown" };
|
|
83
|
+
}
|
|
84
|
+
return { ...CAPABILITY_GAP, argumentKind: argumentKindOf(trimmed) };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* An f-string whose first element is an interpolation: `f"{BASE}/orders/{id}"`.
|
|
88
|
+
*
|
|
89
|
+
* **This is the shape a Python service actually writes**, and until this was
|
|
90
|
+
* added the reader refused every one of them as "not a literal". Counted across
|
|
91
|
+
* five reference repositories: 283 module-level `requests`/`httpx` verb calls,
|
|
92
|
+
* of which **not one** takes a repository-relative literal and **78** take this
|
|
93
|
+
* form. The extractor was built for a shape the corpus does not contain, and no
|
|
94
|
+
* precision draw could have said so — there were no edges to sample.
|
|
95
|
+
*
|
|
96
|
+
* Returns the base expression as written and the remainder as a template. The
|
|
97
|
+
* remainder must begin with `/`, because `f"{base}orders"` is a base whose own
|
|
98
|
+
* trailing slash decides the path and this reader cannot see it.
|
|
99
|
+
*/
|
|
100
|
+
export function assemblyOfSource(raw) {
|
|
101
|
+
const text = raw.trim();
|
|
102
|
+
const match = /^[rRbB]*[fF][rRbB]*("""|'''|"|')([\s\S]*)\1$/.exec(text);
|
|
103
|
+
if (match === null)
|
|
104
|
+
return undefined;
|
|
105
|
+
const body = match[2];
|
|
106
|
+
if (body.includes("\n") || !body.startsWith("{"))
|
|
107
|
+
return undefined;
|
|
108
|
+
const close = body.indexOf("}");
|
|
109
|
+
if (close < 0)
|
|
110
|
+
return undefined;
|
|
111
|
+
const base = body.slice(1, close).trim();
|
|
112
|
+
// A format spec or conversion (`{x!r}`, `{x:>10}`) is not a name this reader
|
|
113
|
+
// can resolve, and neither is an expression with a call in it.
|
|
114
|
+
if (base === "" || /[!:(]/.test(base))
|
|
115
|
+
return undefined;
|
|
116
|
+
const rest = templateOfSource(`f"${body.slice(close + 1)}"`);
|
|
117
|
+
if (rest === undefined || !rest.startsWith("/"))
|
|
118
|
+
return undefined;
|
|
119
|
+
return { base, suffix: rest };
|
|
120
|
+
}
|
|
121
|
+
/** `https://host/api` -> `/api`; `/api` -> `/api`; anything else -> undefined. */
|
|
122
|
+
function pathOfBase(value) {
|
|
123
|
+
if (value.startsWith("/"))
|
|
124
|
+
return { path: value.replace(/\/$/, ""), external: false };
|
|
125
|
+
const url = /^[a-zA-Z][\w+.-]*:\/\/[^/]+(\/.*)?$/.exec(value);
|
|
126
|
+
if (url === null)
|
|
127
|
+
return undefined;
|
|
128
|
+
return { path: (url[1] ?? "").replace(/\/$/, ""), external: true };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A path argument's source text as a template, or `undefined`.
|
|
132
|
+
*
|
|
133
|
+
* `f"/orders/{order_id}"` becomes `/orders/{param}`, which is what
|
|
134
|
+
* `normaliseEndpointPath` produces from the provider's `/orders/{order_id}` —
|
|
135
|
+
* the two must agree or the join does not happen. `{{` is an escaped brace and
|
|
136
|
+
* is not an interpolation.
|
|
137
|
+
*
|
|
138
|
+
* Deliberately narrow: one string, no implicit concatenation, no `%` or
|
|
139
|
+
* `.format()`. Each of those is a path assembled somewhere else, and a template
|
|
140
|
+
* guessed from half of it is an endpoint that does not exist.
|
|
141
|
+
*/
|
|
142
|
+
export function templateOfSource(raw) {
|
|
143
|
+
const text = raw.trim();
|
|
144
|
+
const match = /^(?:[rRbB]*[fF][rRbB]*|[rRbB]*)?("""|'''|"|')([\s\S]*)\1$/.exec(text);
|
|
145
|
+
if (match === null)
|
|
146
|
+
return undefined;
|
|
147
|
+
const body = match[2];
|
|
148
|
+
if (body.includes("\n"))
|
|
149
|
+
return undefined;
|
|
150
|
+
let out = "";
|
|
151
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
152
|
+
const ch = body[i];
|
|
153
|
+
if (ch === "{" && body[i + 1] === "{") {
|
|
154
|
+
out += "{";
|
|
155
|
+
i += 1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (ch === "}" && body[i + 1] === "}") {
|
|
159
|
+
out += "}";
|
|
160
|
+
i += 1;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (ch === "{") {
|
|
164
|
+
const close = body.indexOf("}", i);
|
|
165
|
+
if (close < 0)
|
|
166
|
+
return undefined;
|
|
167
|
+
out += "{param}";
|
|
168
|
+
i = close;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
out += ch;
|
|
172
|
+
}
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Locals holding a session or client constructed from one of these packages.
|
|
177
|
+
*
|
|
178
|
+
* `s = requests.Session()` and `c = httpx.Client()`. The construction is the
|
|
179
|
+
* evidence; a local *called* `session` is not.
|
|
180
|
+
*/
|
|
181
|
+
export function clientLocalsIn(calls, localOfCall, moduleOfImport) {
|
|
182
|
+
const locals = new Map();
|
|
183
|
+
for (const call of calls) {
|
|
184
|
+
if (call.callee === null)
|
|
185
|
+
continue;
|
|
186
|
+
const leaf = call.callee.includes(".")
|
|
187
|
+
? call.callee.slice(call.callee.lastIndexOf(".") + 1)
|
|
188
|
+
: call.callee;
|
|
189
|
+
if (!SESSION_CONSTRUCTORS.has(leaf))
|
|
190
|
+
continue;
|
|
191
|
+
const root = call.callee.split(".")[0];
|
|
192
|
+
const module = call.callee.includes(".") ? root : moduleOfImport(root);
|
|
193
|
+
const resolved = module === undefined ? undefined : moduleOfImport(root) ?? module;
|
|
194
|
+
if (resolved === undefined || !CLIENT_MODULES.has(resolved.split(".")[0]))
|
|
195
|
+
continue;
|
|
196
|
+
const local = localOfCall(call);
|
|
197
|
+
if (local !== undefined)
|
|
198
|
+
locals.set(local, resolved.split(".")[0]);
|
|
199
|
+
}
|
|
200
|
+
return locals;
|
|
201
|
+
}
|
|
202
|
+
/** Every HTTP call written in one file, and every one refused. */
|
|
203
|
+
export function clientCallsIn(file, calls, moduleOfImport, clientLocals,
|
|
204
|
+
/** Module-scope `NAME = "literal"` in this file, for base resolution. */
|
|
205
|
+
constants = new Map(),
|
|
206
|
+
/** `scope::name` -> source text, for a path assembled a few lines earlier. */
|
|
207
|
+
localStrings = new Map(),
|
|
208
|
+
/** `scope.join(".")` -> parameter names, for DEC-242's varies-per-call. */
|
|
209
|
+
functionParams = new Map()) {
|
|
210
|
+
const found = [];
|
|
211
|
+
const refusals = [];
|
|
212
|
+
for (const call of calls) {
|
|
213
|
+
const receiver = call.receiver;
|
|
214
|
+
const attribute = call.attribute;
|
|
215
|
+
if (receiver === null || attribute === null)
|
|
216
|
+
continue;
|
|
217
|
+
// The receiver is either the package itself, or a local this file
|
|
218
|
+
// constructed a session on. Both are provenance; neither is a name.
|
|
219
|
+
const viaModule = CLIENT_MODULES.has(moduleOfImport(receiver)?.split(".")[0] ?? "")
|
|
220
|
+
? moduleOfImport(receiver).split(".")[0]
|
|
221
|
+
: undefined;
|
|
222
|
+
const viaLocal = clientLocals.get(receiver);
|
|
223
|
+
const package_ = viaModule ?? viaLocal;
|
|
224
|
+
if (package_ === undefined)
|
|
225
|
+
continue;
|
|
226
|
+
const paramsHere = functionParams.get(call.scope.join(".")) ?? new Set();
|
|
227
|
+
let method = CLIENT_VERBS.get(attribute);
|
|
228
|
+
let pathIndex = 0;
|
|
229
|
+
if (method === undefined) {
|
|
230
|
+
// `requests.request("POST", url)` — the verb is the first argument, and
|
|
231
|
+
// it is a fact only when it is written as a literal.
|
|
232
|
+
if (attribute !== "request")
|
|
233
|
+
continue;
|
|
234
|
+
const verb = call.hasLiterals[0] === true ? String(call.literals[0]) : undefined;
|
|
235
|
+
if (verb === undefined || !CLIENT_VERBS.has(verb.toLowerCase())) {
|
|
236
|
+
refusals.push({
|
|
237
|
+
file,
|
|
238
|
+
line: call.line,
|
|
239
|
+
rawTarget: `${receiver}.${attribute}`,
|
|
240
|
+
reason: "callMethodNotLiteral",
|
|
241
|
+
// The verb, not a value — never a per-call or environment fact.
|
|
242
|
+
...CAPABILITY_GAP,
|
|
243
|
+
});
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
method = verb.toUpperCase();
|
|
247
|
+
pathIndex = 1;
|
|
248
|
+
}
|
|
249
|
+
const rawPath = call.args[pathIndex];
|
|
250
|
+
if (rawPath === undefined)
|
|
251
|
+
continue;
|
|
252
|
+
let path;
|
|
253
|
+
// `url = f"{BASE}/orders"` two lines above `requests.get(url)`. One hop and
|
|
254
|
+
// only one: the local must be bound **in the same scope**, because two
|
|
255
|
+
// functions in one file each binding `url` are two different paths and
|
|
256
|
+
// merging them attaches a caller to an endpoint it never reaches. A local
|
|
257
|
+
// bound twice in one scope is not resolved either — the last write wins in
|
|
258
|
+
// this map and that is a guess, so the shape is left to the ledger.
|
|
259
|
+
const source = call.hasLiterals[pathIndex] === true
|
|
260
|
+
? undefined
|
|
261
|
+
: localStrings.get(`${call.scope.join(".")}::${rawPath.trim()}`);
|
|
262
|
+
const written = source ?? rawPath;
|
|
263
|
+
if (call.hasLiterals[pathIndex] === true) {
|
|
264
|
+
path = String(call.literals[pathIndex]);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// **The assembly is tried before the plain template, and the order is the
|
|
268
|
+
// whole fix.** `templateOfSource` happily reads
|
|
269
|
+
// `f"{BASE}/orders/{id}"` as the template `{param}/orders/{param}`, which
|
|
270
|
+
// then fails the leading-slash test and files as *"an absolute URL"* — a
|
|
271
|
+
// refusal that is not merely wrong but points at the wrong work. Every one
|
|
272
|
+
// of the 78 base-carrying calls in the reference set landed there.
|
|
273
|
+
const assembly = assemblyOfSource(written);
|
|
274
|
+
if (assembly !== undefined) {
|
|
275
|
+
const value = constants.get(assembly.base);
|
|
276
|
+
const resolved = value === undefined ? undefined : pathOfBase(value);
|
|
277
|
+
if (resolved === undefined) {
|
|
278
|
+
refusals.push({
|
|
279
|
+
file,
|
|
280
|
+
line: call.line,
|
|
281
|
+
rawTarget: assembly.base,
|
|
282
|
+
reason: "callBaseUnresolved",
|
|
283
|
+
...diagnoseArgument(assembly.base, paramsHere),
|
|
284
|
+
});
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
if (resolved.external) {
|
|
288
|
+
// DEC-014 makes an endpoint fileless and host-free, so `/v1/charges`
|
|
289
|
+
// on Stripe and `/v1/charges` here are one node. Claiming it would
|
|
290
|
+
// join this caller to a local route it never reaches.
|
|
291
|
+
refusals.push({
|
|
292
|
+
file,
|
|
293
|
+
line: call.line,
|
|
294
|
+
rawTarget: value,
|
|
295
|
+
reason: "callBaseIsExternalHost",
|
|
296
|
+
blockedBy: null,
|
|
297
|
+
refusalClass: "out-of-scope",
|
|
298
|
+
});
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
path = `${resolved.path}${assembly.suffix}`;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
path = templateOfSource(written);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (path === undefined) {
|
|
308
|
+
// A bare name gets its own row. 73 of the 283 calls in the reference set
|
|
309
|
+
// pass `url`, assembled a few lines earlier, and reading them as "not a
|
|
310
|
+
// literal" files them with genuinely opaque paths — one number for two
|
|
311
|
+
// populations, only one of which is reachable by further work.
|
|
312
|
+
const bare = /^[A-Za-z_][A-Za-z0-9_.]*$/.test(rawPath.trim());
|
|
313
|
+
refusals.push({
|
|
314
|
+
file,
|
|
315
|
+
line: call.line,
|
|
316
|
+
rawTarget: bare ? rawPath.trim() : `${receiver}.${attribute}`,
|
|
317
|
+
reason: bare ? "callPathFromLocal" : "callPathNotLiteral",
|
|
318
|
+
...diagnoseArgument(rawPath.trim(), paramsHere),
|
|
319
|
+
});
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (!path.startsWith("/")) {
|
|
323
|
+
// An absolute URL names a host this repository does not serve, so there
|
|
324
|
+
// is no route on this side of the join for it to meet.
|
|
325
|
+
refusals.push({
|
|
326
|
+
file,
|
|
327
|
+
line: call.line,
|
|
328
|
+
rawTarget: path,
|
|
329
|
+
reason: "callPathNotRelative",
|
|
330
|
+
blockedBy: null,
|
|
331
|
+
refusalClass: "out-of-scope",
|
|
332
|
+
});
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
found.push({
|
|
336
|
+
file,
|
|
337
|
+
line: call.line,
|
|
338
|
+
method,
|
|
339
|
+
path,
|
|
340
|
+
rawPath,
|
|
341
|
+
scope: call.scope,
|
|
342
|
+
client: `${receiver}.${attribute}`,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return { calls: found, refusals };
|
|
346
|
+
}
|
|
347
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;AAE7F,uEAAuE;AACvE,MAAM,CAAC,MAAM,oBAAoB,GAAwB,IAAI,GAAG,CAAC;IAC/D,SAAS;IACT,QAAQ;IACR,aAAa;IACb,eAAe;CAChB,CAAC,CAAC;AAEH,qDAAqD;AACrD,MAAM,CAAC,MAAM,YAAY,GAAgC,IAAI,GAAG,CAAC;IAC/D,CAAC,KAAK,EAAE,KAAK,CAAC;IACd,CAAC,MAAM,EAAE,MAAM,CAAC;IAChB,CAAC,KAAK,EAAE,KAAK,CAAC;IACd,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,MAAM,EAAE,MAAM,CAAC;IAChB,CAAC,SAAS,EAAE,SAAS,CAAC;CACvB,CAAC,CAAC;AA8DH,MAAM,cAAc,GAAkB,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAC1F,MAAM,eAAe,GAAkB,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAE5F,oFAAoF;AACpF,MAAM,iBAAiB,GAAG;IACxB,uCAAuC;IACvC,2CAA2C;IAC3C,qCAAqC;CACtC,CAAC;AAEF,wFAAwF;AACxF,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAC5D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,eAAe,CAAC;IAC/C,IAAI,qDAAqD,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IACrF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,SAAS,gBAAgB,CAAC,IAAwB,EAAE,cAAmC;IACrF,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,cAAc,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,0BAA0B,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,eAAe,CAAC;IACpG,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC/D,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;IAC/D,CAAC;IACD,OAAO,EAAE,GAAG,cAAc,EAAE,YAAY,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;AACtE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,8CAA8C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACvB,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACnE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IACzC,6EAA6E;IAC7E,+DAA+D;IAC/D,IAAI,IAAI,KAAK,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxD,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAC7D,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAClE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,kFAAkF;AAClF,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACtF,MAAM,GAAG,GAAG,qCAAqC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9D,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACnC,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACrE,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,2DAA2D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrF,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACvB,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACpB,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtC,GAAG,IAAI,GAAG,CAAC;YACX,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACtC,GAAG,IAAI,GAAG,CAAC;YACX,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACnC,IAAI,KAAK,GAAG,CAAC;gBAAE,OAAO,SAAS,CAAC;YAChC,GAAG,IAAI,SAAS,CAAC;YACjB,CAAC,GAAG,KAAK,CAAC;YACV,SAAS;QACX,CAAC;QACD,GAAG,IAAI,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAwB,EACxB,WAAiD,EACjD,cAAoD;IAEpD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;YAAE,SAAS;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;YACpC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACrD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAChB,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,QAAQ,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;QACnF,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;YAAE,SAAS;QACrF,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,KAAwB,EACxB,cAAoD,EACpD,YAAyC;AACzC,yEAAyE;AACzE,YAAyC,IAAI,GAAG,EAAE;AAClD,8EAA8E;AAC9E,eAA4C,IAAI,GAAG,EAAE;AACrD,2EAA2E;AAC3E,iBAA2D,IAAI,GAAG,EAAE;IAEpE,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAoB,EAAE,CAAC;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QACjC,IAAI,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;YAAE,SAAS;QAEtD,kEAAkE;QAClE,oEAAoE;QACpE,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACjF,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE;YAC1C,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,SAAS,IAAI,QAAQ,CAAC;QACvC,IAAI,QAAQ,KAAK,SAAS;YAAE,SAAS;QAErC,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAEjF,IAAI,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,wEAAwE;YACxE,qDAAqD;YACrD,IAAI,SAAS,KAAK,SAAS;gBAAE,SAAS;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACjF,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBAChE,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI;oBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,SAAS,EAAE,GAAG,QAAQ,IAAI,SAAS,EAAE;oBACrC,MAAM,EAAE,sBAAsB;oBAC9B,gEAAgE;oBAChE,GAAG,cAAc;iBAClB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YACD,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YAC5B,SAAS,GAAG,CAAC,CAAC;QAChB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACrC,IAAI,OAAO,KAAK,SAAS;YAAE,SAAS;QAEpC,IAAI,IAAwB,CAAC;QAC7B,4EAA4E;QAC5E,uEAAuE;QACvE,uEAAuE;QACvE,0EAA0E;QAC1E,2EAA2E;QAC3E,oEAAoE;QACpE,MAAM,MAAM,GACV,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,IAAI;YAClC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,OAAO,GAAG,MAAM,IAAI,OAAO,CAAC;QAElC,IAAI,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC;YACzC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,0EAA0E;YAC1E,gDAAgD;YAChD,0EAA0E;YAC1E,yEAAyE;YACzE,2EAA2E;YAC3E,mEAAmE;YACnE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAC3C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBAC3C,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBACrE,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI;wBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,SAAS,EAAE,QAAQ,CAAC,IAAI;wBACxB,MAAM,EAAE,oBAAoB;wBAC5B,GAAG,gBAAgB,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;qBAC/C,CAAC,CAAC;oBACH,SAAS;gBACX,CAAC;gBACD,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;oBACtB,qEAAqE;oBACrE,mEAAmE;oBACnE,sDAAsD;oBACtD,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI;wBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,SAAS,EAAE,KAAM;wBACjB,MAAM,EAAE,wBAAwB;wBAChC,SAAS,EAAE,IAAI;wBACf,YAAY,EAAE,cAAc;qBAC7B,CAAC,CAAC;oBACH,SAAS;gBACX,CAAC;gBACD,IAAI,GAAG,GAAG,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC;YAC9C,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAED,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,yEAAyE;YACzE,wEAAwE;YACxE,uEAAuE;YACvE,+DAA+D;YAC/D,MAAM,IAAI,GAAG,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9D,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,SAAS,EAAE;gBAC7D,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,oBAAoB;gBACzD,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,UAAU,CAAC;aAChD,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,wEAAwE;YACxE,uDAAuD;YACvD,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,qBAAqB;gBAC7B,SAAS,EAAE,IAAI;gBACf,YAAY,EAAE,cAAc;aAC7B,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,KAAK,CAAC,IAAI,CAAC;YACT,IAAI;YACJ,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM;YACN,IAAI;YACJ,OAAO;YACP,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,GAAG,QAAQ,IAAI,SAAS,EAAE;SACnC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACpC,CAAC"}
|