@apifuse/provider-sdk 2.1.0-beta.12 → 2.1.0-beta.14
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/AUTHORING.md +134 -0
- package/CHANGELOG.md +8 -0
- package/README.md +8 -0
- package/dist/auth.d.ts +76 -0
- package/dist/auth.js +436 -0
- package/dist/contract.js +1 -0
- package/dist/define.d.ts +4 -1
- package/dist/define.js +109 -46
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/lint.d.ts +1 -0
- package/dist/lint.js +27 -0
- package/dist/provider.d.ts +4 -2
- package/dist/provider.js +2 -1
- package/dist/runtime/auth-flow.js +2 -0
- package/dist/runtime/browser.js +203 -0
- package/dist/server/serve.js +16 -8
- package/dist/types.d.ts +106 -0
- package/package.json +1 -1
- package/src/auth.ts +786 -0
- package/src/contract.ts +1 -0
- package/src/define.ts +158 -48
- package/src/index.ts +10 -0
- package/src/lint.ts +33 -0
- package/src/provider.ts +27 -0
- package/src/runtime/auth-flow.ts +2 -0
- package/src/runtime/browser.ts +293 -1
- package/src/server/serve.ts +39 -4
- package/src/types.ts +136 -0
package/src/runtime/browser.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import type { Frame, LaunchOptions, Locator, Page } from "playwright";
|
|
2
|
+
import type { Frame, LaunchOptions, Locator, Page, Request, Route } from "playwright";
|
|
3
3
|
|
|
4
4
|
import { ProviderError } from "../errors";
|
|
5
5
|
import type {
|
|
@@ -11,11 +11,18 @@ import type {
|
|
|
11
11
|
BrowserLocator,
|
|
12
12
|
BrowserOptions,
|
|
13
13
|
BrowserPage,
|
|
14
|
+
BrowserResourceBody,
|
|
15
|
+
BrowserResourceDecision,
|
|
16
|
+
BrowserResourceMethod,
|
|
17
|
+
BrowserResourcePolicy,
|
|
18
|
+
BrowserResourceRequest,
|
|
14
19
|
} from "../types";
|
|
15
20
|
|
|
16
21
|
const require = createRequire(import.meta.url);
|
|
17
22
|
const DEFAULT_WAIT_TIMEOUT_MS = 30_000;
|
|
18
23
|
const SELECTOR_POLL_INTERVAL_MS = 100;
|
|
24
|
+
const RESOURCE_POLICY_ROUTE_PATTERN = "**/*";
|
|
25
|
+
const DEFAULT_RESOURCE_METHODS = ["GET", "HEAD"] as const;
|
|
19
26
|
|
|
20
27
|
type PlaywrightModule = typeof import("playwright");
|
|
21
28
|
type PlaywrightExtraModule = {
|
|
@@ -62,8 +69,153 @@ type CdpFrameTreeNode = {
|
|
|
62
69
|
};
|
|
63
70
|
};
|
|
64
71
|
|
|
72
|
+
type CdpFetchFulfillParams = {
|
|
73
|
+
readonly requestId: string;
|
|
74
|
+
readonly responseCode: number;
|
|
75
|
+
readonly responseHeaders?: readonly {
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly value: string;
|
|
78
|
+
}[];
|
|
79
|
+
readonly body?: string;
|
|
80
|
+
};
|
|
81
|
+
|
|
65
82
|
type BrowserPageContract = BrowserPage;
|
|
66
83
|
|
|
84
|
+
function toResourceBody(
|
|
85
|
+
body: BrowserResourceBody | undefined,
|
|
86
|
+
): Buffer | string | undefined {
|
|
87
|
+
if (body === undefined || typeof body === "string" || Buffer.isBuffer(body)) {
|
|
88
|
+
return body;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (body instanceof ArrayBuffer) {
|
|
92
|
+
return Buffer.from(new Uint8Array(body));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return Buffer.from(body);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isResourceMethod(method: string): method is BrowserResourceMethod {
|
|
99
|
+
return method === "GET" || method === "HEAD";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function toResourceRequest(
|
|
103
|
+
request: Request,
|
|
104
|
+
): Promise<BrowserResourceRequest | null> {
|
|
105
|
+
const method = request.method().toUpperCase();
|
|
106
|
+
if (!isResourceMethod(method)) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
headers: await request.allHeaders(),
|
|
112
|
+
method,
|
|
113
|
+
resourceType: request.resourceType(),
|
|
114
|
+
url: request.url(),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function toCdpResourceRequest(
|
|
119
|
+
params: unknown,
|
|
120
|
+
): { requestId: string; request: BrowserResourceRequest } | null {
|
|
121
|
+
if (!isRecord(params)) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const requestId = params.requestId;
|
|
126
|
+
const rawRequest = params.request;
|
|
127
|
+
if (typeof requestId !== "string" || !isRecord(rawRequest)) {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const url = rawRequest.url;
|
|
132
|
+
const method = String(rawRequest.method ?? "").toUpperCase();
|
|
133
|
+
if (typeof url !== "string" || !isResourceMethod(method)) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
requestId,
|
|
139
|
+
request: {
|
|
140
|
+
headers: toCdpResourceHeaders(rawRequest.headers),
|
|
141
|
+
method,
|
|
142
|
+
resourceType:
|
|
143
|
+
typeof params.resourceType === "string" ? params.resourceType : undefined,
|
|
144
|
+
url,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getCdpPausedRequestId(params: unknown): string | null {
|
|
150
|
+
if (!isRecord(params) || typeof params.requestId !== "string") {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return params.requestId;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function toCdpResourceHeaders(value: unknown): Record<string, string> {
|
|
158
|
+
if (!isRecord(value)) {
|
|
159
|
+
return {};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const headers: Record<string, string> = {};
|
|
163
|
+
for (const [name, headerValue] of Object.entries(value)) {
|
|
164
|
+
if (typeof headerValue === "string") {
|
|
165
|
+
headers[name] = headerValue;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return headers;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function matchesResourceRoute(
|
|
173
|
+
match: BrowserResourcePolicy["routes"][number]["match"],
|
|
174
|
+
request: BrowserResourceRequest,
|
|
175
|
+
): boolean {
|
|
176
|
+
if (typeof match === "string") {
|
|
177
|
+
return request.url === match;
|
|
178
|
+
}
|
|
179
|
+
if (match instanceof RegExp) {
|
|
180
|
+
return match.test(request.url);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return match(request);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function toCdpFulfillParams(
|
|
187
|
+
requestId: string,
|
|
188
|
+
decision: Extract<BrowserResourceDecision, { readonly action: "fulfill" }>,
|
|
189
|
+
): CdpFetchFulfillParams {
|
|
190
|
+
const body = toResourceBody(decision.body);
|
|
191
|
+
return {
|
|
192
|
+
...(body === undefined
|
|
193
|
+
? {}
|
|
194
|
+
: { body: Buffer.from(body).toString("base64") }),
|
|
195
|
+
...(decision.headers === undefined
|
|
196
|
+
? {}
|
|
197
|
+
: {
|
|
198
|
+
responseHeaders: Object.entries(decision.headers).map(
|
|
199
|
+
([name, value]) => ({ name, value }),
|
|
200
|
+
),
|
|
201
|
+
}),
|
|
202
|
+
requestId,
|
|
203
|
+
responseCode: decision.status ?? 200,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function fulfillResourceRoute(
|
|
208
|
+
route: Route,
|
|
209
|
+
decision: Extract<BrowserResourceDecision, { readonly action: "fulfill" }>,
|
|
210
|
+
): Promise<void> {
|
|
211
|
+
const body = toResourceBody(decision.body);
|
|
212
|
+
await route.fulfill({
|
|
213
|
+
...(body === undefined ? {} : { body }),
|
|
214
|
+
...(decision.headers === undefined ? {} : { headers: decision.headers }),
|
|
215
|
+
status: decision.status ?? 200,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
67
219
|
export type BrowserClientOptions = BrowserOptions & {
|
|
68
220
|
allowedHosts?: string[];
|
|
69
221
|
cdpUrl?: string;
|
|
@@ -398,6 +550,47 @@ class PlaywrightBrowserPage implements BrowserPageContract {
|
|
|
398
550
|
async close(): Promise<void> {
|
|
399
551
|
await this.page.close();
|
|
400
552
|
}
|
|
553
|
+
|
|
554
|
+
async withResourcePolicy<T>(
|
|
555
|
+
policy: BrowserResourcePolicy,
|
|
556
|
+
run: () => Promise<T>,
|
|
557
|
+
): Promise<T> {
|
|
558
|
+
const allowedMethods = new Set(
|
|
559
|
+
policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS,
|
|
560
|
+
);
|
|
561
|
+
const handler = async (route: Route): Promise<void> => {
|
|
562
|
+
const request = await toResourceRequest(route.request());
|
|
563
|
+
if (!request || !allowedMethods.has(request.method)) {
|
|
564
|
+
await route.abort("blockedbyclient");
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
for (const resourceRoute of policy.routes) {
|
|
569
|
+
if (!matchesResourceRoute(resourceRoute.match, request)) {
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const decision = await resourceRoute.handle(request);
|
|
574
|
+
switch (decision.action) {
|
|
575
|
+
case "fulfill":
|
|
576
|
+
await fulfillResourceRoute(route, decision);
|
|
577
|
+
return;
|
|
578
|
+
case "block":
|
|
579
|
+
await route.abort("blockedbyclient");
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
await route.abort("blockedbyclient");
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
await this.page.route(RESOURCE_POLICY_ROUTE_PATTERN, handler);
|
|
588
|
+
try {
|
|
589
|
+
return await run();
|
|
590
|
+
} finally {
|
|
591
|
+
await this.page.unroute(RESOURCE_POLICY_ROUTE_PATTERN, handler);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
401
594
|
}
|
|
402
595
|
|
|
403
596
|
class PlaywrightBrowserClient implements SupportedBrowserClient {
|
|
@@ -1055,6 +1248,105 @@ class CdpPoolBrowserPage implements BrowserPageContract {
|
|
|
1055
1248
|
}
|
|
1056
1249
|
}
|
|
1057
1250
|
|
|
1251
|
+
async withResourcePolicy<T>(
|
|
1252
|
+
policy: BrowserResourcePolicy,
|
|
1253
|
+
run: () => Promise<T>,
|
|
1254
|
+
): Promise<T> {
|
|
1255
|
+
const allowedMethods = new Set(
|
|
1256
|
+
policy.allowedMethods ?? DEFAULT_RESOURCE_METHODS,
|
|
1257
|
+
);
|
|
1258
|
+
const handlePausedRequest = (params: unknown): void => {
|
|
1259
|
+
void this.handleResourcePolicyPausedRequest(
|
|
1260
|
+
params,
|
|
1261
|
+
policy,
|
|
1262
|
+
allowedMethods,
|
|
1263
|
+
);
|
|
1264
|
+
};
|
|
1265
|
+
|
|
1266
|
+
const unsubscribe = this.pageClient.on(
|
|
1267
|
+
"Fetch.requestPaused",
|
|
1268
|
+
handlePausedRequest,
|
|
1269
|
+
);
|
|
1270
|
+
|
|
1271
|
+
try {
|
|
1272
|
+
await this.pageClient.send("Fetch.enable", {
|
|
1273
|
+
patterns: [{ requestStage: "Request", urlPattern: "*" }],
|
|
1274
|
+
});
|
|
1275
|
+
} catch (error) {
|
|
1276
|
+
unsubscribe();
|
|
1277
|
+
throw new ProviderError(
|
|
1278
|
+
"CDP browser target does not support BrowserPage.withResourcePolicy()",
|
|
1279
|
+
{
|
|
1280
|
+
cause: error instanceof Error ? error : undefined,
|
|
1281
|
+
code: "BROWSER_RUNTIME_UNSUPPORTED",
|
|
1282
|
+
fix: "Use a Chromium CDP target with the Fetch domain enabled, or use the local Playwright browser runtime.",
|
|
1283
|
+
},
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
try {
|
|
1288
|
+
return await run();
|
|
1289
|
+
} finally {
|
|
1290
|
+
unsubscribe();
|
|
1291
|
+
await this.pageClient.send("Fetch.disable");
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
private async handleResourcePolicyPausedRequest(
|
|
1296
|
+
params: unknown,
|
|
1297
|
+
policy: BrowserResourcePolicy,
|
|
1298
|
+
allowedMethods: ReadonlySet<BrowserResourceMethod>,
|
|
1299
|
+
): Promise<void> {
|
|
1300
|
+
const requestId = getCdpPausedRequestId(params);
|
|
1301
|
+
if (requestId === null) {
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
try {
|
|
1306
|
+
const parsed = toCdpResourceRequest(params);
|
|
1307
|
+
if (!parsed || !allowedMethods.has(parsed.request.method)) {
|
|
1308
|
+
await this.failCdpResourceRequest(requestId);
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
for (const resourceRoute of policy.routes) {
|
|
1313
|
+
if (!matchesResourceRoute(resourceRoute.match, parsed.request)) {
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
const decision = await resourceRoute.handle(parsed.request);
|
|
1318
|
+
switch (decision.action) {
|
|
1319
|
+
case "fulfill":
|
|
1320
|
+
await this.pageClient.send(
|
|
1321
|
+
"Fetch.fulfillRequest",
|
|
1322
|
+
toCdpFulfillParams(parsed.requestId, decision),
|
|
1323
|
+
);
|
|
1324
|
+
return;
|
|
1325
|
+
case "block":
|
|
1326
|
+
await this.failCdpResourceRequest(parsed.requestId);
|
|
1327
|
+
return;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
await this.failCdpResourceRequest(parsed.requestId);
|
|
1332
|
+
} catch {
|
|
1333
|
+
await this.failCdpResourceRequest(requestId);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
private async failCdpResourceRequest(requestId: string): Promise<void> {
|
|
1338
|
+
try {
|
|
1339
|
+
await this.pageClient.send("Fetch.failRequest", {
|
|
1340
|
+
errorReason: "BlockedByClient",
|
|
1341
|
+
requestId,
|
|
1342
|
+
});
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
if (error instanceof Error) {
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1058
1350
|
private async initialize(): Promise<void> {
|
|
1059
1351
|
if (this.initialized) {
|
|
1060
1352
|
return;
|
package/src/server/serve.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
import { Hono } from "hono";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { AuthAbortError, createAuthFlowHelpers } from "../auth";
|
|
6
7
|
import {
|
|
7
8
|
AuthError,
|
|
8
9
|
ProviderError,
|
|
@@ -329,6 +330,7 @@ function createAuthFlowContext(
|
|
|
329
330
|
provider: ProviderDefinition,
|
|
330
331
|
request: AuthFlowRequest,
|
|
331
332
|
options: ProviderServerOptions = {},
|
|
333
|
+
signal?: AbortSignal,
|
|
332
334
|
): {
|
|
333
335
|
context: FlowContext;
|
|
334
336
|
getPatch: () => Record<string, unknown | null> | undefined;
|
|
@@ -383,6 +385,7 @@ function createAuthFlowContext(
|
|
|
383
385
|
credential,
|
|
384
386
|
context: flowContextStore.context,
|
|
385
387
|
stt: options.stt ?? createSttClientFromEnv(provider.stt),
|
|
388
|
+
auth: createAuthFlowHelpers({ signal }),
|
|
386
389
|
},
|
|
387
390
|
getPatch: flowContextStore.getPatch,
|
|
388
391
|
};
|
|
@@ -1291,6 +1294,7 @@ async function handleAuthFlow(
|
|
|
1291
1294
|
request: AuthFlowRequest,
|
|
1292
1295
|
route: AuthRoute,
|
|
1293
1296
|
options: ProviderServerOptions = {},
|
|
1297
|
+
signal?: AbortSignal,
|
|
1294
1298
|
): Promise<Response | AuthFlowResponse> {
|
|
1295
1299
|
const flow = provider.auth?.flow;
|
|
1296
1300
|
if (!flow) {
|
|
@@ -1303,6 +1307,7 @@ async function handleAuthFlow(
|
|
|
1303
1307
|
provider,
|
|
1304
1308
|
request,
|
|
1305
1309
|
options,
|
|
1310
|
+
signal,
|
|
1306
1311
|
);
|
|
1307
1312
|
try {
|
|
1308
1313
|
const result =
|
|
@@ -1336,6 +1341,11 @@ async function handleAuthFlow(
|
|
|
1336
1341
|
? materializeAuthFlowTurn(provider, request, result)
|
|
1337
1342
|
: result;
|
|
1338
1343
|
return toAuthFlowResponse(materializedResult, getPatch());
|
|
1344
|
+
} catch (error) {
|
|
1345
|
+
if (error instanceof AuthAbortError) {
|
|
1346
|
+
return toAuthFlowResponse(error.turn, getPatch());
|
|
1347
|
+
}
|
|
1348
|
+
throw error;
|
|
1339
1349
|
} finally {
|
|
1340
1350
|
context.stealth.close?.();
|
|
1341
1351
|
}
|
|
@@ -1450,7 +1460,13 @@ export function createServerApp(
|
|
|
1450
1460
|
AuthFlowRequestSchema.parse(rawBody),
|
|
1451
1461
|
c.req.raw.headers,
|
|
1452
1462
|
);
|
|
1453
|
-
const response = await handleAuthFlow(
|
|
1463
|
+
const response = await handleAuthFlow(
|
|
1464
|
+
provider,
|
|
1465
|
+
body,
|
|
1466
|
+
"start",
|
|
1467
|
+
options,
|
|
1468
|
+
c.req.raw.signal,
|
|
1469
|
+
);
|
|
1454
1470
|
logProviderSuccess(
|
|
1455
1471
|
logger,
|
|
1456
1472
|
provider,
|
|
@@ -1495,6 +1511,7 @@ export function createServerApp(
|
|
|
1495
1511
|
body,
|
|
1496
1512
|
"continue",
|
|
1497
1513
|
options,
|
|
1514
|
+
c.req.raw.signal,
|
|
1498
1515
|
);
|
|
1499
1516
|
logProviderSuccess(
|
|
1500
1517
|
logger,
|
|
@@ -1535,7 +1552,13 @@ export function createServerApp(
|
|
|
1535
1552
|
AuthFlowRequestSchema.parse(rawBody),
|
|
1536
1553
|
c.req.raw.headers,
|
|
1537
1554
|
);
|
|
1538
|
-
const response = await handleAuthFlow(
|
|
1555
|
+
const response = await handleAuthFlow(
|
|
1556
|
+
provider,
|
|
1557
|
+
body,
|
|
1558
|
+
"poll",
|
|
1559
|
+
options,
|
|
1560
|
+
c.req.raw.signal,
|
|
1561
|
+
);
|
|
1539
1562
|
logProviderSuccess(
|
|
1540
1563
|
logger,
|
|
1541
1564
|
provider,
|
|
@@ -1575,7 +1598,13 @@ export function createServerApp(
|
|
|
1575
1598
|
AuthFlowRequestSchema.parse(rawBody),
|
|
1576
1599
|
c.req.raw.headers,
|
|
1577
1600
|
);
|
|
1578
|
-
const response = await handleAuthFlow(
|
|
1601
|
+
const response = await handleAuthFlow(
|
|
1602
|
+
provider,
|
|
1603
|
+
body,
|
|
1604
|
+
"refresh",
|
|
1605
|
+
options,
|
|
1606
|
+
c.req.raw.signal,
|
|
1607
|
+
);
|
|
1579
1608
|
logProviderSuccess(
|
|
1580
1609
|
logger,
|
|
1581
1610
|
provider,
|
|
@@ -1615,7 +1644,13 @@ export function createServerApp(
|
|
|
1615
1644
|
AuthFlowRequestSchema.parse(rawBody),
|
|
1616
1645
|
c.req.raw.headers,
|
|
1617
1646
|
);
|
|
1618
|
-
const response = await handleAuthFlow(
|
|
1647
|
+
const response = await handleAuthFlow(
|
|
1648
|
+
provider,
|
|
1649
|
+
body,
|
|
1650
|
+
"abort",
|
|
1651
|
+
options,
|
|
1652
|
+
c.req.raw.signal,
|
|
1653
|
+
);
|
|
1619
1654
|
logProviderSuccess(
|
|
1620
1655
|
logger,
|
|
1621
1656
|
provider,
|
package/src/types.ts
CHANGED
|
@@ -331,9 +331,20 @@ export interface HealthJourneySchedule {
|
|
|
331
331
|
kind: "interval";
|
|
332
332
|
/** ISO 8601 duration, for example PT8H. */
|
|
333
333
|
interval: Iso8601Duration;
|
|
334
|
+
randomize?: HealthScheduleRandomization;
|
|
334
335
|
jitter?: Iso8601Duration;
|
|
335
336
|
}
|
|
336
337
|
|
|
338
|
+
export type HealthScheduleRandomization =
|
|
339
|
+
| {
|
|
340
|
+
mode: "centered";
|
|
341
|
+
maxOffset: Iso8601Duration;
|
|
342
|
+
}
|
|
343
|
+
| {
|
|
344
|
+
mode: "delayed";
|
|
345
|
+
maxDelay: Iso8601Duration;
|
|
346
|
+
};
|
|
347
|
+
|
|
337
348
|
export interface HealthJourneyStep {
|
|
338
349
|
id: string;
|
|
339
350
|
description?: string;
|
|
@@ -612,6 +623,9 @@ export interface HealthCheckCase<TInput = unknown, TOutput = unknown> {
|
|
|
612
623
|
export interface HealthCheckSuite<TInput = unknown, TOutput = unknown> {
|
|
613
624
|
/** Polling interval for the suite. All cases share this cadence. */
|
|
614
625
|
interval: ProbeInterval;
|
|
626
|
+
schedule?: {
|
|
627
|
+
randomize?: HealthScheduleRandomization;
|
|
628
|
+
};
|
|
615
629
|
/** Per-case timeout in milliseconds. Default: 30000. */
|
|
616
630
|
timeoutMs?: number;
|
|
617
631
|
/** Default degradation threshold for cases in this suite. Default: runtime threshold. */
|
|
@@ -1244,6 +1258,45 @@ export interface BrowserFrame {
|
|
|
1244
1258
|
locator(selector: string): BrowserLocator;
|
|
1245
1259
|
}
|
|
1246
1260
|
|
|
1261
|
+
export type BrowserResourceMethod = "GET" | "HEAD";
|
|
1262
|
+
|
|
1263
|
+
export type BrowserResourceRequest = {
|
|
1264
|
+
readonly url: string;
|
|
1265
|
+
readonly method: BrowserResourceMethod;
|
|
1266
|
+
readonly resourceType?: string;
|
|
1267
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
export type BrowserResourceBody = Buffer | Uint8Array | ArrayBuffer | string;
|
|
1271
|
+
|
|
1272
|
+
export type BrowserResourceDecision =
|
|
1273
|
+
| {
|
|
1274
|
+
readonly action: "fulfill";
|
|
1275
|
+
readonly status?: number;
|
|
1276
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
1277
|
+
readonly body?: BrowserResourceBody;
|
|
1278
|
+
}
|
|
1279
|
+
| {
|
|
1280
|
+
readonly action: "block";
|
|
1281
|
+
readonly reason?: string;
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
export type BrowserResourceRoute = {
|
|
1285
|
+
readonly match:
|
|
1286
|
+
| string
|
|
1287
|
+
| RegExp
|
|
1288
|
+
| ((request: BrowserResourceRequest) => boolean);
|
|
1289
|
+
readonly handle: (
|
|
1290
|
+
request: BrowserResourceRequest,
|
|
1291
|
+
) => Promise<BrowserResourceDecision> | BrowserResourceDecision;
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
export type BrowserResourcePolicy = {
|
|
1295
|
+
readonly defaultAction?: "block";
|
|
1296
|
+
readonly allowedMethods?: readonly BrowserResourceMethod[];
|
|
1297
|
+
readonly routes: readonly BrowserResourceRoute[];
|
|
1298
|
+
};
|
|
1299
|
+
|
|
1247
1300
|
export interface BrowserPage extends BrowserFrame {
|
|
1248
1301
|
close(): Promise<void>;
|
|
1249
1302
|
fill(selector: string, text: string): Promise<void>;
|
|
@@ -1257,6 +1310,10 @@ export interface BrowserPage extends BrowserFrame {
|
|
|
1257
1310
|
options?: { timeout?: number },
|
|
1258
1311
|
): Promise<void>;
|
|
1259
1312
|
frames(): Promise<BrowserFrame[]>;
|
|
1313
|
+
withResourcePolicy<T>(
|
|
1314
|
+
policy: BrowserResourcePolicy,
|
|
1315
|
+
run: () => Promise<T>,
|
|
1316
|
+
): Promise<T>;
|
|
1260
1317
|
}
|
|
1261
1318
|
|
|
1262
1319
|
export type BrowserChallengeRequest = {
|
|
@@ -1435,6 +1492,84 @@ export interface ContextScratchpad {
|
|
|
1435
1492
|
|
|
1436
1493
|
export type FlowContextStore = ContextScratchpad;
|
|
1437
1494
|
|
|
1495
|
+
export type AuthSafeJson =
|
|
1496
|
+
| string
|
|
1497
|
+
| number
|
|
1498
|
+
| boolean
|
|
1499
|
+
| null
|
|
1500
|
+
| readonly AuthSafeJson[]
|
|
1501
|
+
| { readonly [key: string]: AuthSafeJson };
|
|
1502
|
+
|
|
1503
|
+
export type AuthSafeData = { readonly [key: string]: AuthSafeJson };
|
|
1504
|
+
|
|
1505
|
+
export type AuthAbortRetry = "never" | "retry" | "after_user_action";
|
|
1506
|
+
|
|
1507
|
+
export type AuthAbortData = Record<string, unknown> & {
|
|
1508
|
+
readonly code: string;
|
|
1509
|
+
readonly message?: string;
|
|
1510
|
+
readonly retry?: AuthAbortRetry;
|
|
1511
|
+
readonly actionHint?: AuthSafeJson;
|
|
1512
|
+
readonly fieldErrors?: { readonly [field: string]: string };
|
|
1513
|
+
readonly details?: AuthSafeData;
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1516
|
+
export interface AuthFlowTerminalContext {
|
|
1517
|
+
readonly signal?: AbortSignal;
|
|
1518
|
+
readonly deadline?: string;
|
|
1519
|
+
complete<TCredential extends Record<string, string>>(options: {
|
|
1520
|
+
readonly credential: TCredential;
|
|
1521
|
+
readonly metadata?: AuthSafeData;
|
|
1522
|
+
readonly data?: AuthSafeData;
|
|
1523
|
+
readonly turnId?: string;
|
|
1524
|
+
readonly expiresAt?: string;
|
|
1525
|
+
}): AuthTurn;
|
|
1526
|
+
abort(options: {
|
|
1527
|
+
readonly code: string;
|
|
1528
|
+
readonly message?: string;
|
|
1529
|
+
readonly retry?: AuthAbortRetry;
|
|
1530
|
+
readonly actionHint?: AuthSafeJson;
|
|
1531
|
+
readonly fieldErrors?: { readonly [field: string]: string };
|
|
1532
|
+
readonly data?: AuthSafeData;
|
|
1533
|
+
readonly turnId?: string;
|
|
1534
|
+
readonly expiresAt?: string;
|
|
1535
|
+
}): AuthTurn;
|
|
1536
|
+
nextForm(
|
|
1537
|
+
options: {
|
|
1538
|
+
readonly hintKey?: ProviderLocaleKeyInput;
|
|
1539
|
+
readonly data?: AuthSafeData;
|
|
1540
|
+
readonly turnId?: string;
|
|
1541
|
+
readonly expiresAt?: string;
|
|
1542
|
+
readonly timing?: AuthTurn["timing"];
|
|
1543
|
+
} & (
|
|
1544
|
+
| {
|
|
1545
|
+
readonly fields: Record<
|
|
1546
|
+
string,
|
|
1547
|
+
{
|
|
1548
|
+
readonly type?: "string" | "email" | "password" | "otp";
|
|
1549
|
+
readonly labelKey?: ProviderLocaleKeyInput;
|
|
1550
|
+
readonly descriptionKey?: ProviderLocaleKeyInput;
|
|
1551
|
+
readonly placeholderKey?: ProviderLocaleKeyInput;
|
|
1552
|
+
readonly required?: boolean;
|
|
1553
|
+
readonly sensitive?: boolean;
|
|
1554
|
+
}
|
|
1555
|
+
>;
|
|
1556
|
+
readonly expectedInput?: never;
|
|
1557
|
+
}
|
|
1558
|
+
| {
|
|
1559
|
+
readonly expectedInput: Record<string, unknown>;
|
|
1560
|
+
readonly fields?: never;
|
|
1561
|
+
}
|
|
1562
|
+
),
|
|
1563
|
+
): AuthTurn;
|
|
1564
|
+
nextPoll(options?: {
|
|
1565
|
+
readonly hintKey?: ProviderLocaleKeyInput;
|
|
1566
|
+
readonly data?: AuthSafeData;
|
|
1567
|
+
readonly turnId?: string;
|
|
1568
|
+
readonly expiresAt?: string;
|
|
1569
|
+
readonly timing?: AuthTurn["timing"];
|
|
1570
|
+
}): AuthTurn;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1438
1573
|
export interface FlowContext {
|
|
1439
1574
|
connectionId?: string;
|
|
1440
1575
|
externalRef?: string;
|
|
@@ -1446,6 +1581,7 @@ export interface FlowContext {
|
|
|
1446
1581
|
credential?: CredentialContext;
|
|
1447
1582
|
context: ContextScratchpad;
|
|
1448
1583
|
stt: SttContext;
|
|
1584
|
+
auth: AuthFlowTerminalContext;
|
|
1449
1585
|
}
|
|
1450
1586
|
|
|
1451
1587
|
export interface AuthTurn {
|