@apifuse/provider-sdk 2.2.0-beta.2 → 2.2.0-beta.20
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 +487 -0
- package/CHANGELOG.md +85 -0
- package/README.md +49 -3
- package/SUBMISSION.md +1 -1
- package/bin/apifuse-check.ts +44 -59
- package/bin/apifuse-create.ts +1 -1
- package/bin/apifuse-dev.ts +27 -52
- package/bin/apifuse-pack-check.ts +14 -0
- package/bin/apifuse-pack-smoke.ts +36 -81
- package/bin/apifuse-pack-types.ts +305 -0
- package/bin/apifuse-perf.ts +45 -127
- package/bin/apifuse-record.ts +659 -111
- package/bin/apifuse-submit-check.ts +546 -48
- package/bin/apifuse-sync-assets.ts +117 -0
- package/bin/apifuse.ts +1 -1
- package/bin/submit-check-delimited-text.ts +50 -0
- package/bin/submit-check-xml-semantics.ts +204 -0
- package/bin/submit-check-xml.ts +134 -0
- package/dist/auth-turn/index.d.ts +3 -3
- 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/cli/commands.d.ts +1 -1
- package/dist/cli/commands.js +8 -0
- package/dist/cli/create.d.ts +3 -0
- package/dist/cli/create.js +34 -35
- package/dist/cli/prompt-assets.d.ts +80 -0
- package/dist/cli/prompt-assets.js +743 -0
- package/dist/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/dist/config/loader.d.ts +176 -8
- package/dist/config/loader.js +424 -95
- 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 +9 -1
- package/dist/define.js +208 -125
- package/dist/dev.d.ts +1 -1
- package/dist/dev.js +1 -1
- package/dist/error-resolution.d.ts +2 -0
- package/dist/error-resolution.js +90 -0
- package/dist/errors.d.ts +22 -1
- package/dist/errors.js +88 -0
- package/dist/fixture-sanitization.d.ts +26 -0
- package/dist/fixture-sanitization.js +216 -0
- 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 +46 -42
- package/dist/index.js +41 -37
- package/dist/lint.js +8 -15
- package/dist/native-address.d.ts +43 -0
- package/dist/native-address.js +281 -0
- package/dist/native-egress-policy.d.ts +31 -0
- package/dist/native-egress-policy.js +288 -0
- package/dist/observability.d.ts +5 -2
- package/dist/observability.js +48 -1
- package/dist/provider.d.ts +12 -11
- package/dist/provider.js +10 -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 +31 -35
- 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 +31 -4
- package/dist/runtime/http.d.ts +2 -2
- package/dist/runtime/http.js +387 -47
- 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 +345 -22
- package/dist/runtime/keyring.js +1 -1
- package/dist/runtime/namespace.js +1 -1
- package/dist/runtime/native-network.d.ts +127 -0
- package/dist/runtime/native-network.js +1298 -0
- 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 +9 -7
- package/dist/runtime/proxy-nodemaven.d.ts +56 -0
- package/dist/runtime/proxy-nodemaven.js +146 -0
- 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 +2 -1
- package/dist/runtime/proxy-telemetry.js +55 -52
- package/dist/runtime/redirects.d.ts +29 -0
- package/dist/runtime/redirects.js +36 -0
- package/dist/runtime/redis.d.ts +1 -1
- package/dist/runtime/redis.js +2 -4
- package/dist/runtime/request-options.d.ts +68 -1
- package/dist/runtime/request-options.js +548 -0
- package/dist/runtime/secrets.d.ts +27 -0
- package/dist/runtime/secrets.js +51 -0
- package/dist/runtime/state.d.ts +2 -2
- package/dist/runtime/state.js +15 -4
- package/dist/runtime/stealth.d.ts +5 -3
- package/dist/runtime/stealth.js +423 -143
- 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-input-tokens.d.ts +2 -1
- package/dist/server/self-test-input-tokens.js +18 -14
- 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 +115 -4
- package/dist/server/serve.js +792 -127
- package/dist/server/types.d.ts +30 -9
- package/dist/server/types.js +8 -7
- package/dist/stateful/errors.d.ts +14 -0
- package/dist/stateful/errors.js +14 -0
- package/dist/stateful/http-provider-event-emitter.d.ts +40 -0
- package/dist/stateful/http-provider-event-emitter.js +237 -0
- package/dist/stateful/http-session-owner-registry.d.ts +44 -0
- package/dist/stateful/http-session-owner-registry.js +210 -0
- package/dist/stateful/index.d.ts +18 -0
- package/dist/stateful/index.js +18 -0
- package/dist/stateful/provider-event-delivery-failures.d.ts +32 -0
- package/dist/stateful/provider-event-delivery-failures.js +43 -0
- package/dist/stateful/provider-event-pipeline-metrics.d.ts +46 -0
- package/dist/stateful/provider-event-pipeline-metrics.js +48 -0
- package/dist/stateful/provider-event-pipeline.d.ts +50 -0
- package/dist/stateful/provider-event-pipeline.js +1 -0
- package/dist/stateful/provider-events.d.ts +101 -0
- package/dist/stateful/provider-events.js +289 -0
- package/dist/stateful/session-key.d.ts +15 -0
- package/dist/stateful/session-key.js +86 -0
- package/dist/stateful/stateful-provider-adapter-context.d.ts +5 -0
- package/dist/stateful/stateful-provider-adapter-context.js +42 -0
- package/dist/stateful/stateful-provider-adapter-metrics.d.ts +15 -0
- package/dist/stateful/stateful-provider-adapter-metrics.js +21 -0
- package/dist/stateful/stateful-provider-adapter.d.ts +98 -0
- package/dist/stateful/stateful-provider-adapter.js +287 -0
- package/dist/stateful/stateful-provider-observability.d.ts +62 -0
- package/dist/stateful/stateful-provider-observability.js +161 -0
- package/dist/stateful/stateful-provider-owner-forwarder.d.ts +41 -0
- package/dist/stateful/stateful-provider-owner-forwarder.js +215 -0
- package/dist/stateful/stateful-provider-runtime-context.d.ts +32 -0
- package/dist/stateful/stateful-provider-runtime-context.js +60 -0
- package/dist/stateful/stateful-provider-runtime-executor.d.ts +34 -0
- package/dist/stateful/stateful-provider-runtime-executor.js +52 -0
- package/dist/stateful/stateful-provider-session-routing.d.ts +71 -0
- package/dist/stateful/stateful-provider-session-routing.js +353 -0
- package/dist/stateful/stateful-provider-session-runtime.d.ts +98 -0
- package/dist/stateful/stateful-provider-session-runtime.js +245 -0
- package/dist/stateful-signing.d.ts +18 -0
- package/dist/stateful-signing.js +27 -0
- package/dist/stealth/profiles.d.ts +1 -1
- package/dist/stealth/profiles.js +5 -14
- package/dist/stream-evidence.d.ts +74 -0
- package/dist/stream-evidence.js +785 -0
- 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 +32 -2
- package/dist/testing/run.js +477 -33
- package/dist/types.d.ts +323 -11
- package/dist/types.js +1 -0
- package/dist/user-input.d.ts +30 -0
- package/dist/user-input.js +66 -0
- package/package.json +16 -5
- package/src/auth-turn/index.ts +1 -1
- package/src/auth.ts +28 -86
- package/src/ceremonies/index.ts +24 -78
- package/src/cli/commands.ts +10 -0
- package/src/cli/create.ts +42 -35
- package/src/cli/prompt-assets.ts +865 -0
- package/src/cli/templates/provider/AGENTS.md.tpl +17 -8
- package/src/config/loader.ts +652 -204
- package/src/contract-serialization.ts +5 -11
- package/src/contract-types.ts +2 -2
- package/src/contract.ts +12 -28
- package/src/define.ts +368 -504
- package/src/dev.ts +4 -9
- package/src/error-resolution.ts +91 -0
- package/src/errors.ts +110 -5
- package/src/fixture-sanitization.ts +247 -0
- 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 +111 -42
- package/src/lint.ts +89 -159
- package/src/native-address.ts +340 -0
- package/src/native-egress-policy.ts +358 -0
- package/src/observability.ts +51 -1
- package/src/provider.ts +65 -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 +79 -132
- package/src/runtime/credential.ts +2 -2
- package/src/runtime/env.ts +1 -1
- package/src/runtime/executor.ts +43 -20
- package/src/runtime/http.ts +494 -57
- package/src/runtime/insights.ts +15 -53
- package/src/runtime/instrumentation.ts +495 -66
- package/src/runtime/keyring.ts +7 -19
- package/src/runtime/namespace.ts +2 -7
- package/src/runtime/native-network.ts +1686 -0
- 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 +29 -42
- package/src/runtime/proxy-nodemaven.ts +221 -0
- package/src/runtime/proxy-retry-policy.ts +3 -3
- package/src/runtime/proxy-telemetry.ts +79 -77
- package/src/runtime/redirects.ts +66 -0
- package/src/runtime/redis.ts +4 -12
- package/src/runtime/request-options.ts +679 -9
- package/src/runtime/secrets.ts +64 -0
- package/src/runtime/state.ts +41 -110
- package/src/runtime/stealth.ts +505 -154
- 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 +29 -7
- package/src/server/self-test-input-tokens.ts +29 -14
- package/src/server/self-test-redaction.ts +2 -2
- package/src/server/self-test.ts +857 -132
- package/src/server/serve.ts +1141 -324
- package/src/server/types.ts +12 -19
- package/src/stateful/README.md +146 -0
- package/src/stateful/errors.ts +23 -0
- package/src/stateful/http-provider-event-emitter.ts +314 -0
- package/src/stateful/http-session-owner-registry.ts +306 -0
- package/src/stateful/index.ts +18 -0
- package/src/stateful/provider-event-delivery-failures.ts +80 -0
- package/src/stateful/provider-event-pipeline-metrics.ts +95 -0
- package/src/stateful/provider-event-pipeline.ts +61 -0
- package/src/stateful/provider-events.ts +462 -0
- package/src/stateful/session-key.ts +111 -0
- package/src/stateful/stateful-provider-adapter-context.ts +59 -0
- package/src/stateful/stateful-provider-adapter-metrics.ts +48 -0
- package/src/stateful/stateful-provider-adapter.ts +562 -0
- package/src/stateful/stateful-provider-observability.ts +261 -0
- package/src/stateful/stateful-provider-owner-forwarder.ts +287 -0
- package/src/stateful/stateful-provider-runtime-context.ts +92 -0
- package/src/stateful/stateful-provider-runtime-executor.ts +96 -0
- package/src/stateful/stateful-provider-session-routing.ts +555 -0
- package/src/stateful/stateful-provider-session-runtime.ts +403 -0
- package/src/stateful-signing.ts +46 -0
- package/src/stealth/profiles.ts +10 -26
- package/src/stream-evidence.ts +988 -0
- package/src/stream.ts +8 -19
- package/src/testing/index.ts +10 -2
- package/src/testing/run.ts +652 -79
- package/src/types.ts +369 -11
- package/src/user-input.ts +118 -0
- package/dist/cli/templates/provider/CLAUDE.md.tpl +0 -1
- package/src/cli/templates/provider/CLAUDE.md.tpl +0 -1
- /package/dist/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/dist/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/fixtures-and-recording/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/health-checks-and-fail-closed/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/normalization-standards/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/pagination-and-counts/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-contract-verification/SKILL.md.tpl +0 -0
- /package/src/cli/templates/provider/{skills → .agents/skills}/upstream-notes/README.md.tpl +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ProviderSecretError } from "../errors.js";
|
|
2
|
+
import type { EnvContext, ProviderDefinition } from "../types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Canonical error code for a declared-but-unprovisioned provider secret.
|
|
6
|
+
*
|
|
7
|
+
* The SDK is the single source of truth for env/secret presence validation:
|
|
8
|
+
* providers declare secrets in `defineProvider({ secrets: [...] })` and the
|
|
9
|
+
* runtime enforces presence before any handler or auth-flow code runs.
|
|
10
|
+
* Provider-local presence guards (requireServiceKey/requireApiKey style) are a
|
|
11
|
+
* deprecated antipattern — see the `sdk-owned-secret-presence` submit-check
|
|
12
|
+
* rule.
|
|
13
|
+
*/
|
|
14
|
+
export const MISSING_SECRET_CODE = "MISSING_SECRET";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Names of declared `required: true` secrets whose env values are unset or
|
|
18
|
+
* whitespace-only. Whitespace-only values count as missing for parity with the
|
|
19
|
+
* `.trim()` guards well-built providers used before the SDK owned this check —
|
|
20
|
+
* a blank value provisioned by a broken secret pipeline must not pass the gate.
|
|
21
|
+
*/
|
|
22
|
+
export function listMissingRequiredSecrets(
|
|
23
|
+
provider: ProviderDefinition,
|
|
24
|
+
env: EnvContext,
|
|
25
|
+
): string[] {
|
|
26
|
+
const missing: string[] = [];
|
|
27
|
+
for (const secret of provider.secrets ?? []) {
|
|
28
|
+
if (secret.required !== true) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const value = env.get(secret.name);
|
|
32
|
+
if (value === undefined || value.trim() === "") {
|
|
33
|
+
missing.push(secret.name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return missing;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Throws the canonical structured missing-secret error when any declared
|
|
41
|
+
* `required: true` secret is absent. All missing names are reported in a
|
|
42
|
+
* single error so operators can provision the full set in one pass instead of
|
|
43
|
+
* discovering them one deploy at a time (the 2026-07-22 unprovisioned-secret
|
|
44
|
+
* incident failure mode).
|
|
45
|
+
*/
|
|
46
|
+
export function assertRequiredSecretsPresent(
|
|
47
|
+
provider: ProviderDefinition,
|
|
48
|
+
env: EnvContext,
|
|
49
|
+
): void {
|
|
50
|
+
const missing = listMissingRequiredSecrets(provider, env);
|
|
51
|
+
if (missing.length === 0) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const names = missing.join(", ");
|
|
55
|
+
throw new ProviderSecretError(
|
|
56
|
+
`Missing required provider secret${missing.length > 1 ? "s" : ""}: ${names}`,
|
|
57
|
+
{
|
|
58
|
+
code: MISSING_SECRET_CODE,
|
|
59
|
+
category: "credential_unavailable",
|
|
60
|
+
retryable: false,
|
|
61
|
+
fix: `Provision ${names} in the provider environment (e.g. Doppler). Declared in defineProvider({ secrets: [...] }).`,
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
}
|
package/src/runtime/state.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { providerStateRedisUrlFromEnv } from "../config/loader";
|
|
2
|
-
import { ProviderError } from "../errors";
|
|
1
|
+
import { providerStateRedisUrlFromEnv } from "../config/loader.js";
|
|
2
|
+
import { ProviderError } from "../errors.js";
|
|
3
3
|
import type {
|
|
4
4
|
ProviderRuntimeState,
|
|
5
5
|
ProviderStateNamespace,
|
|
@@ -7,13 +7,13 @@ import type {
|
|
|
7
7
|
StateNamespaceOptions,
|
|
8
8
|
StateValue,
|
|
9
9
|
StateWriteOptions,
|
|
10
|
-
} from "../types";
|
|
10
|
+
} from "../types.js";
|
|
11
11
|
import {
|
|
12
12
|
createProviderRedisClient,
|
|
13
13
|
ensureRedisReady,
|
|
14
14
|
type ProviderRedisClient,
|
|
15
15
|
withRedisTimeout,
|
|
16
|
-
} from "./redis";
|
|
16
|
+
} from "./redis.js";
|
|
17
17
|
|
|
18
18
|
const DEFAULT_REDIS_TIMEOUT_MS = 250;
|
|
19
19
|
const REDIS_STATE_PREFIX = "apifuse:provider-state:v1";
|
|
@@ -57,37 +57,24 @@ async function withRequiredRedis<T>(operation: () => Promise<T>): Promise<T> {
|
|
|
57
57
|
return await withRedisTimeout(operation, {
|
|
58
58
|
timeoutMs: DEFAULT_REDIS_TIMEOUT_MS,
|
|
59
59
|
onTimeout: () => {
|
|
60
|
-
throw new UnsupportedProviderStateError(
|
|
61
|
-
"Provider runtime state Redis timed out",
|
|
62
|
-
);
|
|
60
|
+
throw new UnsupportedProviderStateError("Provider runtime state Redis timed out");
|
|
63
61
|
},
|
|
64
62
|
onError: () => {
|
|
65
|
-
throw new UnsupportedProviderStateError(
|
|
66
|
-
"Provider runtime state Redis is unavailable",
|
|
67
|
-
);
|
|
63
|
+
throw new UnsupportedProviderStateError("Provider runtime state Redis is unavailable");
|
|
68
64
|
},
|
|
69
65
|
});
|
|
70
66
|
}
|
|
71
67
|
|
|
72
68
|
async function requireRedisReady(redis: ProviderRedisClient): Promise<void> {
|
|
73
69
|
if (await ensureRedisReady(redis, DEFAULT_REDIS_TIMEOUT_MS)) return;
|
|
74
|
-
throw new UnsupportedProviderStateError(
|
|
75
|
-
"Provider runtime state Redis is unavailable",
|
|
76
|
-
);
|
|
70
|
+
throw new UnsupportedProviderStateError("Provider runtime state Redis is unavailable");
|
|
77
71
|
}
|
|
78
72
|
|
|
79
|
-
function providerStatePrefix(
|
|
80
|
-
providerId: string | undefined,
|
|
81
|
-
namespace: string,
|
|
82
|
-
): string {
|
|
73
|
+
function providerStatePrefix(providerId: string | undefined, namespace: string): string {
|
|
83
74
|
return `${REDIS_STATE_PREFIX}:${providerId ?? "default"}:${namespace}`;
|
|
84
75
|
}
|
|
85
76
|
|
|
86
|
-
function providerStateKey(
|
|
87
|
-
providerId: string | undefined,
|
|
88
|
-
namespace: string,
|
|
89
|
-
key: string,
|
|
90
|
-
): string {
|
|
77
|
+
function providerStateKey(providerId: string | undefined, namespace: string, key: string): string {
|
|
91
78
|
return `${providerStatePrefix(providerId, namespace)}:${key}`;
|
|
92
79
|
}
|
|
93
80
|
|
|
@@ -128,13 +115,21 @@ function envelopeFromJson(
|
|
|
128
115
|
// biome-ignore lint/suspicious/noExplicitAny: state envelopes deserialize caller-owned generic values.
|
|
129
116
|
): StateValue<any> | null {
|
|
130
117
|
if (!raw) return null;
|
|
131
|
-
|
|
118
|
+
// A corrupt/undecodable persisted envelope must be treated as absent rather
|
|
119
|
+
// than throwing a raw JSON.parse SyntaxError: an uncaught SyntaxError escapes
|
|
120
|
+
// the provider error taxonomy, is masked as internal_error 500, and is then
|
|
121
|
+
// retried by the hub (2026-07-22 catchtable reserve RCA, candidate A). Returning
|
|
122
|
+
// null also keeps list() from aborting the whole scan on a single bad entry.
|
|
123
|
+
let parsed: unknown;
|
|
124
|
+
try {
|
|
125
|
+
parsed = JSON.parse(raw);
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
132
129
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
133
130
|
return null;
|
|
134
131
|
}
|
|
135
|
-
const record: Record<string, unknown> = Object.fromEntries(
|
|
136
|
-
Object.entries(parsed),
|
|
137
|
-
);
|
|
132
|
+
const record: Record<string, unknown> = Object.fromEntries(Object.entries(parsed));
|
|
138
133
|
if (
|
|
139
134
|
typeof record.version !== "number" ||
|
|
140
135
|
typeof record.expiresAt !== "string" ||
|
|
@@ -182,9 +177,7 @@ class RedisProviderStateNamespace implements ProviderStateNamespace {
|
|
|
182
177
|
|
|
183
178
|
private async activeKeys(): Promise<string[]> {
|
|
184
179
|
await requireRedisReady(this.backend.redis);
|
|
185
|
-
return await withRequiredRedis(() =>
|
|
186
|
-
this.backend.redis.keys(`${this.prefix()}*`),
|
|
187
|
-
);
|
|
180
|
+
return await withRequiredRedis(() => this.backend.redis.keys(`${this.prefix()}*`));
|
|
188
181
|
}
|
|
189
182
|
|
|
190
183
|
private enforceValueSize(value: unknown): void {
|
|
@@ -207,47 +200,29 @@ class RedisProviderStateNamespace implements ProviderStateNamespace {
|
|
|
207
200
|
}
|
|
208
201
|
}
|
|
209
202
|
|
|
210
|
-
async list<T>(options?: {
|
|
211
|
-
limit?: number;
|
|
212
|
-
prefix?: string;
|
|
213
|
-
}): Promise<StateValue<T>[]> {
|
|
203
|
+
async list<T>(options?: { limit?: number; prefix?: string }): Promise<StateValue<T>[]> {
|
|
214
204
|
const keys = (await this.activeKeys()).filter((key) => {
|
|
215
|
-
const publicKey = publicStateKey(
|
|
216
|
-
this.providerId,
|
|
217
|
-
this.namespaceName,
|
|
218
|
-
key,
|
|
219
|
-
);
|
|
205
|
+
const publicKey = publicStateKey(this.providerId, this.namespaceName, key);
|
|
220
206
|
return options?.prefix ? publicKey.startsWith(options.prefix) : true;
|
|
221
207
|
});
|
|
222
208
|
const limited = keys.slice(0, Math.max(0, options?.limit ?? keys.length));
|
|
223
209
|
if (limited.length === 0) return [];
|
|
224
|
-
const values = await withRequiredRedis(() =>
|
|
225
|
-
this.backend.redis.mget(limited),
|
|
226
|
-
);
|
|
210
|
+
const values = await withRequiredRedis(() => this.backend.redis.mget(limited));
|
|
227
211
|
return values.flatMap((raw, index) => {
|
|
228
212
|
const key = limited[index];
|
|
229
213
|
if (!key) return [];
|
|
230
|
-
const value = envelopeFromJson(
|
|
231
|
-
publicStateKey(this.providerId, this.namespaceName, key),
|
|
232
|
-
raw,
|
|
233
|
-
);
|
|
214
|
+
const value = envelopeFromJson(publicStateKey(this.providerId, this.namespaceName, key), raw);
|
|
234
215
|
return value ? [value] : [];
|
|
235
216
|
});
|
|
236
217
|
}
|
|
237
218
|
|
|
238
219
|
async get<T>(key: string): Promise<StateValue<T> | null> {
|
|
239
220
|
await requireRedisReady(this.backend.redis);
|
|
240
|
-
const raw = await withRequiredRedis(() =>
|
|
241
|
-
this.backend.redis.get(this.redisKey(key)),
|
|
242
|
-
);
|
|
221
|
+
const raw = await withRequiredRedis(() => this.backend.redis.get(this.redisKey(key)));
|
|
243
222
|
return envelopeFromJson(key, raw);
|
|
244
223
|
}
|
|
245
224
|
|
|
246
|
-
async set<T>(
|
|
247
|
-
key: string,
|
|
248
|
-
value: T,
|
|
249
|
-
options?: StateWriteOptions,
|
|
250
|
-
): Promise<StateValue<T>> {
|
|
225
|
+
async set<T>(key: string, value: T, options?: StateWriteOptions): Promise<StateValue<T>> {
|
|
251
226
|
this.enforceValueSize(value);
|
|
252
227
|
await this.enforceMaxEntries(key);
|
|
253
228
|
const current = await this.get<T>(key);
|
|
@@ -258,12 +233,7 @@ class RedisProviderStateNamespace implements ProviderStateNamespace {
|
|
|
258
233
|
const expiresAt = resolveExpiresAt(ttl);
|
|
259
234
|
const envelope = redisEnvelope(value, version, createdAt, expiresAt);
|
|
260
235
|
await withRequiredRedis(() =>
|
|
261
|
-
this.backend.redis.set(
|
|
262
|
-
this.redisKey(key),
|
|
263
|
-
JSON.stringify(envelope),
|
|
264
|
-
"PX",
|
|
265
|
-
ttlMs,
|
|
266
|
-
),
|
|
236
|
+
this.backend.redis.set(this.redisKey(key), JSON.stringify(envelope), "PX", ttlMs),
|
|
267
237
|
);
|
|
268
238
|
return {
|
|
269
239
|
key,
|
|
@@ -313,11 +283,7 @@ class RedisProviderStateNamespace implements ProviderStateNamespace {
|
|
|
313
283
|
): Promise<StateValue<Record<string, unknown>>> {
|
|
314
284
|
const current = (await this.get<Record<string, unknown>>(key))?.value ?? {};
|
|
315
285
|
const previous = typeof current[field] === "number" ? current[field] : 0;
|
|
316
|
-
return await this.set(
|
|
317
|
-
key,
|
|
318
|
-
{ ...current, [field]: previous + delta },
|
|
319
|
-
options,
|
|
320
|
-
);
|
|
286
|
+
return await this.set(key, { ...current, [field]: previous + delta }, options);
|
|
321
287
|
}
|
|
322
288
|
}
|
|
323
289
|
|
|
@@ -330,43 +296,26 @@ class RedisProviderRuntimeState implements ProviderRuntimeState {
|
|
|
330
296
|
this.providerId = options.providerId;
|
|
331
297
|
}
|
|
332
298
|
|
|
333
|
-
namespace(
|
|
334
|
-
name
|
|
335
|
-
options: StateNamespaceOptions,
|
|
336
|
-
): ProviderStateNamespace {
|
|
337
|
-
return new RedisProviderStateNamespace(
|
|
338
|
-
this.backend,
|
|
339
|
-
this.providerId,
|
|
340
|
-
name,
|
|
341
|
-
options,
|
|
342
|
-
);
|
|
299
|
+
namespace(name: string, options: StateNamespaceOptions): ProviderStateNamespace {
|
|
300
|
+
return new RedisProviderStateNamespace(this.backend, this.providerId, name, options);
|
|
343
301
|
}
|
|
344
302
|
}
|
|
345
303
|
|
|
346
304
|
export class UnsupportedProviderStateError extends ProviderError {
|
|
347
|
-
constructor(
|
|
348
|
-
message = "Provider runtime state is not available in this runtime",
|
|
349
|
-
) {
|
|
305
|
+
constructor(message = "Provider runtime state is not available in this runtime") {
|
|
350
306
|
super(message, { code: "PROVIDER_STATE_UNSUPPORTED" });
|
|
351
307
|
this.name = "UnsupportedProviderStateError";
|
|
352
308
|
}
|
|
353
309
|
}
|
|
354
310
|
|
|
355
311
|
class UnsupportedProviderStateNamespace implements ProviderStateNamespace {
|
|
356
|
-
async list<T>(_options?: {
|
|
357
|
-
limit?: number;
|
|
358
|
-
prefix?: string;
|
|
359
|
-
}): Promise<StateValue<T>[]> {
|
|
312
|
+
async list<T>(_options?: { limit?: number; prefix?: string }): Promise<StateValue<T>[]> {
|
|
360
313
|
throw new UnsupportedProviderStateError();
|
|
361
314
|
}
|
|
362
315
|
async get<T>(_key: string): Promise<StateValue<T> | null> {
|
|
363
316
|
throw new UnsupportedProviderStateError();
|
|
364
317
|
}
|
|
365
|
-
async set<T>(
|
|
366
|
-
_key: string,
|
|
367
|
-
_value: T,
|
|
368
|
-
_options?: StateWriteOptions,
|
|
369
|
-
): Promise<StateValue<T>> {
|
|
318
|
+
async set<T>(_key: string, _value: T, _options?: StateWriteOptions): Promise<StateValue<T>> {
|
|
370
319
|
throw new UnsupportedProviderStateError();
|
|
371
320
|
}
|
|
372
321
|
async patch<T extends Record<string, unknown>>(
|
|
@@ -398,10 +347,7 @@ class UnsupportedProviderStateNamespace implements ProviderStateNamespace {
|
|
|
398
347
|
}
|
|
399
348
|
|
|
400
349
|
class UnsupportedProviderRuntimeState implements ProviderRuntimeState {
|
|
401
|
-
namespace(
|
|
402
|
-
_name: string,
|
|
403
|
-
_options: StateNamespaceOptions,
|
|
404
|
-
): ProviderStateNamespace {
|
|
350
|
+
namespace(_name: string, _options: StateNamespaceOptions): ProviderStateNamespace {
|
|
405
351
|
return new UnsupportedProviderStateNamespace();
|
|
406
352
|
}
|
|
407
353
|
}
|
|
@@ -420,10 +366,7 @@ class MemoryProviderStateNamespace implements ProviderStateNamespace {
|
|
|
420
366
|
}
|
|
421
367
|
}
|
|
422
368
|
|
|
423
|
-
async list<T>(_options?: {
|
|
424
|
-
limit?: number;
|
|
425
|
-
prefix?: string;
|
|
426
|
-
}): Promise<StateValue<T>[]> {
|
|
369
|
+
async list<T>(_options?: { limit?: number; prefix?: string }): Promise<StateValue<T>[]> {
|
|
427
370
|
this.pruneExpired();
|
|
428
371
|
const rows = Array.from(this.values.values()).filter((value) =>
|
|
429
372
|
_options?.prefix ? value.key.startsWith(_options.prefix) : true,
|
|
@@ -436,17 +379,11 @@ class MemoryProviderStateNamespace implements ProviderStateNamespace {
|
|
|
436
379
|
return this.values.get(key) ?? null;
|
|
437
380
|
}
|
|
438
381
|
|
|
439
|
-
async set<T>(
|
|
440
|
-
key: string,
|
|
441
|
-
value: T,
|
|
442
|
-
options?: StateWriteOptions,
|
|
443
|
-
): Promise<StateValue<T>> {
|
|
382
|
+
async set<T>(key: string, value: T, options?: StateWriteOptions): Promise<StateValue<T>> {
|
|
444
383
|
this.pruneExpired();
|
|
445
384
|
const now = new Date().toISOString();
|
|
446
385
|
const current = this.values.get(key);
|
|
447
|
-
const expiresAt = resolveMemoryStateExpiresAt(
|
|
448
|
-
options?.ttl ?? this.options.defaultTtl,
|
|
449
|
-
);
|
|
386
|
+
const expiresAt = resolveMemoryStateExpiresAt(options?.ttl ?? this.options.defaultTtl);
|
|
450
387
|
const row = {
|
|
451
388
|
key,
|
|
452
389
|
value,
|
|
@@ -499,10 +436,7 @@ class MemoryProviderStateNamespace implements ProviderStateNamespace {
|
|
|
499
436
|
class MemoryProviderRuntimeState implements ProviderRuntimeState {
|
|
500
437
|
readonly namespaces = new Map<string, MemoryProviderStateNamespace>();
|
|
501
438
|
|
|
502
|
-
namespace(
|
|
503
|
-
name: string,
|
|
504
|
-
_options: StateNamespaceOptions,
|
|
505
|
-
): ProviderStateNamespace {
|
|
439
|
+
namespace(name: string, _options: StateNamespaceOptions): ProviderStateNamespace {
|
|
506
440
|
const existing = this.namespaces.get(name);
|
|
507
441
|
if (existing) return existing;
|
|
508
442
|
const created = new MemoryProviderStateNamespace(_options);
|
|
@@ -536,10 +470,7 @@ export function createRedisProviderRuntimeState(
|
|
|
536
470
|
}
|
|
537
471
|
|
|
538
472
|
export function createProviderRuntimeStateFromEnv(
|
|
539
|
-
options: {
|
|
540
|
-
readonly providerId?: string;
|
|
541
|
-
readonly allowMemoryFallback?: boolean;
|
|
542
|
-
} = {},
|
|
473
|
+
options: { readonly providerId?: string; readonly allowMemoryFallback?: boolean } = {},
|
|
543
474
|
): ProviderRuntimeState {
|
|
544
475
|
const redisUrl = providerStateRedisUrlFromEnv();
|
|
545
476
|
if (redisUrl) {
|