@apifuse/provider-sdk 2.2.0-beta.3 → 2.2.0-beta.5
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 +53 -0
- package/CHANGELOG.md +8 -0
- package/bin/apifuse-check.ts +18 -58
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-smoke.ts +22 -81
- package/bin/apifuse-pack-types.ts +266 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +23 -61
- package/bin/apifuse-submit-check.ts +266 -34
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-xml.ts +1 -1
- package/dist/auth-turn/index.d.ts +1 -1
- package/dist/auth.d.ts +2 -2
- package/dist/auth.js +9 -18
- package/dist/ceremonies/index.d.ts +1 -1
- package/dist/ceremonies/index.js +8 -18
- package/dist/config/loader.d.ts +2 -2
- package/dist/config/loader.js +19 -42
- package/dist/contract-serialization.d.ts +2 -2
- package/dist/contract-serialization.js +3 -6
- package/dist/contract-types.d.ts +2 -2
- package/dist/contract.d.ts +3 -3
- package/dist/contract.js +4 -6
- package/dist/define.d.ts +8 -1
- package/dist/define.js +57 -115
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -3
- package/dist/i18n/catalog.d.ts +2 -2
- package/dist/i18n/catalog.js +4 -10
- package/dist/i18n/index.d.ts +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/keys.d.ts +2 -2
- package/dist/index.d.ts +43 -42
- package/dist/index.js +38 -37
- package/dist/lint.js +8 -15
- package/dist/provider.d.ts +11 -11
- package/dist/provider.js +9 -9
- package/dist/public-schema-field-lint.d.ts +1 -1
- package/dist/recipes/gov-api.js +1 -1
- package/dist/runtime/auth-flow.d.ts +1 -1
- package/dist/runtime/auth-flow.js +3 -3
- package/dist/runtime/browser.d.ts +1 -1
- package/dist/runtime/browser.js +15 -29
- package/dist/runtime/cache.d.ts +1 -1
- package/dist/runtime/cache.js +4 -8
- package/dist/runtime/choice.d.ts +1 -1
- package/dist/runtime/choice.js +12 -34
- package/dist/runtime/credential.d.ts +1 -1
- package/dist/runtime/credential.js +1 -1
- package/dist/runtime/env.d.ts +1 -1
- package/dist/runtime/executor.d.ts +1 -1
- package/dist/runtime/executor.js +2 -2
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +5 -5
- package/dist/runtime/insights.d.ts +1 -1
- package/dist/runtime/insights.js +6 -13
- package/dist/runtime/instrumentation.d.ts +2 -2
- package/dist/runtime/instrumentation.js +5 -15
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/otlp.d.ts +1 -1
- package/dist/runtime/perf.d.ts +1 -1
- package/dist/runtime/provider.d.ts +1 -1
- package/dist/runtime/provider.js +1 -2
- package/dist/runtime/proxy-errors.d.ts +1 -1
- package/dist/runtime/proxy-errors.js +3 -5
- package/dist/runtime/proxy-retry-policy.d.ts +2 -2
- package/dist/runtime/proxy-retry-policy.js +2 -2
- package/dist/runtime/proxy-telemetry.d.ts +1 -1
- package/dist/runtime/proxy-telemetry.js +16 -48
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +1 -1
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +3 -3
- package/dist/runtime/stealth.d.ts +2 -2
- package/dist/runtime/stealth.js +6 -6
- package/dist/runtime/stt.d.ts +1 -1
- package/dist/runtime/stt.js +11 -15
- package/dist/runtime/trace.d.ts +2 -2
- package/dist/runtime/trace.js +2 -4
- package/dist/runtime/waterfall.d.ts +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +7 -15
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +1 -1
- package/dist/server/index.d.ts +7 -7
- package/dist/server/index.js +6 -6
- package/dist/server/self-test-redaction.d.ts +1 -1
- package/dist/server/self-test-redaction.js +1 -1
- package/dist/server/self-test.d.ts +104 -3
- package/dist/server/self-test.js +673 -115
- package/dist/server/serve.d.ts +3 -3
- package/dist/server/serve.js +37 -49
- package/dist/server/types.js +2 -7
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream.d.ts +1 -1
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- package/dist/testing/run.d.ts +1 -1
- package/dist/testing/run.js +7 -14
- package/dist/types.d.ts +57 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +6 -4
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +24 -78
- package/src/config/loader.ts +58 -176
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +174 -495
- package/src/dev.ts +4 -9
- package/src/errors.ts +4 -11
- package/src/i18n/catalog.ts +10 -32
- package/src/i18n/index.ts +2 -2
- package/src/i18n/keys.ts +5 -11
- package/src/index.ts +44 -42
- package/src/lint.ts +89 -159
- package/src/provider.ts +12 -11
- package/src/public-schema-field-lint.ts +7 -33
- package/src/recipes/gov-api.ts +2 -5
- package/src/runtime/auth-flow.ts +5 -7
- package/src/runtime/browser.ts +64 -187
- package/src/runtime/cache.ts +20 -67
- package/src/runtime/choice.ts +55 -129
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +9 -19
- package/src/runtime/http.ts +7 -7
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +19 -58
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/otlp.ts +12 -23
- package/src/runtime/perf.ts +1 -1
- package/src/runtime/provider.ts +4 -9
- package/src/runtime/proxy-errors.ts +17 -38
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +23 -72
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +4 -13
- package/src/runtime/state.ts +30 -109
- package/src/runtime/stealth.ts +8 -8
- package/src/runtime/stt.ts +38 -94
- package/src/runtime/trace.ts +14 -44
- package/src/runtime/waterfall.ts +5 -18
- package/src/schema.ts +23 -84
- package/src/serve.ts +1 -1
- package/src/server/index.ts +12 -7
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +89 -238
- package/src/server/types.ts +6 -19
- package/src/stealth/profiles.ts +10 -26
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +2 -2
- package/src/testing/run.ts +19 -67
- package/src/types.ts +58 -0
- package/src/user-input.ts +118 -0
package/dist/auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthError, ProviderError } from "./errors";
|
|
1
|
+
import { AuthError, ProviderError } from "./errors.js";
|
|
2
2
|
const CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY = "__credentialsAuthChallenge";
|
|
3
3
|
const DEFAULT_COMPLETE_TURN_ID = "auth.complete";
|
|
4
4
|
const DEFAULT_ABORT_TURN_ID = "auth.abort";
|
|
@@ -97,7 +97,7 @@ export function createAuthFlowHelpers(options = {}) {
|
|
|
97
97
|
},
|
|
98
98
|
};
|
|
99
99
|
},
|
|
100
|
-
abort({ code, message, retry, actionHint, fieldErrors, data, turnId, expiresAt
|
|
100
|
+
abort({ code, message, retry, actionHint, fieldErrors, data, turnId, expiresAt }) {
|
|
101
101
|
return {
|
|
102
102
|
kind: "abort",
|
|
103
103
|
...authTurnBase({ turnId, defaultTurnId: DEFAULT_ABORT_TURN_ID, expiresAt }),
|
|
@@ -169,12 +169,8 @@ function expectedInputFromFields(fields) {
|
|
|
169
169
|
...(field.type === "password" ? { format: "password" } : {}),
|
|
170
170
|
...(field.type === "otp" ? { format: "otp" } : {}),
|
|
171
171
|
...(field.labelKey ? { nameKey: field.labelKey } : {}),
|
|
172
|
-
...(field.descriptionKey
|
|
173
|
-
|
|
174
|
-
: {}),
|
|
175
|
-
...(field.placeholderKey
|
|
176
|
-
? { placeholderKey: field.placeholderKey }
|
|
177
|
-
: {}),
|
|
172
|
+
...(field.descriptionKey ? { descriptionKey: field.descriptionKey } : {}),
|
|
173
|
+
...(field.placeholderKey ? { placeholderKey: field.placeholderKey } : {}),
|
|
178
174
|
...(field.sensitive || field.type === "password" || field.type === "otp"
|
|
179
175
|
? { sensitive: true }
|
|
180
176
|
: {}),
|
|
@@ -291,17 +287,15 @@ function completeTurn(credentialKeys, result, defaultTurnId) {
|
|
|
291
287
|
};
|
|
292
288
|
}
|
|
293
289
|
function challengeTurn(definition, request) {
|
|
294
|
-
const expectedInput = definition.fields
|
|
295
|
-
? expectedInputFromFields(definition.fields)
|
|
296
|
-
: undefined;
|
|
290
|
+
const expectedInput = definition.fields ? expectedInputFromFields(definition.fields) : undefined;
|
|
297
291
|
return {
|
|
298
292
|
kind: expectedInput ? "form" : "pending",
|
|
299
293
|
turnId: request.turnId ?? definition.turnId ?? `credentials.${request.challengeId}`,
|
|
300
294
|
...(request.expiresAt ? { expiresAt: request.expiresAt } : {}),
|
|
301
|
-
...(request.hintKey ?? definition.hintKey
|
|
295
|
+
...((request.hintKey ?? definition.hintKey)
|
|
302
296
|
? { hintKey: request.hintKey ?? definition.hintKey }
|
|
303
297
|
: {}),
|
|
304
|
-
...(request.timing ?? definition.timing
|
|
298
|
+
...((request.timing ?? definition.timing)
|
|
305
299
|
? { timing: request.timing ?? definition.timing }
|
|
306
300
|
: {}),
|
|
307
301
|
...(expectedInput ? { expectedInput } : {}),
|
|
@@ -378,7 +372,7 @@ async function pollPendingChallenge(ctx, credentialKeys, challenges, pending, co
|
|
|
378
372
|
export function defineCredentialsAuth(options) {
|
|
379
373
|
if (Object.keys(options.fields).length === 0) {
|
|
380
374
|
throw new ProviderError("defineCredentialsAuth requires at least one field", {
|
|
381
|
-
fix:
|
|
375
|
+
fix: 'Pass fields such as { email: { type: "email" }, password: { type: "password" } }.',
|
|
382
376
|
});
|
|
383
377
|
}
|
|
384
378
|
const expectedInput = expectedInputFromFields(options.fields);
|
|
@@ -427,10 +421,7 @@ export function defineCredentialsAuth(options) {
|
|
|
427
421
|
...(options.justification ? { justification: options.justification } : {}),
|
|
428
422
|
},
|
|
429
423
|
context: {
|
|
430
|
-
keys: Array.from(new Set([
|
|
431
|
-
CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY,
|
|
432
|
-
...(options.contextKeys ?? []),
|
|
433
|
-
])),
|
|
424
|
+
keys: Array.from(new Set([CREDENTIALS_AUTH_CHALLENGE_CONTEXT_KEY, ...(options.contextKeys ?? [])])),
|
|
434
425
|
},
|
|
435
426
|
};
|
|
436
427
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthFlowDefinition, AuthTurn } from "../types";
|
|
1
|
+
import type { AuthFlowDefinition, AuthTurn } from "../types.js";
|
|
2
2
|
type JsonObject = Record<string, unknown>;
|
|
3
3
|
export declare function validateCeremonyOutput(turn: unknown): AuthTurn;
|
|
4
4
|
export declare function createOAuth2Ceremony(options: {
|
package/dist/ceremonies/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
3
|
-
import { AUTH_TURN_SCHEMA } from "../auth-turn";
|
|
4
|
-
import { FlowExpiredError, ProviderSecretError, TurnValidationError, ValidationError, } from "../errors";
|
|
3
|
+
import { AUTH_TURN_SCHEMA } from "../auth-turn/index.js";
|
|
4
|
+
import { FlowExpiredError, ProviderSecretError, TurnValidationError, ValidationError, } from "../errors.js";
|
|
5
5
|
const ajv = new Ajv2020({ allErrors: true, strict: true, strictSchema: true });
|
|
6
6
|
// Runtime ceremony-output validation derives from the exported versioned
|
|
7
7
|
// contract: it compiles the exact AUTH_TURN_SCHEMA document shipped at
|
|
@@ -38,11 +38,7 @@ function getRequiredEnv(ctx, key) {
|
|
|
38
38
|
return value;
|
|
39
39
|
}
|
|
40
40
|
function toBase64Url(input) {
|
|
41
|
-
return input
|
|
42
|
-
.toString("base64")
|
|
43
|
-
.replace(/\+/g, "-")
|
|
44
|
-
.replace(/\//g, "_")
|
|
45
|
-
.replace(/=+$/g, "");
|
|
41
|
+
return input.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
46
42
|
}
|
|
47
43
|
function createCodeVerifier() {
|
|
48
44
|
return toBase64Url(randomBytes(32));
|
|
@@ -100,8 +96,7 @@ function withDeclaredFormFieldOrder(expectedInput) {
|
|
|
100
96
|
return expectedInput;
|
|
101
97
|
}
|
|
102
98
|
const existingOrder = expectedInput[FORM_FIELD_ORDER_EXTENSION];
|
|
103
|
-
if (Array.isArray(existingOrder) &&
|
|
104
|
-
existingOrder.every((value) => typeof value === "string")) {
|
|
99
|
+
if (Array.isArray(existingOrder) && existingOrder.every((value) => typeof value === "string")) {
|
|
105
100
|
return expectedInput;
|
|
106
101
|
}
|
|
107
102
|
return {
|
|
@@ -218,8 +213,7 @@ export function createDeviceFlowCeremony(options) {
|
|
|
218
213
|
});
|
|
219
214
|
const data = ensureRecord(response.data);
|
|
220
215
|
const errorCode = getString(data, "error");
|
|
221
|
-
if (errorCode === "authorization_pending" ||
|
|
222
|
-
errorCode === "slow_down") {
|
|
216
|
+
if (errorCode === "authorization_pending" || errorCode === "slow_down") {
|
|
223
217
|
return createTurn("poll", {
|
|
224
218
|
data,
|
|
225
219
|
hint: "Authorization pending.",
|
|
@@ -345,7 +339,7 @@ export function createFormCeremony(options) {
|
|
|
345
339
|
return {
|
|
346
340
|
start: async () => validateCeremonyOutput(buildJsonSchemaForm(options.schema, options.hint ?? "Provide the required input to continue.")),
|
|
347
341
|
continue: (ctx, input = {}) => runCeremonyHandler(async () => {
|
|
348
|
-
const { prevalidate } = await import("../runtime/prevalidate");
|
|
342
|
+
const { prevalidate } = await import("../runtime/prevalidate.js");
|
|
349
343
|
const result = prevalidate(options.schema, input);
|
|
350
344
|
if (!result.valid) {
|
|
351
345
|
throw new ValidationError("Form input failed validation.", {
|
|
@@ -354,9 +348,7 @@ export function createFormCeremony(options) {
|
|
|
354
348
|
}
|
|
355
349
|
return createTurn("complete", {
|
|
356
350
|
data: {
|
|
357
|
-
credential: options.mapCredential
|
|
358
|
-
? options.mapCredential(input)
|
|
359
|
-
: input,
|
|
351
|
+
credential: options.mapCredential ? options.mapCredential(input) : input,
|
|
360
352
|
},
|
|
361
353
|
hint: "Form completed.",
|
|
362
354
|
});
|
|
@@ -428,9 +420,7 @@ export function createSwitchCeremony(options) {
|
|
|
428
420
|
})),
|
|
429
421
|
continue: (ctx, input = {}) => runCeremonyHandler(async () => {
|
|
430
422
|
const storedChoice = ctx.context.get(SWITCH_SELECTION_KEY);
|
|
431
|
-
const choice = typeof storedChoice === "string"
|
|
432
|
-
? storedChoice
|
|
433
|
-
: getString(input, "choice");
|
|
423
|
+
const choice = typeof storedChoice === "string" ? storedChoice : getString(input, "choice");
|
|
434
424
|
if (!choice || !options.choices[choice]) {
|
|
435
425
|
throw new ValidationError("A valid choice is required.");
|
|
436
426
|
}
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Redis from "ioredis";
|
|
2
|
-
import type { ProviderProxyPolicy, TraceConfig } from "../types";
|
|
1
|
+
import { Redis } from "ioredis";
|
|
2
|
+
import type { ProviderProxyPolicy, TraceConfig } from "../types.js";
|
|
3
3
|
export declare const SMARTPROXY_APP_KEY_ENV = "APIFUSE__PROXY__SMARTPROXY_APP_KEY";
|
|
4
4
|
export declare const SMARTPROXY_MAX_LIFETIME_MINUTES = 2000;
|
|
5
5
|
export declare const DEFAULT_SMARTPROXY_POOL_SIZE = 20;
|
package/dist/config/loader.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import Redis from "ioredis";
|
|
4
|
+
import { Redis } from "ioredis";
|
|
5
5
|
export const SMARTPROXY_APP_KEY_ENV = "APIFUSE__PROXY__SMARTPROXY_APP_KEY";
|
|
6
6
|
export const SMARTPROXY_MAX_LIFETIME_MINUTES = 2000;
|
|
7
7
|
export const DEFAULT_SMARTPROXY_POOL_SIZE = 20;
|
|
@@ -160,9 +160,7 @@ function toProxyDiagnostics(value) {
|
|
|
160
160
|
return undefined;
|
|
161
161
|
const diagnostics = {};
|
|
162
162
|
for (const [key, item] of Object.entries(value)) {
|
|
163
|
-
if (typeof item === "string" ||
|
|
164
|
-
typeof item === "number" ||
|
|
165
|
-
typeof item === "boolean") {
|
|
163
|
+
if (typeof item === "string" || typeof item === "number" || typeof item === "boolean") {
|
|
166
164
|
diagnostics[key] = item;
|
|
167
165
|
}
|
|
168
166
|
}
|
|
@@ -249,9 +247,7 @@ function applyStickyProxySession(proxyUrl) {
|
|
|
249
247
|
}
|
|
250
248
|
function buildSmartproxyUsername(username, sessionId, sessionDuration) {
|
|
251
249
|
const parts = username.split("_");
|
|
252
|
-
const configuredLife = parts
|
|
253
|
-
.find((part) => part.startsWith("life-"))
|
|
254
|
-
?.slice("life-".length);
|
|
250
|
+
const configuredLife = parts.find((part) => part.startsWith("life-"))?.slice("life-".length);
|
|
255
251
|
const baseUsername = parts
|
|
256
252
|
.filter((part) => !part.startsWith("session-") && !part.startsWith("life-"))
|
|
257
253
|
.join("_");
|
|
@@ -259,9 +255,7 @@ function buildSmartproxyUsername(username, sessionId, sessionDuration) {
|
|
|
259
255
|
}
|
|
260
256
|
function buildDecodoUsername(username, sessionId, sessionDuration) {
|
|
261
257
|
const withoutSticky = username.replace(/-session-.+-sessionduration-\d+$/, "");
|
|
262
|
-
const baseUsername = withoutSticky.startsWith("user-")
|
|
263
|
-
? withoutSticky
|
|
264
|
-
: `user-${withoutSticky}`;
|
|
258
|
+
const baseUsername = withoutSticky.startsWith("user-") ? withoutSticky : `user-${withoutSticky}`;
|
|
265
259
|
return `${baseUsername}-session-${sessionId}-sessionduration-${sessionDuration}`;
|
|
266
260
|
}
|
|
267
261
|
function syncProxyEnv(config) {
|
|
@@ -358,18 +352,13 @@ function resolvePolicy(options) {
|
|
|
358
352
|
return undefined;
|
|
359
353
|
}
|
|
360
354
|
function resolveProxyProvider(policy) {
|
|
361
|
-
return (policy.provider ??
|
|
362
|
-
process.env[DEFAULT_PROXY_PROVIDER_ENV]?.trim().toLowerCase() ??
|
|
363
|
-
"custom");
|
|
355
|
+
return (policy.provider ?? process.env[DEFAULT_PROXY_PROVIDER_ENV]?.trim().toLowerCase() ?? "custom");
|
|
364
356
|
}
|
|
365
357
|
function resolveSmartproxyCountry(policy) {
|
|
366
|
-
return (policy.geo?.country ??
|
|
367
|
-
process.env[DEFAULT_PROXY_COUNTRY_ENV]?.trim().toUpperCase() ??
|
|
368
|
-
undefined);
|
|
358
|
+
return (policy.geo?.country ?? process.env[DEFAULT_PROXY_COUNTRY_ENV]?.trim().toUpperCase() ?? undefined);
|
|
369
359
|
}
|
|
370
360
|
function resolveSmartproxyLifetime(policy) {
|
|
371
|
-
const configuredLifetime = policy.session?.lifetimeMinutes ??
|
|
372
|
-
readPositiveNumberEnv(DEFAULT_PROXY_LIFETIME_ENV, 30);
|
|
361
|
+
const configuredLifetime = policy.session?.lifetimeMinutes ?? readPositiveNumberEnv(DEFAULT_PROXY_LIFETIME_ENV, 30);
|
|
373
362
|
return Math.min(SMARTPROXY_MAX_LIFETIME_MINUTES, Math.max(1, Math.floor(configuredLifetime)));
|
|
374
363
|
}
|
|
375
364
|
function readPositiveNumberEnv(name, fallback) {
|
|
@@ -389,9 +378,7 @@ function selectProxyPoolIndex(poolSize, attempt = 0) {
|
|
|
389
378
|
if (poolSize <= 1) {
|
|
390
379
|
return 0;
|
|
391
380
|
}
|
|
392
|
-
const normalizedAttempt = Number.isFinite(attempt)
|
|
393
|
-
? Math.max(0, Math.floor(attempt))
|
|
394
|
-
: 0;
|
|
381
|
+
const normalizedAttempt = Number.isFinite(attempt) ? Math.max(0, Math.floor(attempt)) : 0;
|
|
395
382
|
return normalizedAttempt % poolSize;
|
|
396
383
|
}
|
|
397
384
|
function buildSmartproxyCacheKey(policy, affinityKey, lifetimeMinutes) {
|
|
@@ -400,9 +387,7 @@ function buildSmartproxyCacheKey(policy, affinityKey, lifetimeMinutes) {
|
|
|
400
387
|
provider: "smartproxy",
|
|
401
388
|
country: resolveSmartproxyCountry(policy),
|
|
402
389
|
affinity: policy.session?.affinity ?? "request",
|
|
403
|
-
affinityKey: (policy.session?.affinity ?? "request") === "request"
|
|
404
|
-
? undefined
|
|
405
|
-
: affinityKey,
|
|
390
|
+
affinityKey: (policy.session?.affinity ?? "request") === "request" ? undefined : affinityKey,
|
|
406
391
|
lifetimeMinutes,
|
|
407
392
|
poolSize,
|
|
408
393
|
});
|
|
@@ -499,9 +484,7 @@ async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinute
|
|
|
499
484
|
if (acquired === "OK") {
|
|
500
485
|
try {
|
|
501
486
|
return await allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetimeMinutes, startedAt, {
|
|
502
|
-
cacheStatus: options.background
|
|
503
|
-
? "soft_stale_refresh"
|
|
504
|
-
: "allocator",
|
|
487
|
+
cacheStatus: options.background ? "soft_stale_refresh" : "allocator",
|
|
505
488
|
redis,
|
|
506
489
|
poolKey,
|
|
507
490
|
});
|
|
@@ -526,8 +509,7 @@ async function allocateSmartproxyShared(cacheKey, policy, appKey, lifetimeMinute
|
|
|
526
509
|
if (typeof pttl === "number" && pttl <= 0) {
|
|
527
510
|
continue;
|
|
528
511
|
}
|
|
529
|
-
if (Date.now() - startedAt >
|
|
530
|
-
SMARTPROXY_LOCK_POLL_MAX_MS - SMARTPROXY_DEADLINE_MARGIN_MS) {
|
|
512
|
+
if (Date.now() - startedAt > SMARTPROXY_LOCK_POLL_MAX_MS - SMARTPROXY_DEADLINE_MARGIN_MS) {
|
|
531
513
|
break;
|
|
532
514
|
}
|
|
533
515
|
await sleep(Math.min(500, Math.max(50, typeof pttl === "number" ? pttl : 100)));
|
|
@@ -559,7 +541,7 @@ function sleep(ms, signal) {
|
|
|
559
541
|
});
|
|
560
542
|
}
|
|
561
543
|
function smartproxyAllocatorDeadlineMs() {
|
|
562
|
-
return
|
|
544
|
+
return smartproxyAllocatorDeadlineMsForTests ?? SMARTPROXY_ALLOCATOR_DEADLINE_MS;
|
|
563
545
|
}
|
|
564
546
|
function createDeadlineAbortController(deadlineAt) {
|
|
565
547
|
const controller = new AbortController();
|
|
@@ -649,9 +631,7 @@ async function allocateAndStoreSmartproxyPool(cacheKey, policy, appKey, lifetime
|
|
|
649
631
|
}
|
|
650
632
|
}
|
|
651
633
|
const allocatorMs = Math.max(0, Date.now() - allocatorStartedAt);
|
|
652
|
-
const allocatorAttempts = allocation?.attempt ??
|
|
653
|
-
lastFailure?.attempt ??
|
|
654
|
-
SMARTPROXY_ALLOCATOR_MAX_ATTEMPTS;
|
|
634
|
+
const allocatorAttempts = allocation?.attempt ?? lastFailure?.attempt ?? SMARTPROXY_ALLOCATOR_MAX_ATTEMPTS;
|
|
655
635
|
const allocatorBodyClass = lastFailure?.bodyClass ?? allocation?.bodyClass ?? "usable_proxy_endpoints";
|
|
656
636
|
const allocatorStatus = lastFailure ? lastFailure.status : allocation?.status;
|
|
657
637
|
if (!allocation) {
|
|
@@ -804,7 +784,9 @@ function buildSmartproxyAllocatorUrl(policy, appKey, lifetimeMinutes, poolSize)
|
|
|
804
784
|
if (country) {
|
|
805
785
|
params.set("cc", country);
|
|
806
786
|
}
|
|
807
|
-
|
|
787
|
+
// www.smartproxy.org stopped serving the extraction API on 2026-07-21 (the
|
|
788
|
+
// old path 404s into the marketing site); the API lives on the api host.
|
|
789
|
+
return `https://api.smartproxy.org/web_v1/ip/get-ip-v3?${params.toString()}`;
|
|
808
790
|
}
|
|
809
791
|
function parseSmartproxyAllocatorProxies(body) {
|
|
810
792
|
const trimmed = body.trim();
|
|
@@ -813,12 +795,8 @@ function parseSmartproxyAllocatorProxies(body) {
|
|
|
813
795
|
}
|
|
814
796
|
try {
|
|
815
797
|
const parsed = JSON.parse(trimmed);
|
|
816
|
-
const data = parsed && typeof parsed === "object" && "data" in parsed
|
|
817
|
-
|
|
818
|
-
: undefined;
|
|
819
|
-
const list = data && typeof data === "object" && "list" in data
|
|
820
|
-
? data.list
|
|
821
|
-
: undefined;
|
|
798
|
+
const data = parsed && typeof parsed === "object" && "data" in parsed ? parsed.data : undefined;
|
|
799
|
+
const list = data && typeof data === "object" && "list" in data ? data.list : undefined;
|
|
822
800
|
if (Array.isArray(list)) {
|
|
823
801
|
return list
|
|
824
802
|
.map((item) => {
|
|
@@ -826,8 +804,7 @@ function parseSmartproxyAllocatorProxies(body) {
|
|
|
826
804
|
return null;
|
|
827
805
|
}
|
|
828
806
|
const ip = "ip" in item && typeof item.ip === "string" ? item.ip : "";
|
|
829
|
-
const port = "port" in item &&
|
|
830
|
-
(typeof item.port === "string" || typeof item.port === "number")
|
|
807
|
+
const port = "port" in item && (typeof item.port === "string" || typeof item.port === "number")
|
|
831
808
|
? item.port
|
|
832
809
|
: "";
|
|
833
810
|
return ip && port ? `http://${ip}:${port}` : null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type JsonValue } from "./contract-json";
|
|
2
|
-
import type { SchemaLike } from "./types";
|
|
1
|
+
import { type JsonValue } from "./contract-json.js";
|
|
2
|
+
import type { SchemaLike } from "./types.js";
|
|
3
3
|
export declare function describeSchema(schema: SchemaLike): JsonValue;
|
|
4
4
|
export declare function serializeSmsMatcher(value: Record<string, unknown>): Record<string, unknown>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { canonicalJson, compactObject, isRecord, toJsonValue, } from "./contract-json";
|
|
3
|
+
import { canonicalJson, compactObject, isRecord, toJsonValue, } from "./contract-json.js";
|
|
4
4
|
export function describeSchema(schema) {
|
|
5
5
|
if (isZodSchema(schema)) {
|
|
6
6
|
const jsonSchema = zodJsonSchema(schema);
|
|
@@ -9,9 +9,7 @@ export function describeSchema(schema) {
|
|
|
9
9
|
vendor: "zod",
|
|
10
10
|
typeName: getSchemaTypeName(schema),
|
|
11
11
|
jsonSchema,
|
|
12
|
-
jsonSchemaHash: jsonSchema === undefined
|
|
13
|
-
? undefined
|
|
14
|
-
: digest(canonicalJson(jsonSchema)),
|
|
12
|
+
jsonSchemaHash: jsonSchema === undefined ? undefined : digest(canonicalJson(jsonSchema)),
|
|
15
13
|
});
|
|
16
14
|
}
|
|
17
15
|
const standard = isRecord(schema) ? schema["~standard"] : undefined;
|
|
@@ -20,8 +18,7 @@ export function describeSchema(schema) {
|
|
|
20
18
|
kind: "schema",
|
|
21
19
|
standard: "standard-schema-v1",
|
|
22
20
|
vendor: typeof standard.vendor === "string" ? standard.vendor : "unknown",
|
|
23
|
-
version: typeof standard.version === "number" ||
|
|
24
|
-
typeof standard.version === "string"
|
|
21
|
+
version: typeof standard.version === "number" || typeof standard.version === "string"
|
|
25
22
|
? standard.version
|
|
26
23
|
: undefined,
|
|
27
24
|
});
|
package/dist/contract-types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { JsonValue } from "./contract-json";
|
|
2
|
-
import type { ProviderDefinition } from "./types";
|
|
1
|
+
import type { JsonValue } from "./contract-json.js";
|
|
2
|
+
import type { ProviderDefinition } from "./types.js";
|
|
3
3
|
export declare const PROVIDER_CONTRACT_SCHEMA_VERSION = "2026-06-23";
|
|
4
4
|
export interface ProviderContractSnapshot {
|
|
5
5
|
readonly schemaVersion: typeof PROVIDER_CONTRACT_SCHEMA_VERSION;
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { canonicalJson, type JsonPrimitive, type JsonValue } from "./contract-json";
|
|
2
|
-
import { PROVIDER_CONTRACT_SCHEMA_VERSION, type ProviderContractOperation, type ProviderContractSnapshot } from "./contract-types";
|
|
3
|
-
import type { ProviderDefinition } from "./types";
|
|
1
|
+
import { canonicalJson, type JsonPrimitive, type JsonValue } from "./contract-json.js";
|
|
2
|
+
import { PROVIDER_CONTRACT_SCHEMA_VERSION, type ProviderContractOperation, type ProviderContractSnapshot } from "./contract-types.js";
|
|
3
|
+
import type { ProviderDefinition } from "./types.js";
|
|
4
4
|
export { canonicalJson, type JsonPrimitive, type JsonValue, PROVIDER_CONTRACT_SCHEMA_VERSION, type ProviderContractOperation, type ProviderContractSnapshot, };
|
|
5
5
|
export declare function extractProviderContract(provider: ProviderDefinition): ProviderContractSnapshot;
|
|
6
6
|
export declare function digestProviderContract(snapshot: ProviderContractSnapshot): string;
|
package/dist/contract.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { canonicalJson, compactObject, copyRecordWithout, toJsonValue, } from "./contract-json";
|
|
3
|
-
import { describeSchema, serializeSmsMatcher } from "./contract-serialization";
|
|
4
|
-
import { PROVIDER_CONTRACT_SCHEMA_VERSION, } from "./contract-types";
|
|
2
|
+
import { canonicalJson, compactObject, copyRecordWithout, toJsonValue, } from "./contract-json.js";
|
|
3
|
+
import { describeSchema, serializeSmsMatcher } from "./contract-serialization.js";
|
|
4
|
+
import { PROVIDER_CONTRACT_SCHEMA_VERSION, } from "./contract-types.js";
|
|
5
5
|
export { canonicalJson, PROVIDER_CONTRACT_SCHEMA_VERSION, };
|
|
6
6
|
export function extractProviderContract(provider) {
|
|
7
7
|
const auth = extractAuth(provider.auth);
|
|
@@ -27,9 +27,7 @@ export function extractProviderContract(provider) {
|
|
|
27
27
|
operations: Object.entries(provider.operations)
|
|
28
28
|
.sort(([leftId], [rightId]) => leftId.localeCompare(rightId))
|
|
29
29
|
.map(([operationId, operation]) => extractOperation(operationId, operation)),
|
|
30
|
-
...(provider.allowedHosts
|
|
31
|
-
? { allowedHosts: [...provider.allowedHosts].sort() }
|
|
32
|
-
: {}),
|
|
30
|
+
...(provider.allowedHosts ? { allowedHosts: [...provider.allowedHosts].sort() } : {}),
|
|
33
31
|
...(stealth === undefined ? {} : { stealth }),
|
|
34
32
|
...(proxy === undefined ? {} : { proxy }),
|
|
35
33
|
...(stt === undefined ? {} : { stt }),
|
package/dist/define.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport, ProviderAccessConfig, ProviderDefinition, ProviderHealthMonitorConfig, ProviderProxyConfig, ProviderPublicProfile, ProviderReviewed, ProviderSecretDeclaration, ProviderStreamEvent, ProviderSttConfig, SchemaLike, SmsOtpMatcherDefinition, StealthPlatform } from "./types";
|
|
1
|
+
import type { AuthConfig, BrowserEngine, ContextDeclaration, CredentialDeclaration, HealthJourneyDefinition, HealthJourneySchedule, HealthScheduleRandomization, InferSchemaOutput, OperationDefinition, OperationHandlerResult, OperationHttpStreamTransport, OperationSseTransport, OperationWebSocketTransport, ProviderAccessConfig, ProviderDefinition, ProviderDeploymentOverrides, ProviderHealthMonitorConfig, ProviderProxyConfig, ProviderPublicProfile, ProviderReviewed, ProviderSecretDeclaration, ProviderStreamEvent, ProviderSttConfig, SchemaLike, SmsOtpMatcherDefinition, StealthPlatform } from "./types.js";
|
|
2
2
|
type ProviderImplementationSourceAccess = "official_api" | "private_api" | "browser_flow" | "hybrid";
|
|
3
3
|
type ProviderImplementationCredentialStrategy = "apifuse_managed" | "workspace_secret" | "user_oauth" | "user_session" | "none";
|
|
4
4
|
interface ProviderImplementationProfile {
|
|
@@ -41,6 +41,13 @@ export interface ProviderConfig<TOperations extends Record<string, ProviderOpera
|
|
|
41
41
|
id: string;
|
|
42
42
|
version: string;
|
|
43
43
|
runtime: "standard" | "shared" | "browser";
|
|
44
|
+
/**
|
|
45
|
+
* Optional deployment overrides, passed through verbatim onto the returned
|
|
46
|
+
* provider definition. The SDK types this field but does not deep-validate
|
|
47
|
+
* it — the APIFuse registry builder owns deployment validation and
|
|
48
|
+
* resolves omitted fields against the runtime deployment profiles.
|
|
49
|
+
*/
|
|
50
|
+
deployment?: ProviderDeploymentOverrides;
|
|
44
51
|
allowedHosts?: string[];
|
|
45
52
|
stealth?: {
|
|
46
53
|
profile: string;
|