@gate-forge/pack-playwright 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/LICENSE +202 -0
- package/bin/gateforge-witness.js +40 -0
- package/dist/attestation/proxy.d.ts +40 -0
- package/dist/attestation/proxy.d.ts.map +1 -0
- package/dist/attestation/proxy.js +78 -0
- package/dist/attestation/proxy.js.map +1 -0
- package/dist/constants.d.ts +130 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +138 -0
- package/dist/constants.js.map +1 -0
- package/dist/discovery/adapters.d.ts +167 -0
- package/dist/discovery/adapters.d.ts.map +1 -0
- package/dist/discovery/adapters.js +276 -0
- package/dist/discovery/adapters.js.map +1 -0
- package/dist/discovery/discover.d.ts +57 -0
- package/dist/discovery/discover.d.ts.map +1 -0
- package/dist/discovery/discover.js +544 -0
- package/dist/discovery/discover.js.map +1 -0
- package/dist/discovery/index.d.ts +15 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +15 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/inference.d.ts +63 -0
- package/dist/discovery/inference.d.ts.map +1 -0
- package/dist/discovery/inference.js +173 -0
- package/dist/discovery/inference.js.map +1 -0
- package/dist/discovery/pytest-adapter.d.ts +205 -0
- package/dist/discovery/pytest-adapter.d.ts.map +1 -0
- package/dist/discovery/pytest-adapter.js +548 -0
- package/dist/discovery/pytest-adapter.js.map +1 -0
- package/dist/discovery/reconcile.d.ts +106 -0
- package/dist/discovery/reconcile.d.ts.map +1 -0
- package/dist/discovery/reconcile.js +268 -0
- package/dist/discovery/reconcile.js.map +1 -0
- package/dist/discovery/runner-env.d.ts +80 -0
- package/dist/discovery/runner-env.d.ts.map +1 -0
- package/dist/discovery/runner-env.js +139 -0
- package/dist/discovery/runner-env.js.map +1 -0
- package/dist/discovery/static-discovery.d.ts +121 -0
- package/dist/discovery/static-discovery.d.ts.map +1 -0
- package/dist/discovery/static-discovery.js +1028 -0
- package/dist/discovery/static-discovery.js.map +1 -0
- package/dist/discovery/supervised-run.d.ts +110 -0
- package/dist/discovery/supervised-run.d.ts.map +1 -0
- package/dist/discovery/supervised-run.js +328 -0
- package/dist/discovery/supervised-run.js.map +1 -0
- package/dist/discovery/trusted-config.d.ts +103 -0
- package/dist/discovery/trusted-config.d.ts.map +1 -0
- package/dist/discovery/trusted-config.js +117 -0
- package/dist/discovery/trusted-config.js.map +1 -0
- package/dist/fixture/evidence.d.ts +191 -0
- package/dist/fixture/evidence.d.ts.map +1 -0
- package/dist/fixture/evidence.js +366 -0
- package/dist/fixture/evidence.js.map +1 -0
- package/dist/fixture/fixture.d.ts +37 -0
- package/dist/fixture/fixture.d.ts.map +1 -0
- package/dist/fixture/fixture.js +38 -0
- package/dist/fixture/fixture.js.map +1 -0
- package/dist/fixture/witness-client.d.ts +164 -0
- package/dist/fixture/witness-client.d.ts.map +1 -0
- package/dist/fixture/witness-client.js +253 -0
- package/dist/fixture/witness-client.js.map +1 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +16 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +21 -0
- package/dist/json.js.map +1 -0
- package/dist/reporter/ledger.d.ts +105 -0
- package/dist/reporter/ledger.d.ts.map +1 -0
- package/dist/reporter/ledger.js +197 -0
- package/dist/reporter/ledger.js.map +1 -0
- package/dist/reporter/reporter.cjs +105 -0
- package/dist/reporter/reporter.cjs.map +1 -0
- package/dist/reporter/reporter.d.cts +35 -0
- package/dist/reporter/reporter.d.cts.map +1 -0
- package/dist/reporter/reporter.d.ts +167 -0
- package/dist/reporter/reporter.d.ts.map +1 -0
- package/dist/reporter/reporter.js +673 -0
- package/dist/reporter/reporter.js.map +1 -0
- package/dist/setup.d.ts +49 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +167 -0
- package/dist/setup.js.map +1 -0
- package/dist/supervisor/client.d.ts +73 -0
- package/dist/supervisor/client.d.ts.map +1 -0
- package/dist/supervisor/client.js +164 -0
- package/dist/supervisor/client.js.map +1 -0
- package/dist/supervisor/drain.d.ts +45 -0
- package/dist/supervisor/drain.d.ts.map +1 -0
- package/dist/supervisor/drain.js +235 -0
- package/dist/supervisor/drain.js.map +1 -0
- package/dist/supervisor/index.d.ts +16 -0
- package/dist/supervisor/index.d.ts.map +1 -0
- package/dist/supervisor/index.js +13 -0
- package/dist/supervisor/index.js.map +1 -0
- package/dist/supervisor/spool.d.ts +137 -0
- package/dist/supervisor/spool.d.ts.map +1 -0
- package/dist/supervisor/spool.js +263 -0
- package/dist/supervisor/spool.js.map +1 -0
- package/dist/surface.d.ts +134 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +139 -0
- package/dist/surface.js.map +1 -0
- package/dist/witness/adapter-registry.d.ts +38 -0
- package/dist/witness/adapter-registry.d.ts.map +1 -0
- package/dist/witness/adapter-registry.js +158 -0
- package/dist/witness/adapter-registry.js.map +1 -0
- package/dist/witness/bin.d.ts +21 -0
- package/dist/witness/bin.d.ts.map +1 -0
- package/dist/witness/bin.js +172 -0
- package/dist/witness/bin.js.map +1 -0
- package/dist/witness/browser.d.ts +173 -0
- package/dist/witness/browser.d.ts.map +1 -0
- package/dist/witness/browser.js +580 -0
- package/dist/witness/browser.js.map +1 -0
- package/dist/witness/classifications.d.ts +42 -0
- package/dist/witness/classifications.d.ts.map +1 -0
- package/dist/witness/classifications.js +87 -0
- package/dist/witness/classifications.js.map +1 -0
- package/dist/witness/env-attestation.d.ts +98 -0
- package/dist/witness/env-attestation.d.ts.map +1 -0
- package/dist/witness/env-attestation.js +202 -0
- package/dist/witness/env-attestation.js.map +1 -0
- package/dist/witness/loopback-pins.d.ts +79 -0
- package/dist/witness/loopback-pins.d.ts.map +1 -0
- package/dist/witness/loopback-pins.js +244 -0
- package/dist/witness/loopback-pins.js.map +1 -0
- package/dist/witness/server.d.ts +34 -0
- package/dist/witness/server.d.ts.map +1 -0
- package/dist/witness/server.js +2539 -0
- package/dist/witness/server.js.map +1 -0
- package/dist/witness/types.d.ts +643 -0
- package/dist/witness/types.d.ts.map +1 -0
- package/dist/witness/types.js +2 -0
- package/dist/witness/types.js.map +1 -0
- package/package.json +64 -0
- package/python/gateforge_persistence_intents.py +100 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The engine-owned browser (plan Phase 1 item 4): Chromium contexts the
|
|
3
|
+
* WITNESS process creates and drives itself — one isolated context per
|
|
4
|
+
* supervisor-opened test session, closed when the session seals.
|
|
5
|
+
*
|
|
6
|
+
* Test code NEVER touches these pages: the worker holds no handle, no
|
|
7
|
+
* URL, and no capability over them. The engine executes the constrained
|
|
8
|
+
* surface operations (create/read/update/archive) with the
|
|
9
|
+
* consumer-declared locators, and observes each step itself:
|
|
10
|
+
* - the rendered control it clicked and the values it typed;
|
|
11
|
+
* - the actual application request the action caused (captured from the
|
|
12
|
+
* engine page's own network, app-origin only);
|
|
13
|
+
* - the resulting navigation and the rendered row/form it read back.
|
|
14
|
+
*
|
|
15
|
+
* Forgery resistance (why this channel is proof):
|
|
16
|
+
* - fabricated DOM (`page.evaluate` in the worker page) is irrelevant:
|
|
17
|
+
* the engine reads its OWN page;
|
|
18
|
+
* - mocked responses (`page.route` in the worker) are irrelevant: the
|
|
19
|
+
* worker cannot route the engine's traffic;
|
|
20
|
+
* - direct API/Node mutations bypass the engine page entirely: no
|
|
21
|
+
* engine action runs, so no engine-observed action/visible records
|
|
22
|
+
* exist for them — invented suite-submitted records never satisfy;
|
|
23
|
+
* - HTTP-200-while-error: the engine asserts the rendered status
|
|
24
|
+
* itself (created/archived values) AND captures the app response
|
|
25
|
+
* status — a displayed error fails the action fail-closed.
|
|
26
|
+
*
|
|
27
|
+
* Surface selectors are LOCATORS, never proof: a lying descriptor only
|
|
28
|
+
* makes the engine fail to find elements (fail closed).
|
|
29
|
+
*/
|
|
30
|
+
import { chromium } from 'playwright';
|
|
31
|
+
import { declaredSurfaceFields, renderSurfaceTemplate, } from '../surface.js';
|
|
32
|
+
/** Fail-closed engine browser error (surfaced as 409/503 downstream). */
|
|
33
|
+
export class EngineBrowserError extends Error {
|
|
34
|
+
constructor(message) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'EngineBrowserError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Bounded wait for a rendered selector (fail closed, never indefinite). */
|
|
40
|
+
const ENGINE_STEP_TIMEOUT_MS = 15_000;
|
|
41
|
+
/**
|
|
42
|
+
* Owns the engine Chromium instance and its per-session contexts.
|
|
43
|
+
*
|
|
44
|
+
* Args:
|
|
45
|
+
* launcher: browser launcher (default: the pinned Chromium). Tests
|
|
46
|
+
* inject a failing launcher to prove the fail-closed path; the
|
|
47
|
+
* driving and observation below stay engine code regardless of
|
|
48
|
+
* which executable launches.
|
|
49
|
+
*/
|
|
50
|
+
export class EngineBrowserManager {
|
|
51
|
+
launcher;
|
|
52
|
+
browser = null;
|
|
53
|
+
launching = null;
|
|
54
|
+
sessions = new Map();
|
|
55
|
+
/**
|
|
56
|
+
* Pinned `--host-resolver-rules` value binding every attested hostname
|
|
57
|
+
* to its startup-approved loopback IPs. Set once at witness startup
|
|
58
|
+
* (before any launch); the browser is then incapable of resolving
|
|
59
|
+
* those names anywhere but loopback, regardless of later DNS changes.
|
|
60
|
+
*/
|
|
61
|
+
dnsPinRules = null;
|
|
62
|
+
constructor(launcher = chromium) {
|
|
63
|
+
this.launcher = launcher;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Installs the DNS pin rules for the next (first) launch.
|
|
67
|
+
*
|
|
68
|
+
* Args:
|
|
69
|
+
* rules: the `--host-resolver-rules` value, or null when nothing is
|
|
70
|
+
* pinned (plain launch, previous behavior).
|
|
71
|
+
*
|
|
72
|
+
* Throws:
|
|
73
|
+
* EngineBrowserError: when the browser already launched — pins must
|
|
74
|
+
* precede every navigation (fail closed, never silently unbound).
|
|
75
|
+
*/
|
|
76
|
+
setDnsPinRules(rules) {
|
|
77
|
+
if (this.browser !== null || this.launching !== null) {
|
|
78
|
+
throw new EngineBrowserError('DNS pin rules arrive after browser launch: pins must precede every navigation');
|
|
79
|
+
}
|
|
80
|
+
this.dnsPinRules = rules;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Returns the engine page for one session, creating the isolated
|
|
84
|
+
* context on first use.
|
|
85
|
+
*
|
|
86
|
+
* Args:
|
|
87
|
+
* sessionId: the supervisor-opened session the context belongs to.
|
|
88
|
+
*
|
|
89
|
+
* Returns:
|
|
90
|
+
* Promise<Page>: the session's engine-owned page.
|
|
91
|
+
*
|
|
92
|
+
* Throws:
|
|
93
|
+
* EngineBrowserError: when Chromium cannot launch (fail closed —
|
|
94
|
+
* the capability is honestly unavailable at runtime).
|
|
95
|
+
*/
|
|
96
|
+
async pageFor(sessionId) {
|
|
97
|
+
const existing = this.sessions.get(sessionId);
|
|
98
|
+
if (existing !== undefined)
|
|
99
|
+
return existing.page;
|
|
100
|
+
const browser = await this.ensureBrowser();
|
|
101
|
+
const context = await browser.newContext();
|
|
102
|
+
const page = await context.newPage();
|
|
103
|
+
this.sessions.set(sessionId, { context, page });
|
|
104
|
+
return page;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Closes and forgets one session's engine context (session seal).
|
|
108
|
+
* Late engine calls for the session then recreate nothing — callers
|
|
109
|
+
* must refuse sealed sessions before driving (they do: every browser
|
|
110
|
+
* endpoint requires an OPEN session first).
|
|
111
|
+
*
|
|
112
|
+
* Args:
|
|
113
|
+
* sessionId: the sealed session.
|
|
114
|
+
*/
|
|
115
|
+
async closeSession(sessionId) {
|
|
116
|
+
const entry = this.sessions.get(sessionId);
|
|
117
|
+
if (entry === undefined)
|
|
118
|
+
return;
|
|
119
|
+
this.sessions.delete(sessionId);
|
|
120
|
+
await entry.context.close().catch(() => undefined);
|
|
121
|
+
}
|
|
122
|
+
/** Closes every engine context and the browser (witness stop). */
|
|
123
|
+
async closeAll() {
|
|
124
|
+
const entries = [...this.sessions.values()];
|
|
125
|
+
this.sessions.clear();
|
|
126
|
+
await Promise.all(entries.map((entry) => entry.context.close().catch(() => undefined)));
|
|
127
|
+
const browser = this.browser;
|
|
128
|
+
this.browser = null;
|
|
129
|
+
this.launching = null;
|
|
130
|
+
if (browser !== null)
|
|
131
|
+
await browser.close().catch(() => undefined);
|
|
132
|
+
}
|
|
133
|
+
/** Lazy Chromium launch (single flight; fail closed with the cause). */
|
|
134
|
+
async ensureBrowser() {
|
|
135
|
+
if (this.browser !== null)
|
|
136
|
+
return this.browser;
|
|
137
|
+
this.launching ??= this.launcher
|
|
138
|
+
.launch({
|
|
139
|
+
headless: true,
|
|
140
|
+
// DNS binding (loopback-pins): attested hostnames resolve ONLY
|
|
141
|
+
// to their startup-approved loopback IPs inside this browser —
|
|
142
|
+
// a mid-run DNS change cannot move its traffic off loopback.
|
|
143
|
+
...(this.dnsPinRules !== null ? { args: [`--host-resolver-rules=${this.dnsPinRules}`] } : {}),
|
|
144
|
+
})
|
|
145
|
+
.then((browser) => {
|
|
146
|
+
this.browser = browser;
|
|
147
|
+
return browser;
|
|
148
|
+
})
|
|
149
|
+
.catch((error) => {
|
|
150
|
+
this.launching = null;
|
|
151
|
+
throw new EngineBrowserError(`the engine-owned browser cannot launch: ${error.message} — browser ` +
|
|
152
|
+
'contracts stay blocking until a working Chromium is available to the witness process');
|
|
153
|
+
});
|
|
154
|
+
return this.launching;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Captures app-origin responses on one page for the action window. */
|
|
158
|
+
async function captureExchanges(page, appOrigin, action) {
|
|
159
|
+
const captured = [];
|
|
160
|
+
// Replacement-origin enforcement (fake-frontend fix 2026-09-14): every
|
|
161
|
+
// URL the engine page touches during the window — main-frame
|
|
162
|
+
// navigations AND every hop of every redirect chain — must stay on the
|
|
163
|
+
// trusted origin. A form submit, link, or redirect landing elsewhere
|
|
164
|
+
// fails the action closed instead of observing a foreign frontend.
|
|
165
|
+
const violations = [];
|
|
166
|
+
const checkUrl = (url, where) => {
|
|
167
|
+
let origin;
|
|
168
|
+
try {
|
|
169
|
+
origin = new URL(url).origin;
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return; // non-URL (about:blank, data:) — the final page.url check covers landing
|
|
173
|
+
}
|
|
174
|
+
if (origin !== appOrigin)
|
|
175
|
+
violations.push(`${where}: ${url}`);
|
|
176
|
+
};
|
|
177
|
+
// Main-frame navigation guard: page.url() is the main frame's URL,
|
|
178
|
+
// so asserting it on every frame event covers navigations while
|
|
179
|
+
// subresource loads (same URL, no-op) cannot false-positive.
|
|
180
|
+
const mainFrameListener = () => {
|
|
181
|
+
const current = page.url();
|
|
182
|
+
if (current.startsWith('about:') || current.startsWith('data:'))
|
|
183
|
+
return;
|
|
184
|
+
checkUrl(current, 'navigation to foreign origin');
|
|
185
|
+
};
|
|
186
|
+
page.on('framenavigated', mainFrameListener);
|
|
187
|
+
const listener = (response) => {
|
|
188
|
+
// Walk the full redirect chain: the final URL AND every hop must be
|
|
189
|
+
// same-origin — a 303 to a foreign frontend is rejected even though
|
|
190
|
+
// the 303 itself came from the app. Scoped to redirects and document
|
|
191
|
+
// navigations: plain cross-origin SUBRESOURCES (fonts, icons) are
|
|
192
|
+
// not navigations, never become evidence, and must not false-positive.
|
|
193
|
+
const request = response.request();
|
|
194
|
+
const chain = [request.url()];
|
|
195
|
+
let previous = request.redirectedFrom();
|
|
196
|
+
while (previous !== null && typeof previous === 'object' && 'url' in previous) {
|
|
197
|
+
const hop = previous;
|
|
198
|
+
chain.push(hop.url());
|
|
199
|
+
previous = hop.redirectedFrom();
|
|
200
|
+
}
|
|
201
|
+
const involvedRedirect = chain.length > 1;
|
|
202
|
+
let resourceType = '';
|
|
203
|
+
try {
|
|
204
|
+
resourceType = request.resourceType();
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
resourceType = '';
|
|
208
|
+
}
|
|
209
|
+
if (involvedRedirect || resourceType === 'document') {
|
|
210
|
+
for (const url of chain)
|
|
211
|
+
checkUrl(url, 'redirect chain left the trusted origin');
|
|
212
|
+
}
|
|
213
|
+
let url;
|
|
214
|
+
try {
|
|
215
|
+
url = new URL(response.url());
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (url.origin !== appOrigin)
|
|
221
|
+
return;
|
|
222
|
+
const chunks = [];
|
|
223
|
+
void response
|
|
224
|
+
.body()
|
|
225
|
+
.then((body) => chunks.push(body))
|
|
226
|
+
.catch(() => undefined)
|
|
227
|
+
.finally(() => {
|
|
228
|
+
captured.push({
|
|
229
|
+
method: response.request().method().toUpperCase(),
|
|
230
|
+
path: canonicalExchangePath(url.pathname),
|
|
231
|
+
status: response.status(),
|
|
232
|
+
body: Buffer.concat(chunks).slice(0, 16384),
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
page.on('response', listener);
|
|
237
|
+
// The drive's own error (status mismatch, missing row) is recorded
|
|
238
|
+
// but yields to origin violations: a redirect escape is the stronger
|
|
239
|
+
// security signal and must name itself even when the foreign page
|
|
240
|
+
// ALSO fails a rendered check.
|
|
241
|
+
let driveError = null;
|
|
242
|
+
try {
|
|
243
|
+
await action();
|
|
244
|
+
// Let in-flight response bodies settle (bounded): the mutation's own
|
|
245
|
+
// exchange must be captured before the window closes.
|
|
246
|
+
await page.waitForTimeout(250);
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
driveError = error;
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
page.off('response', listener);
|
|
253
|
+
page.off('framenavigated', mainFrameListener);
|
|
254
|
+
}
|
|
255
|
+
// Await the pending body reads (bounded by the step timeout overall).
|
|
256
|
+
await page.waitForTimeout(250);
|
|
257
|
+
// The landing itself must be the trusted origin (backstop for
|
|
258
|
+
// navigations that produced no response event).
|
|
259
|
+
checkUrl(page.url(), 'engine page landed on a foreign origin');
|
|
260
|
+
if (violations.length > 0) {
|
|
261
|
+
throw new EngineBrowserError(`browser action left the trusted application origin '${appOrigin}': ` +
|
|
262
|
+
`${violations.slice(0, 3).join('; ')} — replacement origins and redirects are rejected; ` +
|
|
263
|
+
'no action record is issued for a foreign frontend');
|
|
264
|
+
}
|
|
265
|
+
if (driveError !== null)
|
|
266
|
+
throw driveError;
|
|
267
|
+
return captured;
|
|
268
|
+
}
|
|
269
|
+
/** Canonicalizes an engine-observed path (lockstep with the witness). */
|
|
270
|
+
function canonicalExchangePath(pathname) {
|
|
271
|
+
let path = pathname.split('?')[0]?.split('#')[0] ?? '/';
|
|
272
|
+
if (!path.startsWith('/'))
|
|
273
|
+
path = `/${path}`;
|
|
274
|
+
if (path.length > 1)
|
|
275
|
+
path = path.replace(/\/+$/, '');
|
|
276
|
+
return path;
|
|
277
|
+
}
|
|
278
|
+
/** Reads one row's cells (trimmed text, DOM order). */
|
|
279
|
+
async function rowCells(row) {
|
|
280
|
+
const cells = row.locator('td');
|
|
281
|
+
const count = await cells.count();
|
|
282
|
+
const out = [];
|
|
283
|
+
for (let i = 0; i < count; i++) {
|
|
284
|
+
out.push(((await cells.nth(i).textContent()) ?? '').trim());
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
287
|
+
}
|
|
288
|
+
/** Every entity id rendered in the list right now. */
|
|
289
|
+
async function collectIds(page, surface) {
|
|
290
|
+
const rows = page.locator(surface.list.rowSelector);
|
|
291
|
+
const count = await rows.count();
|
|
292
|
+
const ids = new Set();
|
|
293
|
+
for (let i = 0; i < count; i++) {
|
|
294
|
+
const cells = await rowCells(rows.nth(i));
|
|
295
|
+
const id = (cells[surface.list.idCellIndex] ?? '').trim();
|
|
296
|
+
if (id !== '')
|
|
297
|
+
ids.add(id);
|
|
298
|
+
}
|
|
299
|
+
return ids;
|
|
300
|
+
}
|
|
301
|
+
/** The rendered row for one entity (null when the UI exposes none). */
|
|
302
|
+
async function findRow(page, surface, entityId) {
|
|
303
|
+
const rows = page.locator(surface.list.rowSelector);
|
|
304
|
+
const count = await rows.count();
|
|
305
|
+
for (let i = 0; i < count; i++) {
|
|
306
|
+
const row = rows.nth(i);
|
|
307
|
+
const cells = await rowCells(row);
|
|
308
|
+
if ((cells[surface.list.idCellIndex] ?? '').trim() === entityId)
|
|
309
|
+
return row;
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
/** The rendered field values of one row. */
|
|
314
|
+
async function readRowFields(page, surface, row) {
|
|
315
|
+
void page;
|
|
316
|
+
const cells = await rowCells(row);
|
|
317
|
+
const fields = {};
|
|
318
|
+
for (const [name, index] of Object.entries(surface.list.fieldCellIndexes)) {
|
|
319
|
+
fields[name] = (cells[index] ?? '').trim();
|
|
320
|
+
}
|
|
321
|
+
return fields;
|
|
322
|
+
}
|
|
323
|
+
/** Fills exactly the declared inputs on the current form. */
|
|
324
|
+
async function fillFormFields(page, selectors, fields) {
|
|
325
|
+
for (const [name, selector] of Object.entries(selectors)) {
|
|
326
|
+
const value = fields[name];
|
|
327
|
+
if (value === undefined)
|
|
328
|
+
continue;
|
|
329
|
+
await page.locator(selector).fill(value, { timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/** Reads the current form's input values back. */
|
|
333
|
+
async function readFormFields(page, selectors) {
|
|
334
|
+
const fields = {};
|
|
335
|
+
for (const [name, selector] of Object.entries(selectors)) {
|
|
336
|
+
fields[name] = await page.locator(selector).inputValue({ timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
337
|
+
}
|
|
338
|
+
return fields;
|
|
339
|
+
}
|
|
340
|
+
/** Navigates to the list and proves it rendered. */
|
|
341
|
+
async function gotoList(page, appBase, surface) {
|
|
342
|
+
await page.goto(`${appBase}${surface.list.path}`, { timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
343
|
+
await page.waitForSelector(surface.list.readySelector, { timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
344
|
+
}
|
|
345
|
+
/** Waits for the post-action landing and proves it rendered. */
|
|
346
|
+
async function waitAfterAction(page, surface) {
|
|
347
|
+
await page.waitForURL((url) => url.pathname === surface.afterAction.path, {
|
|
348
|
+
timeout: ENGINE_STEP_TIMEOUT_MS,
|
|
349
|
+
});
|
|
350
|
+
await page.waitForSelector(surface.list.readySelector, { timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Drives one constrained surface operation on the engine page and
|
|
354
|
+
* observes every step itself.
|
|
355
|
+
*
|
|
356
|
+
* Args:
|
|
357
|
+
* page: the session's engine-owned page.
|
|
358
|
+
* appBase: loopback app base the engine navigates (validated upstream).
|
|
359
|
+
* surface: the validated consumer descriptor (locators only).
|
|
360
|
+
* operation: the constrained operation to perform.
|
|
361
|
+
* input: entered fields (create/update) and/or the target entity id.
|
|
362
|
+
*
|
|
363
|
+
* Returns:
|
|
364
|
+
* Promise<EngineActionObservation>: the engine's own observation —
|
|
365
|
+
* observed entity id, entered + rendered fields, captured exchanges.
|
|
366
|
+
*
|
|
367
|
+
* Throws:
|
|
368
|
+
* EngineBrowserError: fail-closed on every untrusted outcome —
|
|
369
|
+
* missing rows, ambiguous creation, wrong rendered status, no
|
|
370
|
+
* application request, or an application error status.
|
|
371
|
+
*/
|
|
372
|
+
export async function driveEngineAction(page, appBase, surface, operation, input) {
|
|
373
|
+
const appOrigin = new URL(appBase).origin;
|
|
374
|
+
try {
|
|
375
|
+
return await driveEngineActionInner(page, appBase, appOrigin, surface, operation, input);
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
// Every Playwright failure (timeout, navigation, detached frame) is
|
|
379
|
+
// an engine-observed unsuccessful action — never a 500: the test
|
|
380
|
+
// fails and the gate blocks with the precise cause.
|
|
381
|
+
if (error instanceof EngineBrowserError)
|
|
382
|
+
throw error;
|
|
383
|
+
throw new EngineBrowserError(`browser.${operation} failed: ${error.message}`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/** The operation dispatch (wrapped by {@link driveEngineAction}). */
|
|
387
|
+
async function driveEngineActionInner(page, appBase, appOrigin, surface, operation, input) {
|
|
388
|
+
switch (operation) {
|
|
389
|
+
case 'create': {
|
|
390
|
+
const fields = declaredSurfaceFields('browser.create', input.fields ?? {}, surface.create.fields);
|
|
391
|
+
let entityId = '';
|
|
392
|
+
let rendered = {};
|
|
393
|
+
const exchanges = await captureExchanges(page, appOrigin, async () => {
|
|
394
|
+
await gotoList(page, appBase, surface);
|
|
395
|
+
const before = await collectIds(page, surface);
|
|
396
|
+
await page.goto(`${appBase}${surface.create.formPath}`, { timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
397
|
+
await page.waitForSelector(surface.create.formReadySelector, { timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
398
|
+
await fillFormFields(page, surface.create.fields, fields);
|
|
399
|
+
await page.locator(surface.create.submitSelector).click({ timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
400
|
+
await waitAfterAction(page, surface);
|
|
401
|
+
const created = [...(await collectIds(page, surface))].filter((id) => !before.has(id));
|
|
402
|
+
if (created.length !== 1) {
|
|
403
|
+
throw new EngineBrowserError(`browser.create observed ${String(created.length)} new entities, expected exactly one — ` +
|
|
404
|
+
'the rendered outcome is ambiguous, so no action record is issued');
|
|
405
|
+
}
|
|
406
|
+
entityId = created[0];
|
|
407
|
+
const row = await findRow(page, surface, entityId);
|
|
408
|
+
if (row === null) {
|
|
409
|
+
throw new EngineBrowserError(`browser.create: the rendered list exposes no row for the created entity ${entityId}`);
|
|
410
|
+
}
|
|
411
|
+
rendered = await readRowFields(page, surface, row);
|
|
412
|
+
if (surface.status.createdValue !== undefined) {
|
|
413
|
+
const shown = rendered[surface.status.field] ?? '';
|
|
414
|
+
if (shown !== surface.status.createdValue) {
|
|
415
|
+
throw new EngineBrowserError(`browser.create: entity ${entityId} rendered ${surface.status.field} '${shown}', ` +
|
|
416
|
+
`expected '${surface.status.createdValue}' — the application displays an error state, ` +
|
|
417
|
+
'so the action fails instead of earning proof');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
requireAppRequest(exchanges, 'create');
|
|
422
|
+
return { entityId, enteredFields: fields, renderedFields: rendered, exchanges };
|
|
423
|
+
}
|
|
424
|
+
case 'read': {
|
|
425
|
+
const entityId = input.entityId ?? '';
|
|
426
|
+
if (entityId === '')
|
|
427
|
+
throw new EngineBrowserError('browser.read requires { entityId }');
|
|
428
|
+
let rendered = {};
|
|
429
|
+
const exchanges = await captureExchanges(page, appOrigin, async () => {
|
|
430
|
+
await gotoList(page, appBase, surface);
|
|
431
|
+
const row = await findRow(page, surface, entityId);
|
|
432
|
+
if (row === null) {
|
|
433
|
+
throw new EngineBrowserError(`browser.read: the rendered UI exposes no row for ${entityId}`);
|
|
434
|
+
}
|
|
435
|
+
const edit = row.locator(surface.edit.linkSelector);
|
|
436
|
+
if ((await edit.count()) === 0) {
|
|
437
|
+
throw new EngineBrowserError(`browser.read: the rendered UI exposes no navigation control for ${entityId}`);
|
|
438
|
+
}
|
|
439
|
+
await edit.click({ timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
440
|
+
await page.waitForSelector(renderSurfaceTemplate(surface.edit.formReadySelectorTemplate, entityId), {
|
|
441
|
+
timeout: ENGINE_STEP_TIMEOUT_MS,
|
|
442
|
+
});
|
|
443
|
+
rendered = await readFormFields(page, surface.edit.fields);
|
|
444
|
+
});
|
|
445
|
+
return { entityId, enteredFields: {}, renderedFields: rendered, exchanges };
|
|
446
|
+
}
|
|
447
|
+
case 'update': {
|
|
448
|
+
const entityId = input.entityId ?? '';
|
|
449
|
+
if (entityId === '')
|
|
450
|
+
throw new EngineBrowserError('browser.update requires { entityId }');
|
|
451
|
+
const fields = declaredSurfaceFields('browser.update', input.fields ?? {}, surface.edit.fields);
|
|
452
|
+
let rendered = {};
|
|
453
|
+
const exchanges = await captureExchanges(page, appOrigin, async () => {
|
|
454
|
+
await gotoList(page, appBase, surface);
|
|
455
|
+
const row = await findRow(page, surface, entityId);
|
|
456
|
+
if (row === null) {
|
|
457
|
+
throw new EngineBrowserError(`browser.update: the rendered UI exposes no row for ${entityId}`);
|
|
458
|
+
}
|
|
459
|
+
await row.locator(surface.edit.linkSelector).click({ timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
460
|
+
await page.waitForSelector(renderSurfaceTemplate(surface.edit.formReadySelectorTemplate, entityId), {
|
|
461
|
+
timeout: ENGINE_STEP_TIMEOUT_MS,
|
|
462
|
+
});
|
|
463
|
+
await fillFormFields(page, surface.edit.fields, fields);
|
|
464
|
+
await page
|
|
465
|
+
.locator(renderSurfaceTemplate(surface.edit.saveSelectorTemplate, entityId))
|
|
466
|
+
.first()
|
|
467
|
+
.click({ timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
468
|
+
await waitAfterAction(page, surface);
|
|
469
|
+
const updatedRow = await findRow(page, surface, entityId);
|
|
470
|
+
if (updatedRow === null) {
|
|
471
|
+
throw new EngineBrowserError(`browser.update: entity ${entityId} vanished after update`);
|
|
472
|
+
}
|
|
473
|
+
rendered = await readRowFields(page, surface, updatedRow);
|
|
474
|
+
});
|
|
475
|
+
requireAppRequest(exchanges, 'update');
|
|
476
|
+
return { entityId, enteredFields: fields, renderedFields: rendered, exchanges };
|
|
477
|
+
}
|
|
478
|
+
case 'delete': {
|
|
479
|
+
const entityId = input.entityId ?? '';
|
|
480
|
+
if (entityId === '')
|
|
481
|
+
throw new EngineBrowserError('browser.archive requires { entityId }');
|
|
482
|
+
const deleteFields = { ...surface.deleteFields };
|
|
483
|
+
let rendered = {};
|
|
484
|
+
const exchanges = await captureExchanges(page, appOrigin, async () => {
|
|
485
|
+
await gotoList(page, appBase, surface);
|
|
486
|
+
const row = await findRow(page, surface, entityId);
|
|
487
|
+
if (row === null) {
|
|
488
|
+
throw new EngineBrowserError(`browser.archive: the rendered UI exposes no row for ${entityId}`);
|
|
489
|
+
}
|
|
490
|
+
const control = row.locator(renderSurfaceTemplate(surface.archive.controlSelectorTemplate, entityId));
|
|
491
|
+
if ((await control.count()) === 0) {
|
|
492
|
+
throw new EngineBrowserError(`browser.archive: entity ${entityId} exposes no archive control (already archived?)`);
|
|
493
|
+
}
|
|
494
|
+
await control.click({ timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
495
|
+
await waitAfterAction(page, surface);
|
|
496
|
+
const archivedRow = await findRow(page, surface, entityId);
|
|
497
|
+
if (archivedRow === null) {
|
|
498
|
+
throw new EngineBrowserError(`browser.archive: entity ${entityId} vanished after archive`);
|
|
499
|
+
}
|
|
500
|
+
rendered = await readRowFields(page, surface, archivedRow);
|
|
501
|
+
if (surface.status.archivedValue !== undefined) {
|
|
502
|
+
const shown = rendered[surface.status.field] ?? '';
|
|
503
|
+
if (shown !== surface.status.archivedValue) {
|
|
504
|
+
throw new EngineBrowserError(`browser.archive: entity ${entityId} rendered ${surface.status.field} '${shown}', ` +
|
|
505
|
+
`expected '${surface.status.archivedValue}' — the application displays an error ` +
|
|
506
|
+
'state, so the action fails instead of earning proof');
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
requireAppRequest(exchanges, 'delete');
|
|
511
|
+
return { entityId, enteredFields: deleteFields, renderedFields: rendered, exchanges };
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Requires that the action window captured at least one application
|
|
517
|
+
* request that the app did not reject: a rendered click that caused no
|
|
518
|
+
* request (or only error statuses) proves no application behavior.
|
|
519
|
+
*
|
|
520
|
+
* Args:
|
|
521
|
+
* exchanges: the engine-captured app-origin exchanges.
|
|
522
|
+
* operation: the performed operation (diagnostic only).
|
|
523
|
+
*
|
|
524
|
+
* Throws:
|
|
525
|
+
* EngineBrowserError: when no acceptable application request exists.
|
|
526
|
+
*/
|
|
527
|
+
function requireAppRequest(exchanges, operation) {
|
|
528
|
+
const mutation = exchanges.filter((entry) => entry.method !== 'GET' && entry.method !== 'HEAD');
|
|
529
|
+
if (mutation.length === 0) {
|
|
530
|
+
throw new EngineBrowserError(`browser.${operation}: the engine captured no application request for the action — ` +
|
|
531
|
+
'a rendered click that caused no request proves no application behavior');
|
|
532
|
+
}
|
|
533
|
+
const accepted = mutation.find((entry) => entry.status < 400);
|
|
534
|
+
if (accepted === undefined) {
|
|
535
|
+
throw new EngineBrowserError(`browser.${operation}: every captured application request was rejected ` +
|
|
536
|
+
`(${mutation.map((entry) => `${entry.method} ${entry.path} → ${String(entry.status)}`).join(', ')}) — ` +
|
|
537
|
+
'the application refused the action, so it fails instead of earning proof');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Re-reads the rendered result for one entity on the engine page
|
|
542
|
+
* (the `visible.confirm` counterpart): row read for mutations, form
|
|
543
|
+
* read for reads.
|
|
544
|
+
*
|
|
545
|
+
* Args:
|
|
546
|
+
* page: the session's engine-owned page.
|
|
547
|
+
* appBase: loopback app base.
|
|
548
|
+
* surface: the validated consumer descriptor.
|
|
549
|
+
* operation: the original action (decides row vs form readback).
|
|
550
|
+
* entityId: the engine-observed entity id.
|
|
551
|
+
*
|
|
552
|
+
* Returns:
|
|
553
|
+
* Promise<Record<string, string>>: the rendered fields.
|
|
554
|
+
*
|
|
555
|
+
* Throws:
|
|
556
|
+
* EngineBrowserError: when the UI no longer exposes the entity.
|
|
557
|
+
*/
|
|
558
|
+
export async function readEngineVisible(page, appBase, surface, operation, entityId) {
|
|
559
|
+
try {
|
|
560
|
+
await gotoList(page, appBase, surface);
|
|
561
|
+
const row = await findRow(page, surface, entityId);
|
|
562
|
+
if (row === null) {
|
|
563
|
+
throw new EngineBrowserError(`browser.visible: the rendered UI exposes no row for ${entityId}`);
|
|
564
|
+
}
|
|
565
|
+
if (operation === 'read') {
|
|
566
|
+
await row.locator(surface.edit.linkSelector).click({ timeout: ENGINE_STEP_TIMEOUT_MS });
|
|
567
|
+
await page.waitForSelector(renderSurfaceTemplate(surface.edit.formReadySelectorTemplate, entityId), {
|
|
568
|
+
timeout: ENGINE_STEP_TIMEOUT_MS,
|
|
569
|
+
});
|
|
570
|
+
return readFormFields(page, surface.edit.fields);
|
|
571
|
+
}
|
|
572
|
+
return readRowFields(page, surface, row);
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
if (error instanceof EngineBrowserError)
|
|
576
|
+
throw error;
|
|
577
|
+
throw new EngineBrowserError(`browser.visible failed: ${error.message}`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/witness/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,QAAQ,EAA6E,MAAM,YAAY,CAAC;AACjH,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GAEtB,MAAM,eAAe,CAAC;AA+BvB,yEAAyE;AACzE,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAOD,4EAA4E;AAC5E,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAEtC;;;;;;;;GAQG;AACH,MAAM,OAAO,oBAAoB;IAYF;IAXrB,OAAO,GAAmB,IAAI,CAAC;IAC/B,SAAS,GAA4B,IAAI,CAAC;IACjC,QAAQ,GAAG,IAAI,GAAG,EAAmD,CAAC;IACvF;;;;;OAKG;IACK,WAAW,GAAkB,IAAI,CAAC;IAE1C,YAA6B,WAAkC,QAAQ;QAA1C,aAAQ,GAAR,QAAQ,CAAkC;IAAG,CAAC;IAE3E;;;;;;;;;;OAUG;IACH,cAAc,CAAC,KAAoB;QACjC,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;YACrD,MAAM,IAAI,kBAAkB,CAC1B,+EAA+E,CAChF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;IAC3B,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,OAAO,CAAC,SAAiB;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,QAAQ,CAAC,IAAI,CAAC;QACjD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,YAAY,CAAC,SAAiB;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO;QAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAChC,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACrD,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,QAAQ;QACZ,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACxF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,OAAO,KAAK,IAAI;YAAE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC;IAED,wEAAwE;IAChE,KAAK,CAAC,aAAa;QACzB,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC;QAC/C,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,QAAQ;aAC7B,MAAM,CAAC;YACN,QAAQ,EAAE,IAAI;YACd,+DAA+D;YAC/D,+DAA+D;YAC/D,6DAA6D;YAC7D,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,yBAAyB,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9F,CAAC;aACD,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACvB,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACxB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,MAAM,IAAI,kBAAkB,CAC1B,2CAA4C,KAAe,CAAC,OAAO,aAAa;gBAC9E,sFAAsF,CACzF,CAAC;QACJ,CAAC,CAAC,CAAC;QACL,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;CACF;AAED,uEAAuE;AACvE,KAAK,UAAU,gBAAgB,CAC7B,IAAU,EACV,SAAiB,EACjB,MAA2B;IAE3B,MAAM,QAAQ,GAA6B,EAAE,CAAC;IAC9C,uEAAuE;IACvE,6DAA6D;IAC7D,uEAAuE;IACvE,qEAAqE;IACrE,mEAAmE;IACnE,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,KAAa,EAAQ,EAAE;QAAM,IAAI,MAAc,CAAC;QAC7E,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,yEAAyE;QACnF,CAAC;QACD,IAAI,MAAM,KAAK,SAAS;YAAE,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,GAAG,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC;IACF,mEAAmE;IACnE,gEAAgE;IAChE,6DAA6D;IAC7D,MAAM,iBAAiB,GAAG,GAAS,EAAE;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO;QACxE,QAAQ,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;IACpD,CAAC,CAAC;IACF,IAAI,CAAC,EAAE,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,CAAC,QAAkB,EAAQ,EAAE;QAC5C,oEAAoE;QACpE,oEAAoE;QACpE,qEAAqE;QACrE,kEAAkE;QAClE,uEAAuE;QACvE,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;QACnC,MAAM,KAAK,GAAa,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACxC,IAAI,QAAQ,GAAY,OAAO,CAAC,cAAc,EAAE,CAAC;QACjD,OAAO,QAAQ,KAAK,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,IAAK,QAAoC,EAAE,CAAC;YAC3G,MAAM,GAAG,GAAG,QAAwD,CAAC;YACrE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;YACtB,QAAQ,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;QAClC,CAAC;QACD,MAAM,gBAAgB,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1C,IAAI,YAAY,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,YAAY,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,YAAY,GAAG,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,gBAAgB,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;YACpD,KAAK,MAAM,GAAG,IAAI,KAAK;gBAAE,QAAQ,CAAC,GAAG,EAAE,wCAAwC,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,GAAQ,CAAC;QACb,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,QAAQ;aACV,IAAI,EAAE;aACN,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACjC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aACtB,OAAO,CAAC,GAAG,EAAE;YACZ,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE;gBACjD,IAAI,EAAE,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACzC,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE;gBACzB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;IACF,IAAI,CAAC,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9B,mEAAmE;IACnE,qEAAqE;IACrE,kEAAkE;IAClE,+BAA+B;IAC/B,IAAI,UAAU,GAAY,IAAI,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,EAAE,CAAC;QACf,qEAAqE;QACrE,sDAAsD;QACtD,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,UAAU,GAAG,KAAK,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC/B,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC;IAChD,CAAC;IACD,sEAAsE;IACtE,MAAM,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;IAC/B,8DAA8D;IAC9D,gDAAgD;IAChD,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,wCAAwC,CAAC,CAAC;IAC/D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,kBAAkB,CAC1B,uDAAuD,SAAS,KAAK;YACnE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,qDAAqD;YACzF,mDAAmD,CACtD,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,KAAK,IAAI;QAAE,MAAM,UAAU,CAAC;IAC1C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,yEAAyE;AACzE,SAAS,qBAAqB,CAAC,QAAgB;IAC7C,IAAI,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;IACxD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IAC7C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uDAAuD;AACvD,KAAK,UAAU,QAAQ,CAAC,GAAY;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;IAClC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,sDAAsD;AACtD,KAAK,UAAU,UAAU,CAAC,IAAU,EAAE,OAA0B;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1D,IAAI,EAAE,KAAK,EAAE;YAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,OAAO,CAAC,IAAU,EAAE,OAA0B,EAAE,QAAgB;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,QAAQ;YAAE,OAAO,GAAG,CAAC;IAC9E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4CAA4C;AAC5C,KAAK,UAAU,aAAa,CAC1B,IAAU,EACV,OAA0B,EAC1B,GAAY;IAEZ,KAAK,IAAI,CAAC;IACV,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC1E,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,6DAA6D;AAC7D,KAAK,UAAU,cAAc,CAC3B,IAAU,EACV,SAAiC,EACjC,MAA8B;IAE9B,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,kDAAkD;AAClD,KAAK,UAAU,cAAc,CAC3B,IAAU,EACV,SAAiC;IAEjC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,oDAAoD;AACpD,KAAK,UAAU,QAAQ,CAAC,IAAU,EAAE,OAAe,EAAE,OAA0B;IAC7E,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;IACvF,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;AAC9F,CAAC;AAED,gEAAgE;AAChE,KAAK,UAAU,eAAe,CAAC,IAAU,EAAE,OAA0B;IACnE,MAAM,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,OAAO,CAAC,WAAW,CAAC,IAAI,EAAE;QACxE,OAAO,EAAE,sBAAsB;KAChC,CAAC,CAAC;IACH,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAU,EACV,OAAe,EACf,OAA0B,EAC1B,SAA0B,EAC1B,KAA6D;IAE7D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,sBAAsB,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC3F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,oEAAoE;QACpE,iEAAiE;QACjE,oDAAoD;QACpD,IAAI,KAAK,YAAY,kBAAkB;YAAE,MAAM,KAAK,CAAC;QACrD,MAAM,IAAI,kBAAkB,CAAC,WAAW,SAAS,YAAa,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3F,CAAC;AACH,CAAC;AAED,qEAAqE;AACrE,KAAK,UAAU,sBAAsB,CACnC,IAAU,EACV,OAAe,EACf,SAAiB,EACjB,OAA0B,EAC1B,SAA0B,EAC1B,KAA6D;IAE7D,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,MAAM,GAAG,qBAAqB,CAAC,gBAAgB,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAClG,IAAI,QAAQ,GAAG,EAAE,CAAC;YAClB,IAAI,QAAQ,GAA2B,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE;gBACnE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBACvC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBAC/C,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBAC7F,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBAClG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC1D,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBAC7F,MAAM,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACrC,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBACvF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzB,MAAM,IAAI,kBAAkB,CAC1B,2BAA2B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,wCAAwC;wBACvF,kEAAkE,CACrE,CAAC;gBACJ,CAAC;gBACD,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAW,CAAC;gBAChC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBACnD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;oBACjB,MAAM,IAAI,kBAAkB,CAC1B,2EAA2E,QAAQ,EAAE,CACtF,CAAC;gBACJ,CAAC;gBACD,QAAQ,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;gBACnD,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;oBAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;oBACnD,IAAI,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;wBAC1C,MAAM,IAAI,kBAAkB,CAC1B,0BAA0B,QAAQ,aAAa,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,KAAK;4BAChF,aAAa,OAAO,CAAC,MAAM,CAAC,YAAY,+CAA+C;4BACvF,8CAA8C,CACjD,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YACH,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACvC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QAClF,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;YACtC,IAAI,QAAQ,KAAK,EAAE;gBAAE,MAAM,IAAI,kBAAkB,CAAC,oCAAoC,CAAC,CAAC;YACxF,IAAI,QAAQ,GAA2B,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE;gBACnE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBACvC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBACnD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;oBACjB,MAAM,IAAI,kBAAkB,CAAC,oDAAoD,QAAQ,EAAE,CAAC,CAAC;gBAC/F,CAAC;gBACD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACpD,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC/B,MAAM,IAAI,kBAAkB,CAC1B,mEAAmE,QAAQ,EAAE,CAC9E,CAAC;gBACJ,CAAC;gBACD,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBACtD,MAAM,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,EAAE;oBAClG,OAAO,EAAE,sBAAsB;iBAChC,CAAC,CAAC;gBACH,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7D,CAAC,CAAC,CAAC;YACH,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QAC9E,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;YACtC,IAAI,QAAQ,KAAK,EAAE;gBAAE,MAAM,IAAI,kBAAkB,CAAC,sCAAsC,CAAC,CAAC;YAC1F,MAAM,MAAM,GAAG,qBAAqB,CAAC,gBAAgB,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAChG,IAAI,QAAQ,GAA2B,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE;gBACnE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBACvC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBACnD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;oBACjB,MAAM,IAAI,kBAAkB,CAAC,sDAAsD,QAAQ,EAAE,CAAC,CAAC;gBACjG,CAAC;gBACD,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBACxF,MAAM,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,EAAE;oBAClG,OAAO,EAAE,sBAAsB;iBAChC,CAAC,CAAC;gBACH,MAAM,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACxD,MAAM,IAAI;qBACP,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC,CAAC;qBAC3E,KAAK,EAAE;qBACP,KAAK,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBAC9C,MAAM,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACrC,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBAC1D,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;oBACxB,MAAM,IAAI,kBAAkB,CAAC,0BAA0B,QAAQ,wBAAwB,CAAC,CAAC;gBAC3F,CAAC;gBACD,QAAQ,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YAC5D,CAAC,CAAC,CAAC;YACH,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACvC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QAClF,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;YACtC,IAAI,QAAQ,KAAK,EAAE;gBAAE,MAAM,IAAI,kBAAkB,CAAC,uCAAuC,CAAC,CAAC;YAC3F,MAAM,YAAY,GAAG,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YACjD,IAAI,QAAQ,GAA2B,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,EAAE;gBACnE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBACvC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBACnD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;oBACjB,MAAM,IAAI,kBAAkB,CAAC,uDAAuD,QAAQ,EAAE,CAAC,CAAC;gBAClG,CAAC;gBACD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,uBAAuB,EAAE,QAAQ,CAAC,CAAC,CAAC;gBACtG,IAAI,CAAC,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;oBAClC,MAAM,IAAI,kBAAkB,CAC1B,2BAA2B,QAAQ,iDAAiD,CACrF,CAAC;gBACJ,CAAC;gBACD,MAAM,OAAO,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;gBACzD,MAAM,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBACrC,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;gBAC3D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;oBACzB,MAAM,IAAI,kBAAkB,CAAC,2BAA2B,QAAQ,yBAAyB,CAAC,CAAC;gBAC7F,CAAC;gBACD,QAAQ,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;gBAC3D,IAAI,OAAO,CAAC,MAAM,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;oBAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;oBACnD,IAAI,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;wBAC3C,MAAM,IAAI,kBAAkB,CAC1B,2BAA2B,QAAQ,aAAa,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,KAAK,KAAK;4BACjF,aAAa,OAAO,CAAC,MAAM,CAAC,aAAa,wCAAwC;4BACjF,qDAAqD,CACxD,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YACH,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACvC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;QACxF,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,iBAAiB,CAAC,SAAmC,EAAE,SAAiB;IAC/E,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IAChG,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,kBAAkB,CAC1B,WAAW,SAAS,gEAAgE;YAClF,wEAAwE,CAC3E,CAAC;IACJ,CAAC;IACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAC9D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,kBAAkB,CAC1B,WAAW,SAAS,oDAAoD;YACtE,IAAI,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,MAAM,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM;YACvG,0EAA0E,CAC7E,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAU,EACV,OAAe,EACf,OAA0B,EAC1B,SAA0B,EAC1B,QAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACvC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACnD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,kBAAkB,CAAC,uDAAuD,QAAQ,EAAE,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YACzB,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAC;YACxF,MAAM,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,EAAE;gBAClG,OAAO,EAAE,sBAAsB;aAChC,CAAC,CAAC;YACH,OAAO,cAAc,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnD,CAAC;QACD,OAAO,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,kBAAkB;YAAE,MAAM,KAAK,CAAC;QACrD,MAAM,IAAI,kBAAkB,CAAC,2BAA4B,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;IACtF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type Classification } from '@gate-forge/core';
|
|
2
|
+
/** The reporter-visible projection of one resource's classification. */
|
|
3
|
+
export interface ClassificationView {
|
|
4
|
+
primaryKey: string[];
|
|
5
|
+
exposure: string;
|
|
6
|
+
plane: string;
|
|
7
|
+
evidenceAdapter?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Which evidence lane proves user-facing reachability (`'claims'` =
|
|
10
|
+
* the http.endpoint lane; adapter-free). MUST ride along: without it a
|
|
11
|
+
* claims-lane entry (user-facing, no adapter) re-validates downstream
|
|
12
|
+
* as "user-facing resources require an 'evidenceAdapter'" and the
|
|
13
|
+
* reporter grades the claim unclassified.
|
|
14
|
+
*/
|
|
15
|
+
evidenceLane?: 'adapter' | 'claims';
|
|
16
|
+
lifecycle: {
|
|
17
|
+
create: boolean;
|
|
18
|
+
read: boolean;
|
|
19
|
+
update: boolean;
|
|
20
|
+
delete: boolean;
|
|
21
|
+
deleteSemantics?: 'hard' | 'archive';
|
|
22
|
+
archiveFields?: Record<string, string | number | boolean>;
|
|
23
|
+
updateableFields?: string[];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Loads the classifications document.
|
|
28
|
+
*
|
|
29
|
+
* Args:
|
|
30
|
+
* path: absolute path to the classifications YAML (or null/'' = none).
|
|
31
|
+
*
|
|
32
|
+
* Returns:
|
|
33
|
+
* Record<string, Classification>: validated per-resource map (empty
|
|
34
|
+
* when no document is configured).
|
|
35
|
+
*
|
|
36
|
+
* Throws:
|
|
37
|
+
* AdapterRegistryError: fail-closed load/validation problems.
|
|
38
|
+
*/
|
|
39
|
+
export declare function loadClassifications(path: string | null | undefined): Record<string, Classification>;
|
|
40
|
+
/** Projects a classification for the reporter surface. */
|
|
41
|
+
export declare function toClassificationView(entry: Classification): ClassificationView;
|
|
42
|
+
//# sourceMappingURL=classifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifications.d.ts","sourceRoot":"","sources":["../../src/witness/classifications.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAG1B,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACpC,SAAS,EAAE;QACT,MAAM,EAAE,OAAO,CAAC;QAChB,IAAI,EAAE,OAAO,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE,OAAO,CAAC;QAChB,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACrC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;QAC1D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC7B,CAAC;CACH;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAkCnG;AAED,0DAA0D;AAC1D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,GAAG,kBAAkB,CAuB9E"}
|