@cat-factory/contracts 0.336.0 → 0.337.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/environment-reachability.d.ts +246 -0
- package/dist/environment-reachability.d.ts.map +1 -0
- package/dist/environment-reachability.js +248 -0
- package/dist/environment-reachability.js.map +1 -0
- package/dist/environments.d.ts +276 -1
- package/dist/environments.d.ts.map +1 -1
- package/dist/environments.js +35 -0
- package/dist/environments.js.map +1 -1
- package/dist/execution.d.ts +1 -0
- package/dist/execution.d.ts.map +1 -1
- package/dist/frontend.d.ts +52 -1
- package/dist/frontend.d.ts.map +1 -1
- package/dist/frontend.js +69 -9
- package/dist/frontend.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/routes/agent-runs.d.ts +2 -0
- package/dist/routes/agent-runs.d.ts.map +1 -1
- package/dist/routes/bug-hunt.d.ts +2 -0
- package/dist/routes/bug-hunt.d.ts.map +1 -1
- package/dist/routes/environmentUserHandlers.d.ts +6 -0
- package/dist/routes/environmentUserHandlers.d.ts.map +1 -1
- package/dist/routes/environments.d.ts +105 -0
- package/dist/routes/environments.d.ts.map +1 -1
- package/dist/routes/execution.d.ts +10 -0
- package/dist/routes/execution.d.ts.map +1 -1
- package/dist/routes/human-review.d.ts +1 -0
- package/dist/routes/human-review.d.ts.map +1 -1
- package/dist/routes/human-test.d.ts +5 -0
- package/dist/routes/human-test.d.ts.map +1 -1
- package/dist/routes/visual-confirm.d.ts +3 -0
- package/dist/routes/visual-confirm.d.ts.map +1 -1
- package/dist/routes/workspaces.d.ts +2 -0
- package/dist/routes/workspaces.d.ts.map +1 -1
- package/dist/snapshot.d.ts +1 -0
- package/dist/snapshot.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
/**
|
|
3
|
+
* One address a provider states will carry traffic for its environment's URL host.
|
|
4
|
+
*
|
|
5
|
+
* The motivating shape is an org running per-PR preview environments whose per-environment DNS
|
|
6
|
+
* record lives in an internal view while the load balancers fronting it are ordinary names and
|
|
7
|
+
* the ingress routes on the `Host` header. The address exists and a container's egress reaches
|
|
8
|
+
* it. The only missing thing is a name-to-address mapping, which is exactly what a hosts-file
|
|
9
|
+
* entry (or a Kubernetes `hostAliases` entry) is.
|
|
10
|
+
*
|
|
11
|
+
* `label` is for the human reading a diagnostic ("internal ALB", "public ALB"), never for
|
|
12
|
+
* matching: the platform picks by PROBING, never by name.
|
|
13
|
+
*/
|
|
14
|
+
export declare const environmentAddressSchema: v.ObjectSchema<{
|
|
15
|
+
/** An IP literal. Never a name: a name would just be the lookup that already failed. */
|
|
16
|
+
readonly address: v.StringSchema<undefined>;
|
|
17
|
+
/** What this address IS, for the diagnostic. Never load-bearing. */
|
|
18
|
+
readonly label: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
19
|
+
}, undefined>;
|
|
20
|
+
export type EnvironmentAddress = v.InferOutput<typeof environmentAddressSchema>;
|
|
21
|
+
/**
|
|
22
|
+
* Why a `ready` environment could not be reached, at the layer the platform can observe.
|
|
23
|
+
*
|
|
24
|
+
* Separate members rather than one "unreachable" because they need different reactions and name
|
|
25
|
+
* different owners: `name_unresolved` with candidates that also failed is an environment nobody
|
|
26
|
+
* can reach, `no_candidate` is a PROVIDER that never told us where the thing lives, and
|
|
27
|
+
* `connection_refused` is a route that carries to a box with nothing listening, which is the
|
|
28
|
+
* deployed workload rather than the network.
|
|
29
|
+
*
|
|
30
|
+
* - `no_candidate` the environment carries no URL, or one with no host to probe. There
|
|
31
|
+
* was nothing to try, which is not the same as trying and failing.
|
|
32
|
+
* - `name_unresolved` the URL's host resolved nowhere, and no stated address carried either.
|
|
33
|
+
* - `no_route` something resolved and the connect never completed (timeout,
|
|
34
|
+
* host/network unreachable). The expensive failure: a lookup that
|
|
35
|
+
* worked followed by a connect that hangs.
|
|
36
|
+
* - `connection_refused` the route carries and nothing is listening on the port.
|
|
37
|
+
* - `address_refused` the provider stated an address the platform will not dial: loopback,
|
|
38
|
+
* link-local/vendor metadata, or a non-canonical literal. Recorded as an
|
|
39
|
+
* attempt rather than dropped, because a refused input is an omission the
|
|
40
|
+
* operator has to be able to see.
|
|
41
|
+
* - `probe_failed` the probe itself errored in a way it could not classify. Kept apart
|
|
42
|
+
* from the three above so "we could not tell" never renders as a
|
|
43
|
+
* verdict about the environment.
|
|
44
|
+
*/
|
|
45
|
+
export declare const environmentUnreachableReasonSchema: v.PicklistSchema<["no_candidate", "name_unresolved", "no_route", "connection_refused", "address_refused", "probe_failed"], undefined>;
|
|
46
|
+
export type EnvironmentUnreachableReason = v.InferOutput<typeof environmentUnreachableReasonSchema>;
|
|
47
|
+
/** One target the proof tried, in the order it was tried, and what came back. */
|
|
48
|
+
export declare const environmentRouteAttemptSchema: v.ObjectSchema<{
|
|
49
|
+
/** `host:port` for the name itself, `host@address:port` for a stated address. */
|
|
50
|
+
readonly target: v.StringSchema<undefined>;
|
|
51
|
+
/** `carried`, or the {@link EnvironmentUnreachableReason} that target produced. */
|
|
52
|
+
readonly outcome: v.StringSchema<undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* What the probe said when it could not classify its own failure, capped for a rendered
|
|
55
|
+
* surface. The ONLY field carrying WHY a `probe_failed` attempt failed.
|
|
56
|
+
*
|
|
57
|
+
* Kept because `probe_failed` names no layer by design, so without this an operator reading a
|
|
58
|
+
* proof cannot tell a TLS or resolver fault from a runtime restriction from a bug in the probe:
|
|
59
|
+
* the three need different fixes and the reason renders identically for all of them. Absent for
|
|
60
|
+
* every other outcome, which is already self-describing.
|
|
61
|
+
*/
|
|
62
|
+
readonly detail: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
63
|
+
}, undefined>;
|
|
64
|
+
export type EnvironmentRouteAttempt = v.InferOutput<typeof environmentRouteAttemptSchema>;
|
|
65
|
+
/**
|
|
66
|
+
* What the platform PROVED about reaching an environment, once, at the moment it went `ready`.
|
|
67
|
+
*
|
|
68
|
+
* `state` splits along TWO axes and both are load-bearing. `reached` and `not_reached` are
|
|
69
|
+
* verdicts about the ENVIRONMENT, and only `not_reached` fails a deployer frame. `inconclusive`
|
|
70
|
+
* and `unproved` are verdicts about the PLATFORM, and neither may ever fail anything: collapsing
|
|
71
|
+
* either into `not_reached` turns a diagnostic into a second way for a healthy deploy to die,
|
|
72
|
+
* which is the one failure mode this whole module must not introduce, and collapsing either into
|
|
73
|
+
* `reached` hands a tester the unbacked claim it exists to retire.
|
|
74
|
+
*
|
|
75
|
+
* - `inconclusive` the platform LOOKED and established nothing either way: a probe that
|
|
76
|
+
* errored in a way it could not classify, or an environment with no address to
|
|
77
|
+
* dial. Narrated, because "we could not tell" is exactly the fact that stops an
|
|
78
|
+
* agent concluding the environment is dead.
|
|
79
|
+
* - `unproved` nothing was wired to open a socket, so nothing was tried. SILENT (see
|
|
80
|
+
* {@link reachabilityNote}): it is the standing state of every deployment with
|
|
81
|
+
* no prober, and a line on every prompt is a line nobody reads on the one
|
|
82
|
+
* prompt where it matters.
|
|
83
|
+
*/
|
|
84
|
+
export declare const environmentRouteProofSchema: v.ObjectSchema<{
|
|
85
|
+
readonly state: v.PicklistSchema<["reached", "not_reached", "inconclusive", "unproved"], undefined>;
|
|
86
|
+
/**
|
|
87
|
+
* The stated address that CARRIED, or null when the URL's own name carried (the ordinary case)
|
|
88
|
+
* and when nothing carried at all. Read `state` to tell those two apart.
|
|
89
|
+
*
|
|
90
|
+
* This is the field a container bridge is built from, which is why the proof publishes the
|
|
91
|
+
* candidate that carried rather than the first that resolved: a bridge built from an unproved
|
|
92
|
+
* address is recorded as successfully applied while the tester still fails, and the evidence
|
|
93
|
+
* then points further from the cause than no bridge at all did.
|
|
94
|
+
*/
|
|
95
|
+
readonly via: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
96
|
+
/**
|
|
97
|
+
* The {@link EnvironmentUnreachableReason} when `state` is `not_reached` or `inconclusive`,
|
|
98
|
+
* else null. An open string on the wire so a stored proof written by an older build never fails
|
|
99
|
+
* to parse; readers that branch on it treat an unknown value as "not one of the cases I handle".
|
|
100
|
+
*/
|
|
101
|
+
readonly reason: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
102
|
+
/** Every target tried, in order. Recorded whether or not one carried. */
|
|
103
|
+
readonly attempts: v.ArraySchema<v.ObjectSchema<{
|
|
104
|
+
/** `host:port` for the name itself, `host@address:port` for a stated address. */
|
|
105
|
+
readonly target: v.StringSchema<undefined>;
|
|
106
|
+
/** `carried`, or the {@link EnvironmentUnreachableReason} that target produced. */
|
|
107
|
+
readonly outcome: v.StringSchema<undefined>;
|
|
108
|
+
/**
|
|
109
|
+
* What the probe said when it could not classify its own failure, capped for a rendered
|
|
110
|
+
* surface. The ONLY field carrying WHY a `probe_failed` attempt failed.
|
|
111
|
+
*
|
|
112
|
+
* Kept because `probe_failed` names no layer by design, so without this an operator reading a
|
|
113
|
+
* proof cannot tell a TLS or resolver fault from a runtime restriction from a bug in the probe:
|
|
114
|
+
* the three need different fixes and the reason renders identically for all of them. Absent for
|
|
115
|
+
* every other outcome, which is already self-describing.
|
|
116
|
+
*/
|
|
117
|
+
readonly detail: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
118
|
+
}, undefined>, undefined>;
|
|
119
|
+
/** When the proof ran (epoch ms). */
|
|
120
|
+
readonly checkedAt: v.NumberSchema<undefined>;
|
|
121
|
+
}, undefined>;
|
|
122
|
+
export type EnvironmentRouteProof = v.InferOutput<typeof environmentRouteProofSchema>;
|
|
123
|
+
/**
|
|
124
|
+
* Everything the platform knows about ADDRESSING an environment, beside the one string it knows
|
|
125
|
+
* about naming it.
|
|
126
|
+
*
|
|
127
|
+
* Two halves because they come from two places and one of them is a claim: `candidates` is what
|
|
128
|
+
* the PROVIDER said, `proof` is what the platform TRIED. Keeping the claim after the proof runs
|
|
129
|
+
* is deliberate: an operator debugging a dead environment wants to see which addresses were
|
|
130
|
+
* offered as well as which were reached, and a re-probe on a later poll re-reads the same claim.
|
|
131
|
+
*/
|
|
132
|
+
export declare const environmentReachabilitySchema: v.ObjectSchema<{
|
|
133
|
+
/** Addresses the provider states carry traffic for the URL's host, in ITS preference order. */
|
|
134
|
+
readonly candidates: v.ArraySchema<v.ObjectSchema<{
|
|
135
|
+
/** An IP literal. Never a name: a name would just be the lookup that already failed. */
|
|
136
|
+
readonly address: v.StringSchema<undefined>;
|
|
137
|
+
/** What this address IS, for the diagnostic. Never load-bearing. */
|
|
138
|
+
readonly label: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
139
|
+
}, undefined>, undefined>;
|
|
140
|
+
/** What proving found, or null when nothing has probed this environment yet. */
|
|
141
|
+
readonly proof: v.NullableSchema<v.ObjectSchema<{
|
|
142
|
+
readonly state: v.PicklistSchema<["reached", "not_reached", "inconclusive", "unproved"], undefined>;
|
|
143
|
+
/**
|
|
144
|
+
* The stated address that CARRIED, or null when the URL's own name carried (the ordinary case)
|
|
145
|
+
* and when nothing carried at all. Read `state` to tell those two apart.
|
|
146
|
+
*
|
|
147
|
+
* This is the field a container bridge is built from, which is why the proof publishes the
|
|
148
|
+
* candidate that carried rather than the first that resolved: a bridge built from an unproved
|
|
149
|
+
* address is recorded as successfully applied while the tester still fails, and the evidence
|
|
150
|
+
* then points further from the cause than no bridge at all did.
|
|
151
|
+
*/
|
|
152
|
+
readonly via: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
153
|
+
/**
|
|
154
|
+
* The {@link EnvironmentUnreachableReason} when `state` is `not_reached` or `inconclusive`,
|
|
155
|
+
* else null. An open string on the wire so a stored proof written by an older build never fails
|
|
156
|
+
* to parse; readers that branch on it treat an unknown value as "not one of the cases I handle".
|
|
157
|
+
*/
|
|
158
|
+
readonly reason: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
159
|
+
/** Every target tried, in order. Recorded whether or not one carried. */
|
|
160
|
+
readonly attempts: v.ArraySchema<v.ObjectSchema<{
|
|
161
|
+
/** `host:port` for the name itself, `host@address:port` for a stated address. */
|
|
162
|
+
readonly target: v.StringSchema<undefined>;
|
|
163
|
+
/** `carried`, or the {@link EnvironmentUnreachableReason} that target produced. */
|
|
164
|
+
readonly outcome: v.StringSchema<undefined>;
|
|
165
|
+
/**
|
|
166
|
+
* What the probe said when it could not classify its own failure, capped for a rendered
|
|
167
|
+
* surface. The ONLY field carrying WHY a `probe_failed` attempt failed.
|
|
168
|
+
*
|
|
169
|
+
* Kept because `probe_failed` names no layer by design, so without this an operator reading a
|
|
170
|
+
* proof cannot tell a TLS or resolver fault from a runtime restriction from a bug in the probe:
|
|
171
|
+
* the three need different fixes and the reason renders identically for all of them. Absent for
|
|
172
|
+
* every other outcome, which is already self-describing.
|
|
173
|
+
*/
|
|
174
|
+
readonly detail: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
175
|
+
}, undefined>, undefined>;
|
|
176
|
+
/** When the proof ran (epoch ms). */
|
|
177
|
+
readonly checkedAt: v.NumberSchema<undefined>;
|
|
178
|
+
}, undefined>, undefined>;
|
|
179
|
+
}, undefined>;
|
|
180
|
+
export type EnvironmentReachability = v.InferOutput<typeof environmentReachabilitySchema>;
|
|
181
|
+
/**
|
|
182
|
+
* The reachability facts an agent (or a container dispatch) is handed for ONE environment it is
|
|
183
|
+
* being pointed at.
|
|
184
|
+
*
|
|
185
|
+
* A flattened projection rather than the stored shape, because the reader's question is narrower
|
|
186
|
+
* than the operator's: it needs the address it may dial and the layer that failed, not the
|
|
187
|
+
* provider's full candidate list. `state: 'reached'` with no `address` means the name itself
|
|
188
|
+
* carried, which is the case that needs no narration at all.
|
|
189
|
+
*/
|
|
190
|
+
export interface EnvironmentReachabilityNote {
|
|
191
|
+
/**
|
|
192
|
+
* Deliberately NOT the proof's full state union: an `unproved` note is unrepresentable, because
|
|
193
|
+
* the projection withholds it (see {@link reachabilityNote}). A reader that branched on
|
|
194
|
+
* `'unproved'` here would be writing a case its input can never hold.
|
|
195
|
+
*/
|
|
196
|
+
state: Exclude<EnvironmentRouteProof['state'], 'unproved'>;
|
|
197
|
+
/** The address that carried, when the name did not. */
|
|
198
|
+
address?: string;
|
|
199
|
+
reason?: string;
|
|
200
|
+
/** What a `probe_failed` attempt said, when one did. See the attempt's own `detail`. */
|
|
201
|
+
detail?: string;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Project a stored {@link EnvironmentReachability} onto the note an agent or a dispatch reads, or
|
|
205
|
+
* undefined when there is nothing to say.
|
|
206
|
+
*
|
|
207
|
+
* Undefined in two cases, and they are the same fact from two directions: no proof has been
|
|
208
|
+
* written, and a proof recording that nothing was WIRED to probe. "Nothing has looked" is the
|
|
209
|
+
* ordinary state of an environment mid-provision AND the permanent state of a deployment with no
|
|
210
|
+
* prober, so narrating it would put an unverified-reachability warning on every prompt of such a
|
|
211
|
+
* deployment and train a reader to skip the section that matters. The row still records the
|
|
212
|
+
* `unproved` proof, because when the probe ran is a fact an operator reads off the environment.
|
|
213
|
+
*/
|
|
214
|
+
export declare function reachabilityNote(reachability: EnvironmentReachability | null | undefined): EnvironmentReachabilityNote | undefined;
|
|
215
|
+
/** Where an environment URL is dialled: its host, the port, and the scheme it names. */
|
|
216
|
+
export interface EnvironmentCoordinates {
|
|
217
|
+
host: string;
|
|
218
|
+
/** Explicit from the URL, else the scheme default (443/80), else null: nothing to dial. */
|
|
219
|
+
port: number | null;
|
|
220
|
+
/** URL scheme without the trailing colon (e.g. `https`). */
|
|
221
|
+
scheme: string;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Derive the coordinates of an environment URL, or null when there is no URL or it does not parse.
|
|
225
|
+
*
|
|
226
|
+
* ONE deriver, here rather than beside either of its two readers, because they have to agree about
|
|
227
|
+
* the same string and the divergence is not cosmetic: the route proof DIALS what this returns and
|
|
228
|
+
* the Tester prompt states it to the agent as the environment's Host / Port / Scheme. Two parsers
|
|
229
|
+
* mean an agent told to dial coordinates the platform never probed, and the copies this replaced
|
|
230
|
+
* had already diverged on the one case that mattered (an unknown scheme became port `0` in one and
|
|
231
|
+
* `null` in the other, and `0` is what routed a non-http URL into a failed deploy).
|
|
232
|
+
*
|
|
233
|
+
* The parse is hand-rolled because contracts compiles against `lib: ["ES2022"]` with no DOM and no
|
|
234
|
+
* Node types, so `URL` is unavailable here exactly as it is in kernel, and this is the one package
|
|
235
|
+
* both readers can see. The three things a naive split gets wrong are handled: userinfo (whose
|
|
236
|
+
* password may itself contain `@`, so the LAST one separates it from the host), a bracketed IPv6
|
|
237
|
+
* literal (kept bracketed, which is what `URL.hostname` also returns), and an explicit port, where
|
|
238
|
+
* a MALFORMED one answers null rather than being dropped so a garbled URL cannot be silently
|
|
239
|
+
* dialled on the scheme default. Deliberately not attempted: IDNA/punycode and percent-decoding,
|
|
240
|
+
* neither of which an environment URL from a provider needs.
|
|
241
|
+
*
|
|
242
|
+
* An unknown scheme yields `port: null` (there is no default to invent), which every caller reads
|
|
243
|
+
* as "nothing to dial".
|
|
244
|
+
*/
|
|
245
|
+
export declare function deriveEnvironmentCoordinates(url: string | null | undefined): EnvironmentCoordinates | null;
|
|
246
|
+
//# sourceMappingURL=environment-reachability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-reachability.d.ts","sourceRoot":"","sources":["../src/environment-reachability.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB;IACnC,wFAAwF;;IAExF,oEAAoE;;aAEpE,CAAA;AACF,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,kCAAkC,uIAO7C,CAAA;AACF,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,kCAAkC,CAAC,CAAA;AAEnG,iFAAiF;AACjF,eAAO,MAAM,6BAA6B;IACxC,iFAAiF;;IAEjF,mFAAmF;;IAEnF;;;;;;;;OAQG;;aAEH,CAAA;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,2BAA2B;;IAEtC;;;;;;;;OAQG;;IAEH;;;;OAIG;;IAEH,yEAAyE;;QAtDzE,iFAAiF;;QAEjF,mFAAmF;;QAEnF;;;;;;;;WAQG;;;IA4CH,qCAAqC;;aAErC,CAAA;AACF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,2BAA2B,CAAC,CAAA;AAErF;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B;IACxC,+FAA+F;;QAlH/F,wFAAwF;;QAExF,oEAAoE;;;IAkHpE,gFAAgF;;;QAnChF;;;;;;;;WAQG;;QAEH;;;;WAIG;;QAEH,yEAAyE;;YAtDzE,iFAAiF;;YAEjF,mFAAmF;;YAEnF;;;;;;;;eAQG;;;QA4CH,qCAAqC;;;aAmBrC,CAAA;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF;;;;;;;;GAQG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,KAAK,EAAE,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,CAAA;IAC1D,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,GACvD,2BAA2B,GAAG,SAAS,CAUzC;AAED,wFAAwF;AACxF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,2FAA2F;IAC3F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAA;CACf;AAKD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B,sBAAsB,GAAG,IAAI,CAW/B"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// Whether a `ready` environment can actually be REACHED, as opposed to whether it was
|
|
2
|
+
// provisioned.
|
|
3
|
+
//
|
|
4
|
+
// These are two questions, and the platform only ever asked the first one. An environment
|
|
5
|
+
// carries one nullable `url`, so "reachable" has meant "a URL exists"; the tester is then handed
|
|
6
|
+
// a name, gets `curl` exit code 000, and reports the one hypothesis its own task makes salient,
|
|
7
|
+
// that the ENVIRONMENT is down. That reading is wrong often enough to be expensive: a name that
|
|
8
|
+
// does not resolve, a route that does not carry, and an application that answers 503 are three
|
|
9
|
+
// different faults with three different owners, and a connection failure renders all three
|
|
10
|
+
// identically.
|
|
11
|
+
//
|
|
12
|
+
// The vocabulary here is the platform's answer to the LOWER two layers. It is deliberately a
|
|
13
|
+
// SIBLING of `EnvironmentFailureReason` rather than an extension of it: that one is
|
|
14
|
+
// provisioning-scoped in every member and in its docstring (`cluster_unreachable` means the
|
|
15
|
+
// PROVIDER could not be reached), and a reaching failure against an environment the provider
|
|
16
|
+
// calls `ready` is a different question for a different audience. Only the one-word verdict the
|
|
17
|
+
// DEPLOYER settles on lives over there (`environment_unreachable`), because the deployer settles
|
|
18
|
+
// in that vocabulary.
|
|
19
|
+
import * as v from 'valibot';
|
|
20
|
+
/**
|
|
21
|
+
* One address a provider states will carry traffic for its environment's URL host.
|
|
22
|
+
*
|
|
23
|
+
* The motivating shape is an org running per-PR preview environments whose per-environment DNS
|
|
24
|
+
* record lives in an internal view while the load balancers fronting it are ordinary names and
|
|
25
|
+
* the ingress routes on the `Host` header. The address exists and a container's egress reaches
|
|
26
|
+
* it. The only missing thing is a name-to-address mapping, which is exactly what a hosts-file
|
|
27
|
+
* entry (or a Kubernetes `hostAliases` entry) is.
|
|
28
|
+
*
|
|
29
|
+
* `label` is for the human reading a diagnostic ("internal ALB", "public ALB"), never for
|
|
30
|
+
* matching: the platform picks by PROBING, never by name.
|
|
31
|
+
*/
|
|
32
|
+
export const environmentAddressSchema = v.object({
|
|
33
|
+
/** An IP literal. Never a name: a name would just be the lookup that already failed. */
|
|
34
|
+
address: v.string(),
|
|
35
|
+
/** What this address IS, for the diagnostic. Never load-bearing. */
|
|
36
|
+
label: v.optional(v.string()),
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Why a `ready` environment could not be reached, at the layer the platform can observe.
|
|
40
|
+
*
|
|
41
|
+
* Separate members rather than one "unreachable" because they need different reactions and name
|
|
42
|
+
* different owners: `name_unresolved` with candidates that also failed is an environment nobody
|
|
43
|
+
* can reach, `no_candidate` is a PROVIDER that never told us where the thing lives, and
|
|
44
|
+
* `connection_refused` is a route that carries to a box with nothing listening, which is the
|
|
45
|
+
* deployed workload rather than the network.
|
|
46
|
+
*
|
|
47
|
+
* - `no_candidate` the environment carries no URL, or one with no host to probe. There
|
|
48
|
+
* was nothing to try, which is not the same as trying and failing.
|
|
49
|
+
* - `name_unresolved` the URL's host resolved nowhere, and no stated address carried either.
|
|
50
|
+
* - `no_route` something resolved and the connect never completed (timeout,
|
|
51
|
+
* host/network unreachable). The expensive failure: a lookup that
|
|
52
|
+
* worked followed by a connect that hangs.
|
|
53
|
+
* - `connection_refused` the route carries and nothing is listening on the port.
|
|
54
|
+
* - `address_refused` the provider stated an address the platform will not dial: loopback,
|
|
55
|
+
* link-local/vendor metadata, or a non-canonical literal. Recorded as an
|
|
56
|
+
* attempt rather than dropped, because a refused input is an omission the
|
|
57
|
+
* operator has to be able to see.
|
|
58
|
+
* - `probe_failed` the probe itself errored in a way it could not classify. Kept apart
|
|
59
|
+
* from the three above so "we could not tell" never renders as a
|
|
60
|
+
* verdict about the environment.
|
|
61
|
+
*/
|
|
62
|
+
export const environmentUnreachableReasonSchema = v.picklist([
|
|
63
|
+
'no_candidate',
|
|
64
|
+
'name_unresolved',
|
|
65
|
+
'no_route',
|
|
66
|
+
'connection_refused',
|
|
67
|
+
'address_refused',
|
|
68
|
+
'probe_failed',
|
|
69
|
+
]);
|
|
70
|
+
/** One target the proof tried, in the order it was tried, and what came back. */
|
|
71
|
+
export const environmentRouteAttemptSchema = v.object({
|
|
72
|
+
/** `host:port` for the name itself, `host@address:port` for a stated address. */
|
|
73
|
+
target: v.string(),
|
|
74
|
+
/** `carried`, or the {@link EnvironmentUnreachableReason} that target produced. */
|
|
75
|
+
outcome: v.string(),
|
|
76
|
+
/**
|
|
77
|
+
* What the probe said when it could not classify its own failure, capped for a rendered
|
|
78
|
+
* surface. The ONLY field carrying WHY a `probe_failed` attempt failed.
|
|
79
|
+
*
|
|
80
|
+
* Kept because `probe_failed` names no layer by design, so without this an operator reading a
|
|
81
|
+
* proof cannot tell a TLS or resolver fault from a runtime restriction from a bug in the probe:
|
|
82
|
+
* the three need different fixes and the reason renders identically for all of them. Absent for
|
|
83
|
+
* every other outcome, which is already self-describing.
|
|
84
|
+
*/
|
|
85
|
+
detail: v.optional(v.string()),
|
|
86
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* What the platform PROVED about reaching an environment, once, at the moment it went `ready`.
|
|
89
|
+
*
|
|
90
|
+
* `state` splits along TWO axes and both are load-bearing. `reached` and `not_reached` are
|
|
91
|
+
* verdicts about the ENVIRONMENT, and only `not_reached` fails a deployer frame. `inconclusive`
|
|
92
|
+
* and `unproved` are verdicts about the PLATFORM, and neither may ever fail anything: collapsing
|
|
93
|
+
* either into `not_reached` turns a diagnostic into a second way for a healthy deploy to die,
|
|
94
|
+
* which is the one failure mode this whole module must not introduce, and collapsing either into
|
|
95
|
+
* `reached` hands a tester the unbacked claim it exists to retire.
|
|
96
|
+
*
|
|
97
|
+
* - `inconclusive` the platform LOOKED and established nothing either way: a probe that
|
|
98
|
+
* errored in a way it could not classify, or an environment with no address to
|
|
99
|
+
* dial. Narrated, because "we could not tell" is exactly the fact that stops an
|
|
100
|
+
* agent concluding the environment is dead.
|
|
101
|
+
* - `unproved` nothing was wired to open a socket, so nothing was tried. SILENT (see
|
|
102
|
+
* {@link reachabilityNote}): it is the standing state of every deployment with
|
|
103
|
+
* no prober, and a line on every prompt is a line nobody reads on the one
|
|
104
|
+
* prompt where it matters.
|
|
105
|
+
*/
|
|
106
|
+
export const environmentRouteProofSchema = v.object({
|
|
107
|
+
state: v.picklist(['reached', 'not_reached', 'inconclusive', 'unproved']),
|
|
108
|
+
/**
|
|
109
|
+
* The stated address that CARRIED, or null when the URL's own name carried (the ordinary case)
|
|
110
|
+
* and when nothing carried at all. Read `state` to tell those two apart.
|
|
111
|
+
*
|
|
112
|
+
* This is the field a container bridge is built from, which is why the proof publishes the
|
|
113
|
+
* candidate that carried rather than the first that resolved: a bridge built from an unproved
|
|
114
|
+
* address is recorded as successfully applied while the tester still fails, and the evidence
|
|
115
|
+
* then points further from the cause than no bridge at all did.
|
|
116
|
+
*/
|
|
117
|
+
via: v.nullable(v.string()),
|
|
118
|
+
/**
|
|
119
|
+
* The {@link EnvironmentUnreachableReason} when `state` is `not_reached` or `inconclusive`,
|
|
120
|
+
* else null. An open string on the wire so a stored proof written by an older build never fails
|
|
121
|
+
* to parse; readers that branch on it treat an unknown value as "not one of the cases I handle".
|
|
122
|
+
*/
|
|
123
|
+
reason: v.nullable(v.string()),
|
|
124
|
+
/** Every target tried, in order. Recorded whether or not one carried. */
|
|
125
|
+
attempts: v.array(environmentRouteAttemptSchema),
|
|
126
|
+
/** When the proof ran (epoch ms). */
|
|
127
|
+
checkedAt: v.number(),
|
|
128
|
+
});
|
|
129
|
+
/**
|
|
130
|
+
* Everything the platform knows about ADDRESSING an environment, beside the one string it knows
|
|
131
|
+
* about naming it.
|
|
132
|
+
*
|
|
133
|
+
* Two halves because they come from two places and one of them is a claim: `candidates` is what
|
|
134
|
+
* the PROVIDER said, `proof` is what the platform TRIED. Keeping the claim after the proof runs
|
|
135
|
+
* is deliberate: an operator debugging a dead environment wants to see which addresses were
|
|
136
|
+
* offered as well as which were reached, and a re-probe on a later poll re-reads the same claim.
|
|
137
|
+
*/
|
|
138
|
+
export const environmentReachabilitySchema = v.object({
|
|
139
|
+
/** Addresses the provider states carry traffic for the URL's host, in ITS preference order. */
|
|
140
|
+
candidates: v.array(environmentAddressSchema),
|
|
141
|
+
/** What proving found, or null when nothing has probed this environment yet. */
|
|
142
|
+
proof: v.nullable(environmentRouteProofSchema),
|
|
143
|
+
});
|
|
144
|
+
/**
|
|
145
|
+
* Project a stored {@link EnvironmentReachability} onto the note an agent or a dispatch reads, or
|
|
146
|
+
* undefined when there is nothing to say.
|
|
147
|
+
*
|
|
148
|
+
* Undefined in two cases, and they are the same fact from two directions: no proof has been
|
|
149
|
+
* written, and a proof recording that nothing was WIRED to probe. "Nothing has looked" is the
|
|
150
|
+
* ordinary state of an environment mid-provision AND the permanent state of a deployment with no
|
|
151
|
+
* prober, so narrating it would put an unverified-reachability warning on every prompt of such a
|
|
152
|
+
* deployment and train a reader to skip the section that matters. The row still records the
|
|
153
|
+
* `unproved` proof, because when the probe ran is a fact an operator reads off the environment.
|
|
154
|
+
*/
|
|
155
|
+
export function reachabilityNote(reachability) {
|
|
156
|
+
const proof = reachability?.proof;
|
|
157
|
+
if (!proof || proof.state === 'unproved')
|
|
158
|
+
return undefined;
|
|
159
|
+
const detail = proof.attempts.find((attempt) => attempt.detail)?.detail;
|
|
160
|
+
return {
|
|
161
|
+
state: proof.state,
|
|
162
|
+
...(proof.via ? { address: proof.via } : {}),
|
|
163
|
+
...(proof.reason ? { reason: proof.reason } : {}),
|
|
164
|
+
...(detail ? { detail } : {}),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** `scheme://` plus everything up to the path, query or fragment: the authority. */
|
|
168
|
+
const URL_AUTHORITY = /^([a-z][a-z0-9+.-]*):\/\/([^/?#]*)/i;
|
|
169
|
+
/**
|
|
170
|
+
* Derive the coordinates of an environment URL, or null when there is no URL or it does not parse.
|
|
171
|
+
*
|
|
172
|
+
* ONE deriver, here rather than beside either of its two readers, because they have to agree about
|
|
173
|
+
* the same string and the divergence is not cosmetic: the route proof DIALS what this returns and
|
|
174
|
+
* the Tester prompt states it to the agent as the environment's Host / Port / Scheme. Two parsers
|
|
175
|
+
* mean an agent told to dial coordinates the platform never probed, and the copies this replaced
|
|
176
|
+
* had already diverged on the one case that mattered (an unknown scheme became port `0` in one and
|
|
177
|
+
* `null` in the other, and `0` is what routed a non-http URL into a failed deploy).
|
|
178
|
+
*
|
|
179
|
+
* The parse is hand-rolled because contracts compiles against `lib: ["ES2022"]` with no DOM and no
|
|
180
|
+
* Node types, so `URL` is unavailable here exactly as it is in kernel, and this is the one package
|
|
181
|
+
* both readers can see. The three things a naive split gets wrong are handled: userinfo (whose
|
|
182
|
+
* password may itself contain `@`, so the LAST one separates it from the host), a bracketed IPv6
|
|
183
|
+
* literal (kept bracketed, which is what `URL.hostname` also returns), and an explicit port, where
|
|
184
|
+
* a MALFORMED one answers null rather than being dropped so a garbled URL cannot be silently
|
|
185
|
+
* dialled on the scheme default. Deliberately not attempted: IDNA/punycode and percent-decoding,
|
|
186
|
+
* neither of which an environment URL from a provider needs.
|
|
187
|
+
*
|
|
188
|
+
* An unknown scheme yields `port: null` (there is no default to invent), which every caller reads
|
|
189
|
+
* as "nothing to dial".
|
|
190
|
+
*/
|
|
191
|
+
export function deriveEnvironmentCoordinates(url) {
|
|
192
|
+
if (!url)
|
|
193
|
+
return null;
|
|
194
|
+
const match = URL_AUTHORITY.exec(url.trim());
|
|
195
|
+
if (!match)
|
|
196
|
+
return null;
|
|
197
|
+
const scheme = (match[1] ?? '').toLowerCase();
|
|
198
|
+
const authority = match[2] ?? '';
|
|
199
|
+
const hostPort = authority.slice(authority.lastIndexOf('@') + 1);
|
|
200
|
+
const split = splitHostPort(hostPort);
|
|
201
|
+
if (!split)
|
|
202
|
+
return null;
|
|
203
|
+
const port = split.port ?? defaultPortForScheme(scheme);
|
|
204
|
+
return { host: split.host, port, scheme };
|
|
205
|
+
}
|
|
206
|
+
/** The default port a scheme implies, or null when it implies none the platform knows. */
|
|
207
|
+
function defaultPortForScheme(scheme) {
|
|
208
|
+
return scheme === 'https' ? 443 : scheme === 'http' ? 80 : null;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Split an authority's `host[:port]` half, or null when it is not one.
|
|
212
|
+
*
|
|
213
|
+
* Null rather than a best guess for every malformed shape: an unclosed bracket, an unbracketed
|
|
214
|
+
* literal carrying several colons (an IPv6 address that a URL may not spell that way), and a port
|
|
215
|
+
* that is not a number in range. Each of those is a URL `new URL` would throw on, and answering
|
|
216
|
+
* with a host anyway is how a garbled URL gets dialled.
|
|
217
|
+
*/
|
|
218
|
+
function splitHostPort(hostPort) {
|
|
219
|
+
if (hostPort.startsWith('[')) {
|
|
220
|
+
const close = hostPort.indexOf(']');
|
|
221
|
+
if (close < 1)
|
|
222
|
+
return null;
|
|
223
|
+
const host = hostPort.slice(0, close + 1).toLowerCase();
|
|
224
|
+
const rest = hostPort.slice(close + 1);
|
|
225
|
+
if (rest === '')
|
|
226
|
+
return { host, port: null };
|
|
227
|
+
if (!rest.startsWith(':'))
|
|
228
|
+
return null;
|
|
229
|
+
const port = parsePort(rest.slice(1));
|
|
230
|
+
return port === null ? null : { host, port };
|
|
231
|
+
}
|
|
232
|
+
const colon = hostPort.indexOf(':');
|
|
233
|
+
if (colon < 0)
|
|
234
|
+
return hostPort ? { host: hostPort.toLowerCase(), port: null } : null;
|
|
235
|
+
if (hostPort.indexOf(':', colon + 1) >= 0)
|
|
236
|
+
return null;
|
|
237
|
+
const host = hostPort.slice(0, colon).toLowerCase();
|
|
238
|
+
const port = parsePort(hostPort.slice(colon + 1));
|
|
239
|
+
return host && port !== null ? { host, port } : null;
|
|
240
|
+
}
|
|
241
|
+
/** A port as written in a URL: digits, 1-65535. Null for anything else. */
|
|
242
|
+
function parsePort(raw) {
|
|
243
|
+
if (!/^\d{1,5}$/.test(raw))
|
|
244
|
+
return null;
|
|
245
|
+
const port = Number(raw);
|
|
246
|
+
return port >= 1 && port <= 65535 ? port : null;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=environment-reachability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-reachability.js","sourceRoot":"","sources":["../src/environment-reachability.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,eAAe;AACf,EAAE;AACF,0FAA0F;AAC1F,iGAAiG;AACjG,gGAAgG;AAChG,gGAAgG;AAChG,+FAA+F;AAC/F,2FAA2F;AAC3F,eAAe;AACf,EAAE;AACF,6FAA6F;AAC7F,oFAAoF;AACpF,4FAA4F;AAC5F,6FAA6F;AAC7F,gGAAgG;AAChG,iGAAiG;AACjG,sBAAsB;AAEtB,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,wFAAwF;IACxF,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,oEAAoE;IACpE,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC9B,CAAC,CAAA;AAGF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAC3D,cAAc;IACd,iBAAiB;IACjB,UAAU;IACV,oBAAoB;IACpB,iBAAiB;IACjB,cAAc;CACf,CAAC,CAAA;AAGF,iFAAiF;AACjF,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,iFAAiF;IACjF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,mFAAmF;IACnF,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB;;;;;;;;OAQG;IACH,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;CAC/B,CAAC,CAAA;AAGF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAClD,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC;IACzE;;;;;;;;OAQG;IACH,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC9B,yEAAyE;IACzE,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,6BAA6B,CAAC;IAChD,qCAAqC;IACrC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;CACtB,CAAC,CAAA;AAGF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,+FAA+F;IAC/F,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC;IAC7C,gFAAgF;IAChF,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,2BAA2B,CAAC;CAC/C,CAAC,CAAA;AA0BF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAAwD;IAExD,MAAM,KAAK,GAAG,YAAY,EAAE,KAAK,CAAA;IACjC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU;QAAE,OAAO,SAAS,CAAA;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACvE,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9B,CAAA;AACH,CAAC;AAWD,oFAAoF;AACpF,MAAM,aAAa,GAAG,qCAAqC,CAAA;AAE3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,4BAA4B,CAC1C,GAA8B;IAE9B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IACrB,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;IAC7C,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAChC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;IAChE,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IACrC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,oBAAoB,CAAC,MAAM,CAAC,CAAA;IACvD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;AAC3C,CAAC;AAED,0FAA0F;AAC1F,SAAS,oBAAoB,CAAC,MAAc;IAC1C,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACjE,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,QAAgB;IACrC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QACnC,IAAI,KAAK,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;QACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;QACtC,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;QAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAA;QACtC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QACrC,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC9C,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IACnC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IACpF,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACtD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;IACnD,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAA;IACjD,OAAO,IAAI,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AACtD,CAAC;AAED,2EAA2E;AAC3E,SAAS,SAAS,CAAC,GAAW;IAC5B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACxB,OAAO,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA;AACjD,CAAC"}
|