@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,4 +1,11 @@
|
|
|
1
1
|
import { isSensitiveFixtureKey, REDACTED_FIXTURE_VALUE } from "../fixture-sanitization.js";
|
|
2
|
+
import {
|
|
3
|
+
type DiagnosticRedactor,
|
|
4
|
+
redactedKeyAllocator,
|
|
5
|
+
isRedactedKey,
|
|
6
|
+
REDACTION_FAILED,
|
|
7
|
+
redactDiagnosticText,
|
|
8
|
+
} from "../runtime/diagnostic-redactor.js";
|
|
2
9
|
import {
|
|
3
10
|
OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
4
11
|
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
|
|
@@ -22,6 +29,16 @@ import type { TraceConfig } from "../types.js";
|
|
|
22
29
|
|
|
23
30
|
type EnvLike = Record<string, string | undefined>;
|
|
24
31
|
|
|
32
|
+
const TRACE_RESOURCE_ATTRIBUTE_SANITIZER = Symbol.for(
|
|
33
|
+
"@apifuse/provider-sdk/runtime/trace-resource-attribute-sanitizer",
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
type ResourceAttributesWithSanitizer = Record<string, string> & {
|
|
37
|
+
[TRACE_RESOURCE_ATTRIBUTE_SANITIZER]?: (
|
|
38
|
+
attributes: Record<string, string>,
|
|
39
|
+
) => Record<string, string>;
|
|
40
|
+
};
|
|
41
|
+
|
|
25
42
|
// Warn once per environment object: process.env in production, each injected env in tests.
|
|
26
43
|
const warnedEnvironments = new WeakSet<EnvLike>();
|
|
27
44
|
const warnedResourceEnvironments = new WeakSet<EnvLike>();
|
|
@@ -73,22 +90,52 @@ function resolveServerOTLPExportOptions(
|
|
|
73
90
|
function resolveExportResourceAttributes(
|
|
74
91
|
requestAttributes: Record<string, string>,
|
|
75
92
|
env: EnvLike,
|
|
76
|
-
|
|
93
|
+
redact?: DiagnosticRedactor,
|
|
94
|
+
): {
|
|
95
|
+
attributes: Record<string, string>;
|
|
96
|
+
sanitize: (attributes: Record<string, string>) => Record<string, string>;
|
|
97
|
+
} {
|
|
77
98
|
const resolution = resolveOTLPResourceAttributes({}, env);
|
|
78
99
|
warnDiscardedResourceAttributes(env, resolution);
|
|
79
|
-
const operatorAttributes =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
const operatorAttributes = resolution.attributes;
|
|
101
|
+
const requestKeys = new Set(Object.keys(requestAttributes));
|
|
102
|
+
return {
|
|
103
|
+
// Keep the raw snapshot private until export. A credential registered after
|
|
104
|
+
// trace creation must be matched before any key or value is bounded.
|
|
105
|
+
attributes: { ...operatorAttributes, ...requestAttributes },
|
|
106
|
+
sanitize(attributes) {
|
|
107
|
+
const nextKey = redactedKeyAllocator(Object.keys(attributes));
|
|
108
|
+
const sanitizedOperatorAttributes: Record<string, string> = {};
|
|
109
|
+
const rawRequestAttributes: Record<string, string> = {};
|
|
110
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
111
|
+
const unchangedOperatorValue = !requestKeys.has(key) && operatorAttributes[key] === value;
|
|
112
|
+
if (unchangedOperatorValue) {
|
|
113
|
+
const redactedKey = isRedactedKey(key) ? key : redactDiagnosticText(key, redact);
|
|
114
|
+
const keyChanged = redactedKey !== key;
|
|
115
|
+
const sanitizedKey = keyChanged ? nextKey() : sanitizeSpanNameForOutput(key);
|
|
116
|
+
sanitizedOperatorAttributes[sanitizedKey] = keyChanged
|
|
117
|
+
? redactedKey === REDACTION_FAILED
|
|
118
|
+
? REDACTION_FAILED
|
|
119
|
+
: REDACTED_FIXTURE_VALUE
|
|
120
|
+
: isSensitiveFixtureKey(key)
|
|
121
|
+
? REDACTED_FIXTURE_VALUE
|
|
122
|
+
: sanitizeSpanNameForOutput(value, redact);
|
|
123
|
+
} else {
|
|
124
|
+
rawRequestAttributes[key] = value;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const sanitizedRequestAttributes = Object.fromEntries(
|
|
128
|
+
Object.entries(
|
|
129
|
+
sanitizeTraceAttributes(
|
|
130
|
+
rawRequestAttributes,
|
|
131
|
+
redact,
|
|
132
|
+
Object.keys(sanitizedOperatorAttributes),
|
|
133
|
+
),
|
|
134
|
+
).map(([key, value]) => [key, String(value)]),
|
|
135
|
+
);
|
|
136
|
+
return { ...sanitizedOperatorAttributes, ...sanitizedRequestAttributes };
|
|
137
|
+
},
|
|
138
|
+
};
|
|
92
139
|
}
|
|
93
140
|
|
|
94
141
|
/** Server-only trace output policy. Shared programmatic trace callers stay in-memory. */
|
|
@@ -96,12 +143,14 @@ export function resolveServerTraceContextOptions(
|
|
|
96
143
|
config: TraceConfig,
|
|
97
144
|
resourceAttributes: Record<string, string>,
|
|
98
145
|
env: EnvLike = process.env,
|
|
146
|
+
redact?: DiagnosticRedactor,
|
|
99
147
|
): CreateTraceContextOptions {
|
|
100
148
|
const resolved = resolveTraceContextOptions(config);
|
|
101
149
|
const outputEnabled = config.enabled !== false && config.exporter !== "none";
|
|
102
150
|
const consoleHook =
|
|
103
151
|
outputEnabled && (config.exporter === "console" || config.exporter === "json")
|
|
104
|
-
? (span: Span) =>
|
|
152
|
+
? (span: Span) =>
|
|
153
|
+
console.log(JSON.stringify(sanitizeSpanForOutput(span, resourceAttributes, redact)))
|
|
105
154
|
: undefined;
|
|
106
155
|
const onSpan =
|
|
107
156
|
consoleHook && resolved.onSpan
|
|
@@ -114,15 +163,26 @@ export function resolveServerTraceContextOptions(
|
|
|
114
163
|
outputEnabled && config.exporter === "otlp"
|
|
115
164
|
? resolveServerOTLPExportOptions(config, env)
|
|
116
165
|
: undefined;
|
|
166
|
+
const exportResources = exportOptions
|
|
167
|
+
? resolveExportResourceAttributes(resourceAttributes, env, redact)
|
|
168
|
+
: undefined;
|
|
169
|
+
if (exportResources) {
|
|
170
|
+
Object.defineProperty(
|
|
171
|
+
exportResources.attributes as ResourceAttributesWithSanitizer,
|
|
172
|
+
TRACE_RESOURCE_ATTRIBUTE_SANITIZER,
|
|
173
|
+
{ value: exportResources.sanitize },
|
|
174
|
+
);
|
|
175
|
+
}
|
|
117
176
|
|
|
118
177
|
return {
|
|
119
178
|
maxSpans: resolved.maxSpans,
|
|
120
179
|
onSpan,
|
|
180
|
+
...(redact ? { redact } : {}),
|
|
121
181
|
...(exportOptions
|
|
122
182
|
? {
|
|
123
183
|
exportOptions,
|
|
124
|
-
resourceAttributes:
|
|
125
|
-
sanitizeSpanForExport: (span: Span) => sanitizeSpanForOutput(span),
|
|
184
|
+
resourceAttributes: exportResources?.attributes,
|
|
185
|
+
sanitizeSpanForExport: (span: Span) => sanitizeSpanForOutput(span, undefined, redact),
|
|
126
186
|
}
|
|
127
187
|
: {}),
|
|
128
188
|
};
|
package/src/testing/run.ts
CHANGED
|
@@ -2,7 +2,13 @@ import { describe, expect, it } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import { ProviderError } from "../errors.js";
|
|
4
4
|
import { createProviderCache } from "../runtime/cache.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createTestHandleContext } from "../runtime/handle.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Stable connection scope for the local test harness so bound handles (drafts,
|
|
9
|
+
* default cursors) can be exercised exactly as in a connected production call.
|
|
10
|
+
*/
|
|
11
|
+
const STANDARD_TEST_CONNECTION_ID = "standard-test-connection";
|
|
6
12
|
import { createMemoryProviderRuntimeState } from "../runtime/state.js";
|
|
7
13
|
import { createUnsupportedOcrClient } from "../runtime/ocr.js";
|
|
8
14
|
import { createUnsupportedSttClient } from "../runtime/stt.js";
|
|
@@ -319,7 +325,7 @@ function createUpstreamContext(
|
|
|
319
325
|
getAccessToken: () => undefined,
|
|
320
326
|
getScopes: () => [],
|
|
321
327
|
};
|
|
322
|
-
const request = { headers: {} };
|
|
328
|
+
const request = { headers: {}, connectionId: STANDARD_TEST_CONNECTION_ID };
|
|
323
329
|
const state = createMemoryProviderRuntimeState();
|
|
324
330
|
const nativeEgress = provider.native
|
|
325
331
|
? createNativeEgressAuthorization({ egress: provider.native.network })
|
|
@@ -578,10 +584,9 @@ function createUpstreamContext(
|
|
|
578
584
|
resolver: {
|
|
579
585
|
solve: async () => unsupported("ctx.resolver.solve"),
|
|
580
586
|
},
|
|
581
|
-
|
|
587
|
+
handle: createTestHandleContext({
|
|
582
588
|
providerId: `standard-test-${operationName}`,
|
|
583
589
|
request,
|
|
584
|
-
credential,
|
|
585
590
|
state,
|
|
586
591
|
}),
|
|
587
592
|
} as ProviderContext;
|
|
@@ -622,7 +627,7 @@ export function createSnapshotContext(rawFixture: unknown): ProviderContext {
|
|
|
622
627
|
getAccessToken: () => undefined,
|
|
623
628
|
getScopes: () => [],
|
|
624
629
|
};
|
|
625
|
-
const request = { headers: {} };
|
|
630
|
+
const request = { headers: {}, connectionId: STANDARD_TEST_CONNECTION_ID };
|
|
626
631
|
const state = createMemoryProviderRuntimeState();
|
|
627
632
|
const streamCaptureGroup = findStreamCaptureGroup(rawFixture);
|
|
628
633
|
let nextCaptureItem = 0;
|
|
@@ -709,10 +714,9 @@ export function createSnapshotContext(rawFixture: unknown): ProviderContext {
|
|
|
709
714
|
resolver: {
|
|
710
715
|
solve: async () => unsupported("ctx.resolver.solve"),
|
|
711
716
|
},
|
|
712
|
-
|
|
717
|
+
handle: createTestHandleContext({
|
|
713
718
|
providerId: "standard-test",
|
|
714
719
|
request,
|
|
715
|
-
credential,
|
|
716
720
|
state,
|
|
717
721
|
}),
|
|
718
722
|
} satisfies Omit<ProviderContext, "native"> as unknown as ProviderContext;
|
|
@@ -4,20 +4,33 @@ import {
|
|
|
4
4
|
REDACTED_FIXTURE_VALUE,
|
|
5
5
|
sanitizeDiagnosticText,
|
|
6
6
|
} from "./fixture-sanitization.js";
|
|
7
|
+
import {
|
|
8
|
+
type DiagnosticRedactor,
|
|
9
|
+
diagnosticStructuredRedactor,
|
|
10
|
+
redactDiagnosticAttributeKey,
|
|
11
|
+
copyDiagnosticAttributeTypes,
|
|
12
|
+
isDiagnosticBigintAttribute,
|
|
13
|
+
redactedKeyAllocator,
|
|
14
|
+
isRedactedKey,
|
|
15
|
+
redactCriticalDiagnosticText,
|
|
16
|
+
diagnosticAttributeRedactor,
|
|
17
|
+
REDACTION_FAILED,
|
|
18
|
+
redactDiagnosticText,
|
|
19
|
+
} from "./runtime/diagnostic-redactor.js";
|
|
7
20
|
import type { TraceSpan } from "./types.js";
|
|
8
21
|
|
|
9
22
|
const MAX_TRACE_TEXT_LENGTH = 300;
|
|
10
23
|
|
|
11
|
-
function sanitizeTraceText(value: string): string {
|
|
12
|
-
const encoded = sanitizeDiagnosticText(value);
|
|
24
|
+
function sanitizeTraceText(value: string, redact?: DiagnosticRedactor): string {
|
|
25
|
+
const encoded = sanitizeDiagnosticText(redactDiagnosticText(value, redact));
|
|
13
26
|
return encoded.length > MAX_TRACE_TEXT_LENGTH
|
|
14
27
|
? `${encoded.slice(0, MAX_TRACE_TEXT_LENGTH)}… [truncated]`
|
|
15
28
|
: encoded;
|
|
16
29
|
}
|
|
17
30
|
|
|
18
31
|
/** Span names are SDK-authored identifiers; retain them while preventing log injection. */
|
|
19
|
-
export function sanitizeSpanNameForOutput(value: string): string {
|
|
20
|
-
const encoded = encodeDiagnosticControls(value);
|
|
32
|
+
export function sanitizeSpanNameForOutput(value: string, redact?: DiagnosticRedactor): string {
|
|
33
|
+
const encoded = encodeDiagnosticControls(redactDiagnosticText(value, redact));
|
|
21
34
|
return encoded.length > MAX_TRACE_TEXT_LENGTH
|
|
22
35
|
? `${encoded.slice(0, MAX_TRACE_TEXT_LENGTH)}… [truncated]`
|
|
23
36
|
: encoded;
|
|
@@ -25,39 +38,73 @@ export function sanitizeSpanNameForOutput(value: string): string {
|
|
|
25
38
|
|
|
26
39
|
export function sanitizeTraceAttributes(
|
|
27
40
|
attributes: Record<string, unknown>,
|
|
41
|
+
redact?: DiagnosticRedactor,
|
|
42
|
+
reservedKeys: Iterable<string> = [],
|
|
28
43
|
): Record<string, string | number | boolean> {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const nextKey = redactedKeyAllocator([...Object.keys(attributes), ...reservedKeys]);
|
|
45
|
+
return copyDiagnosticAttributeTypes(
|
|
46
|
+
attributes,
|
|
47
|
+
Object.fromEntries(
|
|
48
|
+
Object.entries(attributes).map(([key, value]) => {
|
|
49
|
+
const redactedKey = isRedactedKey(key) ? key : redactDiagnosticAttributeKey(key, redact);
|
|
50
|
+
const keyChanged = redactedKey !== key;
|
|
51
|
+
const sanitizedKey = keyChanged ? nextKey() : sanitizeTraceText(key);
|
|
52
|
+
const valueRedactor =
|
|
53
|
+
typeof value === "bigint" || isDiagnosticBigintAttribute(attributes, key)
|
|
54
|
+
? diagnosticStructuredRedactor(redact)
|
|
55
|
+
: diagnosticAttributeRedactor(key, redact);
|
|
56
|
+
const sanitizedValue = keyChanged
|
|
57
|
+
? redactedKey === REDACTION_FAILED
|
|
58
|
+
? REDACTION_FAILED
|
|
59
|
+
: REDACTED_FIXTURE_VALUE
|
|
60
|
+
: isSensitiveFixtureKey(key)
|
|
61
|
+
? REDACTED_FIXTURE_VALUE
|
|
62
|
+
: typeof value === "bigint" || isDiagnosticBigintAttribute(attributes, key)
|
|
63
|
+
? String(value).length < 8
|
|
64
|
+
? String(value)
|
|
65
|
+
: sanitizeTraceText(String(value), valueRedactor)
|
|
66
|
+
: typeof value === "string"
|
|
67
|
+
? sanitizeTraceText(value, valueRedactor)
|
|
68
|
+
: typeof value === "number" || typeof value === "boolean"
|
|
69
|
+
? (() => {
|
|
70
|
+
if (String(value).length < 8) return value;
|
|
71
|
+
const redacted = redactDiagnosticText(
|
|
72
|
+
String(value),
|
|
73
|
+
diagnosticStructuredRedactor(valueRedactor),
|
|
74
|
+
);
|
|
75
|
+
return redacted === String(value) ? value : sanitizeTraceText(redacted);
|
|
76
|
+
})()
|
|
77
|
+
: sanitizeTraceText(String(value), valueRedactor);
|
|
78
|
+
return [sanitizedKey, sanitizedValue];
|
|
79
|
+
}),
|
|
80
|
+
),
|
|
40
81
|
);
|
|
41
82
|
}
|
|
42
83
|
|
|
43
84
|
export function sanitizeSpanForOutput(
|
|
44
85
|
span: TraceSpan,
|
|
45
86
|
additionalAttributes?: Record<string, string>,
|
|
87
|
+
redact?: DiagnosticRedactor,
|
|
46
88
|
): TraceSpan {
|
|
47
89
|
// Keep this schema explicit so future fields are not silently added to a
|
|
48
90
|
// process output path before their trust boundary has been reviewed.
|
|
49
91
|
return {
|
|
50
|
-
id: span.id,
|
|
51
|
-
name: sanitizeSpanNameForOutput(span.name),
|
|
92
|
+
id: redactCriticalDiagnosticText(span.id, redact),
|
|
93
|
+
name: sanitizeSpanNameForOutput(redactCriticalDiagnosticText(span.name, redact)),
|
|
52
94
|
startedAt: span.startedAt,
|
|
53
95
|
endedAt: span.endedAt,
|
|
54
96
|
duration_ms: span.duration_ms,
|
|
55
97
|
status: span.status,
|
|
56
|
-
attributes: sanitizeTraceAttributes(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
98
|
+
attributes: sanitizeTraceAttributes(
|
|
99
|
+
copyDiagnosticAttributeTypes(span.attributes, {
|
|
100
|
+
...span.attributes,
|
|
101
|
+
...(additionalAttributes ?? {}),
|
|
102
|
+
}),
|
|
103
|
+
redact,
|
|
104
|
+
),
|
|
105
|
+
...(span.error !== undefined ? { error: sanitizeTraceText(span.error, redact) } : {}),
|
|
106
|
+
...(span.parentId !== undefined
|
|
107
|
+
? { parentId: redactCriticalDiagnosticText(span.parentId, redact) }
|
|
108
|
+
: {}),
|
|
62
109
|
};
|
|
63
110
|
}
|
package/src/types.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
|
|
|
@@ -1997,140 +1998,6 @@ export interface ProviderRequestContext {
|
|
|
1997
1998
|
headers: Record<string, string>;
|
|
1998
1999
|
}
|
|
1999
2000
|
|
|
2000
|
-
export interface ProviderChoiceBindingOptions {
|
|
2001
|
-
connection?: boolean;
|
|
2002
|
-
credentialKeys?: readonly string[];
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
export type ProviderChoiceConsumeMode = "never" | "on-parse" | "explicit";
|
|
2006
|
-
|
|
2007
|
-
export type ProviderChoiceConsumeResult =
|
|
2008
|
-
| { readonly status: "consumed" }
|
|
2009
|
-
| { readonly status: "already-consumed" }
|
|
2010
|
-
| { readonly status: "unsupported" };
|
|
2011
|
-
|
|
2012
|
-
export type ProviderChoiceExplicitParseResult =
|
|
2013
|
-
| {
|
|
2014
|
-
readonly status: "active";
|
|
2015
|
-
readonly payload: Record<string, unknown>;
|
|
2016
|
-
/** Stable, opaque key for provider-owned idempotency records. */
|
|
2017
|
-
readonly replayKey: string;
|
|
2018
|
-
/** Atomically claims a word token. Inline tokens report unsupported. */
|
|
2019
|
-
consume(): Promise<ProviderChoiceConsumeResult>;
|
|
2020
|
-
}
|
|
2021
|
-
| {
|
|
2022
|
-
readonly status: "consumed";
|
|
2023
|
-
/** Use this key to read the provider-owned result before returning an error. */
|
|
2024
|
-
readonly replayKey: string;
|
|
2025
|
-
};
|
|
2026
|
-
|
|
2027
|
-
export type ProviderChoiceStorageOptions =
|
|
2028
|
-
| {
|
|
2029
|
-
readonly mode: "inline";
|
|
2030
|
-
}
|
|
2031
|
-
| {
|
|
2032
|
-
readonly mode: "server";
|
|
2033
|
-
readonly namespace: string;
|
|
2034
|
-
readonly state?: ProviderRuntimeState;
|
|
2035
|
-
readonly ttl?: ProviderStateDurationString;
|
|
2036
|
-
readonly maxEntries: number;
|
|
2037
|
-
readonly maxValueBytes: number;
|
|
2038
|
-
readonly unavailable?: "reject";
|
|
2039
|
-
}
|
|
2040
|
-
| {
|
|
2041
|
-
readonly mode: "auto";
|
|
2042
|
-
readonly namespace: string;
|
|
2043
|
-
readonly state?: ProviderRuntimeState;
|
|
2044
|
-
readonly ttl?: ProviderStateDurationString;
|
|
2045
|
-
readonly maxInlineBytes: number;
|
|
2046
|
-
readonly maxEntries: number;
|
|
2047
|
-
readonly maxValueBytes: number;
|
|
2048
|
-
readonly unavailable?: "reject";
|
|
2049
|
-
};
|
|
2050
|
-
|
|
2051
|
-
export interface ProviderChoiceIssueOptions<
|
|
2052
|
-
TPayload extends Record<string, unknown>,
|
|
2053
|
-
> {
|
|
2054
|
-
prefix: string;
|
|
2055
|
-
purpose: string;
|
|
2056
|
-
payload: TPayload;
|
|
2057
|
-
ttlMs: number;
|
|
2058
|
-
nowMs?: number;
|
|
2059
|
-
bind?: ProviderChoiceBindingOptions;
|
|
2060
|
-
/** Server storage only: standard emits four words; high emits five. */
|
|
2061
|
-
strength?: "standard" | "high";
|
|
2062
|
-
storage?: ProviderChoiceStorageOptions;
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
export interface ProviderChoiceParseOptions {
|
|
2066
|
-
token: string;
|
|
2067
|
-
prefix: string;
|
|
2068
|
-
purpose: string;
|
|
2069
|
-
ttlMs?: number;
|
|
2070
|
-
nowMs?: number;
|
|
2071
|
-
futureToleranceMs?: number;
|
|
2072
|
-
bind?: ProviderChoiceBindingOptions;
|
|
2073
|
-
storage?: ProviderChoiceStorageOptions;
|
|
2074
|
-
/** Defaults to never, preserving reusable choice-token parse semantics. */
|
|
2075
|
-
consume?: ProviderChoiceConsumeMode;
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
export interface ProviderChoiceContext {
|
|
2079
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2080
|
-
options: ProviderChoiceIssueOptions<TPayload> & {
|
|
2081
|
-
readonly storage?: { readonly mode: "inline" };
|
|
2082
|
-
},
|
|
2083
|
-
): string;
|
|
2084
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2085
|
-
options: ProviderChoiceIssueOptions<TPayload> & {
|
|
2086
|
-
readonly storage: Extract<
|
|
2087
|
-
ProviderChoiceStorageOptions,
|
|
2088
|
-
{ readonly mode: "server" }
|
|
2089
|
-
>;
|
|
2090
|
-
},
|
|
2091
|
-
): Promise<string>;
|
|
2092
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2093
|
-
options: ProviderChoiceIssueOptions<TPayload> & {
|
|
2094
|
-
readonly storage: Extract<
|
|
2095
|
-
ProviderChoiceStorageOptions,
|
|
2096
|
-
{ readonly mode: "auto" }
|
|
2097
|
-
>;
|
|
2098
|
-
},
|
|
2099
|
-
): string | Promise<string>;
|
|
2100
|
-
issue<TPayload extends Record<string, unknown>>(
|
|
2101
|
-
options: ProviderChoiceIssueOptions<TPayload>,
|
|
2102
|
-
): string | Promise<string>;
|
|
2103
|
-
parse(
|
|
2104
|
-
options: ProviderChoiceParseOptions & { readonly consume: "explicit" },
|
|
2105
|
-
): Promise<ProviderChoiceExplicitParseResult>;
|
|
2106
|
-
parse(
|
|
2107
|
-
options: ProviderChoiceParseOptions & {
|
|
2108
|
-
readonly storage?: { readonly mode: "inline" };
|
|
2109
|
-
},
|
|
2110
|
-
): Record<string, unknown>;
|
|
2111
|
-
parse(
|
|
2112
|
-
options: ProviderChoiceParseOptions & {
|
|
2113
|
-
readonly storage: Extract<
|
|
2114
|
-
ProviderChoiceStorageOptions,
|
|
2115
|
-
{ readonly mode: "server" }
|
|
2116
|
-
>;
|
|
2117
|
-
},
|
|
2118
|
-
): Promise<Record<string, unknown>>;
|
|
2119
|
-
parse(
|
|
2120
|
-
options: ProviderChoiceParseOptions & {
|
|
2121
|
-
readonly storage: Extract<
|
|
2122
|
-
ProviderChoiceStorageOptions,
|
|
2123
|
-
{ readonly mode: "auto" }
|
|
2124
|
-
>;
|
|
2125
|
-
},
|
|
2126
|
-
): Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
2127
|
-
parse(
|
|
2128
|
-
options: ProviderChoiceParseOptions,
|
|
2129
|
-
):
|
|
2130
|
-
| Record<string, unknown>
|
|
2131
|
-
| Promise<Record<string, unknown> | ProviderChoiceExplicitParseResult>;
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2134
2001
|
export interface ContextScratchpad {
|
|
2135
2002
|
get(key: string): unknown;
|
|
2136
2003
|
set(key: string, value: unknown): void;
|
|
@@ -2402,8 +2269,8 @@ export type ProviderContext<TConfig = Record<string, unknown>> = {
|
|
|
2402
2269
|
& ("ocr" extends keyof TConfig ? { ocr: OcrContext } : Record<never, never>)
|
|
2403
2270
|
& ("stt" extends keyof TConfig ? { stt: SttContext } : Record<never, never>)
|
|
2404
2271
|
& ("resolver" extends keyof TConfig ? { resolver: ResolverContext } : Record<never, never>)
|
|
2405
|
-
& ("
|
|
2406
|
-
? {
|
|
2272
|
+
& ("handle" extends keyof TConfig
|
|
2273
|
+
? { handle: HandleContext }
|
|
2407
2274
|
: Record<never, never>);
|
|
2408
2275
|
|
|
2409
2276
|
/** Backwards-compatible name for a declaration-derived provider context. */
|
|
@@ -2588,7 +2455,7 @@ export interface ProviderDefinition<TContext = ProviderContext> {
|
|
|
2588
2455
|
engine: BrowserEngine;
|
|
2589
2456
|
};
|
|
2590
2457
|
auth?: AuthConfig;
|
|
2591
|
-
|
|
2458
|
+
handle?: readonly HandleKind[];
|
|
2592
2459
|
reviewed?: ProviderReviewed;
|
|
2593
2460
|
access?: ProviderAccessConfig;
|
|
2594
2461
|
secrets?: ProviderSecretDeclaration[];
|
package/dist/choice-token.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export type ProviderChoiceTokenPayload = Record<string, unknown>;
|
|
2
|
-
export type ProviderChoiceTokenErrorReason = "invalid_shape" | "invalid_signature" | "invalid_payload" | "invalid_binding" | "stale";
|
|
3
|
-
export declare class ProviderChoiceTokenError extends Error {
|
|
4
|
-
readonly reason: ProviderChoiceTokenErrorReason;
|
|
5
|
-
constructor(reason: ProviderChoiceTokenErrorReason, message: string);
|
|
6
|
-
}
|
|
7
|
-
export interface CreateProviderChoiceTokenOptions<TPayload extends ProviderChoiceTokenPayload> {
|
|
8
|
-
prefix: string;
|
|
9
|
-
payload: TPayload;
|
|
10
|
-
secret: string;
|
|
11
|
-
}
|
|
12
|
-
export interface ParseProviderChoiceTokenOptions {
|
|
13
|
-
token: string;
|
|
14
|
-
prefix: string;
|
|
15
|
-
secret: string;
|
|
16
|
-
}
|
|
17
|
-
export interface FreshProviderChoiceIssuedAtOptions {
|
|
18
|
-
ttlMs: number;
|
|
19
|
-
nowMs?: number;
|
|
20
|
-
futureToleranceMs?: number;
|
|
21
|
-
}
|
|
22
|
-
export declare function createProviderChoiceToken<TPayload extends ProviderChoiceTokenPayload>(options: CreateProviderChoiceTokenOptions<TPayload>): string;
|
|
23
|
-
export declare function parseProviderChoiceToken(options: ParseProviderChoiceTokenOptions): ProviderChoiceTokenPayload;
|
|
24
|
-
export declare function assertFreshProviderChoiceIssuedAt(issuedAtMs: unknown, options: FreshProviderChoiceIssuedAtOptions): number;
|
package/dist/choice-token.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, createHash, createHmac, randomBytes, timingSafeEqual, } from "node:crypto";
|
|
2
|
-
export class ProviderChoiceTokenError extends Error {
|
|
3
|
-
reason;
|
|
4
|
-
constructor(reason, message) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.name = "ProviderChoiceTokenError";
|
|
7
|
-
this.reason = reason;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
export function createProviderChoiceToken(options) {
|
|
11
|
-
const iv = randomBytes(12);
|
|
12
|
-
const cipher = createCipheriv("aes-256-gcm", choiceEncryptionKey(options.secret), iv);
|
|
13
|
-
const encryptedPayload = Buffer.concat([
|
|
14
|
-
cipher.update(JSON.stringify(options.payload), "utf8"),
|
|
15
|
-
cipher.final(),
|
|
16
|
-
]).toString("base64url");
|
|
17
|
-
const authTag = cipher.getAuthTag().toString("base64url");
|
|
18
|
-
const encodedIv = iv.toString("base64url");
|
|
19
|
-
const signature = signProviderChoiceTokenBody(`${options.prefix}.${encodedIv}.${encryptedPayload}.${authTag}`, options.secret);
|
|
20
|
-
return `${options.prefix}.${encodedIv}.${encryptedPayload}.${authTag}.${signature}`;
|
|
21
|
-
}
|
|
22
|
-
export function parseProviderChoiceToken(options) {
|
|
23
|
-
const [actualPrefix, encodedIv, encryptedPayload, authTag, signature, ...extra] = options.token.split(".");
|
|
24
|
-
if (actualPrefix !== options.prefix ||
|
|
25
|
-
!encodedIv ||
|
|
26
|
-
!encryptedPayload ||
|
|
27
|
-
!authTag ||
|
|
28
|
-
!signature ||
|
|
29
|
-
extra.length > 0) {
|
|
30
|
-
throw new ProviderChoiceTokenError("invalid_shape", "Provider choice token shape is invalid.");
|
|
31
|
-
}
|
|
32
|
-
const signedBody = `${options.prefix}.${encodedIv}.${encryptedPayload}.${authTag}`;
|
|
33
|
-
const expectedSignature = signProviderChoiceTokenBody(signedBody, options.secret);
|
|
34
|
-
const actualBuffer = Buffer.from(signature);
|
|
35
|
-
const expectedBuffer = Buffer.from(expectedSignature);
|
|
36
|
-
if (actualBuffer.length !== expectedBuffer.length ||
|
|
37
|
-
!timingSafeEqual(actualBuffer, expectedBuffer)) {
|
|
38
|
-
throw new ProviderChoiceTokenError("invalid_signature", "Provider choice token signature is invalid.");
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const decipher = createDecipheriv("aes-256-gcm", choiceEncryptionKey(options.secret), Buffer.from(encodedIv, "base64url"));
|
|
42
|
-
decipher.setAuthTag(Buffer.from(authTag, "base64url"));
|
|
43
|
-
const decrypted = Buffer.concat([
|
|
44
|
-
decipher.update(Buffer.from(encryptedPayload, "base64url")),
|
|
45
|
-
decipher.final(),
|
|
46
|
-
]).toString("utf8");
|
|
47
|
-
const parsed = JSON.parse(decrypted);
|
|
48
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
49
|
-
throw new Error("payload is not an object");
|
|
50
|
-
}
|
|
51
|
-
return Object.fromEntries(Object.entries(parsed));
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
throw new ProviderChoiceTokenError("invalid_payload", "Provider choice token payload is invalid.");
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
export function assertFreshProviderChoiceIssuedAt(issuedAtMs, options) {
|
|
58
|
-
const parsed = typeof issuedAtMs === "number" ? issuedAtMs : Number(issuedAtMs);
|
|
59
|
-
const nowMs = options.nowMs ?? Date.now();
|
|
60
|
-
const futureToleranceMs = options.futureToleranceMs ?? 30_000;
|
|
61
|
-
if (!Number.isFinite(parsed) ||
|
|
62
|
-
parsed <= 0 ||
|
|
63
|
-
nowMs - parsed > options.ttlMs ||
|
|
64
|
-
parsed - nowMs > futureToleranceMs) {
|
|
65
|
-
throw new ProviderChoiceTokenError("stale", "Provider choice token is stale.");
|
|
66
|
-
}
|
|
67
|
-
return parsed;
|
|
68
|
-
}
|
|
69
|
-
function choiceEncryptionKey(secret) {
|
|
70
|
-
return createHash("sha256").update(secret).digest();
|
|
71
|
-
}
|
|
72
|
-
function signProviderChoiceTokenBody(body, secret) {
|
|
73
|
-
return createHmac("sha256", secret).update(body).digest("base64url");
|
|
74
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare const CHOICE_WORDLIST_SIZE = 1296;
|
|
2
|
-
export declare const CHOICE_WORD_ENTROPY_BITS: number;
|
|
3
|
-
export declare const STANDARD_CHOICE_WORD_COUNT = 4;
|
|
4
|
-
export declare const HIGH_CHOICE_WORD_COUNT = 5;
|
|
5
|
-
export declare const STANDARD_CHOICE_ENTROPY_BITS: number;
|
|
6
|
-
export declare const HIGH_CHOICE_ENTROPY_BITS: number;
|
|
7
|
-
export declare const EFF_SHORT_WORDLIST_2: readonly string[];
|
|
8
|
-
export declare function choiceWordAt(index: number): string;
|
|
9
|
-
export declare function isChoiceWord(value: string): boolean;
|
package/dist/runtime/choice.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { CredentialContext, EnvContext, ProviderChoiceConsumeMode, ProviderChoiceContext, ProviderRequestContext, ProviderRuntimeState } from "../types.js";
|
|
2
|
-
export declare const PROVIDER_RUNTIME_CHOICE_TOKEN_MASTER_SECRET_ENV = "APIFUSE__PROVIDER_RUNTIME__CHOICE_TOKEN_MASTER_SECRET";
|
|
3
|
-
export type ProviderChoiceTelemetryEvent = {
|
|
4
|
-
readonly providerId: string;
|
|
5
|
-
readonly purpose: string;
|
|
6
|
-
readonly operation: "parse" | "consume";
|
|
7
|
-
readonly format: "word" | "legacy";
|
|
8
|
-
readonly outcome: "success" | "not-found" | "invalid" | "unsupported" | "error";
|
|
9
|
-
readonly consumeMode: ProviderChoiceConsumeMode;
|
|
10
|
-
readonly consumed: boolean;
|
|
11
|
-
readonly replay: boolean;
|
|
12
|
-
};
|
|
13
|
-
export type CreateProviderChoiceContextOptions = {
|
|
14
|
-
readonly providerId: string;
|
|
15
|
-
readonly env?: EnvContext;
|
|
16
|
-
readonly request?: ProviderRequestContext;
|
|
17
|
-
readonly credential?: CredentialContext;
|
|
18
|
-
readonly state?: ProviderRuntimeState;
|
|
19
|
-
readonly masterSecret?: string;
|
|
20
|
-
readonly kid?: string;
|
|
21
|
-
/** Receives allowlisted metadata only; token and payload values are never included. */
|
|
22
|
-
readonly onTelemetry?: (event: ProviderChoiceTelemetryEvent) => void;
|
|
23
|
-
};
|
|
24
|
-
export declare function createProviderChoiceContext(options: CreateProviderChoiceContextOptions): ProviderChoiceContext;
|
|
25
|
-
export declare function createTestProviderChoiceContext(options: Omit<CreateProviderChoiceContextOptions, "masterSecret"> & {
|
|
26
|
-
readonly masterSecret?: string;
|
|
27
|
-
}): ProviderChoiceContext;
|