@apifuse/provider-sdk 2.2.0-beta.26 → 2.2.0-beta.28
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/CHANGELOG.md +8 -0
- package/bin/apifuse-dev.ts +2 -2
- package/bin/apifuse-pack-smoke.ts +1 -1
- package/bin/apifuse-pack-types.ts +2 -1
- package/bin/apifuse-perf.ts +2 -4
- package/bin/apifuse-record.ts +1 -1
- package/dist/auth-turn/index.d.ts +1 -1
- package/dist/auth-turn/index.js +1 -1
- package/dist/ceremonies/index.js +52 -11
- package/dist/config/loader.d.ts +1 -1
- package/dist/config/loader.js +4 -2
- package/dist/contract-serialization.d.ts +1 -20
- package/dist/contract-serialization.js +8 -587
- package/dist/contract.d.ts +0 -2
- package/dist/contract.js +5 -9
- package/dist/index.d.ts +9 -8
- package/dist/index.js +6 -8
- package/dist/provider.d.ts +3 -2
- package/dist/provider.js +2 -2
- package/dist/runtime/auth-flow.js +1 -1
- package/dist/runtime/http.d.ts +1 -0
- package/dist/runtime/http.js +135 -12
- package/dist/runtime/instrumentation.js +1 -1
- package/dist/runtime/native-network-errors.d.ts +33 -0
- package/dist/runtime/native-network-errors.js +69 -0
- package/dist/runtime/native-network.d.ts +2 -33
- package/dist/runtime/native-network.js +2 -68
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +4 -2
- package/dist/runtime/resolver-config.d.ts +6 -0
- package/dist/runtime/resolver-config.js +6 -0
- package/dist/runtime/resolver-shared.d.ts +3 -0
- package/dist/runtime/resolver-shared.js +12 -0
- package/dist/runtime/resolver-vendors/twocaptcha.d.ts +2 -1
- package/dist/runtime/resolver-vendors/twocaptcha.js +80 -43
- package/dist/runtime/resolver-vendors/types.d.ts +1 -1
- package/dist/runtime/resolver.d.ts +6 -10
- package/dist/runtime/resolver.js +19 -18
- package/dist/runtime/state.js +5 -115
- package/dist/runtime/stealth-cookies.d.ts +20 -0
- package/dist/runtime/stealth-cookies.js +111 -0
- package/dist/runtime/stealth.js +7 -130
- package/dist/schema.d.ts +0 -63
- package/dist/schema.js +8 -808
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/self-test.d.ts +13 -0
- package/dist/server/self-test.js +124 -46
- package/dist/server/serve-implementation.d.ts +199 -0
- package/dist/server/serve-implementation.js +2054 -0
- package/dist/server/serve.d.ts +1 -187
- package/dist/server/serve.js +1 -1827
- package/dist/stateful/errors.d.ts +5 -0
- package/dist/stateful/errors.js +10 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +1 -5
- package/dist/stateful/stateful-provider-session-routing.js +2 -10
- package/dist/stream.js +7 -1
- package/package.json +27 -2
- package/src/auth-turn/index.ts +1 -1
- package/src/ceremonies/index.ts +68 -18
- package/src/config/loader.ts +5 -2
- package/src/contract-serialization.ts +8 -859
- package/src/contract.ts +5 -16
- package/src/index.ts +18 -34
- package/src/provider.ts +12 -24
- package/src/runtime/auth-flow.ts +1 -1
- package/src/runtime/http.ts +155 -11
- package/src/runtime/instrumentation.ts +1 -1
- package/src/runtime/native-network-errors.ts +99 -0
- package/src/runtime/native-network.ts +16 -97
- package/src/runtime/redis.ts +7 -2
- package/src/runtime/resolver-config.ts +6 -0
- package/src/runtime/resolver-shared.ts +17 -0
- package/src/runtime/resolver-vendors/twocaptcha.ts +100 -49
- package/src/runtime/resolver-vendors/types.ts +1 -0
- package/src/runtime/resolver.ts +47 -22
- package/src/runtime/state.ts +5 -144
- package/src/runtime/stealth-cookies.ts +132 -0
- package/src/runtime/stealth.ts +14 -157
- package/src/schema.ts +9 -1060
- package/src/serve.ts +6 -1
- package/src/server/index.ts +1 -0
- package/src/server/self-test.ts +184 -59
- package/src/server/serve-implementation.ts +3024 -0
- package/src/server/serve.ts +1 -2661
- package/src/stateful/errors.ts +12 -0
- package/src/stateful/stateful-provider-session-routing.ts +2 -11
- package/src/stream.ts +8 -1
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export type StatefulControlPlaneOperation = "resolve" | "acquire" | "renew" | "release";
|
|
2
|
+
export declare class StatefulRoutingDeadlineError extends Error {
|
|
3
|
+
readonly requestId: string;
|
|
4
|
+
readonly deadlineAt: string;
|
|
5
|
+
constructor(requestId: string, deadlineAt: string);
|
|
6
|
+
}
|
|
2
7
|
export declare class StatefulControlPlaneError extends Error {
|
|
3
8
|
readonly code: string;
|
|
4
9
|
readonly operation: StatefulControlPlaneOperation;
|
package/dist/stateful/errors.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
export class StatefulRoutingDeadlineError extends Error {
|
|
2
|
+
requestId;
|
|
3
|
+
deadlineAt;
|
|
4
|
+
constructor(requestId, deadlineAt) {
|
|
5
|
+
super(`Stateful operation request ${requestId} deadline has expired.`);
|
|
6
|
+
this.name = "StatefulRoutingDeadlineError";
|
|
7
|
+
this.requestId = requestId;
|
|
8
|
+
this.deadlineAt = deadlineAt;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
1
11
|
export class StatefulControlPlaneError extends Error {
|
|
2
12
|
code;
|
|
3
13
|
operation;
|
|
@@ -40,11 +40,7 @@ export interface StatefulSessionRouterOptions {
|
|
|
40
40
|
readonly clock?: () => Date;
|
|
41
41
|
readonly metricEmitter?: StatefulProviderMetricEmitter;
|
|
42
42
|
}
|
|
43
|
-
export
|
|
44
|
-
readonly requestId: string;
|
|
45
|
-
readonly deadlineAt: string;
|
|
46
|
-
constructor(requestId: string, deadlineAt: string);
|
|
47
|
-
}
|
|
43
|
+
export { StatefulRoutingDeadlineError } from "./errors.js";
|
|
48
44
|
export declare class StatefulRoutingOwnershipError extends Error {
|
|
49
45
|
readonly requestId: string;
|
|
50
46
|
readonly sessionKey: SessionKey;
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
+
import { StatefulRoutingDeadlineError } from "./errors.js";
|
|
1
2
|
import { NOOP_STATEFUL_PROVIDER_METRIC_EMITTER, } from "./stateful-provider-observability.js";
|
|
2
3
|
export { forwardingContextFromStatefulRuntimeContext, isStatefulOperationRuntimeContext, providerContextFromStatefulRuntimeContext, statefulForwardingContextFromProviderRequest, withStatefulLocalProviderContext, } from "./stateful-provider-runtime-context.js";
|
|
3
|
-
export
|
|
4
|
-
requestId;
|
|
5
|
-
deadlineAt;
|
|
6
|
-
constructor(requestId, deadlineAt) {
|
|
7
|
-
super(`Stateful operation request ${requestId} deadline has expired.`);
|
|
8
|
-
this.name = "StatefulRoutingDeadlineError";
|
|
9
|
-
this.requestId = requestId;
|
|
10
|
-
this.deadlineAt = deadlineAt;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
4
|
+
export { StatefulRoutingDeadlineError } from "./errors.js";
|
|
13
5
|
export class StatefulRoutingOwnershipError extends Error {
|
|
14
6
|
requestId;
|
|
15
7
|
sessionKey;
|
package/dist/stream.js
CHANGED
|
@@ -41,16 +41,22 @@ function sseFieldValue(value, field) {
|
|
|
41
41
|
}
|
|
42
42
|
export async function* readableBytes(body) {
|
|
43
43
|
const reader = body.getReader();
|
|
44
|
+
let completed = false;
|
|
44
45
|
try {
|
|
45
46
|
for (;;) {
|
|
46
47
|
const { value, done } = await reader.read();
|
|
47
|
-
if (done)
|
|
48
|
+
if (done) {
|
|
49
|
+
completed = true;
|
|
48
50
|
return;
|
|
51
|
+
}
|
|
49
52
|
if (value)
|
|
50
53
|
yield value;
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
finally {
|
|
57
|
+
if (!completed) {
|
|
58
|
+
await reader.cancel().catch(() => undefined);
|
|
59
|
+
}
|
|
54
60
|
reader.releaseLock();
|
|
55
61
|
}
|
|
56
62
|
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.2.0-beta.
|
|
2
|
+
"version": "2.2.0-beta.28",
|
|
3
3
|
"name": "@apifuse/provider-sdk",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
@@ -61,6 +61,31 @@
|
|
|
61
61
|
"import": "./dist/server/index.js",
|
|
62
62
|
"default": "./dist/server/index.js"
|
|
63
63
|
},
|
|
64
|
+
"./runtime/browser": {
|
|
65
|
+
"types": "./dist/runtime/browser.d.ts",
|
|
66
|
+
"import": "./dist/runtime/browser.js",
|
|
67
|
+
"default": "./dist/runtime/browser.js"
|
|
68
|
+
},
|
|
69
|
+
"./runtime/native-network": {
|
|
70
|
+
"types": "./dist/runtime/native-network.d.ts",
|
|
71
|
+
"import": "./dist/runtime/native-network.js",
|
|
72
|
+
"default": "./dist/runtime/native-network.js"
|
|
73
|
+
},
|
|
74
|
+
"./runtime/prevalidate": {
|
|
75
|
+
"types": "./dist/runtime/prevalidate.d.ts",
|
|
76
|
+
"import": "./dist/runtime/prevalidate.js",
|
|
77
|
+
"default": "./dist/runtime/prevalidate.js"
|
|
78
|
+
},
|
|
79
|
+
"./runtime/resolver": {
|
|
80
|
+
"types": "./dist/runtime/resolver.d.ts",
|
|
81
|
+
"import": "./dist/runtime/resolver.js",
|
|
82
|
+
"default": "./dist/runtime/resolver.js"
|
|
83
|
+
},
|
|
84
|
+
"./runtime/stealth": {
|
|
85
|
+
"types": "./dist/runtime/stealth.d.ts",
|
|
86
|
+
"import": "./dist/runtime/stealth.js",
|
|
87
|
+
"default": "./dist/runtime/stealth.js"
|
|
88
|
+
},
|
|
64
89
|
"./stateful": {
|
|
65
90
|
"types": "./dist/stateful/index.d.ts",
|
|
66
91
|
"import": "./dist/stateful/index.js",
|
|
@@ -97,6 +122,7 @@
|
|
|
97
122
|
"@biomejs/biome": "^2.5.0",
|
|
98
123
|
"@types/bun": "latest",
|
|
99
124
|
"@types/node": "^25.9.3",
|
|
125
|
+
"ajv": "^8.17",
|
|
100
126
|
"typescript": "6.0.3"
|
|
101
127
|
},
|
|
102
128
|
"dependencies": {
|
|
@@ -104,7 +130,6 @@
|
|
|
104
130
|
"@rgrove/parse-xml": "4.2.2",
|
|
105
131
|
"@types/ms": "^2.1.0",
|
|
106
132
|
"acorn": "^8.17.0",
|
|
107
|
-
"ajv": "^8.17",
|
|
108
133
|
"hono": "^4.12.25",
|
|
109
134
|
"ioredis": "^5.11.1",
|
|
110
135
|
"ms": "^2.1.3",
|
package/src/auth-turn/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ export const AUTH_TURN_SCHEMA_ARTIFACT_PATH = "dist/auth-turn/auth-turn.v1.schem
|
|
|
24
24
|
*
|
|
25
25
|
* This is the exact codification of the runtime validation the SDK applies to
|
|
26
26
|
* ceremony outputs (see `validateCeremonyOutput` in `src/ceremonies`), which
|
|
27
|
-
*
|
|
27
|
+
* evaluates this same document. `kind` is an OPEN string on the wire: the known
|
|
28
28
|
* kinds in {@link TURN_KINDS} are tooling metadata, never a wire constraint.
|
|
29
29
|
*
|
|
30
30
|
* The committed artifact at `src/auth-turn/auth-turn.v1.schema.json` (shipped
|
package/src/ceremonies/index.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
|
|
3
|
-
import Ajv2020 from "ajv/dist/2020.js";
|
|
4
|
-
|
|
5
3
|
import { AUTH_TURN_SCHEMA, type KnownAuthTurnKind } from "../auth-turn/index.js";
|
|
6
4
|
import {
|
|
7
5
|
FlowExpiredError,
|
|
@@ -16,13 +14,14 @@ type TurnKind = KnownAuthTurnKind;
|
|
|
16
14
|
type CeremonyHandler = AuthFlowInputHandler;
|
|
17
15
|
|
|
18
16
|
type JsonObject = Record<string, unknown>;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
type JsonSchemaNode = {
|
|
18
|
+
readonly additionalProperties?: boolean;
|
|
19
|
+
readonly minimum?: number;
|
|
20
|
+
readonly minLength?: number;
|
|
21
|
+
readonly properties?: Readonly<Record<string, JsonSchemaNode>>;
|
|
22
|
+
readonly required?: readonly string[];
|
|
23
|
+
readonly type?: "number" | "object" | "string";
|
|
24
|
+
};
|
|
26
25
|
|
|
27
26
|
const OAUTH2_STATE_KEY = "__oauth2_state";
|
|
28
27
|
const OAUTH2_PKCE_VERIFIER_KEY = "__oauth2_pkce_verifier";
|
|
@@ -45,6 +44,56 @@ function isRecord(value: unknown): value is JsonObject {
|
|
|
45
44
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
46
45
|
}
|
|
47
46
|
|
|
47
|
+
function validateSchemaNode(
|
|
48
|
+
value: unknown,
|
|
49
|
+
schema: JsonSchemaNode,
|
|
50
|
+
path: string,
|
|
51
|
+
errors: string[],
|
|
52
|
+
): void {
|
|
53
|
+
if (schema.type === "object") {
|
|
54
|
+
if (!isRecord(value)) {
|
|
55
|
+
errors.push(`${path} must be object`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const properties = schema.properties ?? {};
|
|
59
|
+
for (const required of schema.required ?? []) {
|
|
60
|
+
if (!Object.hasOwn(value, required) || value[required] === undefined) {
|
|
61
|
+
errors.push(`${path} must have required property '${required}'`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (schema.additionalProperties === false) {
|
|
65
|
+
for (const key of Object.keys(value)) {
|
|
66
|
+
if (!Object.hasOwn(properties, key)) {
|
|
67
|
+
errors.push(`${path} must NOT have additional property '${key}'`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const [key, childSchema] of Object.entries(properties)) {
|
|
72
|
+
if (Object.hasOwn(value, key) && value[key] !== undefined) {
|
|
73
|
+
validateSchemaNode(value[key], childSchema, `${path}/${key}`, errors);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (schema.type === "string") {
|
|
80
|
+
if (typeof value !== "string") {
|
|
81
|
+
errors.push(`${path} must be string`);
|
|
82
|
+
} else if (schema.minLength !== undefined && value.length < schema.minLength) {
|
|
83
|
+
errors.push(`${path} must NOT have fewer than ${schema.minLength} characters`);
|
|
84
|
+
}
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (schema.type === "number") {
|
|
89
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
90
|
+
errors.push(`${path} must be number`);
|
|
91
|
+
} else if (schema.minimum !== undefined && value < schema.minimum) {
|
|
92
|
+
errors.push(`${path} must be >= ${schema.minimum}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
48
97
|
function ensureRecord(value: unknown): JsonObject {
|
|
49
98
|
return isRecord(value) ? value : {};
|
|
50
99
|
}
|
|
@@ -162,14 +211,17 @@ function withDeclaredFormFieldOrder(expectedInput: JsonObject): JsonObject {
|
|
|
162
211
|
}
|
|
163
212
|
|
|
164
213
|
export function validateCeremonyOutput(turn: unknown): AuthTurn {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
214
|
+
// Evaluate the exact exported versioned schema without eagerly initializing
|
|
215
|
+
// a general-purpose JSON Schema compiler in every provider process. Contract
|
|
216
|
+
// parity tests compare this focused evaluator with AJV over all fixtures and
|
|
217
|
+
// edge probes, so the runtime and shipped document remain locked together.
|
|
218
|
+
const errors: string[] = [];
|
|
219
|
+
validateSchemaNode(turn, AUTH_TURN_SCHEMA, "", errors);
|
|
220
|
+
if (errors.length > 0) {
|
|
221
|
+
throw new TurnValidationError(errors.join("; "));
|
|
170
222
|
}
|
|
171
223
|
|
|
172
|
-
return turn;
|
|
224
|
+
return turn as AuthTurn;
|
|
173
225
|
}
|
|
174
226
|
|
|
175
227
|
export function createOAuth2Ceremony(options: {
|
|
@@ -285,9 +337,7 @@ export function createOAuth2ProxiedStart(
|
|
|
285
337
|
}
|
|
286
338
|
const clientId = getRequiredEnv(ctx, options.clientIdEnvKey);
|
|
287
339
|
if (!ctx.flowId) {
|
|
288
|
-
throw new ValidationError(
|
|
289
|
-
"OAuth2 proxied start requires the gateway flow id.",
|
|
290
|
-
);
|
|
340
|
+
throw new ValidationError("OAuth2 proxied start requires the gateway flow id.");
|
|
291
341
|
}
|
|
292
342
|
|
|
293
343
|
const authorizePath = new URL(options.authorizeUrl).pathname;
|
package/src/config/loader.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
|
|
5
|
-
import { Redis } from "ioredis";
|
|
6
|
+
import type { Redis } from "ioredis";
|
|
6
7
|
|
|
7
8
|
import type { ProviderProxyPolicy, ProviderProxyProvider, TraceConfig } from "../types.js";
|
|
8
9
|
import {
|
|
@@ -217,6 +218,7 @@ type ProxyRedisClient = Pick<
|
|
|
217
218
|
>;
|
|
218
219
|
|
|
219
220
|
const proxyCache = new Map<string, CachedProxyPool>();
|
|
221
|
+
const require = createRequire(import.meta.url);
|
|
220
222
|
const proxyInflight = new Map<string, Promise<SmartproxyAllocationResult>>();
|
|
221
223
|
const invalidatedProxyKeys = new Map<string, number>();
|
|
222
224
|
const redisClients = new Map<string, ProxyRedisClient>();
|
|
@@ -276,7 +278,8 @@ function getProxyRedis(): ProxyRedisClient | undefined {
|
|
|
276
278
|
const existing = redisClients.get(redisUrl);
|
|
277
279
|
if (existing) return existing;
|
|
278
280
|
|
|
279
|
-
const
|
|
281
|
+
const { Redis: RedisClient } = require("ioredis") as typeof import("ioredis");
|
|
282
|
+
const redis = new RedisClient(redisUrl, {
|
|
280
283
|
connectTimeout: REDIS_TIMEOUT_MS,
|
|
281
284
|
enableOfflineQueue: false,
|
|
282
285
|
lazyConnect: true,
|