@apifuse/provider-sdk 2.2.0-beta.59 → 2.2.0-beta.61
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 +33 -16
- package/CHANGELOG.md +8 -0
- package/bin/apifuse-dev.ts +7 -8
- package/bin/apifuse-pack-types.ts +22 -0
- package/bin/apifuse-perf.ts +45 -10
- package/bin/apifuse-record.ts +6 -8
- package/bin/apifuse-submit-check.ts +2 -2
- package/dist/config/loader.js +13 -12
- package/dist/define.d.ts +3 -2
- package/dist/define.js +1 -1
- package/dist/dev.js +3 -1
- package/dist/engine.d.ts +7 -6
- package/dist/engine.js +6 -6
- package/dist/error-resolution.js +21 -4
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +9 -0
- package/dist/handle-meta.d.ts +39 -0
- package/dist/handle-meta.js +29 -0
- package/dist/handle.d.ts +187 -0
- package/dist/handle.js +254 -0
- package/dist/i18n/catalog.js +21 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -3
- package/dist/lint.d.ts +3 -0
- package/dist/lint.js +388 -62
- package/dist/provider.d.ts +4 -4
- package/dist/provider.js +3 -3
- package/dist/runtime/browser.js +2 -1
- package/dist/runtime/cache.js +3 -2
- package/dist/runtime/diagnostic-env-values.d.ts +5 -0
- package/dist/runtime/diagnostic-env-values.js +57 -0
- package/dist/runtime/diagnostic-env.d.ts +11 -0
- package/dist/runtime/diagnostic-env.js +35 -0
- package/dist/runtime/diagnostic-redactor.d.ts +49 -0
- package/dist/runtime/diagnostic-redactor.js +569 -0
- package/dist/runtime/egress-lease.d.ts +2 -0
- package/dist/runtime/egress-lease.js +4 -1
- package/dist/runtime/env.js +2 -1
- package/dist/runtime/handle-wordlist.d.ts +43 -0
- package/dist/runtime/{choice-wordlist.js → handle-wordlist.js} +127 -9
- package/dist/runtime/handle.d.ts +71 -0
- package/dist/runtime/handle.js +895 -0
- package/dist/runtime/instrumentation.js +3 -0
- package/dist/runtime/native-network.d.ts +10 -1
- package/dist/runtime/native-network.js +230 -29
- package/dist/runtime/native-telemetry.d.ts +142 -0
- package/dist/runtime/native-telemetry.js +231 -0
- package/dist/runtime/ocr.js +2 -1
- package/dist/runtime/otlp.js +5 -4
- package/dist/runtime/proxy-nodemaven.js +3 -2
- package/dist/runtime/request-options.d.ts +7 -0
- package/dist/runtime/request-options.js +62 -22
- package/dist/runtime/resolver-vendors/browser.js +0 -1
- package/dist/runtime/resolver.js +3 -2
- package/dist/runtime/state.d.ts +4 -1
- package/dist/runtime/state.js +32 -17
- package/dist/runtime/stt.js +3 -2
- package/dist/runtime/trace.d.ts +5 -0
- package/dist/runtime/trace.js +115 -20
- package/dist/schema.d.ts +11 -0
- package/dist/schema.js +56 -2
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/self-test-redaction.js +8 -33
- package/dist/server/sensitive-values.d.ts +26 -0
- package/dist/server/sensitive-values.js +128 -0
- package/dist/server/serve-implementation.d.ts +4 -11
- package/dist/server/serve-implementation.js +305 -96
- package/dist/server/trace-output.d.ts +2 -1
- package/dist/server/trace-output.js +46 -14
- package/dist/testing/run.js +10 -7
- package/dist/trace-sanitization.d.ts +4 -3
- package/dist/trace-sanitization.js +45 -22
- package/dist/types.d.ts +4 -107
- package/package.json +2 -1
- package/src/config/loader.ts +13 -12
- package/src/define.ts +4 -3
- package/src/dev.ts +3 -1
- package/src/engine.ts +8 -8
- package/src/error-resolution.ts +21 -4
- package/src/errors.ts +13 -0
- package/src/handle-meta.ts +58 -0
- package/src/handle.ts +529 -0
- package/src/i18n/catalog.ts +23 -1
- package/src/index.ts +28 -13
- package/src/lint.ts +456 -71
- package/src/provider.ts +10 -20
- package/src/runtime/browser.ts +2 -1
- package/src/runtime/cache.ts +3 -2
- package/src/runtime/diagnostic-env-values.ts +56 -0
- package/src/runtime/diagnostic-env.ts +55 -0
- package/src/runtime/diagnostic-redactor.ts +662 -0
- package/src/runtime/egress-lease.ts +6 -1
- package/src/runtime/env.ts +2 -1
- package/src/runtime/{choice-wordlist.ts → handle-wordlist.ts} +130 -9
- package/src/runtime/handle.ts +1162 -0
- package/src/runtime/instrumentation.ts +3 -0
- package/src/runtime/native-network.ts +281 -33
- package/src/runtime/native-telemetry.ts +366 -0
- package/src/runtime/ocr.ts +2 -1
- package/src/runtime/otlp.ts +5 -4
- package/src/runtime/proxy-nodemaven.ts +3 -2
- package/src/runtime/request-options.ts +76 -28
- package/src/runtime/resolver-vendors/browser.ts +0 -1
- package/src/runtime/resolver.ts +3 -2
- package/src/runtime/state.ts +44 -16
- package/src/runtime/stt.ts +3 -2
- package/src/runtime/trace.ts +168 -26
- package/src/schema.ts +63 -2
- package/src/server/index.ts +17 -0
- package/src/server/self-test-redaction.ts +10 -40
- package/src/server/sensitive-values.ts +197 -0
- package/src/server/serve-implementation.ts +388 -128
- package/src/server/trace-output.ts +77 -17
- package/src/testing/run.ts +11 -7
- package/src/trace-sanitization.ts +70 -23
- package/src/types.ts +4 -137
- package/dist/choice-token.d.ts +0 -24
- package/dist/choice-token.js +0 -74
- package/dist/runtime/choice-wordlist.d.ts +0 -9
- package/dist/runtime/choice.d.ts +0 -27
- package/dist/runtime/choice.js +0 -801
- package/src/choice-token.ts +0 -165
- package/src/runtime/choice.ts +0 -1197
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { type DiagnosticRedactor } from "../runtime/diagnostic-redactor.js";
|
|
1
2
|
import { type CreateTraceContextOptions } from "../runtime/trace.js";
|
|
2
3
|
import type { TraceConfig } from "../types.js";
|
|
3
4
|
type EnvLike = Record<string, string | undefined>;
|
|
4
5
|
/** Server-only trace output policy. Shared programmatic trace callers stay in-memory. */
|
|
5
|
-
export declare function resolveServerTraceContextOptions(config: TraceConfig, resourceAttributes: Record<string, string>, env?: EnvLike): CreateTraceContextOptions;
|
|
6
|
+
export declare function resolveServerTraceContextOptions(config: TraceConfig, resourceAttributes: Record<string, string>, env?: EnvLike, redact?: DiagnosticRedactor): CreateTraceContextOptions;
|
|
6
7
|
export {};
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { isSensitiveFixtureKey, REDACTED_FIXTURE_VALUE } from "../fixture-sanitization.js";
|
|
2
|
+
import { redactedKeyAllocator, isRedactedKey, REDACTION_FAILED, redactDiagnosticText, } from "../runtime/diagnostic-redactor.js";
|
|
2
3
|
import { OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, resolveOTLPExportOptions, resolveOTLPResourceAttributes, } from "../runtime/otlp.js";
|
|
3
4
|
import { resolveTraceContextOptions, } from "../runtime/trace.js";
|
|
4
5
|
import { sanitizeSpanForOutput, sanitizeSpanNameForOutput, sanitizeTraceAttributes, } from "../trace-sanitization.js";
|
|
6
|
+
const TRACE_RESOURCE_ATTRIBUTE_SANITIZER = Symbol.for("@apifuse/provider-sdk/runtime/trace-resource-attribute-sanitizer");
|
|
5
7
|
// Warn once per environment object: process.env in production, each injected env in tests.
|
|
6
8
|
const warnedEnvironments = new WeakSet();
|
|
7
9
|
const warnedResourceEnvironments = new WeakSet();
|
|
@@ -36,25 +38,48 @@ function resolveServerOTLPExportOptions(config, env) {
|
|
|
36
38
|
* environment are identifiers rather than request data: their values are kept, with control
|
|
37
39
|
* characters neutralized, length bounded, and secret-named keys redacted.
|
|
38
40
|
*/
|
|
39
|
-
function resolveExportResourceAttributes(requestAttributes, env) {
|
|
41
|
+
function resolveExportResourceAttributes(requestAttributes, env, redact) {
|
|
40
42
|
const resolution = resolveOTLPResourceAttributes({}, env);
|
|
41
43
|
warnDiscardedResourceAttributes(env, resolution);
|
|
42
|
-
const operatorAttributes =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
const operatorAttributes = resolution.attributes;
|
|
45
|
+
const requestKeys = new Set(Object.keys(requestAttributes));
|
|
46
|
+
return {
|
|
47
|
+
// Keep the raw snapshot private until export. A credential registered after
|
|
48
|
+
// trace creation must be matched before any key or value is bounded.
|
|
49
|
+
attributes: { ...operatorAttributes, ...requestAttributes },
|
|
50
|
+
sanitize(attributes) {
|
|
51
|
+
const nextKey = redactedKeyAllocator(Object.keys(attributes));
|
|
52
|
+
const sanitizedOperatorAttributes = {};
|
|
53
|
+
const rawRequestAttributes = {};
|
|
54
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
55
|
+
const unchangedOperatorValue = !requestKeys.has(key) && operatorAttributes[key] === value;
|
|
56
|
+
if (unchangedOperatorValue) {
|
|
57
|
+
const redactedKey = isRedactedKey(key) ? key : redactDiagnosticText(key, redact);
|
|
58
|
+
const keyChanged = redactedKey !== key;
|
|
59
|
+
const sanitizedKey = keyChanged ? nextKey() : sanitizeSpanNameForOutput(key);
|
|
60
|
+
sanitizedOperatorAttributes[sanitizedKey] = keyChanged
|
|
61
|
+
? redactedKey === REDACTION_FAILED
|
|
62
|
+
? REDACTION_FAILED
|
|
63
|
+
: REDACTED_FIXTURE_VALUE
|
|
64
|
+
: isSensitiveFixtureKey(key)
|
|
65
|
+
? REDACTED_FIXTURE_VALUE
|
|
66
|
+
: sanitizeSpanNameForOutput(value, redact);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
rawRequestAttributes[key] = value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const sanitizedRequestAttributes = Object.fromEntries(Object.entries(sanitizeTraceAttributes(rawRequestAttributes, redact, Object.keys(sanitizedOperatorAttributes))).map(([key, value]) => [key, String(value)]));
|
|
73
|
+
return { ...sanitizedOperatorAttributes, ...sanitizedRequestAttributes };
|
|
74
|
+
},
|
|
75
|
+
};
|
|
51
76
|
}
|
|
52
77
|
/** Server-only trace output policy. Shared programmatic trace callers stay in-memory. */
|
|
53
|
-
export function resolveServerTraceContextOptions(config, resourceAttributes, env = process.env) {
|
|
78
|
+
export function resolveServerTraceContextOptions(config, resourceAttributes, env = process.env, redact) {
|
|
54
79
|
const resolved = resolveTraceContextOptions(config);
|
|
55
80
|
const outputEnabled = config.enabled !== false && config.exporter !== "none";
|
|
56
81
|
const consoleHook = outputEnabled && (config.exporter === "console" || config.exporter === "json")
|
|
57
|
-
? (span) => console.log(JSON.stringify(sanitizeSpanForOutput(span, resourceAttributes)))
|
|
82
|
+
? (span) => console.log(JSON.stringify(sanitizeSpanForOutput(span, resourceAttributes, redact)))
|
|
58
83
|
: undefined;
|
|
59
84
|
const onSpan = consoleHook && resolved.onSpan
|
|
60
85
|
? (span) => {
|
|
@@ -65,14 +90,21 @@ export function resolveServerTraceContextOptions(config, resourceAttributes, env
|
|
|
65
90
|
const exportOptions = outputEnabled && config.exporter === "otlp"
|
|
66
91
|
? resolveServerOTLPExportOptions(config, env)
|
|
67
92
|
: undefined;
|
|
93
|
+
const exportResources = exportOptions
|
|
94
|
+
? resolveExportResourceAttributes(resourceAttributes, env, redact)
|
|
95
|
+
: undefined;
|
|
96
|
+
if (exportResources) {
|
|
97
|
+
Object.defineProperty(exportResources.attributes, TRACE_RESOURCE_ATTRIBUTE_SANITIZER, { value: exportResources.sanitize });
|
|
98
|
+
}
|
|
68
99
|
return {
|
|
69
100
|
maxSpans: resolved.maxSpans,
|
|
70
101
|
onSpan,
|
|
102
|
+
...(redact ? { redact } : {}),
|
|
71
103
|
...(exportOptions
|
|
72
104
|
? {
|
|
73
105
|
exportOptions,
|
|
74
|
-
resourceAttributes:
|
|
75
|
-
sanitizeSpanForExport: (span) => sanitizeSpanForOutput(span),
|
|
106
|
+
resourceAttributes: exportResources?.attributes,
|
|
107
|
+
sanitizeSpanForExport: (span) => sanitizeSpanForOutput(span, undefined, redact),
|
|
76
108
|
}
|
|
77
109
|
: {}),
|
|
78
110
|
};
|
package/dist/testing/run.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { ProviderError } from "../errors.js";
|
|
3
3
|
import { createProviderCache } from "../runtime/cache.js";
|
|
4
|
-
import {
|
|
4
|
+
import { createTestHandleContext } from "../runtime/handle.js";
|
|
5
|
+
/**
|
|
6
|
+
* Stable connection scope for the local test harness so bound handles (drafts,
|
|
7
|
+
* default cursors) can be exercised exactly as in a connected production call.
|
|
8
|
+
*/
|
|
9
|
+
const STANDARD_TEST_CONNECTION_ID = "standard-test-connection";
|
|
5
10
|
import { createMemoryProviderRuntimeState } from "../runtime/state.js";
|
|
6
11
|
import { createUnsupportedOcrClient } from "../runtime/ocr.js";
|
|
7
12
|
import { createUnsupportedSttClient } from "../runtime/stt.js";
|
|
@@ -190,7 +195,7 @@ function createUpstreamContext(provider, operationName, upstreamStub) {
|
|
|
190
195
|
getAccessToken: () => undefined,
|
|
191
196
|
getScopes: () => [],
|
|
192
197
|
};
|
|
193
|
-
const request = { headers: {} };
|
|
198
|
+
const request = { headers: {}, connectionId: STANDARD_TEST_CONNECTION_ID };
|
|
194
199
|
const state = createMemoryProviderRuntimeState();
|
|
195
200
|
const nativeEgress = provider.native
|
|
196
201
|
? createNativeEgressAuthorization({ egress: provider.native.network })
|
|
@@ -411,10 +416,9 @@ function createUpstreamContext(provider, operationName, upstreamStub) {
|
|
|
411
416
|
resolver: {
|
|
412
417
|
solve: async () => unsupported("ctx.resolver.solve"),
|
|
413
418
|
},
|
|
414
|
-
|
|
419
|
+
handle: createTestHandleContext({
|
|
415
420
|
providerId: `standard-test-${operationName}`,
|
|
416
421
|
request,
|
|
417
|
-
credential,
|
|
418
422
|
state,
|
|
419
423
|
}),
|
|
420
424
|
};
|
|
@@ -447,7 +451,7 @@ export function createSnapshotContext(rawFixture) {
|
|
|
447
451
|
getAccessToken: () => undefined,
|
|
448
452
|
getScopes: () => [],
|
|
449
453
|
};
|
|
450
|
-
const request = { headers: {} };
|
|
454
|
+
const request = { headers: {}, connectionId: STANDARD_TEST_CONNECTION_ID };
|
|
451
455
|
const state = createMemoryProviderRuntimeState();
|
|
452
456
|
const streamCaptureGroup = findStreamCaptureGroup(rawFixture);
|
|
453
457
|
let nextCaptureItem = 0;
|
|
@@ -521,10 +525,9 @@ export function createSnapshotContext(rawFixture) {
|
|
|
521
525
|
resolver: {
|
|
522
526
|
solve: async () => unsupported("ctx.resolver.solve"),
|
|
523
527
|
},
|
|
524
|
-
|
|
528
|
+
handle: createTestHandleContext({
|
|
525
529
|
providerId: "standard-test",
|
|
526
530
|
request,
|
|
527
|
-
credential,
|
|
528
531
|
state,
|
|
529
532
|
}),
|
|
530
533
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type DiagnosticRedactor } from "./runtime/diagnostic-redactor.js";
|
|
1
2
|
import type { TraceSpan } from "./types.js";
|
|
2
3
|
/** Span names are SDK-authored identifiers; retain them while preventing log injection. */
|
|
3
|
-
export declare function sanitizeSpanNameForOutput(value: string): string;
|
|
4
|
-
export declare function sanitizeTraceAttributes(attributes: Record<string, unknown>): Record<string, string | number | boolean>;
|
|
5
|
-
export declare function sanitizeSpanForOutput(span: TraceSpan, additionalAttributes?: Record<string, string
|
|
4
|
+
export declare function sanitizeSpanNameForOutput(value: string, redact?: DiagnosticRedactor): string;
|
|
5
|
+
export declare function sanitizeTraceAttributes(attributes: Record<string, unknown>, redact?: DiagnosticRedactor, reservedKeys?: Iterable<string>): Record<string, string | number | boolean>;
|
|
6
|
+
export declare function sanitizeSpanForOutput(span: TraceSpan, additionalAttributes?: Record<string, string>, redact?: DiagnosticRedactor): TraceSpan;
|
|
@@ -1,45 +1,68 @@
|
|
|
1
1
|
import { encodeDiagnosticControls, isSensitiveFixtureKey, REDACTED_FIXTURE_VALUE, sanitizeDiagnosticText, } from "./fixture-sanitization.js";
|
|
2
|
+
import { diagnosticStructuredRedactor, redactDiagnosticAttributeKey, copyDiagnosticAttributeTypes, isDiagnosticBigintAttribute, redactedKeyAllocator, isRedactedKey, redactCriticalDiagnosticText, diagnosticAttributeRedactor, REDACTION_FAILED, redactDiagnosticText, } from "./runtime/diagnostic-redactor.js";
|
|
2
3
|
const MAX_TRACE_TEXT_LENGTH = 300;
|
|
3
|
-
function sanitizeTraceText(value) {
|
|
4
|
-
const encoded = sanitizeDiagnosticText(value);
|
|
4
|
+
function sanitizeTraceText(value, redact) {
|
|
5
|
+
const encoded = sanitizeDiagnosticText(redactDiagnosticText(value, redact));
|
|
5
6
|
return encoded.length > MAX_TRACE_TEXT_LENGTH
|
|
6
7
|
? `${encoded.slice(0, MAX_TRACE_TEXT_LENGTH)}… [truncated]`
|
|
7
8
|
: encoded;
|
|
8
9
|
}
|
|
9
10
|
/** Span names are SDK-authored identifiers; retain them while preventing log injection. */
|
|
10
|
-
export function sanitizeSpanNameForOutput(value) {
|
|
11
|
-
const encoded = encodeDiagnosticControls(value);
|
|
11
|
+
export function sanitizeSpanNameForOutput(value, redact) {
|
|
12
|
+
const encoded = encodeDiagnosticControls(redactDiagnosticText(value, redact));
|
|
12
13
|
return encoded.length > MAX_TRACE_TEXT_LENGTH
|
|
13
14
|
? `${encoded.slice(0, MAX_TRACE_TEXT_LENGTH)}… [truncated]`
|
|
14
15
|
: encoded;
|
|
15
16
|
}
|
|
16
|
-
export function sanitizeTraceAttributes(attributes) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
export function sanitizeTraceAttributes(attributes, redact, reservedKeys = []) {
|
|
18
|
+
const nextKey = redactedKeyAllocator([...Object.keys(attributes), ...reservedKeys]);
|
|
19
|
+
return copyDiagnosticAttributeTypes(attributes, Object.fromEntries(Object.entries(attributes).map(([key, value]) => {
|
|
20
|
+
const redactedKey = isRedactedKey(key) ? key : redactDiagnosticAttributeKey(key, redact);
|
|
21
|
+
const keyChanged = redactedKey !== key;
|
|
22
|
+
const sanitizedKey = keyChanged ? nextKey() : sanitizeTraceText(key);
|
|
23
|
+
const valueRedactor = typeof value === "bigint" || isDiagnosticBigintAttribute(attributes, key)
|
|
24
|
+
? diagnosticStructuredRedactor(redact)
|
|
25
|
+
: diagnosticAttributeRedactor(key, redact);
|
|
26
|
+
const sanitizedValue = keyChanged
|
|
27
|
+
? redactedKey === REDACTION_FAILED
|
|
28
|
+
? REDACTION_FAILED
|
|
29
|
+
: REDACTED_FIXTURE_VALUE
|
|
30
|
+
: isSensitiveFixtureKey(key)
|
|
31
|
+
? REDACTED_FIXTURE_VALUE
|
|
32
|
+
: typeof value === "bigint" || isDiagnosticBigintAttribute(attributes, key)
|
|
33
|
+
? String(value).length < 8
|
|
34
|
+
? String(value)
|
|
35
|
+
: sanitizeTraceText(String(value), valueRedactor)
|
|
36
|
+
: typeof value === "string"
|
|
37
|
+
? sanitizeTraceText(value, valueRedactor)
|
|
38
|
+
: typeof value === "number" || typeof value === "boolean"
|
|
39
|
+
? (() => {
|
|
40
|
+
if (String(value).length < 8)
|
|
41
|
+
return value;
|
|
42
|
+
const redacted = redactDiagnosticText(String(value), diagnosticStructuredRedactor(valueRedactor));
|
|
43
|
+
return redacted === String(value) ? value : sanitizeTraceText(redacted);
|
|
44
|
+
})()
|
|
45
|
+
: sanitizeTraceText(String(value), valueRedactor);
|
|
46
|
+
return [sanitizedKey, sanitizedValue];
|
|
47
|
+
})));
|
|
27
48
|
}
|
|
28
|
-
export function sanitizeSpanForOutput(span, additionalAttributes) {
|
|
49
|
+
export function sanitizeSpanForOutput(span, additionalAttributes, redact) {
|
|
29
50
|
// Keep this schema explicit so future fields are not silently added to a
|
|
30
51
|
// process output path before their trust boundary has been reviewed.
|
|
31
52
|
return {
|
|
32
|
-
id: span.id,
|
|
33
|
-
name: sanitizeSpanNameForOutput(span.name),
|
|
53
|
+
id: redactCriticalDiagnosticText(span.id, redact),
|
|
54
|
+
name: sanitizeSpanNameForOutput(redactCriticalDiagnosticText(span.name, redact)),
|
|
34
55
|
startedAt: span.startedAt,
|
|
35
56
|
endedAt: span.endedAt,
|
|
36
57
|
duration_ms: span.duration_ms,
|
|
37
58
|
status: span.status,
|
|
38
|
-
attributes: sanitizeTraceAttributes({
|
|
59
|
+
attributes: sanitizeTraceAttributes(copyDiagnosticAttributeTypes(span.attributes, {
|
|
39
60
|
...span.attributes,
|
|
40
61
|
...(additionalAttributes ?? {}),
|
|
41
|
-
}),
|
|
42
|
-
...(span.error !== undefined ? { error: sanitizeTraceText(span.error) } : {}),
|
|
43
|
-
...(span.parentId !== undefined
|
|
62
|
+
}), redact),
|
|
63
|
+
...(span.error !== undefined ? { error: sanitizeTraceText(span.error, redact) } : {}),
|
|
64
|
+
...(span.parentId !== undefined
|
|
65
|
+
? { parentId: redactCriticalDiagnosticText(span.parentId, redact) }
|
|
66
|
+
: {}),
|
|
44
67
|
};
|
|
45
68
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type ms from "ms";
|
|
2
|
+
import type { HandleContext, HandleKind } from "./handle.js";
|
|
2
3
|
import type { HealthScenario } from "./health-scenario.js";
|
|
3
4
|
import type { SerializedCookieJar } from "tough-cookie";
|
|
4
5
|
import type { infer as ZodInfer, ZodType } from "zod";
|
|
@@ -1660,110 +1661,6 @@ export interface ProviderRequestContext {
|
|
|
1660
1661
|
connectionId?: string;
|
|
1661
1662
|
headers: Record<string, string>;
|
|
1662
1663
|
}
|
|
1663
|
-
export interface ProviderChoiceBindingOptions {
|
|
1664
|
-
connection?: boolean;
|
|
1665
|
-
credentialKeys?: readonly string[];
|
|
1666
|
-
}
|
|
1667
|
-
export type ProviderChoiceConsumeMode = "never" | "on-parse" | "explicit";
|
|
1668
|
-
export type ProviderChoiceConsumeResult = {
|
|
1669
|
-
readonly status: "consumed";
|
|
1670
|
-
} | {
|
|
1671
|
-
readonly status: "already-consumed";
|
|
1672
|
-
} | {
|
|
1673
|
-
readonly status: "unsupported";
|
|
1674
|
-
};
|
|
1675
|
-
export type ProviderChoiceExplicitParseResult = {
|
|
1676
|
-
readonly status: "active";
|
|
1677
|
-
readonly payload: Record<string, unknown>;
|
|
1678
|
-
/** Stable, opaque key for provider-owned idempotency records. */
|
|
1679
|
-
readonly replayKey: string;
|
|
1680
|
-
/** Atomically claims a word token. Inline tokens report unsupported. */
|
|
1681
|
-
consume(): Promise<ProviderChoiceConsumeResult>;
|
|
1682
|
-
} | {
|
|
1683
|
-
readonly status: "consumed";
|
|
1684
|
-
/** Use this key to read the provider-owned result before returning an error. */
|
|
1685
|
-
readonly replayKey: string;
|
|
1686
|
-
};
|
|
1687
|
-
export type ProviderChoiceStorageOptions = {
|
|
1688
|
-
readonly mode: "inline";
|
|
1689
|
-
} | {
|
|
1690
|
-
readonly mode: "server";
|
|
1691
|
-
readonly namespace: string;
|
|
1692
|
-
readonly state?: ProviderRuntimeState;
|
|
1693
|
-
readonly ttl?: ProviderStateDurationString;
|
|
1694
|
-
readonly maxEntries: number;
|
|
1695
|
-
readonly maxValueBytes: number;
|
|
1696
|
-
readonly unavailable?: "reject";
|
|
1697
|
-
} | {
|
|
1698
|
-
readonly mode: "auto";
|
|
1699
|
-
readonly namespace: string;
|
|
1700
|
-
readonly state?: ProviderRuntimeState;
|
|
1701
|
-
readonly ttl?: ProviderStateDurationString;
|
|
1702
|
-
readonly maxInlineBytes: number;
|
|
1703
|
-
readonly maxEntries: number;
|
|
1704
|
-
readonly maxValueBytes: number;
|
|
1705
|
-
readonly unavailable?: "reject";
|
|
1706
|
-
};
|
|
1707
|
-
export interface ProviderChoiceIssueOptions<TPayload extends Record<string, unknown>> {
|
|
1708
|
-
prefix: string;
|
|
1709
|
-
purpose: string;
|
|
1710
|
-
payload: TPayload;
|
|
1711
|
-
ttlMs: number;
|
|
1712
|
-
nowMs?: number;
|
|
1713
|
-
bind?: ProviderChoiceBindingOptions;
|
|
1714
|
-
/** Server storage only: standard emits four words; high emits five. */
|
|
1715
|
-
strength?: "standard" | "high";
|
|
1716
|
-
storage?: ProviderChoiceStorageOptions;
|
|
1717
|
-
}
|
|
1718
|
-
export interface ProviderChoiceParseOptions {
|
|
1719
|
-
token: string;
|
|
1720
|
-
prefix: string;
|
|
1721
|
-
purpose: string;
|
|
1722
|
-
ttlMs?: number;
|
|
1723
|
-
nowMs?: number;
|
|
1724
|
-
futureToleranceMs?: number;
|
|
1725
|
-
bind?: ProviderChoiceBindingOptions;
|
|
1726
|
-
storage?: ProviderChoiceStorageOptions;
|
|
1727
|
-
/** Defaults to never, preserving reusable choice-token parse semantics. */
|
|
1728
|
-
consume?: ProviderChoiceConsumeMode;
|
|
1729
|
-
}
|
|
1730
|
-
export interface ProviderChoiceContext {
|
|
1731
|
-
issue<TPayload extends Record<string, unknown>>(options: ProviderChoiceIssueOptions<TPayload> & {
|
|
1732
|
-
readonly storage?: {
|
|
1733
|
-
readonly mode: "inline";
|
|
1734
|
-
};
|
|
1735
|
-
}): string;
|
|
1736
|
-
issue<TPayload extends Record<string, unknown>>(options: ProviderChoiceIssueOptions<TPayload> & {
|
|
1737
|
-
readonly storage: Extract<ProviderChoiceStorageOptions, {
|
|
1738
|
-
readonly mode: "server";
|
|
1739
|
-
}>;
|
|
1740
|
-
}): Promise<string>;
|
|
1741
|
-
issue<TPayload extends Record<string, unknown>>(options: ProviderChoiceIssueOptions<TPayload> & {
|
|
1742
|
-
readonly storage: Extract<ProviderChoiceStorageOptions, {
|
|
1743
|
-
readonly mode: "auto";
|
|
1744
|
-
}>;
|
|
1745
|
-
}): string | Promise<string>;
|
|
1746
|
-
issue<TPayload extends Record<string, unknown>>(options: ProviderChoiceIssueOptions<TPayload>): string | Promise<string>;
|
|
1747
|
-
parse(options: ProviderChoiceParseOptions & {
|
|
1748
|
-
readonly consume: "explicit";
|
|
1749
|
-
}): Promise<ProviderChoiceExplicitParseResult>;
|
|
1750
|
-
parse(options: ProviderChoiceParseOptions & {
|
|
1751
|
-
readonly storage?: {
|
|
1752
|
-
readonly mode: "inline";
|
|
1753
|
-
};
|
|
1754
|
-
}): Record<string, unknown>;
|
|
1755
|
-
parse(options: ProviderChoiceParseOptions & {
|
|
1756
|
-
readonly storage: Extract<ProviderChoiceStorageOptions, {
|
|
1757
|
-
readonly mode: "server";
|
|
1758
|
-
}>;
|
|
1759
|
-
}): Promise<Record<string, unknown>>;
|
|
1760
|
-
parse(options: ProviderChoiceParseOptions & {
|
|
1761
|
-
readonly storage: Extract<ProviderChoiceStorageOptions, {
|
|
1762
|
-
readonly mode: "auto";
|
|
1763
|
-
}>;
|
|
1764
|
-
}): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
1765
|
-
parse(options: ProviderChoiceParseOptions): Record<string, unknown> | Promise<Record<string, unknown> | ProviderChoiceExplicitParseResult>;
|
|
1766
|
-
}
|
|
1767
1664
|
export interface ContextScratchpad {
|
|
1768
1665
|
get(key: string): unknown;
|
|
1769
1666
|
set(key: string, value: unknown): void;
|
|
@@ -1997,8 +1894,8 @@ export type ProviderContext<TConfig = Record<string, unknown>> = {
|
|
|
1997
1894
|
stt: SttContext;
|
|
1998
1895
|
} : Record<never, never>) & ("resolver" extends keyof TConfig ? {
|
|
1999
1896
|
resolver: ResolverContext;
|
|
2000
|
-
} : Record<never, never>) & ("
|
|
2001
|
-
|
|
1897
|
+
} : Record<never, never>) & ("handle" extends keyof TConfig ? {
|
|
1898
|
+
handle: HandleContext;
|
|
2002
1899
|
} : Record<never, never>);
|
|
2003
1900
|
/** Backwards-compatible name for a declaration-derived provider context. */
|
|
2004
1901
|
export type ProviderContextFor<TConfig> = ProviderContext<TConfig>;
|
|
@@ -2159,7 +2056,7 @@ export interface ProviderDefinition<TContext = ProviderContext> {
|
|
|
2159
2056
|
engine: BrowserEngine;
|
|
2160
2057
|
};
|
|
2161
2058
|
auth?: AuthConfig;
|
|
2162
|
-
|
|
2059
|
+
handle?: readonly HandleKind[];
|
|
2163
2060
|
reviewed?: ProviderReviewed;
|
|
2164
2061
|
access?: ProviderAccessConfig;
|
|
2165
2062
|
secrets?: ProviderSecretDeclaration[];
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.2.0-beta.
|
|
2
|
+
"version": "2.2.0-beta.61",
|
|
3
3
|
"name": "@apifuse/provider-sdk",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
@@ -110,6 +110,7 @@
|
|
|
110
110
|
"format": "biome format --write",
|
|
111
111
|
"type-check": "tsc --noEmit",
|
|
112
112
|
"test": "bun test",
|
|
113
|
+
"bench:redaction": "bun scripts/benchmark-request-redaction.ts",
|
|
113
114
|
"check": "bun run lint && bun run type-check && bun run lint:deprecated && bun run lint:test-typesafety && bun run build",
|
|
114
115
|
"api:update": "bun run build && bun scripts/api-reports.ts update",
|
|
115
116
|
"api:check": "bun run build && bun scripts/api-reports.ts check",
|
package/src/config/loader.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "../runtime/diagnostic-env.js";
|
|
1
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
@@ -256,8 +257,8 @@ const SMARTPROXY_EXTRACTION_SOFT_REFRESH_MS = 10_000;
|
|
|
256
257
|
|
|
257
258
|
function redisUrlFromEnv(): string | undefined {
|
|
258
259
|
return (
|
|
259
|
-
|
|
260
|
-
|
|
260
|
+
readDiagnosticEnv(PROVIDER_CACHE_REDIS_URL_ENV)?.trim() ||
|
|
261
|
+
readDiagnosticEnv(REDIS_URL_ENV)?.trim() ||
|
|
261
262
|
undefined
|
|
262
263
|
);
|
|
263
264
|
}
|
|
@@ -268,9 +269,9 @@ export function providerCacheRedisUrlFromEnv(): string | undefined {
|
|
|
268
269
|
|
|
269
270
|
export function providerStateRedisUrlFromEnv(): string | undefined {
|
|
270
271
|
return (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
272
|
+
readDiagnosticEnv(PROVIDER_STATE_REDIS_URL_ENV)?.trim() ||
|
|
273
|
+
readDiagnosticEnv(PROVIDER_CACHE_REDIS_URL_ENV)?.trim() ||
|
|
274
|
+
readDiagnosticEnv(REDIS_URL_ENV)?.trim() ||
|
|
274
275
|
undefined
|
|
275
276
|
);
|
|
276
277
|
}
|
|
@@ -693,12 +694,12 @@ export async function resolveWithVendor(
|
|
|
693
694
|
const startedAt = Date.now();
|
|
694
695
|
const username = (
|
|
695
696
|
context.credentials === undefined
|
|
696
|
-
?
|
|
697
|
+
? readDiagnosticEnv(NODEMAVEN_USERNAME_ENV)
|
|
697
698
|
: context.credentials[NODEMAVEN_USERNAME_ENV]
|
|
698
699
|
)?.trim();
|
|
699
700
|
const password = (
|
|
700
701
|
context.credentials === undefined
|
|
701
|
-
?
|
|
702
|
+
? readDiagnosticEnv(NODEMAVEN_PASSWORD_ENV)
|
|
702
703
|
: context.credentials[NODEMAVEN_PASSWORD_ENV]
|
|
703
704
|
)?.trim();
|
|
704
705
|
const filter =
|
|
@@ -749,7 +750,7 @@ export async function resolveWithVendor(
|
|
|
749
750
|
// smartproxy allocation-style vendor.
|
|
750
751
|
const appKey = (
|
|
751
752
|
context.credentials === undefined
|
|
752
|
-
?
|
|
753
|
+
? readDiagnosticEnv(SMARTPROXY_APP_KEY_ENV)
|
|
753
754
|
: context.credentials[SMARTPROXY_APP_KEY_ENV]
|
|
754
755
|
)?.trim();
|
|
755
756
|
if (!appKey) {
|
|
@@ -838,18 +839,18 @@ function vendorHasCredentials(
|
|
|
838
839
|
if (vendor === "nodemaven") {
|
|
839
840
|
return Boolean(
|
|
840
841
|
(credentials === undefined
|
|
841
|
-
?
|
|
842
|
+
? readDiagnosticEnv(NODEMAVEN_USERNAME_ENV)
|
|
842
843
|
: credentials[NODEMAVEN_USERNAME_ENV]
|
|
843
844
|
)?.trim() &&
|
|
844
845
|
(credentials === undefined
|
|
845
|
-
?
|
|
846
|
+
? readDiagnosticEnv(NODEMAVEN_PASSWORD_ENV)
|
|
846
847
|
: credentials[NODEMAVEN_PASSWORD_ENV]
|
|
847
848
|
)?.trim(),
|
|
848
849
|
);
|
|
849
850
|
}
|
|
850
851
|
return Boolean(
|
|
851
852
|
(credentials === undefined
|
|
852
|
-
?
|
|
853
|
+
? readDiagnosticEnv(SMARTPROXY_APP_KEY_ENV)
|
|
853
854
|
: credentials[SMARTPROXY_APP_KEY_ENV]
|
|
854
855
|
)?.trim(),
|
|
855
856
|
);
|
|
@@ -1757,7 +1758,7 @@ function markSmartproxyCacheInvalidated(options: ProxyResolutionOptions = {}): s
|
|
|
1757
1758
|
const lifetimeMinutes = resolveSmartproxyLifetime(policy);
|
|
1758
1759
|
const appKey = (
|
|
1759
1760
|
options.engineCredentials === undefined
|
|
1760
|
-
?
|
|
1761
|
+
? readDiagnosticEnv(SMARTPROXY_APP_KEY_ENV)
|
|
1761
1762
|
: options.engineCredentials[SMARTPROXY_APP_KEY_ENV]
|
|
1762
1763
|
)?.trim();
|
|
1763
1764
|
if (!appKey) return undefined;
|
package/src/define.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "./native-egress-policy.js";
|
|
22
22
|
import { safeParseSchemaSync } from "./schema.js";
|
|
23
23
|
import { resolveHealthCheckInputDateTokens } from "./server/self-test-input-tokens.js";
|
|
24
|
+
import type { HandleKind } from "./handle.js";
|
|
24
25
|
import type {
|
|
25
26
|
AuthConfig,
|
|
26
27
|
BrowserEngine,
|
|
@@ -601,8 +602,8 @@ export interface ProviderDeclaration {
|
|
|
601
602
|
resolver?: ProviderResolverConfig;
|
|
602
603
|
browser?: { engine: BrowserEngine };
|
|
603
604
|
auth?: AuthConfig;
|
|
604
|
-
/** Declares the
|
|
605
|
-
|
|
605
|
+
/** Declares the handle kinds this provider issues and accepts (ADR-0012). */
|
|
606
|
+
handle?: readonly HandleKind[];
|
|
606
607
|
reviewed?: ProviderReviewed;
|
|
607
608
|
access?: ProviderAccessConfig;
|
|
608
609
|
/** Declares secret requirements; this does not add a `ctx.secrets` member. */
|
|
@@ -3042,7 +3043,7 @@ function finalizeProvider<
|
|
|
3042
3043
|
resolver: config.resolver,
|
|
3043
3044
|
browser: config.browser,
|
|
3044
3045
|
auth: config.auth,
|
|
3045
|
-
|
|
3046
|
+
handle: config.handle,
|
|
3046
3047
|
reviewed: config.reviewed,
|
|
3047
3048
|
access: config.access,
|
|
3048
3049
|
secrets: config.secrets,
|
package/src/dev.ts
CHANGED
|
@@ -17,7 +17,9 @@ export function createDevServer(
|
|
|
17
17
|
|
|
18
18
|
return {
|
|
19
19
|
start: () => {
|
|
20
|
-
|
|
20
|
+
// Local development has no Redis by default; handles (ADR-0012) need a
|
|
21
|
+
// state backend, so fall back to memory state when none is configured.
|
|
22
|
+
void serve(provider, { port, engine: options?.engine, allowMemoryStateFallback: true });
|
|
21
23
|
console.log(
|
|
22
24
|
`[apifuse dev] ${provider.id}@${provider.version} running at http://localhost:${port}`,
|
|
23
25
|
);
|
package/src/engine.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readDiagnosticEnv } from "./runtime/diagnostic-env.js";
|
|
1
2
|
import { ProviderError } from "./errors.js";
|
|
2
3
|
import {
|
|
3
4
|
APIFUSE__RESOLVER__2CAPTCHA__API_KEY,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
OTEL_RESOURCE_ATTRIBUTES,
|
|
14
15
|
OTEL_SERVICE_NAME,
|
|
15
16
|
} from "./runtime/otlp.js";
|
|
17
|
+
import type { HandleContext } from "./handle.js";
|
|
16
18
|
import type {
|
|
17
19
|
AuthContext,
|
|
18
20
|
BrowserClient,
|
|
@@ -22,7 +24,6 @@ import type {
|
|
|
22
24
|
NativeContext,
|
|
23
25
|
OcrContext,
|
|
24
26
|
ProviderCache,
|
|
25
|
-
ProviderChoiceContext,
|
|
26
27
|
ProviderContext,
|
|
27
28
|
ProviderDefinition,
|
|
28
29
|
ProviderFilesContext,
|
|
@@ -117,8 +118,8 @@ function isEngineOwnedEngineEnvName(name: string): boolean {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
|
-
* Hosted runtime settings the engine reads for OCR, STT, cache keying,
|
|
121
|
-
*
|
|
121
|
+
* Hosted runtime settings the engine reads for OCR, STT, cache keying, and
|
|
122
|
+
* the CDP pool. Providers never own them: declarations are rejected
|
|
122
123
|
* and projections drop them, like the vendor credentials above.
|
|
123
124
|
*/
|
|
124
125
|
export const ENGINE_OWNED_RUNTIME_ENV_NAMES = [
|
|
@@ -127,7 +128,6 @@ export const ENGINE_OWNED_RUNTIME_ENV_NAMES = [
|
|
|
127
128
|
"APIFUSE__OCR__API_KEY",
|
|
128
129
|
"APIFUSE__CLOUDFLARE__ACCOUNT_ID",
|
|
129
130
|
"APIFUSE__CACHE__KEY_PEPPER",
|
|
130
|
-
"APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET",
|
|
131
131
|
] as const;
|
|
132
132
|
|
|
133
133
|
const ENGINE_OWNED_RUNTIME_ENV_NAME_SET = new Set<string>(ENGINE_OWNED_RUNTIME_ENV_NAMES);
|
|
@@ -160,7 +160,7 @@ export function readEngineProxyCredentials(
|
|
|
160
160
|
): Readonly<Record<string, string>> {
|
|
161
161
|
return Object.fromEntries(
|
|
162
162
|
ENGINE_OWNED_PROXY_CREDENTIAL_ENV_NAMES.flatMap((name) => {
|
|
163
|
-
const value = environment
|
|
163
|
+
const value = readDiagnosticEnv(name, environment)?.trim();
|
|
164
164
|
return value ? [[name, value] as const] : [];
|
|
165
165
|
}),
|
|
166
166
|
);
|
|
@@ -174,7 +174,7 @@ export function createProviderEnvironment(
|
|
|
174
174
|
return Object.fromEntries(
|
|
175
175
|
declaredNames.flatMap((name) => {
|
|
176
176
|
if (isEngineOwnedEnvName(name)) return [];
|
|
177
|
-
const value = environment
|
|
177
|
+
const value = readDiagnosticEnv(name, environment);
|
|
178
178
|
return value === undefined ? [] : [[name, value] as const];
|
|
179
179
|
}),
|
|
180
180
|
);
|
|
@@ -236,7 +236,7 @@ export const PROVIDER_CAPABILITY_KEYS = [
|
|
|
236
236
|
"ocr",
|
|
237
237
|
"stt",
|
|
238
238
|
"resolver",
|
|
239
|
-
"
|
|
239
|
+
"handle",
|
|
240
240
|
] as const;
|
|
241
241
|
|
|
242
242
|
export type ProviderCapabilityKey = (typeof PROVIDER_CAPABILITY_KEYS)[number];
|
|
@@ -248,7 +248,7 @@ export type ProviderEngineBindingCandidates = Partial<
|
|
|
248
248
|
readonly credential: CredentialContext;
|
|
249
249
|
readonly files: ProviderFilesContext;
|
|
250
250
|
readonly auth: AuthContext;
|
|
251
|
-
readonly
|
|
251
|
+
readonly handle: HandleContext;
|
|
252
252
|
}
|
|
253
253
|
> & {
|
|
254
254
|
readonly request?: ProviderRequestContext;
|
package/src/error-resolution.ts
CHANGED
|
@@ -20,10 +20,17 @@ export const SDK_OWNED_PROVIDER_ERROR_CODES = new Set([
|
|
|
20
20
|
"refresh_not_supported",
|
|
21
21
|
"RUNTIME_UNSUPPORTED",
|
|
22
22
|
"PROVIDER_STATE_UNSUPPORTED",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
23
|
+
"HANDLE_INVALID",
|
|
24
|
+
"HANDLE_NOT_FOUND",
|
|
25
|
+
"HANDLE_EXPIRED",
|
|
26
|
+
"HANDLE_KIND_MISMATCH",
|
|
27
|
+
"HANDLE_BUSY",
|
|
28
|
+
"HANDLE_COMMITTED",
|
|
29
|
+
"HANDLE_CONNECTION_REQUIRED",
|
|
30
|
+
"HANDLE_STORAGE_UNAVAILABLE",
|
|
31
|
+
"HANDLE_TOO_LARGE",
|
|
32
|
+
"HANDLE_INVALID_DATA",
|
|
33
|
+
"PICK_NOT_OFFERED",
|
|
27
34
|
"unsupported_stealth_cookie_store_version",
|
|
28
35
|
"provider_secret_error",
|
|
29
36
|
"credential_key_error",
|
|
@@ -130,6 +137,16 @@ export const SDK_STATUS_MAPPED_PROVIDER_ERROR_CODES: ReadonlyMap<string, Provide
|
|
|
130
137
|
["EGRESS_LEASE_INVALID", 409],
|
|
131
138
|
["REPLAY_SESSION_MISMATCH", 409],
|
|
132
139
|
["REPLAY_ALREADY_ATTEMPTED", 409],
|
|
140
|
+
// Handles (ADR-0012): only caller-side faults are mapped. Storage,
|
|
141
|
+
// connection-scope, size, and schema faults stay 500 because the provider
|
|
142
|
+
// or host owns them.
|
|
143
|
+
["HANDLE_INVALID", 400],
|
|
144
|
+
["HANDLE_KIND_MISMATCH", 400],
|
|
145
|
+
["PICK_NOT_OFFERED", 400],
|
|
146
|
+
["HANDLE_NOT_FOUND", 404],
|
|
147
|
+
["HANDLE_EXPIRED", 410],
|
|
148
|
+
["HANDLE_COMMITTED", 409],
|
|
149
|
+
["HANDLE_BUSY", 409],
|
|
133
150
|
["UPSTREAM_ERROR", 502],
|
|
134
151
|
["BLOCKED", 502],
|
|
135
152
|
["OCR_UNAVAILABLE", 503],
|